文章目录
前言
大家好久不见~,出走一个星期归来继续写博客了。
在计算机视觉中,颜色识别是最基础却最核心的任务之一(比如垃圾分类、物体追踪、工业分拣)。相比RGB模型,HSV(Hue-Saturation-Value) 更适合颜色处理——它将颜色分解为“色调、饱和度、明度”三个感知维度,能大幅降低光照变化的影响。
对于初学者来说,颜色识别是一个很好的实战任务,这里我会给出一个使用的颜色调节器和实战代码,最后仍然会给到真正应用起来的c++代码。
一、HSV颜色模型原理:为什么比RGB更适合颜色识别?
1. RGB vs HSV:本质区别
- RGB:基于“发光元件”的加色模型(红+绿=黄,红+蓝=紫),适合屏幕显示,但颜色信息与亮度耦合(比如同一颜色变亮/变暗,RGB值会全变)。
- HSV:基于“人类感知”的颜色模型,将颜色拆分为三个独立维度:
- H(Hue,色调):颜色的“种类”(比如红、绿、蓝),范围
0-179(OpenCV中是0-360°的一半,用8位存储)。 - S(Saturation,饱和度):颜色的“鲜艳程度”(比如浅红→深红,S从低到高),范围
0-255。 - V(Value,明度):颜色的“亮度”(比如红→黑,V从高到低),范围
0-255。
- H(Hue,色调):颜色的“种类”(比如红、绿、蓝),范围
2. 为什么HSV适合颜色识别?
HSV将“颜色本身”(H)与“亮度/鲜艳度”(S/V)分离,我们可以只调整H范围锁定颜色种类,再用S/V过滤光照干扰(比如避免太暗或太浅的颜色)。
例如:红色的HSV范围通常是H:0-10或H:170-179(因为H是环形,0°和360°都是红),S≥100(避免浅粉),V≥100(避免暗红)。
二、实战1:HSV颜色调节器(实时看颜色变化)
我们用OpenCV的createTrackbar做一个交互式调节器——滑动H/S/V滑动条,右侧实时显示对应的颜色。
1. 完整代码
import cv2
import numpy as np
def hsv_color_viewer():
"""HSV颜色查看器:滑动条调整H/S/V,实时显示对应颜色"""
window_name = "HSV Color Viewer"
cv2.namedWindow(window_name, cv2.WINDOW_NORMAL)
h_init, s_init, v_init = 0, 255, 255
cv2.createTrackbar("H", window_name, h_init, 179, lambda x: None)
cv2.createTrackbar("S", window_name, s_init, 255, lambda x: None)
cv2.createTrackbar("V", window_name, v_init, 255, lambda x: None)
while True:
h = cv2.getTrackbarPos("H", window_name)
s = cv2.getTrackbarPos("S", window_name)
v = cv2.getTrackbarPos("V", window_name)
color_block = np.zeros((300, 500, 3), dtype=np.uint8)
color_block[:] = (h, s, v)
bgr_block = cv2.cvtColor(color_block, cv2.COLOR_HSV2BGR)
# 添加文字提示HSV值
display_img = bgr_block.copy()
text = f"H:{h} S:{s} V:{v}"
cv2.putText(display_img, text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (255,255,255), 2)
cv2.imshow(window_name, display_img)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cv2.destroyAllWindows()
if __name__ == "__main__":
hsv_color_viewer()

