You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

树莓派4中Python3.9 Thonny适配Python3.11编写的OpenCV代码问题

树莓派4 Python3.9环境适配模拟仪表识别代码方案

错误根源分析

  1. 摄像头读取失败:树莓派原生摄像头与OpenCV默认GStreamer后端兼容性差,导致内存分配失败,无法捕获帧
  2. 文件读取错误:摄像头未成功捕获帧,目标图片文件未生成,cv2.imread返回None,触发'NoneType' object has no attribute 'shape'异常
  3. 代码逻辑漏洞:存在循环嵌套错误、资源未释放等问题

适配步骤与修改代码

1. 摄像头读取适配(二选一)

方案A:调整OpenCV VideoCapture参数

指定V4L2后端规避GStreamer兼容性问题:

# 替换原cam = cv2.VideoCapture(0)为:
cam = cv2.VideoCapture(0, cv2.CAP_V4L2)
# 按需设置分辨率
cam.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cam.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)

方案B:改用Picamera2(推荐,树莓派原生支持)

先安装依赖:

sudo apt install python3-picamera2

替换摄像头读取逻辑:

from picamera2 import Picamera2

def take_measure(...):
    # 初始化Picamera2
    picam2 = Picamera2()
    config = picam2.create_preview_configuration(main={"format": 'RGB888', "size": (640, 480)})
    picam2.configure(config)
    picam2.start()
    
    cv2.namedWindow("test")
    frame_bgr = None
    while True:
        frame = picam2.capture_array()
        frame_bgr = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
        cv2.imshow("test", frame_bgr)
        k = cv2.waitKey(1)
        if k % 256 == 27:
            print("Escape hit, closing...")
            break
        elif k % 256 == 32:
            cv2.imwrite("analog_gauge_30.png", frame_bgr)
            print("analog_gauge_30.png written!")
            break
    
    picam2.stop()
    cv2.destroyWindow("test")

2. 完善错误处理

添加空值判断,避免None对象操作:

if frame_bgr is None:
    print("Failed to capture valid frame")
    return None, None

img = frame_bgr
height, width = img.shape[:2]

3. 修复代码逻辑漏洞

  • 拆分嵌套循环,修正刻度坐标计算逻辑:
# 计算刻度线起点
for i in range(0, interval):
    for j in range(0, 2):
        if j % 2 == 0:
            p1[i][j] = x + 0.9 * r * np.cos(separation * i * np.pi / 180)
        else:
            p1[i][j] = y + 0.9 * r * np.sin(separation * i * np.pi / 180)

text_offset_x = 10
text_offset_y = 5
# 计算刻度线终点和文本位置
for i in range(0, interval):
    for j in range(0, 2):
        if j % 2 == 0:
            p2[i][j] = x + r * np.cos(separation * i * np.pi / 180)
            p_text[i][j] = x - text_offset_x + 1.2 * r * np.cos((separation) * (i + 9) * np.pi / 180)
        else:
            p2[i][j] = y + r * np.sin(separation * i * np.pi / 180)
            p_text[i][j] = y + text_offset_y + 1.2 * r * np.sin((separation) * (i + 9) * np.pi / 180)
  • 添加资源释放逻辑,在程序退出时销毁所有窗口:
cv2.destroyAllWindows()

4. 树莓派环境配置

  • 启用摄像头:运行sudo raspi-config,选择Interface Options -> Camera,启用后重启设备
  • 安装依赖库:
sudo apt install python3-opencv python3-numpy

完整适配后代码(Picamera2版本)

import cv2
import numpy as np
from picamera2 import Picamera2

def avg_circles(circles, b):
    avg_x = 0
    avg_y = 0
    avg_r = 0
    for i in range(b):
        avg_x = avg_x + circles[0][i][0]
        avg_y = avg_y + circles[0][i][1]
        avg_r = avg_r + circles[0][i][2]
    avg_x = int(avg_x / (b))
    avg_y = int(avg_y / (b))
    avg_r = int(avg_r / (b))
    return avg_x, avg_y, avg_r

def dist_2_pts(x1, y1, x2, y2):
    return np.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)

