使用 HSV 颜色空间进行颜色识别,核心思想是利用其将颜色信息与亮度信息分离的特性,通过设定色相(H)、饱和度(S)、明度(V)三个维度的阈值范围,来精准地筛选出目标颜色。这种方法比在 RGB 空间识别更稳定,对光照变化不那么敏感。
下面是一个完整的、基于 Python 和 OpenCV 的颜色识别流程,从原理到代码实现,手把手带你完成。
🎯 核心原理:为什么是 HSV?
在RGB 模型中,一个物体的颜色会受到光照强度的显著影响。同一个红苹果,在阳光下和阴影里,其 R、G、B三个数值会剧烈变化,导致识别困难。
而在 HSV 空间中,颜色被分解为:
- 色相 (Hue): 决定“是什么颜色”,相对稳定。
- 饱和度 (Saturation):决定“颜色有多纯”,受光照影响较小。
- 明度 (Value):决定“颜色有多亮”,直接反映光照强度。
因此,我们只需锁定H 和 S的大致范围,并为V 设置一个较宽的区间,就能在不同光照条件下稳定地识别出目标颜色。
🛠️ 实战步骤:从零开始识别颜色
整个识别流程可以概括为以下五个步骤:
- 读取图像并转换颜色空间:将摄像头或图片读取的 BGR 格式图像转换为 HSV 格式。
- 定义颜色阈值:根据目标颜色,设定 H、S、V 三个通道的下限和上限。
- 创建掩膜 (Mask):使用
cv2.inRange()函数,根据阈值从 HSV 图像中提取出目标颜色区域,生成一个二值图像(黑白图)。 - 图像后处理:对掩膜进行形态学操作(如开运算、闭运算),以去除噪点、填充空洞,使目标区域更完整。
- 提取与显示结果:将处理好的掩膜与原始图像进行“按位与”操作,最终只保留目标颜色的部分。
🧑💻 代码实现:实时颜色识别
以下是一个完整的 Python 脚本,它会打开你的摄像头,并实时识别画面中的红色物体。
python
import cv2 import numpy as np # 1. 打开摄像头 cap = cv2.VideoCapture(0) while True: # 2. 读取一帧图像 ret, frame = cap.read() if not ret: break # 3. 将 BGR 图像转换为 HSV 图像 hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) # 4. 定义红色的 HSV 阈值范围 # 注意:红色在色相环的两端(0度和180度附近),所以需要定义两个范围 lower_red1 = np.array() upper_red1 = np.array() lower_red2 = np.array() upper_red2 = np.array() # 5. 根据阈值创建两个掩膜 mask1 = cv2.inRange(hsv, lower_red1, upper_red1) mask2 = cv2.inRange(hsv, lower_red2, upper_red2) # 6. 将两个掩膜合并 mask = cv2.bitwise_or(mask1, mask2) # 7. 图像后处理:形态学操作,去除噪点 kernel = np.ones((5,5),np.uint8) mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) # 开运算:先腐蚀后膨胀,去除小噪点 mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) # 闭运算:先膨胀后腐蚀,填充空洞 # 8. 将掩膜与原图进行按位与操作,提取目标区域 result = cv2.bitwise_and(frame, frame, mask=mask) # 9. 显示结果 cv2.imshow('Original Frame', frame) cv2.imshow('Mask', mask) cv2.imshow('Result', result) # 按 'q' 键退出 if cv2.waitKey(1) & 0xFF == ord('q'): break # 释放资源 cap.release() cv2.destroyAllWindows()🔑 关键点:如何确定 HSV 阈值?
这是颜色识别中最关键也最需要技巧的一步。网上的阈值参数(如lower_red)只能作为参考,因为摄像头、光照、物体材质都会影响最终的 HSV 值。
最佳实践是使用交互式滑动条来现场调试。你可以编写一个带有滑动条的程序,实时调整 H、S、V 的上下限,直到掩膜中只剩下你想要的目标物体。
以下是创建 HSV 阈值调试工具的代码:
python
import cv2 import numpy as np def nothing(x): pass # 创建一个窗口 cv2.namedWindow('Trackbars') # 创建6个滑动条,分别对应H,S,V的最小值和最大值 cv2.createTrackbar('H Min', 'Trackbars', 0, 179, nothing) cv2.createTrackbar('H Max', 'Trackbars', 10, 179, nothing) cv2.createTrackbar('S Min', 'Trackbars', 43, 255, nothing) cv2.createTrackbar('S Max', 'Trackbars', 255, 255, nothing) cv2.createTrackbar('V Min', 'Trackbars', 46, 255, nothing) cv2.createTrackbar('V Max', 'Trackbars', 255, 255, nothing) # 打开摄像头 cap = cv2.VideoCapture(0) while True: ret, frame = cap.read() if not ret: break # 转换为HSV hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) # 获取滑动条的当前位置 h_min = cv2.getTrackbarPos('H Min', 'Trackbars') h_max = cv2.getTrackbarPos('H Max', 'Trackbars') s_min = cv2.getTrackbarPos('S Min', 'Trackbars') s_max = cv2.getTrackbarPos('S Max', 'Trackbars') v_min = cv2.getTrackbarPos('V Min', 'Trackbars') v_max = cv2.getTrackbarPos('V Max', 'Trackbars') # 定义阈值并创建掩膜 lower_val = np.array([h_min, s_min, v_min]) upper_val = np.array([h_max, s_max, v_max]) mask = cv2.inRange(hsv, lower_val, upper_val) # 显示原图和掩膜 cv2.imshow('Frame', frame) cv2.imshow('Mask', mask) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()📊 常见颜色的 HSV 范围参考 (OpenCV格式)
表格
| 颜色 | H (色相) | S (饱和度) | V (明度) |
|---|---|---|---|
| 红色 | 0-10 和 156-180 | 43-255 | 46-255 |
| 绿色 | 35-77 | 43-255 | 46-255 |
| 蓝色 | 100-124 | 43-255 | 46-255 |
| 黄色 | 26-34 | 43-255 | 46-255 |
注意:OpenCV 中 H 的范围是 0-179,S 和 V 的范围是 0-255。上表中的数值仅为起点,务必使用调试工具根据实际环境进行微调。