2. 效果说明
- 运行后会弹出一个窗口,包含三个滑动条和一个颜色块。
- 滑动H条:颜色会从红→黄→绿→青→蓝→紫循环变化(对应H的0-179)。
- 滑动S条:颜色从“灰”变“艳”(比如S=0是灰色,S=255是纯颜色)。
- 滑动V条:颜色从“黑”变“亮”(比如V=0是黑色,V=255是最亮的颜色)。
三、实战2:颜色识别(用滑动条调HSV阈值,识别摄像头目标)
学会看HSV颜色后,我们可以用滑动条调整HSV范围,实时识别摄像头中的目标颜色(比如红色小球、蓝色积木)。
1. 核心步骤
颜色识别的流程可总结为:
摄像头读帧 → 转HSV → 定义HSV阈值 → 做掩码(筛选目标颜色) → 形态学去噪 → 查找轮廓 → 绘制结果
2. 完整代码
import cv2
import numpy as np
def stack_images(scale, img_array):
""" 拼接图像 """
rows = len(img_array)
cols = len(img_array[0]) if rows > 0 else 0
if rows == 0 or cols == 0:
return None
width = img_array[0][0].shape[1]
height = img_array[0][0].shape[0]
for r in range(rows):
for c in range(cols):
if img_array[r][c].shape[:2] != (height, width):
img_array[r][c] = cv2.resize(img_array[r][c], (width, height))
if len(img_array[r][c].shape) == 2:
img_array[r][c] = cv2.cvtColor(img_array[r][c], cv2.COLOR_GRAY2BGR)
hor_imgs = [np.hstack(img_array[r]) for r in range(rows)]
ver_img = np.vstack(hor_imgs)
if scale != 1:
ver_img = cv2.resize(ver_img, (0, 0), fx=scale, fy=scale)
return ver_img
def color_detection_multi_mode(image_path= "test.jpg"):
cv2.namedWindow("Color Detection", cv2.WINDOW_NORMAL)
init_vals = {
"H Min": 0, "H Max": 10,
"S Min": 100, "S Max": 255,
"V Min": 100, "V Max": 255
}
for key, val in init_vals.items():
max_val = 179 if "H" in key else 255
cv2.createTrackbar(key, "Color Detection", val, max_val, lambda x: None)
kernel = np.ones((5, 5), np.uint8)
# 初始状态为摄像头模式
mode = 'camera'
cap = cv2.VideoCapture(0)
while True:
if mode == 'camera':
ret, frame = cap.read()
if not ret:
print("无法读取摄像头帧!")
break
elif mode == 'image':
frame = cv2.imread(image_path)
if frame is None:
print(f"无法加载图片:{image_path}")
frame = np.zeros((480, 640, 3), np.uint8)
else:
frame = np.zeros((480, 640, 3), np.uint8)
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
h_min = cv2.getTrackbarPos("H Min", "Color Detection")
h_max = cv2.getTrackbarPos("H Max", "Color Detection")
s_min = cv2.getTrackbarPos("S Min", "Color Detection")
s_max = cv2.getTrackbarPos("S Max", "Color Detection")
v_min = cv2.getTrackbarPos("V Min", "Color Detection")
v_max = cv2.getTrackbarPos("V Max", "Color Detection")
lower = np.array([h_min, s_min, v_min])
upper = np.array([h_max, s_max, v_max])
mask = cv2.inRange(hsv, lower, upper)
mask = cv2.erode(mask, kernel, iterations=1)
mask = cv2.dilate(mask, kernel, iterations=1)
disp_frame = frame.copy()
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
if cv2.contourArea(cnt) > 1000:
x, y, w, h = cv2.boundingRect(cnt)
cv2.rectangle(disp_frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
cv2.putText(disp_frame, "Target", (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0,255,0), 2)
disp_mask = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)
cv2.putText(disp_frame, f"Mode: {mode.upper()}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,255,255), 2)
cv2.putText(disp_frame, "Original", (10, 70), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,255,0), 2)
cv2.putText(disp_mask, "Mask", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,255,0), 2)
combined = stack_images(0.6, [[disp_frame, disp_mask]])
cv2.imshow("Color Detection", combined)
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
break
elif key == ord('c'):
# 切换摄像头模式
mode = 'camera'
if cap is None or not cap.isOpened():
cap = cv2.VideoCapture(0)
print("切换到摄像头模式")
elif key == ord('i'):
# 切换图片模式
mode = 'image'
print(f"切换到图片模式,图片路径:{image_path}")
if cap is not None and cap.isOpened():
cap.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
image_path="color.jpg"
color_detection_multi_mode(image_path)
3. 代码说明与调试技巧
使用方法
该python示例代码实现了摄像头检测和图片检测两种模式,如果不设置image_path路径则无法使用图片检测功能。

执行代码后首先进入摄像头检测界面,在该界面中按下i可切换成你指定路径图片的检测,由于只使用到了numy和opencv所以并没有做比较完善的ui界面

在图片检测界面中按下c可切换回摄像头实时检测画面
点击HSV数值还可以手动输入