def take_measure(threshold_img, threshold_ln, minLineLength, maxLineGap, diff1LowerBound, diff1UpperBound, diff2LowerBound, diff2UpperBound):
    picam2 = Picamera2()
    config = picam2.create_preview_configuration(main={"format": 'RGB888', "size": (640, 480)})
    picam2.configure(config)
    picam2.start()
    
    cv2.namedWindow("test")
    frame_bgr = None
    while True:
        frame = picam2.capture_array()
        frame_bgr = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
        cv2.imshow("test", frame_bgr)
        k = cv2.waitKey(1)
        if k % 256 == 27:
            print("Escape hit, closing...")
            break
        elif k % 256 == 32:
            cv2.imwrite("analog_gauge_30.png", frame_bgr)
            print("analog_gauge_30.png written!")
            break
    
    picam2.stop()
    cv2.destroyWindow("test")
    
    if frame_bgr is None:
        print("Failed to capture valid frame")
        return None, None
    
    img = frame_bgr
    height, width = img.shape[:2]
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, 20)
    
    if circles is not None:
        a, b, c = circles.shape
        x, y, r = avg_circles(circles, b)
        cv2.circle(img, (x, y), r, (0, 255, 0), 3, cv2.LINE_AA)
        cv2.circle(img, (x, y), 2, (0, 255, 0), 3, cv2.LINE_AA)
        
        min_angle = 0
        max_angle = 360
        min_value = 0
        max_value = 16
        separation = 10
        interval = int(360 / separation)
        p1 = np.zeros((interval, 2))
        p2 = np.zeros((interval, 2))
        p_text = np.zeros((interval, 2))
        
        # 计算刻度线起点
        for i in range(0, interval):
            for j in range(0, 2):
                if j % 2 == 0:
                    p1[i][j] = x + 0.9 * r * np.cos(separation * i * np.pi / 180)
                else:
                    p1[i][j] = y + 0.9 * r * np.sin(separation * i * np.pi / 180)
        
        text_offset_x = 10
        text_offset_y = 5
        # 计算刻度线终点和文本位置
        for i in range(0, interval):
            for j in range(0, 2):
                if j % 2 == 0:
                    p2[i][j] = x + r * np.cos(separation * i * np.pi / 180)
                    p_text[i][j] = x - text_offset_x + 1.2 * r * np.cos((separation) * (i + 9) * np.pi / 180)
                else:
                    p2[i][j] = y + r * np.sin(separation * i * np.pi / 180)
                    p_text[i][j] = y + text_offset_y + 1.2 * r * np.sin((separation) * (i + 9) * np.pi / 180)
        
        # 绘制刻度线和文本
        for i in range(0, interval):
            cv2.line(img, (int(p1[i][0]), int(p1[i][1])), (int(p2[i][0]), int(p2[i][1])), (0, 255, 0), 2)
            cv2.putText(img, '%s' % (int(i * separation)), (int(p_text[i][0]), int(p_text[i][1])), cv2.FONT_HERSHEY_SIMPLEX, 0.3, (255, 0, 0), 1, cv2.LINE_AA)
        
        cv2.putText(img, "Gauge OK!", (50, 75), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2, cv2.LINE_AA)
        gray3 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        maxValue = 255
        th, dst2 = cv2.threshold(gray3, threshold_img, maxValue, cv2.THRESH_BINARY_INV)
        
        dst2 = cv2.medianBlur(dst2, 5)
        dst2 = cv2.Canny(dst2, 50, 150)
        dst2 = cv2.GaussianBlur(dst2, (5, 5), 0)
        
        in_loop = 0
        lines = cv2.HoughLinesP(image=dst2, rho=3, theta=np.pi / 180, threshold=threshold_ln, minLineLength=minLineLength, maxLineGap=maxLineGap)
        final_line_list = []
        
        if lines is not None:
            for i in range(0, len(lines)):
                for x1, y1, x2, y2 in lines[i]:
                    diff1 = dist_2_pts(x, y, x1, y1)
                    diff2 = dist_2_pts(x, y, x2, y2)
                    if diff1 > diff2:
                        diff1, diff2 = diff2, diff1
                    if ((diff1 < diff1UpperBound * r) and (diff1 > diff1LowerBound * r) and (diff2 < diff2UpperBound * r) and (diff2 > diff2LowerBound * r)):
                        final_line_list.append([x1, y1, x2, y2])
                        in_loop = 1
        
        if in_loop == 1:
            x1, y1, x2, y2 = final_line_list[0]
            cv2.line(img, (x1, y1), (x2, y2), (0, 255, 255), 2)
            
            dist_pt_0 = dist_2_pts(x, y, x1, y1)
            dist_pt_1 = dist_2_pts(x, y, x2, y2)
            
            if dist_pt_0 > dist_pt_1:
                x_angle = x1 - x
                y_angle = y - y1
            else:
                x_angle = x2 - x
                y_angle = y - y2
            
            res = np.arctan2(y_angle, x_angle)
            res = np.rad2deg(res)
            
            # 修正角度计算逻辑
            if x_angle > 0 and y_angle > 0:
                final_angle = 270 - res
            elif x_angle < 0 and y_angle > 0:
                final_angle = 90 - res
            elif x_angle < 0 and y_angle < 0:
                final_angle = 90 - res
            elif x_angle > 0 and y_angle < 0:
                final_angle = 270 - res
            else:
                final_angle = 0
            
            # 转换为仪表数值
            old_min = float(min_angle)
            old_max = float(max_angle)
            new_min = float(min_value)
            new_max = float(max_value)
            old_value = final_angle
            old_range = old_max - old_min
            new_range = new_max - new_min
            new_value = (((old_value - old_min) * new_range) / old_range) + new_min - 1.4
            
            cv2.putText(img, "Indicator OK!", (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2, cv2.LINE_AA)
            cv2.putText(img, f"{new_value:.1f}", (50, 100), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2, cv2.LINE_AA)
            print("Res:", res)
            print("Final Angle: ", final_angle)
            print("New value", new_value)
        else:
            cv2.putText(img, "Can't see the gauge!", (50, 100), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 255), 2, cv2.LINE_AA)
    else:
        cv2.putText(img, "Can't detect gauge circle!", (50, 100), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 255), 2, cv2.LINE_AA)
    
    return img, img

if __name__ == "__main__":
    threshold_img = 120
    threshold_ln = 150
    minLineLength = 40
    maxLineGap = 8
    diff1LowerBound = 0.15
    diff1UpperBound = 0.25
    diff2LowerBound = 0.5
    diff2UpperBound = 1.0
    
    while True:
        img, img2 = take_measure(threshold_img, threshold_ln, minLineLength, maxLineGap, diff1LowerBound, diff1UpperBound, diff2LowerBound, diff2UpperBound)
        if img is not None:
            cv2.imshow('Analog Gauge RESULT', img2)
        if cv2.waitKey(1) == ord('q'):
            break
    
    cv2.destroyAllWindows()

内容的提问来源于stack exchange,提问作者user22417774

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 17:07:32