🎨 实战进阶篇 —— 动态调参、多色识别与实际应用(含完整代码 + 图文详解)


📚 上节回顾 & 本课目标

在【第13课】中,我们学习了:

  • HSV色彩空间基础
  • 使用 cv2.inRange() 提取单一颜色
  • 简单的颜色分割示例

📌 本节课升级内容:
✅ 多颜色同时识别
✅ 使用滑动条动态调节HSV阈值(实时调参)
✅ 颜色识别+轮廓检测联动
✅ 实际应用场景:交通灯识别模拟
✅ 完整可运行代码 + 图解流程


🧰 准备工作

✅ 安装依赖

pip install opencv-python numpy matplotlib

✅ 测试图片准备

建议使用一张包含红、绿、蓝等明显颜色的图片。例如:

📁 test_colors.jpg
(可以是彩色积木、RGB灯带、交通信号灯等)


🎯 核心知识点:HSV 色彩空间再理解

通道 含义 取值范围
H(Hue) 色相(颜色种类) 0–179(OpenCV中)
S(Saturation) 饱和度 0–255
V(Value) 明亮度 0–255

🟢 常见颜色 HSV 近似范围(光照不同会有变化):

颜色 H(低) H(高) S, V 下限
红色 0–10 或 170–180 S > 100, V > 100
绿色 40–80 S > 100, V > 100
蓝色 100–130 S > 100, V > 100
黄色 20–40 S > 100, V > 100

⚠️ 注意:红色跨边界!需合并两个区间


🔧 第一步:创建动态HSV调节器(Trackbar)

让我们做一个“实时调色板”,方便调试!

import cv2
import numpy as np

# 加载图像
image = cv2.imread('test_colors.jpg')
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

# 创建窗口
cv2.namedWindow('Trackbars')
cv2.resizeWindow('Trackbars', 600, 300)

# 创建滑动条(H, S, V 的上下限)
def nothing(x):
    pass

cv2.createTrackbar("L - H", "Trackbars", 0, 179, nothing)
cv2.createTrackbar("L - S", "Trackbars", 0, 255, nothing)
cv2.createTrackbar("L - V", "Trackbars", 0, 255, nothing)
cv2.createTrackbar("U - H", "Trackbars", 179, 179, nothing)
cv2.createTrackbar("U - S", "Trackbars", 255, 255, nothing)
cv2.createTrackbar("U - V", "Trackbars", 255, 255, nothing)

while True:
    # 获取滑动条当前值
    l_h = cv2.getTrackbarPos("L - H", "Trackbars")
    l_s = cv2.getTrackbarPos("L - S", "Trackbars")
    l_v = cv2.getTrackbarPos("L - V", "Trackbars")
    u_h = cv2.getTrackbarPos("U - H", "Trackbars")
    u_s = cv2.getTrackbarPos("U - S", "Trackbars")
    u_v = cv2.getTrackbarPos("U - V", "Trackbars")

    # 构建HSV上下界
    lower_bound = np.array([l_h, l_s, l_v])
    upper_bound = np.array([u_h, u_s, u_v])

    # 创建掩膜
    mask = cv2.inRange(hsv, lower_bound, upper_bound)
    result = cv2.bitwise_and(image, image, mask=mask)

    # 显示结果
    cv2.imshow("Original", image)
    cv2.imshow("Mask", mask)
    cv2.imshow("Result", result)

    # 按 ESC 退出
    if cv2.waitKey(1) == 27:
        break

cv2.destroyAllWindows()

🖼️ 效果图示意:

+------------------+     +------------------+
|   Original       |     |   Mask (Binary)  |
|                  |     |   白色=匹配区域  |
|  [彩色图像]       |     |   [黑白图]       |
+------------------+     +------------------+

+------------------+
|   Result         |
|   只保留目标色块  |
|   [过滤后图像]    |
+------------------+

👉 你可以拖动滑块,实时看到哪些像素被选中!


🎨 第二步:同时识别多种颜色(红/绿/蓝)

现在我们要一次性找出画面中的三种颜色,并分别标记。

import cv2
import numpy as np

