视频捕获中颜色过渡检测故障:白线跨黄绿区域单次打印失效
问题描述
我正在做一个项目,需要追踪一条从黄色区域延伸到绿色区域的白线,当白线穿过颜色交界点时,希望只打印一次"Perfect"。我写了下面的代码,但没实现预期效果;试过用像素计数器检测帧间颜色变化,也没用。
import cv2 import numpy as np import time # 指定HSV格式下要检测的颜色上下限 lower = np.array([35, 100, 140]) upper = np.array([70, 255, 255]) # 这个范围用来检测黄色 # 捕获摄像头画面 cap = cv2.VideoCapture(0) prev_count = 0 curr_count = 0 frame_count = 0 perfect_printed = False last_perfect_time = 0 while True: success, video = cap.read() # 读取摄像头画面 img = cv2.cvtColor(video, cv2.COLOR_BGR2HSV) # 将BGR图像转换为HSV格式 mask = cv2.inRange(img, lower, upper) # 对图像做掩码,筛选目标颜色 mask_contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 在掩码图像中查找轮廓 # 查找所有轮廓的位置 if len(mask_contours) != 0: for mask_contour in mask_contours: area = cv2.contourArea(mask_contour) if area > 5 and area < 30: x, y, w, h = cv2.boundingRect(mask_contour) cv2.rectangle(video, (x, y), (x + w, y + h), (0, 0, 255), 1) # 绘制矩形框 curr_count += area cv2.imshow("mask image", mask) # 显示掩码图像 cv2.imshow("window", video) # 显示摄像头画面 # 检测两帧内像素计数是否减少超过10 if frame_count == 1: if (prev_count - curr_count) > 9 and not perfect_printed and time.time() - last_perfect_time > 2: print("Perfect") cv2.imwrite('perfect_screenshot.jpg', video) perfect_printed = True last_perfect_time = time.time() prev_count = curr_count curr_count = 0 frame_count = 0 else: frame_count += 1 # 如果已经打印过,重置标记 if perfect_printed: perfect_printed = False # 按下Esc键关闭窗口 if cv2.waitKey(1) == 27: break cap.release() cv2.destroyAllWindows()
场景截图说明
- 图片1:画面左侧为黄色区域,右侧为绿色区域,一条白线从黄色区域向绿色区域延伸,尚未到达交界点
- 图片2:白线已穿过黄、绿区域的交界点,部分进入绿色区域
问题分析与修复
你的代码核心问题在于**perfect_printed标记被立即重置**——每次循环末尾都会把它设为False,导致即使触发了打印条件,下一次循环也会让标记失效;同时帧计数的逻辑依赖黄色区域的面积变化,稳定性差,无法准确捕捉白线跨越交界的瞬间。
以下是修复后的代码,核心改进点:
- 移除错误的
perfect_printed强制重置逻辑,改用冷却时间控制标记重置 - 直接追踪白线的位置,通过预设的交界线判断跨越动作,比面积变化更精准
- 增加白色线条的检测逻辑,直接锁定目标白线
import cv2 import numpy as np import time # 黄色HSV范围(可根据实际场景微调) lower_yellow = np.array([20, 100, 100]) upper_yellow = np.array([30, 255, 255]) # 绿色HSV范围 lower_green = np.array([40, 40, 40]) upper_green = np.array([70, 255, 255]) cap = cv2.VideoCapture(0) # 定义交界线位置(默认取画面垂直中线,可根据实际场景调整) border_x = None # 标记是否已打印过Perfect has_printed = False # 记录白线之前的位置状态(True=在黄色区域,False=在绿色区域) prev_line_in_yellow = None # 记录上次触发时间,用于冷却 last_trigger_time = 0 while True: success, frame = cap.read() if not success: break # 第一次读取画面时初始化交界线 if border_x is None: border_x = frame.shape[1] // 2 cv2.line(frame, (border_x, 0), (border_x, frame.shape[0]), (255, 0, 0), 2) hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) # 检测白色线条的HSV范围 lower_white = np.array([0, 0, 200]) upper_white = np.array([180, 25, 255]) white_mask = cv2.inRange(hsv, lower_white, upper_white) # 提取白色线条的轮廓,取面积最大的作为目标白线 contours, _ = cv2.findContours(white_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) current_line_in_yellow = None if contours: max_contour = max(contours, key=cv2.contourArea) x, y, w, h = cv2.boundingRect(max_contour) # 计算白线中心的x坐标,判断是否在黄色区域(交界线左侧) line_center_x = x + w // 2 current_line_in_yellow = line_center_x < border_x # 绘制白线的包围框和中心标记 cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2) cv2.circle(frame, (line_center_x, y+h//2), 3, (0, 0, 255), -1) # 判断是否完成跨越动作:之前在黄色区域,现在进入绿色区域,且未打印过 if prev_line_in_yellow is not None and current_line_in_yellow is not None: if prev_line_in_yellow and not current_line_in_yellow and not has_printed: print("Perfect") cv2.imwrite('perfect_screenshot.jpg', frame) has_printed = True last_trigger_time = time.time() # 冷却2秒后重置标记,允许下次触发 if has_printed and time.time() - last_trigger_time > 2: has_printed = False prev_line_in_yellow = current_line_in_yellow cv2.imshow("White Line Tracking", frame) cv2.imshow("White Mask", white_mask) # 按下Esc键退出 if cv2.waitKey(1) == 27: break cap.release() cv2.destroyAllWindows()
关键修复说明
- 直接追踪白线的中心位置,通过交界线判断跨越动作,避免了原代码依赖黄色区域面积变化的不稳定问题
- 用冷却时间控制
has_printed标记的重置,确保单次跨越只触发一次"Perfect"打印 - 增加白色线条检测,直接锁定目标追踪对象,逻辑更清晰
内容的提问来源于stack exchange,提问作者Learning2code
相关产品推荐
相关产品推荐

