Python-OpenCV HSV颜色空间与inRange颜色分割:按颜色提取目标物体

目录

一、BGR与HSV颜色空间理解

1.BGR空间

2.HSV三通道含义

二、inRange函数:颜色区间筛选生成mask掩码

三、bitwise_and按掩码提取彩色目标

常用基础位运算补充

四、预处理流程(颜色分割标准流水线)

[五、颜色分割 + 轮廓检测,统计指定颜色物体数量](#五、颜色分割 + 轮廓检测,统计指定颜色物体数量)

[六、摄像头实时 HSV 颜色追踪](#六、摄像头实时 HSV 颜色追踪)

[七、HSV 滑动调参工具](#七、HSV 滑动调参工具)

[八、本章 API 总览表](#八、本章 API 总览表)

九、本章高频踩坑清单

[十、方案选型:什么时候用灰度阈值,什么时候用 HSV 颜色分割](#十、方案选型:什么时候用灰度阈值,什么时候用 HSV 颜色分割)

十一、本章知识小结


上一章我们学习轮廓进阶全套能力,依靠灰度阈值、二值图像、轮廓检测完成物体识别计数。但是灰度阈值是依靠像素明暗区分前景背景,当画面光照复杂、物体和背景亮度接近的时候,灰度分割效果会很差。

这时可以使用HSV 颜色空间,依据色相(颜色本身)做分割,不受亮度干扰,可以直接把画面中指定颜色的物体提取出来。本章学习 BGR 与 HSV 互相转换、inRange 颜色阈值筛选、掩码 mask 位运算、颜色提取综合实战,包含静态图片处理以及摄像头实时颜色追踪案例。

OpenCV 读取图片默认是 BGR 通道,没有直接读取 HSV 格式图片的能力 ,必须先读取 BGR,再通过cv2.cvtColor()转换为 HSV。

一、BGR与HSV颜色空间理解

1.BGR空间

前面所有章节我们一直在使用BGR:由蓝、绿、红三个通道混合出色彩。

BGR的痛点:亮度变化会直接改变三个通道数值。同一个红色物体,强光下变亮、暗光下变暗,BGR三个数值波动巨大,很难固定一组数值筛选红色。

2.HSV三通道含义

HSV把颜色拆解为三个独立维度:

  1. H(Hue 色相):代表是什么颜色 。OpenCV中H取值范围:0 ~ 179
    • 0左右:红色;30左右:黄色;60左右:绿色;120左右:蓝色;160左右:品红
  2. S(Saturation 饱和度):颜色鲜艳程度 ,取值0 ~ 255。数值越高颜色越浓;S很低的时候颜色接近灰白。
  3. V(Value 明度/亮度):明暗程度 ,取值0 ~ 255。控制像素亮暗,不受颜色种类影响。

核心优势:把「什么颜色」和「亮不亮」拆分开。筛选目标颜色主要依靠H,对光照强弱具备一定抗干扰能力。

转换函数:

python 复制代码
# BGR → HSV
hsv_img = cv2.cvtColor(bgr_img, cv2.COLOR_BGR2HSV)
# HSV → BGR
bgr_img = cv2.cvtColor(hsv_img, cv2.COLOR_HSV2BGR)
python 复制代码
import cv2
import numpy as np
img = cv2.imread(r"D:\PYTHON\color_test.jpg")
if img is None:
    print("图片读取失败,请替换为正确路径")
    exit()

#BGR图像转HSV图像
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
cv2.imshow("BGR原图", img)
cv2.imshow("HSV图像", hsv)

while True:
    key = cv2.waitKey(10)
    if key == ord('q') or key == 27:
        break
cv2.destroyAllWindows()

注意:直接imshow显示HSV图像,图片看起来会很奇怪。因为imshow依旧按照BGR规则解析矩阵,HSV图像仅用于计算,不要直接拿来观察色彩

二、inRange函数:颜色区间筛选生成mask掩码

cv2.inRange(hsv图像, 下限lowerb, 上限upperb)

作用:设置HSV的最低、最高阈值,在hsv图里面把落在区间内的像素置255(白色),其余像素置0(黑色)。输出的结果叫做mask掩码(二值掩码图)

参数格式:lowerb = np.array([H_min, S_min, V_min])upperb = np.array([H_max, S_max, V_max])

掩码mask规则:

  • 在HSV区间内 → mask该位置=255(白色,保留区域)
  • 不在HSV区间内 → mask该位置=0(黑色,屏蔽区域)
python 复制代码
import cv2
import numpy as np
img = cv2.imread(r"D:\PYTHON\color_test.jpg")
if img is None:
    print("图片读取失败,请替换为正确路径")
    exit()
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

#示例:红色区间,根据实际图片微调数值
lower_red = np.array([0, 120, 80])
upper_red = np.array([10, 255, 255])

#生成掩码mask
mask = cv2.inRange(hsv, lower_red, upper_red)
cv2.imshow("原图", img)
cv2.imshow("mask掩码图", mask)

while True:
    key = cv2.waitKey(10)
    if key == ord('q'):
        break
cv2.destroyAllWindows()

输出mask是一张二值黑白图,白色代表满足该颜色的像素。

常见坑:H在OpenCV只有0‑179,不要直接使用PS的0‑360色相数值,需要除以2。

三、bitwise_and按掩码提取彩色目标

得到mask掩码之后,使用按位与cv2.bitwise_and(),利用掩码把原图中mask白色区域保留,黑色区域全部置黑。实现只把目标颜色物体抠出来

python 复制代码
import cv2
import numpy as np
img = cv2.imread(r"D:\PYTHON\color_test.jpg")
if img is None:
    print("图片读取失败,请替换为正确路径")
    exit()

hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
#红色阈值区间
lower_red = np.array([0, 120, 80])
upper_red = np.array([10, 255, 255])
mask = cv2.inRange(hsv, lower_red, upper_red)

#使用mask掩码,提取原图中目标颜色部分
result = cv2.bitwise_and(img, img, mask=mask)
cv2.imshow("原图", img)
cv2.imshow("mask掩码", mask)
cv2.imshow("提取颜色结果", result)

while True:
    key = cv2.waitKey(10)
    if key == ord('q'):
        break
cv2.destroyAllWindows()

常用基础位运算补充

函数 作用
cv2.bitwise_and(src1,src2,mask=mask) 按位与,掩码抠图,保留mask白色对应区域
cv2.bitwise_or() 按位或,多张掩码合并
cv2.bitwise_not(mask) 图像取反,黑白互换
cv2.bitwise_xor() 异或,取两张图差异部分

实操小技巧:inRange生成的mask往往带有细碎噪点,一般配合形态学操作(开运算闭运算)清理掩码里面的小白点、小黑洞。

四、预处理流程(颜色分割标准流水线)

原图 → BGR转HSV → inRange生成mask → 形态学操作清理mask噪点孔洞 → bitwise_and提取彩色目标 → 可选:对mask做轮廓检测计数

python 复制代码
import cv2
import numpy as np
img = cv2.imread(r"D:\PYTHON\color_test.jpg")
if img is None:
    print("图片读取失败,请替换为正确路径")
    exit()

hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lower_red = np.array([0, 120, 80])
upper_red = np.array([10, 255, 255])
mask = cv2.inRange(hsv, lower_red, upper_red)

#形态学清理掩码噪点
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
mask_clean = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)   # 去除外部噪点
mask_clean = cv2.morphologyEx(mask_clean, cv2.MORPH_CLOSE, kernel) # 填补内部孔洞
result = cv2.bitwise_and(img, img, mask=mask_clean)

cv2.imshow("原始mask",mask)
cv2.imshow("形态学清理后mask",mask_clean)
cv2.imshow("颜色提取结果",result)
cv2.waitKey(0)
cv2.destroyAllWindows()

五、颜色分割 + 轮廓检测,统计指定颜色物体数量

mask 清理干净之后,可以直接在 mask 上运行 findContours 轮廓检测,统计画面里面红色物体的个数。

python 复制代码
import cv2
import numpy as np
img = cv2.imread(r"D:\PYTHON\color_test.jpg")
if img is None:
    print("图片读取失败,请替换为正确路径")
    exit()

hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lower_red = np.array([0, 120, 80])
upper_red = np.array([10, 255, 255])
mask = cv2.inRange(hsv, lower_red, upper_red)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(3,3))
mask_clean = cv2.morphologyEx(mask,cv2.MORPH_OPEN,kernel)
mask_clean = cv2.morphologyEx(mask_clean,cv2.MORPH_CLOSE,kernel)

#在清理完成的掩码图上查找轮廓
contours, hierarchy = cv2.findContours(mask_clean, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
draw_img = img.copy()
count = 0
min_area = 120

for cnt in contours:
    area = cv2.contourArea(cnt)
    if area < min_area:
        continue

    count +=1
    x,y,w,h = cv2.boundingRect(cnt)
    cv2.rectangle(draw_img,(x,y),(x+w,y+h),(0,255,0),2)
    cv2.putText(draw_img,f"No.{count}",(x,y-8),cv2.FONT_HERSHEY_SIMPLEX,0.45,(0,0,255),1)

print(f"检测到目标颜色物体总数:{count}")
cv2.imshow("mask_clean",mask_clean)
cv2.imshow("物体标记结果",draw_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

六、摄像头实时 HSV 颜色追踪

读取摄像头每一帧,实时做 HSV 颜色分割,框选出画面内目标颜色物体。

python 复制代码
import cv2
import numpy as np
vc = cv2.VideoCapture(0)
cv2.namedWindow("摄像头颜色追踪",cv2.WINDOW_NORMAL)
cv2.resizeWindow("摄像头颜色追踪",800,600)

#红色HSV阈值,可以根据实际效果修改
lower_red = np.array([0, 120, 80])
upper_red = np.array([10, 255, 255])
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(3,3))
min_area = 150

while vc.isOpened():
    ret,frame = vc.read()
    if not ret:
        break

    hsv = cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)
    mask = cv2.inRange(hsv,lower_red,upper_red)

    #形态学清理mask
    mask_clean = cv2.morphologyEx(mask,cv2.MORPH_OPEN,kernel)
    mask_clean = cv2.morphologyEx(mask_clean,cv2.MORPH_CLOSE,kernel)
    contours,hierarchy =                                             cv2.findContours(mask_clean,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
    draw_frame = frame.copy()

    for cnt in contours:
        area = cv2.contourArea(cnt)
        if area &lt; min_area:
            continue
        x,y,w,h = cv2.boundingRect(cnt)
        cv2.rectangle(draw_frame,(x,y),(x+w,y+h),(0,255,0),2)

        #计算物体中心坐标,适配机器人颜色追踪需求
        cx = x + w // 2
        cy = y + h // 2
        print(f"目标物体中心:cx={cx}, cy={cy}")

    cv2.imshow("摄像头颜色追踪",draw_frame)
    cv2.imshow("mask掩码",mask_clean)
    key = cv2.waitKey(1000//24)
    if key &amp; 0xFF == ord('q'):
        break

vc.release()
cv2.destroyAllWindows()

实操提示:摄像头环境光照变化大,如果识别不稳定,适当调小 S、V 下限,放宽阈值。

七、HSV 滑动调参工具

手动猜 HSV 数值很麻烦,可以创建滑动条,实时调节 H、S、V 上下限,观察 mask 效果,拿到适合自己图片的阈值。注意:本工具只针对单张静态图片调参,hsv 在循环外只计算一次,滑动条修改阈值时原图不会变化,这是正常现象,不要误以为它支持摄像头实时调参。

python 复制代码
import cv2
import numpy as np
def callback(x):
    pass

img = cv2.imread(r"D:\PYTHON\color_test.jpg")
if img is None:
    print("图片读取失败,请替换为正确路径")
    exit()

hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
cv2.namedWindow("HSV调参窗口",cv2.WINDOW_NORMAL)
cv2.resizeWindow("HSV调参窗口",700,500)

#创建滑动条 H(0‑179) S、V(0‑255)
cv2.createTrackbar("H_min","HSV调参窗口",0,179,callback)
cv2.createTrackbar("H_max","HSV调参窗口",10,179,callback)
cv2.createTrackbar("S_min","HSV调参窗口",100,255,callback)
cv2.createTrackbar("S_max","HSV调参窗口",255,255,callback)
cv2.createTrackbar("V_min","HSV调参窗口",80,255,callback)
cv2.createTrackbar("V_max","HSV调参窗口",255,255,callback)

while True:
    h_min = cv2.getTrackbarPos("H_min","HSV调参窗口")
    h_max = cv2.getTrackbarPos("H_max","HSV调参窗口")
    s_min = cv2.getTrackbarPos("S_min","HSV调参窗口")
    s_max = cv2.getTrackbarPos("S_max","HSV调参窗口")
    v_min = cv2.getTrackbarPos("V_min","HSV调参窗口")
    v_max = cv2.getTrackbarPos("V_max","HSV调参窗口")

    lower = np.array([h_min,s_min,v_min])
    upper = np.array([h_max,s_max,v_max])
    mask = cv2.inRange(hsv,lower,upper)
    res = cv2.bitwise_and(img,img,mask=mask)
    cv2.imshow("mask",mask)
    cv2.imshow("HSV调参窗口",res)
    key = cv2.waitKey(10)

    if key == ord('q'):
    # 打印最终阈值,可以复制到正式项目
        print(f"lower = np.array([{h_min},{s_min},{v_min}])")
        print(f"upper = np.array([{h_max},{s_max},{v_max}])")
        break
cv2.destroyAllWindows()

八、本章 API 总览表

函数 功能说明
cv2.cvtColor(img,cv2.COLOR_BGR2HSV) BGR 图像转为 HSV 图像
cv2.inRange(hsv,lowerb,upperb) HSV 区间筛选,生成二值 mask 掩码
cv2.bitwise_and(img,img,mask=mask) 根据掩码提取原图对应颜色区域
cv2.bitwise_not(mask) 掩码黑白取反
cv2.createTrackbar() 创建滑动条,用于调试 HSV 阈值

九、本章高频踩坑清单

红色色相跨 0 边界,需要两组 inRange 掩码做 or 合并,完整示例:

python 复制代码
import cv2
import numpy as np
img = cv2.imread(r"D:\PYTHON\color_test.jpg")
if img is None:
    print("图片读取失败,请替换为正确路径")
    exit()

hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
#红色跨边界:H 0-10 和 160-179 两组区间
lower_red1 = np.array([0, 120, 80])
upper_red1 = np.array([10, 255, 255])
lower_red2 = np.array([160, 120, 80])
upper_red2 = np.array([179, 255, 255])

mask1 = cv2.inRange(hsv, lower_red1, upper_red1)
mask2 = cv2.inRange(hsv, lower_red2, upper_red2)
mask = cv2.bitwise_or(mask1, mask2)  # 两组掩码合并
result = cv2.bitwise_and(img, img, mask=mask)

cv2.imshow("mask", mask)
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
  1. OpenCV 中 Hue 色相取值范围0‑179,不是 0‑360。PS、画图软件的色相数值要除以 2 再使用;
  2. 不要直接 imshow 显示 HSV 图像,HSV 矩阵只是计算用,imshow 会错误解析色彩;
  3. inRange 输出 mask 会存在噪点,务必搭配形态学开运算、闭运算清理掩码;
  4. S 饱和度不要设置太低:S 过低时物体发白、发灰白,H 色相会失效,一般 S_min 建议不低于 40~60;
  5. V 明度要根据光照环境调整:强光环境适当拉高 V_max,暗光环境适当降低 V_min,避免目标物体因过亮或过暗被漏掉;
  6. 图片路径使用 r 原始字符串,避免转义字符导致图片读取返回 None;
  7. 摄像头实时颜色追踪,光照变化会严重影响分割效果,尽量保证光源稳定。

十、方案选型:什么时候用灰度阈值,什么时候用 HSV 颜色分割

学完本章,很多人会纠结:到底该用灰度阈值还是 HSV 颜色分割?其实两者并不冲突,选型主要看两个问题:物体和背景靠什么区分 ,以及光照环境是否稳定

  • 物体和背景明暗差距大,颜色不重要:优先用灰度 + threshold。例如白纸黑字、深色零件放在浅色传送带上,这类场景灰度阈值简单高效,不需要转 HSV,处理速度也更快。
  • 物体依靠颜色区分,或光照变化大:优先用 HSV + inRange。例如红色球在绿色草地上、彩色积木分类,这类场景颜色是核心特征,HSV 依靠色相筛选,对光照强弱具备一定抗干扰能力。

简单记忆:看明暗选灰度,看颜色选 HSV。实际项目中两者也经常配合使用,例如先用 HSV 提取目标颜色区域,再对结果做灰度阈值或轮廓检测,完成更复杂的识别任务。

十一、本章知识小结

  1. BGR 色彩空间容易受亮度干扰;HSV 把图像拆分为 H 色相、S 饱和度、V 明度,依靠色相筛选颜色,抗光照干扰更强;
  2. 使用cv2.cvtColor()完成 BGR 与 HSV 互相转换;
  3. cv2.inRange()设置 HSV 上下限,生成 mask 掩码,白色代表符合颜色条件的像素;
  4. bitwise_and 位运算配合 mask 掩码,从原图抠取目标颜色区域;
  5. mask 掩码一般要经过形态学操作,去除噪点、填补孔洞;mask 可以直接送入 findContours 做轮廓检测,实现颜色物体计数;
  6. inRange 输出的 mask 本身就是 uint8 单通道二值图,可以直接送入 findContours,不需要再做 threshold 二值化;
  7. 滑动条工具可以快速调试 HSV 阈值,拿到适合自己场景的参数;
  8. 红色比较特殊色相跨过 0 边界,复杂场景需要两组掩码合并。

下一章:OpenCV 仿射变换、透视变换,图像任意角度旋转矫正,实现文档扫描矫正实战。

相关推荐
小小测试开发1 小时前
Agent 安全测试:pytest 原生红队框架 RAMPART 实战解析
人工智能·pytest
FunTester1 小时前
DeepSeek Harness 常用插件介绍
人工智能·语言模型·常用插件·deepseek·harness
九硕智慧建筑一体化厂家1 小时前
楼宇自控|智慧建筑核心引擎!楼宇自控系统,赋能建筑高效低碳运维
运维·人工智能·笔记·智慧城市
byte轻骑兵1 小时前
【BlueZ 】蓝牙 SDAP 协议入门:BlueZ 中设备服务发现的基础逻辑
linux·人工智能·bluez·电脑蓝牙·嵌入式蓝牙
老衲の少女心1 小时前
【AI项目】用 LangGraph 构建智能批改与解题系统:条件路由、SSE流式输出Agent工作流实践
人工智能
RobinDevNotes1 小时前
K8s+Megatron-LM+vLLM打通超大模型训练全链路
人工智能·kubernetes
资源大佬星 课it1 小时前
人工智能机器学习系统班,人工智能深度学习系统班
人工智能
连线Insight1 小时前
从谷歌到百度,AI正在打开搜索增长空间
人工智能
DevNo1 小时前
旅行素材太多不会剪?三款手机AI工具实测体验
人工智能