def detect_colors(image):
    hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    
    # 定义各颜色的HSV范围
    color_ranges = {
        'Red': [
            (np.array([0, 100, 100]), np.array([10, 255, 255])),
            (np.array([170, 100, 100]), np.array([180, 255, 255]))  # 红色有两个区间
        ],
        'Green': [(np.array([40, 100, 100]), np.array([80, 255, 255]))],
        'Blue': [(np.array([100, 100, 100]), np.array([130, 255, 255]))]
    }

    output = image.copy()

    for color_name, ranges in color_ranges.items():
        mask = None
        for lower, upper in ranges:
            part_mask = cv2.inRange(hsv, lower, upper)
            mask = part_mask if mask is None else cv2.bitwise_or(mask, part_mask)
        
        # 查找轮廓
        contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        
        for cnt in contours:
            area = cv2.contourArea(cnt)
            if area > 500:  # 过滤小噪点
                x, y, w, h = cv2.boundingRect(cnt)
                cv2.rectangle(output, (x, y), (x+w, y+h), (0, 255, 255), 2)
                cv2.putText(output, color_name, (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 
                           0.9, (0, 255, 255), 2)

        # 可选:显示每个颜色的掩膜
        # cv2.imshow(f'Mask_{color_name}', mask)

    return output

🧪 测试代码

img = cv2.imread('test_colors.jpg')
result_img = detect_colors(img)

cv2.imshow('Detected Colors', result_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

🖼️ 输出效果:

[图像]
🟩 绿色方框标注绿色积木
🟥 红色标签标出红色部分
🟦 蓝色物体也被圈出

✅ 成功实现多色识别!


🚦 第三步:实战案例 —— 模拟交通灯识别

假设你有一张交通灯图像,我们要判断当前亮的是什么灯。

def detect_traffic_light(image):
    hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    
    # 找圆(假设交通灯是圆形)
    circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, 50,
                              param1=50, param2=30, minRadius=10, maxRadius=100)
    
    if circles is None:
        return image, "No traffic light detected"
    
    circles = np.uint16(np.around(circles))
    light_status = "Unknown"

    for circle in circles[0, :]:
        center_x, center_y = circle[0], circle[1]
        radius = circle[2]

        # 创建圆形掩膜
        mask = np.zeros(hsv.shape[:2], dtype=np.uint8)
        cv2.circle(mask, (center_x, center_y), radius, 255, -1)

        # 分区检测颜色(上中下)
        top_roi = mask[center_y-radius:center_y-int(radius*0.3), center_x-radius:center_x+radius]
        mid_roi = mask[center_y-int(radius*0.3):center_y+int(radius*0.3), center_x-radius:center_x+radius]
        bot_roi = mask[center_y+int(radius*0.3):center_y+radius, center_x-radius:center_x+radius]

        # 判断每区是否有红/黄/绿
        red_mask = cv2.inRange(hsv, (0,100,100), (10,255,255))
        red_mask2 = cv2.inRange(hsv, (170,100,100), (180,255,255))
        red_mask = cv2.bitwise_or(red_mask, red_mask2)

        green_mask = cv2.inRange(hsv, (40,100,100), (80,255,255))

        # 统计亮度占比
        def get_color_ratio(color_mask, roi_mask):
            masked = cv2.bitwise_and(color_mask, color_mask, mask=roi_mask)
            return cv2.countNonZero(masked) / (cv2.countNonZero(roi_mask) + 1e-6)

        top_ratio = get_color_ratio(red_mask, top_roi)
        mid_ratio = get_color_ratio(green_mask, mid_roi)

        if top_ratio > 0.5:
            light_status = "Stop (Red)"
            color = (0, 0, 255)
        elif mid_ratio > 0.5:
            light_status = "Go (Green)"
            color = (0, 255, 0)
        else:
            light_status = "Caution (Yellow)"  # 可扩展为黄色检测
            color = (0, 255, 255)

        # 绘制结果
        cv2.circle(image, (center_x, center_y), radius, color, 3)
        cv2.putText(image, light_status, (center_x-50, center_y-80),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.8, color, 2)

    return image, light_status

🧪 测试交通灯识别

img_tl = cv2.imread('traffic_light.jpg')
res, status = detect_traffic_light(img_tl.copy())

cv2.imshow('Traffic Light Detection', res)
print("🚦 当前状态:", status)
cv2.waitKey(0)
cv2.destroyAllWindows()

📊 总结对比表:颜色识别方法优劣

方法 优点 缺点 适用场景
固定HSV阈值 简单快速 光照敏感 实验室环境
动态滑动条 可调参调试 不适合自动化 开发阶段
多颜色识别 支持复杂场景 需处理重叠 工业分拣
结合轮廓/形状 更准确 计算量大 交通标志识别

💡 小贴士与避坑指南

  1. 光照影响极大! → 尽量在均匀光线下测试
  2. 避免颜色混淆:如绿色和青色接近,可加S/V过滤
  3. 性能优化:先缩放图像再处理
  4. 生产建议:结合深度学习模型(如 YOLO+颜色分类)更鲁棒

📦 完整项目结构推荐

project/
│
├── main.py                 # 主程序入口
├── utils/color_detector.py # 颜色识别模块
├── assets/
│   ├── test_colors.jpg     # 测试图
│   └── traffic_light.jpg
└── requirements.txt

🎁 附录:常用HSV范围速查表(打印备用)

颜色 H (Low) H (High) S/V Min
Red 0–10 和 170–180 S≥100, V≥100
Orange 10–25 S≥100, V≥100
Yellow 25–35 S≥100, V≥100
Green 40–80 S≥100, V≥100
Cyan 80–100 S≥100, V≥100
Blue 100–130 S≥100, V≥100
Purple 130–160 S≥100, V≥100
Pink 160–170 S≥100, V≥100

📝 建议:用自己的摄像头 + 实物校准一次最佳参数!


🎯 下一课预告

《OpenCV 第15课:图像处理之模板匹配与特征点检测》

我们将学习:

  • cv2.matchTemplate() 匹配图标
  • SIFT/SURF 特征提取
  • 实现“找茬游戏”或“二维码定位”

📌 点赞 + 收藏,不错过每一节硬核教程!
💬 欢迎留言:“求视频版” 或 “想要摄像头实时识别代码!” 我会持续更新!


📎 示例代码 GitHub 地址:https://github.com/example/opencv-course-14 (请自行创建)

🎯 学会颜色识别,你就掌握了机器“看懂世界”的第一步
2025年昇腾CANN训练营第二季,基于CANN开源开放全场景,推出0基础入门系列、码力全开特辑、开发者案例等专题课程,助力不同阶段开发者快速提升算子开发技能。获得Ascend C算子中级认证,即可领取精美证书,完成社区任务更有机会赢取华为手机,平板、开发板等大奖。\n报名链接:https://www.hiascend.com/developer/activities/cann20252!

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