如何使用OpenCV Python实现精准颜色检测 支持多类别颜色识别
准确率优化方案
- 先修复基础逻辑bug:原判断语句存在分支重叠问题,当蓝色为最大值时,第一个if输出Blue后,第二个if判断不满足会触发else输出Red,导致同时输出两个结果,需要把第二个if改为elif,形成互斥判断分支。
- 更换颜色空间:RGB空间对光照变化非常敏感,建议转换为HSV(色相、饱和度、明度)空间做颜色判断,排除光照亮度对颜色识别的干扰。
- 增加帧平滑处理:取最近3~5帧的颜色均值做判断,避免单帧画面偶然波动导致的结果跳变。
- 限制检测区域:默认只取画面中心70%的区域计算主色,排除边缘无关背景的干扰,也可支持用户手动框选检测区域。
- 增加饱和度过滤:提前过滤掉饱和度低于阈值的像素(比如灰度、白色、黑色区域),避免无彩色区域拉偏均值计算。
多颜色拓展方案
完全可以支持更多颜色识别,核心是在HSV空间为每种目标颜色配置色相(H)、饱和度(S)的阈值范围,常见颜色的参考阈值(OpenCV中H值范围是0180,S、V是0255):
- 红色:H 0~10 或 170~180,S 120~255,V 70~255
- 橙色:H 11~25,S 120~255,V 70~255
- 黄色:H 26~35,S 120~255,V 70~255
- 绿色:H 36~77,S 120~255,V 70~255
- 蓝色:H 78~124,S 120~255,V 70~255
- 紫色:H 125~155,S 120~255,V 70~255
- 棕色:H 156~170,S 80~200,V 50~180
计算逻辑改为统计每个颜色阈值范围内的像素占比,占比最高的即为当前帧主色。
优化后完整代码
import cv2 import numpy as np from collections import deque # 初始化摄像头 vid = cv2.VideoCapture(0) # 缓存最近5帧的像素统计结果做平滑 frame_buffer = deque(maxlen=5) # 定义HSV颜色阈值,格式:[颜色名, [H下限, S下限, V下限], [H上限, S上限, V上限]] color_ranges = [ ["红色", [0, 120, 70], [10, 255, 255]], ["红色", [170, 120, 70], [180, 255, 255]], ["橙色", [11, 120, 70], [25, 255, 255]], ["黄色", [26, 120, 70], [35, 255, 255]], ["绿色", [36, 120, 70], [77, 255, 255]], ["蓝色", [78, 120, 70], [124, 255, 255]], ["紫色", [125, 120, 70], [155, 255, 255]], ["棕色", [156, 80, 50], [170, 200, 180]] ] while True: ret, frame = vid.read() if not ret: break # 取画面中心70%区域作为检测ROI h, w = frame.shape[:2] roi_x1, roi_x2 = int(w*0.15), int(w*0.85) roi_y1, roi_y2 = int(h*0.15), int(h*0.85) roi = frame[roi_y1:roi_y2, roi_x1:roi_x2] # 绘制ROI框 cv2.rectangle(frame, (roi_x1, roi_y1), (roi_x2, roi_y2), (0,255,0), 2) # 转换为HSV空间 hsv_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV) # 统计每个颜色的像素数 color_count = {} for color_name, lower, upper in color_ranges: mask = cv2.inRange(hsv_roi, np.array(lower), np.array(upper)) count = cv2.countNonZero(mask) color_count[color_name] = color_count.get(color_name, 0) + count # 把当前帧统计结果加入缓存 frame_buffer.append(color_count) # 计算缓存中所有帧的统计均值 avg_count = {} for cnt in frame_buffer: for k, v in cnt.items(): avg_count[k] = avg_count.get(k, 0) + v avg_count = {k: v/len(frame_buffer) for k, v in avg_count.items()} # 找出占比最高的颜色,若所有颜色像素占比低于10%则判定为无明显主色 max_color = max(avg_count.items(), key=lambda x: x[1]) total_pixel = roi.shape[0] * roi.shape[1] if max_color[1] / total_pixel < 0.1: result = "无明显主色" else: result = f"主色:{max_color[0]}" # 显示结果 cv2.putText(frame, result, (30, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,0,255), 2) cv2.imshow("主色检测", frame) # 按q键退出 if cv2.waitKey(100) & 0xFF == ord('q'): break # 释放资源 vid.release() cv2.destroyAllWindows()
内容的提问来源于stack exchange,提问作者SirFire
相关产品推荐
相关产品推荐

