Python OpenCV检测图像预定义HSL颜色区间占比的实现方法
高效实现方案
你当前用cv2.inRange+numpy切片抽样的思路是最优解,全走C层实现比Python层逐像素循环快100倍以上,只要补上数值范围对齐、跨0色相区间处理、通道顺序校准几个逻辑即可,不需要自己写遍历逻辑。
核心注意点
- 通道顺序校准:
cv2.COLOR_BGR2HLS输出的三通道顺序是H(色相) -> L(亮度) -> S(饱和度),不是常规HSL的H-S-L顺序,传阈值时不要把S和L的位置写反。 - 数值范围对齐:OpenCV对uint8类型的HLS图像取值规则如下,你之前写的
[75,0,0]~[155,1,1]阈值范围是错的,S和L通道要做对应转换:- H通道:取值0179,对应实际色相0360度(存储时值为实际角度/2)
- L通道:取值0255,对应亮度01(存储时值为实际比例*255)
- S通道:取值0255,对应饱和度01(存储时值为实际比例*255)
- 抽样逻辑直接用numpy切片实现:
sampled_hls = hls[::step, ::step],步长默认取5即可,不需要逐像素跳步遍历,切片操作无额外内存拷贝,速度极快。总抽样像素数直接通过sampled_hls.shape[0] * sampled_hls.shape[1]计算,适配任意尺寸图像。 - 跨0色相区间处理:当hS>hE时(即色相区间跨越0度红色域),不需要写复杂的边界判断,把区间拆成
hS~179和0~hE两段分别做inRange,再将两个掩码按位或即可得到完整命中掩码。 - 多区间统计互不干扰:因为允许区间重叠,每个颜色区间单独生成掩码统计计数即可,最终占比为
区间命中数/总抽样像素数*100,总和超过100%为正常情况。
可直接复用的实现代码
import cv2 import numpy as np # 配置参数 SAMPLE_STEP = 5 # 像素抽样步长 # 替换为你自己的colorRange实例列表即可 color_range_list = [ {"name": "常规绿色", "hS":70, "hE":150, "sS":0.2, "sE":1.0, "lS":0.1, "lE":0.9}, {"name": "跨0度红色", "hS":340, "hE":20, "sS":0.3, "sE":1.0, "lS":0.2, "lE":0.8} ] # ---------------------- 图像加载 ---------------------- # 本地文件读取放开这行即可 # img_bgr = cv2.imread("test.jpg") # 摄像头Bayer格式缓冲区加载沿用你原有逻辑 img_raw = np.ndarray(buffer=component.data.copy(), dtype=np.uint8, shape=(component.height, component.width, 1)) img_bgr = cv2.cvtColor(img_raw, cv2.COLOR_BayerBG2BGR) # ---------------------- 预处理与抽样 ---------------------- img_hls = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2HLS) sampled_hls = img_hls[::SAMPLE_STEP, ::SAMPLE_STEP] total_sample_px = sampled_hls.shape[0] * sampled_hls.shape[1] stat_result = {} # ---------------------- 逐个区间统计 ---------------------- for cr in color_range_list: h_start, h_end = cr["hS"], cr["hE"] # 阈值转换:H从0-360度转0-179,L/S从0-1转0-255 lower_1 = np.array([ h_start // 2, int(cr["lS"] * 255), int(cr["sS"] * 255) ], dtype=np.uint8) upper_1 = np.array([ h_end // 2 if h_start < h_end else 179, int(cr["lE"] * 255), int(cr["sE"] * 255) ], dtype=np.uint8) mask_1 = cv2.inRange(sampled_hls, lower_1, upper_1) if h_start > h_end: # 处理跨0度的色相区间,拼接第二段掩码 lower_2 = np.array([0, int(cr["lS"]*255), int(cr["sS"]*255)], dtype=np.uint8) upper_2 = np.array([h_end//2, int(cr["lE"]*255), int(cr["sE"]*255)], dtype=np.uint8) mask_2 = cv2.inRange(sampled_hls, lower_2, upper_2) final_mask = cv2.bitwise_or(mask_1, mask_2) else: final_mask = mask_1 hit_count = cv2.countNonZero(final_mask) hit_ratio = round(hit_count / total_sample_px * 100, 2) stat_result[cr["name"]] = {"count": hit_count, "ratio": hit_ratio} # 输出结果 print(f"总抽样分析像素数:{total_sample_px}") for color_name, res in stat_result.items(): print(f"{color_name}: 命中{res['count']}像素,占比{res['ratio']}%")
适配说明
如果你定义colorRange时H值已经是OpenCV要求的0179范围、S/L值已经是0255范围,直接去掉代码里对应的
//2和*255转换逻辑即可。
这套实现全程无Python层像素遍历,哪怕步长设为1做全像素统计,处理1080P图像也能达到毫秒级速度,完全适配摄像头实时流处理场景。
内容的提问来源于stack exchange,提问作者sharkyenergy
相关产品推荐
相关产品推荐

