Python OpenCV基于YCbCr色彩空间的颜色检测问题咨询
OpenCV YCbCr色彩空间绿色区域检测问题排查
现有代码的核心错误
你当前代码检测不到绿色的根本原因是阈值定义完全不符合cv.inRange()的调用规则:
cv.inRange()要求lower数组每个通道的数值必须小于等于upper数组对应通道的数值,你当前定义的lower_green = [93, 147, 72]、upper_green = [75, 113, 87]中,Y通道93>75、Cr通道147>113,阈值区间完全无效,生成的掩膜会是全黑状态,自然检测不到任何区域。- 注意OpenCV中
cv.COLOR_BGR2YCrCb转换后的通道顺序是[Y(亮度), Cr(红色差分量), Cb(蓝色差分量)],不是很多资料里写的Y、Cb、Cr顺序,通道顺序搞反再怎么调阈值都不会有正确结果。
绿色在YCrCb空间下的参考阈值(可根据你自己数据集的光照情况微调):
- Y通道:30~250(不要把亮度区间卡太死,避免明暗变化导致漏检)
- Cr通道:70~120(绿色对应的Cr分量偏低)
- Cb通道:90~130
修正后的基础检测代码如下:
import cv2 as cv import numpy as np img_ycrcb = cv.cvtColor(img3, cv.COLOR_BGR2YCrCb) # 保证所有通道下限值 <= 上限值,明确dtype避免类型错误 lower_green = np.array([30, 70, 90], dtype=np.uint8) upper_green = np.array([250, 120, 130], dtype=np.uint8) mask_green = cv.inRange(img_ycrcb, lower_green, upper_green) # 如需显示正常的彩色绿色区域,要在原始BGR图上做掩膜运算,不要在YCrCb图上操作 green_area = cv.bitwise_and(img3, img3, mask=mask_green) cv.imshow('YCrCb converted image', img_ycrcb) cv.imshow('Green mask', mask_green) cv.imshow('Detected green area', green_area) cv.waitKey(0) cv.destroyAllWindows()
两个高频疑问解答
1. 同时检测红、绿区域是否需要做按位或运算
根据你的实际需求选择:
- 如果需要一次性提取图中所有红色+绿色的区域:分别生成红色掩膜、绿色掩膜后,对两个掩膜做按位或运算,用合并后的掩膜提取区域即可。不要直接把两个颜色的阈值拼到一组上下限里,因为红、绿在Cr、Cb通道的数值区间不连续,硬拼阈值会出现大量漏检、误检。
- 如果需要分别统计红、绿区域的面积、位置等独立指标:不要合并掩膜,保留两个颜色的独立掩膜单独计算即可。
附YCrCb空间下红色的参考阈值(通道顺序Y、Cr、Cb):lower_red = np.array([30, 140, 80], dtype=np.uint8),upper_red = np.array([250, 190, 130], dtype=np.uint8),可根据实际场景微调。
2. 掩膜计算的执行时机
不要等所有图像都存入数组后再做掩膜计算,应该在遍历导入图像的过程中同步处理,原因如下:
- 内存占用更低:如果把所有原始图像全量存入数组,哪怕仅100张1080P分辨率的图片,也会占用数GB内存,边读边处理只需要保留单张原图和处理后的结果,内存压力会小很多。
- 逻辑更简单:不需要额外写循环遍历高维图像数组做逐图计算,读一张、处理一张、存储需要的结果即可。
批量处理参考代码:
import os import cv2 as cv import numpy as np # 提前固定阈值 lower_green = np.array([30, 70, 90], dtype=np.uint8) upper_green = np.array([250, 120, 130], dtype=np.uint8) lower_red = np.array([30, 140, 80], dtype=np.uint8) upper_red = np.array([250, 190, 130], dtype=np.uint8) img_folder = "your/image/dataset/path" process_result = [] for filename in os.listdir(img_folder): if filename.lower().endswith(('.png', '.jpg', '.jpeg')): # 读入单张图片 img = cv.imread(os.path.join(img_folder, filename)) # 色彩空间转换+掩膜计算 img_ycrcb = cv.cvtColor(img, cv.COLOR_BGR2YCrCb) mask_green = cv.inRange(img_ycrcb, lower_green, upper_green) mask_red = cv.inRange(img_ycrcb, lower_red, upper_red) # 需要合并区域就做按位或 mask_total = cv.bitwise_or(mask_green, mask_red) detect_res = cv.bitwise_and(img, img, mask=mask_total) # 仅存储需要的结果,不要冗余存储原始大图 process_result.append({ "filename": filename, "green_pixel_count": np.sum(mask_green > 0), "red_pixel_count": np.sum(mask_red > 0), "detect_img": detect_res }) # 全部处理完成后,再按需把结果转成numpy数组 X = np.array([item["detect_img"] for item in process_result])
小提示:不要直接硬抄公开的阈值数值,最好拿自己数据集里的3~5张样图,用OpenCV滚动条写个简易实时调参工具,调到阈值能准确覆盖目标颜色再固定,比手动试数效率高很多。
内容的提问来源于stack exchange,提问作者pratama
相关产品推荐
相关产品推荐

