树莓派3无人驾驶项目OpenCV cvtColor报错(-215) scn==3||4求助
问题分析与解决方案
我帮你排查下这个问题哈~你遇到的error: (-215) scn == 3 || scn == 4 in function cvtColor错误,本质是传入cv2.cvtColor()的图像不是有效的3通道(BGR)或4通道(BGRA)矩阵,而图像能成功存储是因为大部分时候摄像头能正常读取帧,报错只是偶尔读取失败的情况。下面具体说问题根源和修复方案:
问题根源
未检查摄像头帧读取状态
你的代码里直接用cap.read()的返回值im,但没有判断ret是否为True。当摄像头初始化慢、偶尔丢帧,或者多线程竞争摄像头资源时,ret会是False,此时im是无效的空矩阵,传给cvtColor()就会触发这个错误。线程与定时器逻辑混乱
process_img()里先启动了一个定时器,然后又嵌套了while True循环;main()里还再次启动定时器调用process_img(),这会导致多个循环同时读取摄像头,加剧帧读取失败的概率,还会造成资源冲突。全局变量使用不当
你把gray设为全局变量,当某次帧读取失败时,gray没有被正确赋值,后续依赖gray的边缘检测逻辑也会出问题。
修复后的代码
下面是调整后的代码,我标注了关键修改点:
import numpy as np import cv2 #import RPi.GPIO as GPIO cap = cv2.VideoCapture(0) i = 0 LEdge = 0 REdge = 0 lineErr = 0 #GPIO.setmode(GPIO.BOARD) #GPIO.setup(12,GPIO.OUT) #p = GPIO.PWM(12,100) #p.start(0) def fetch_img(image): global i # 这里只用局部变量gray,避免全局变量污染 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) img_gray_blur = cv2.GaussianBlur(gray, (5,5), 0) canny_edges = cv2.Canny(img_gray_blur, 10, 70) ret, mask = cv2.threshold(canny_edges, 90, 255, cv2.THRESH_BINARY) i += 1 fn = '/home/alex/All IT Courses/Master Computer Vision OpenCV/images/img/'+str(i)+'.jpg' cv2.imwrite(fn, mask) return mask, gray def process_img(): global LEdge, REdge, lineErr # 去掉多余的定时器,用单一循环持续处理帧 while True: ret, im = cap.read() # 关键:检查是否成功读取帧,失败则跳过本次循环 if not ret: print("Failed to capture frame, skipping...") continue # 获取处理后的mask和有效灰度图 mask, gray = fetch_img(im) cv2.imshow("Test", mask) # 边缘检测逻辑:基于有效灰度图处理 for j in range(1, 640): gray[240][j] = 0 if gray[240][j] < 80 else 1 # 初始化边缘值,避免未找到边缘时计算出错 REdge = 320 for k in range(320, 640, 1): if gray[240][k] == 0: REdge = k break LEdge = 320 for k in range(320, 0, -1): if gray[240][k] == 0: LEdge = k break lineErr = (REdge + LEdge) / 2 - 320 print(lineErr) if cv2.waitKey(1) == 13: # 13是Enter键的ASCII码 break cap.release() cv2.destroyAllWindows() def main(): # 只启动一次处理循环,避免多线程竞争摄像头 process_img() # 以下PWM控制代码可按需恢复 # try: # while 1: # dc = 8 - lineErr/40 # if dc > 10: # dc = 10 # if dc < 6: # dc = 6 # p.ChangeDutyCycle(dc) # except KeyboardInterrupt: # pass # p.stop() # GPIO.cleanup() if __name__ == "__main__": main()
关键修改说明
- 添加帧读取状态检查:每次
cap.read()后判断ret,无效帧直接跳过,避免传入无效图像给cvtColor()。 - 重构线程/定时器逻辑:去掉多余的定时器,用单一
while True循环持续处理帧,避免多实例竞争摄像头资源。 - 优化变量作用域:把
gray改为fetch_img()的局部变量,处理后返回有效灰度图,避免全局变量带来的赋值异常。 - 初始化边缘默认值:给
LEdge和REdge设置默认值(画面中心),避免未检测到边缘时计算lineErr出错。
内容的提问来源于stack exchange,提问作者Alex M
相关产品推荐
相关产品推荐

