视觉学习篇——OpenCV实战:用HSV实现颜色识别(从原理到调节器+代码)

PyTorch 2.8

PyTorch 2.8

PyTorch
Cuda

PyTorch 是一个开源的 Python 机器学习库,基于 Torch 库,底层由 C++ 实现,应用于人工智能领域,如计算机视觉和自然语言处理


前言

大家好久不见~,出走一个星期归来继续写博客了。
在计算机视觉中,颜色识别是最基础却最核心的任务之一(比如垃圾分类、物体追踪、工业分拣)。相比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

2. 为什么HSV适合颜色识别?

HSV将“颜色本身”(H)与“亮度/鲜艳度”(S/V)分离,我们可以只调整H范围锁定颜色种类,再用S/V过滤光照干扰(比如避免太暗或太浅的颜色)。

例如:红色的HSV范围通常是H:0-10H: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-179100-255100-255
绿色35-85100-255100-255
蓝色100-130100-255100-255
黄色20-35100-255100-255

总结

本文通过HSV颜色调节器帮你直观理解HSV模型,再通过颜色识别代码教你如何用OpenCV实现实时颜色检测。核心要点:

  1. HSV分离颜色、饱和度、明度,更适合颜色处理。
  2. 颜色识别的关键是调整HSV阈值,过滤背景干扰。
  3. 形态学操作和轮廓查找是去除噪声、定位目标的常用手段。

赶紧运行代码试试吧!调整滑动条,看看不同HSV值对应的颜色,再试试识别你身边的彩色物体~ 🎨

遇到问题可以在评论区留言,我会及时解答!

您可能感兴趣的与本文相关的镜像

PyTorch 2.8

PyTorch 2.8

PyTorch
Cuda

PyTorch 是一个开源的 Python 机器学习库,基于 Torch 库,底层由 C++ 实现,应用于人工智能领域,如计算机视觉和自然语言处理

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值