OpenCV统计粉色像素占比恒为0但黑色检测正常的问题修复
问题描述
编写了基于OpenCV的指定图像内黑色、粉色像素占比检测代码,目前黑色像素统计结果符合预期,但即便图像中明确包含粉色内容,粉色像素占比始终返回0.0%,原始实现代码如下:
import cv2 as cv import numpy as np import os for i in os.listdir("C:\\Users\\vikas\\Documents\\PyTest"): img = cv.cvtColor(cv.imread(f"C:\\Users\\vikas\\Documents\\PyTest\\{i}"), cv.COLOR_BGR2RGB) black = [0,0,0] pink = [255, 192, 203] diff_black = 20 diff_pink = 20 boundaries_black = [([ black[0], black[1], black[2] ],[ black[0]+diff_black, black[1]+diff_black, black[2]+diff_black ])] boundaries_pink = [([ pink[0]-diff_pink, pink[1]-diff_pink, pink[2]-diff_pink ],[ pink[0]+diff_pink, pink[1]+diff_pink, pink[2]+diff_pink ])] scale = 0.3 height = int(img.shape[0]*scale) width = int(img.shape[1]*scale) newSize = (width, height) img = cv.resize(img, newSize, None, None, None, cv.INTER_AREA) for (lower, upper) in boundaries_black: lower = np.array(lower, dtype=np.uint8) upper = np.array(upper, dtype=np.uint8) mask = cv.inRange(img, lower, upper) ratio_black = cv.countNonZero(mask)/(img.size/3) colorPercent = (ratio_black*100)/scale print(f"black pixel percentage: {np.round(colorPercent, 2)}") for (lower, upper) in boundaries_pink: lower = np.array(lower, dtype=np.uint8) upper = np.array(upper, dtype=np.uint8) mask = cv.inRange(img, lower, upper) ratio_pink = cv.countNonZero(mask)/(img.size/3) colorPercent = (ratio_pink*100)/scale print(f"pink pixel percentage: {np.round(colorPercent, 2)}")
问题根源
- 核心错误:粉色阈值计算触发uint8类型溢出
定义的标准粉色RGB值为[255, 192, 203],设置容差diff_pink=20后,R通道的上界计算为255+20=275,但np.uint8类型的取值范围只有0~255,275会被截断为275-256=19。此时R通道的判断区间变成「≥235 且 ≤19」,属于无效区间,cv.inRange要求每个通道的下界值必须小于等于上界值,该区间不会匹配到任何像素,因此粉色像素统计结果始终为0。 - 占比计算逻辑错误
将图片缩放到原尺寸的30%后,图片总像素数已经发生变化,此时直接统计mask非零像素占当前图片总像素的比例即可,额外除以scale的操作属于逻辑错误,会导致最终占比结果失真。 - 鲁棒性缺失
遍历目标文件夹时没有过滤非图片文件,也没有判断图片是否读取成功,遇到文件夹、非图片格式文件时会直接触发程序报错。
修复方案
- 计算颜色阈值上下界时,手动把通道值截断到0~255的合法区间,避免uint8溢出/下溢
- 删除占比计算时多余的
/scale操作,缩放后直接计算当前图的像素占比即可 - 增加文件过滤、图片读取成功判断,提升代码鲁棒性
- 可选优化:粉色属于对色相敏感的颜色,在HSV颜色空间做阈值分割,抗光照变化干扰的效果比RGB空间更好
修复后可正常运行的代码如下:
import cv2 as cv import numpy as np import os target_dir = "C:\\Users\\vikas\\Documents\\PyTest" # 支持的图片格式后缀 valid_exts = ('.jpg', '.jpeg', '.png', '.bmp') # 定义颜色和容差 black = [0,0,0] pink = [255, 192, 203] diff_black = 20 diff_pink = 20 # 提前计算阈值,手动截断到0-255区间避免uint8溢出 def get_boundary(color, diff): lower = np.array([max(0, c - diff) for c in color], dtype=np.uint8) upper = np.array([min(255, c + diff) for c in color], dtype=np.uint8) return lower, upper black_lower, black_upper = get_boundary(black, diff_black) pink_lower, pink_upper = get_boundary(pink, diff_pink) scale = 0.3 for i in os.listdir(target_dir): # 过滤非图片文件 if not i.lower().endswith(valid_exts): continue img_path = os.path.join(target_dir, i) img = cv.imread(img_path) # 判断图片是否读取成功 if img is None: print(f"跳过无法读取的文件:{i}") continue # OpenCV默认读入为BGR格式,转RGB img = cv.cvtColor(img, cv.COLOR_BGR2RGB) # 缩放图片 height = int(img.shape[0]*scale) width = int(img.shape[1]*scale) newSize = (width, height) img = cv.resize(img, newSize, interpolation=cv.INTER_AREA) total_pixel = img.shape[0] * img.shape[1] # 统计黑色像素占比 mask_black = cv.inRange(img, black_lower, black_upper) ratio_black = cv.countNonZero(mask_black) / total_pixel * 100 print(f"文件:{i} | 黑色像素占比: {np.round(ratio_black, 2)}%") # 统计粉色像素占比 mask_pink = cv.inRange(img, pink_lower, pink_upper) ratio_pink = cv.countNonZero(mask_pink) / total_pixel * 100 print(f"文件:{i} | 粉色像素占比: {np.round(ratio_pink, 2)}%")
内容的提问来源于stack exchange,提问作者Praagya Agrawal
相关产品推荐
相关产品推荐