我们并不需要追求完美实现将同一个颜色完美包括,那样太费时间了,只需要尽可能的将其分开即可

关键函数解释
cv2.cvtColor(frame, cv2.COLOR_BGR2HSV):将BGR图像转为HSV格式。cv2.inRange(hsv, lower, upper):根据HSV范围生成掩码(二进制图像,目标颜色为白,背景为黑)。cv2.findContours():查找掩码中的轮廓(目标物体的边界)。cv2.boundingRect():计算轮廓的 bounding box(矩形框)。
调试技巧
- 调整HSV阈值:先运行程序,将摄像头的光对准目标颜色,然后滑动H/S/V的Min/Max条,直到掩码中只有目标物体(没有背景)。
- 处理环形H值:比如红色有两个范围(H:0-10和H:170-179),可以将两个范围的掩码合并(
mask = cv2.bitwise_or(mask1, mask2))。 - 形态学操作:如果掩码有很多噪声,增大
kernel的大小(比如(7,7))或迭代次数(iterations=2)。
#include <opencv2/opencv.hpp>
using namespace cv;
int main(int argc, char** argv) {
// 检查命令行参数(图片路径)
bool use_camera = true;
string image_path;
if (argc == 2) {
image_path = argv[1];
use_camera = false;
} else if (argc > 2) {
cerr << "Usage: " << argv[0] << " [image_path]" << endl;
return -1;
}
// 创建窗口
const string windowName = "HSV Color Detector";
namedWindow(windowName, WINDOW_NORMAL);
// 初始化HSV阈值(以你调整的数值为准)
Scalar lower(159, 60, 168);
Scalar upper(179, 255, 255);
Mat frame, hsvFrame, mask, eroded, dilated;
Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5)); // 5x5矩形内核
if (use_camera) {
// 打开摄像头
VideoCapture cap(0);
if (!cap.isOpened()) {
cerr << "Error: Could not open camera!" << endl;
return -1;
}
while (true) {
cap >> frame;
if (frame.empty()) break;
// HSV转换与处理
cvtColor(frame, hsvFrame, COLOR_BGR2HSV);
inRange(hsvFrame, lower, upper, mask);
erode(mask, eroded, kernel);
dilate(eroded, dilated, kernel);
// 显示结果
imshow(windowName, dilated);
// 退出条件
if (waitKey(1) == 'q') break;
}
cap.release();
} else {
// 处理图片
Mat image = imread(image_path);
if (image.empty()) {
cerr << "Error: Could not load image " << image_path << endl;
return -1;
}
cvtColor(image, hsvFrame, COLOR_BGR2HSV);
inRange(hsvFrame, lower, upper, mask);
erode(mask, eroded, kernel);
dilate(eroded, dilated, kernel);
// 显示结果
imshow("Original Image", image);
imshow(windowName, dilated);
waitKey(0);
}
destroyAllWindows();
return 0;
}
#编译命令
g++ color_detector.cpp -o color_detector `pkg-config --cflags --libs opencv4`
运行时如果不给图片路径则默认摄像头检测

四、扩展:如何适配其他颜色?
不同颜色的HSV范围参考(OpenCV):
| 颜色 | H范围(Min-Max) | S范围(Min-Max) | V范围(Min-Max) |
|---|---|---|---|
| 红色 | 0-10 或 170-179 | 100-255 | 100-255 |
| 绿色 | 35-85 | 100-255 | 100-255 |
| 蓝色 | 100-130 | 100-255 | 100-255 |
| 黄色 | 20-35 | 100-255 | 100-255 |
总结
本文通过HSV颜色调节器帮你直观理解HSV模型,再通过颜色识别代码教你如何用OpenCV实现实时颜色检测。核心要点:
- HSV分离颜色、饱和度、明度,更适合颜色处理。
- 颜色识别的关键是调整HSV阈值,过滤背景干扰。
- 形态学操作和轮廓查找是去除噪声、定位目标的常用手段。
赶紧运行代码试试吧!调整滑动条,看看不同HSV值对应的颜色,再试试识别你身边的彩色物体~ 🎨
遇到问题可以在评论区留言,我会及时解答!

6521

被折叠的 条评论
为什么被折叠?



