OpenCV运动检测程序中cv2.findContours返回空值问题求助
问题排查与修复方案
核心问题:OpenCV版本差异导致findContours返回值错误
cv2.findContours的返回值在不同OpenCV版本中存在差异:
- OpenCV 3.x 返回:
(image, contours, hierarchy) - OpenCV 4.x+ 返回:
(contours, hierarchy)
你的代码中使用[1]获取返回值,在OpenCV4.x中实际拿到的是**层级信息(hierarchy)**而非轮廓(contours),这直接导致后续遍历轮廓时无有效数据。
修复方式:
用imutils库提供的兼容函数自动适配版本,或者直接针对OpenCV4.x取第一个返回值:
# 兼容OpenCV3和4的写法 cnts = cv2.findContours(dilate_image.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts = imutils.grab_contours(cnts)
或者确定使用OpenCV4.x+时,直接写:
cnts = cv2.findContours(dilate_image.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
其他影响检测的问题修复
- 帧尺寸不匹配
你在初始化first_frame后才对frame做resize,但greyscale_image仍保持原始摄像头帧尺寸,导致frame_delta尺寸和绘制矩形的frame尺寸不一致,即使检测到轮廓也无法正确显示。
修复:先resize帧再处理灰度图:
while True: ret, frame = video_capture.read() if not ret: break text = 'Unoccupied' frame = imutils.resize(frame, width=500) # 先统一帧尺寸 greyscale_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 后续模糊处理基于resize后的灰度图
- 阈值设置过高
设置的阈值100过高,普通环境下大部分亮度变化无法触发二值化,导致无有效轮廓。建议调低至25-30:
thresh = cv2.threshold(frame_delta, 25, 255, cv2.THRESH_BINARY)[1]
- 增加帧读取有效性判断
添加ret判断,避免摄像头读取失败导致代码崩溃:
ret, frame = video_capture.read() if not ret: print("无法读取摄像头帧") break
完整修复后的代码
import cv2 import time import datetime import imutils def motion_detection(): video_capture = cv2.VideoCapture(0, cv2.CAP_DSHOW) time.sleep(2) first_frame = None while True: ret, frame = video_capture.read() if not ret: print("摄像头读取失败") break text = 'Unoccupied' # 先统一帧尺寸 frame = imutils.resize(frame, width=500) greyscale_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) gaussian_frame = cv2.GaussianBlur(greyscale_frame, (21, 21), 0) greyscale_image = cv2.blur(gaussian_frame, (5, 5)) if first_frame is None: first_frame = greyscale_image else: frame_delta = cv2.absdiff(first_frame, greyscale_image) # 适配普通环境的阈值 thresh = cv2.threshold(frame_delta, 25, 255, cv2.THRESH_BINARY)[1] dilate_image = cv2.dilate(thresh, None, iterations=2) # 兼容版本的轮廓获取 cnts = cv2.findContours(dilate_image.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts = imutils.grab_contours(cnts) for c in cnts: if cv2.contourArea(c) > 800: (x, y, w, h) = cv2.boundingRect(c) cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2) text = 'Occupied' # 绘制状态文本与时间戳 cv2.putText(frame, '{+} Room Status: %s' % text, (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2) cv2.putText(frame, datetime.datetime.now().strftime('%A %d %B %Y %I:%M:%S%p'), (10, frame.shape[0] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.35, (0, 0, 255), 1) cv2.imshow('Security Feed', frame) if first_frame is not None: cv2.imshow('Threshold(foreground mask)', dilate_image) cv2.imshow('Frame_delta', frame_delta) key = cv2.waitKey(1) & 0xFF if key == ord('q'): break video_capture.release() cv2.destroyAllWindows() if __name__ == '__main__': motion_detection()
内容的提问来源于stack exchange,提问作者Toby
相关产品推荐
相关产品推荐

