contribute上次修改:2026-09-29

2.7 HSV 颜色检测

总目录:1.1 OpenCV介绍与学习路线 | 第二章:基础图像处理 | 编号:2.7

← 上一节:2.6 形态学处理 | 下一节:3.1 Canny边缘检测 →


2.7 HSV 颜色检测

假设我们要找橙色小车。最直观的办法不是告诉电脑“这看起来很橙”,而是规定:H、S、V 都落在某个范围里的像素留下,其余全部丢掉。

一、颜色检测其实是范围筛选

BGR 原图
   ↓ cvtColor
HSV 图像
   ↓ inRange(lower, upper)
黑白蒙版 mask
   ↓ bitwise_and
只保留目标颜色的结果图

HSV 各通道范围见 四、HSV:把颜色种类和明暗拆开。

二、inRange:生成颜色蒙版

hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

lower = np.array([5, 120, 100], dtype=np.uint8)
upper = np.array([25, 255, 255], dtype=np.uint8)

mask = cv2.inRange(hsv, lower, upper)

注意输入应当是 hsv,原笔记中一处写成了 cv2.inRange(img, ...),那会拿 BGR 数字套 HSV 阈值,结果当然乱跑。

mask 是单通道二值图:

范围内   → 255(白)
范围外   → 0(黑)

三、bitwise_and:把目标颜色抠出来

result = cv2.bitwise_and(image, image, mask=mask)

同一张原图参与按位与,mask 决定哪些位置允许保留。白色位置保留原图,黑色位置变黑。

2.7-image-009

如果最终目的是找轮廓,其实可以直接对 mask 做形态学和 findContours;result 主要用于给人看。

四、用滑动条寻找阈值

import cv2
import numpy as np

def nothing(_):
    pass

cv2.namedWindow("Trackbars", cv2.WINDOW_NORMAL)
cv2.createTrackbar("H min", "Trackbars", 0, 179, nothing)
cv2.createTrackbar("H max", "Trackbars", 179, 179, nothing)
cv2.createTrackbar("S min", "Trackbars", 0, 255, nothing)
cv2.createTrackbar("S max", "Trackbars", 255, 255, nothing)
cv2.createTrackbar("V min", "Trackbars", 0, 255, nothing)
cv2.createTrackbar("V max", "Trackbars", 255, 255, nothing)

while True:
    image = cv2.imread("resources/car.png")
    if image is None:
        raise FileNotFoundError("resources/car.png")

    hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

    lower = np.array([
        cv2.getTrackbarPos("H min", "Trackbars"),
        cv2.getTrackbarPos("S min", "Trackbars"),
        cv2.getTrackbarPos("V min", "Trackbars"),
    ])
    upper = np.array([
        cv2.getTrackbarPos("H max", "Trackbars"),
        cv2.getTrackbarPos("S max", "Trackbars"),
        cv2.getTrackbarPos("V max", "Trackbars"),
    ])

    mask = cv2.inRange(hsv, lower, upper)
    result = cv2.bitwise_and(image, image, mask=mask)

    cv2.imshow("Mask", mask)
    cv2.imshow("Result", result)

    if cv2.waitKey(1) & 0xFF == ord("q"):
        break

cv2.destroyAllWindows()

原笔记里滑动条代码主要以截图存在,新版已经还原成可复制代码。

五、红色为什么经常需要两段范围

色相是一个圆环。红色落在 H 的起点附近,也会绕到末端附近,所以常分两段:

lower_1 = np.array([0, 120, 80])
upper_1 = np.array([10, 255, 255])
lower_2 = np.array([170, 120, 80])
upper_2 = np.array([179, 255, 255])

mask_1 = cv2.inRange(hsv, lower_1, upper_1)
mask_2 = cv2.inRange(hsv, lower_2, upper_2)
mask = cv2.bitwise_or(mask_1, mask_2)

六、颜色阈值怎样保存和验证

不要只在一张图上调到“完美”:

  1. 采集白天、晚上、强光、阴影和不同距离的样本;
  2. 记录阈值,而不是每次打开滑动条重调;
  3. 检查漏检和误检;
  4. 先固定相机曝光和白平衡,再谈稳定阈值;
  5. 对 mask 做轻度 开闭运算,不要用巨大核掩盖阈值错误。

可以把阈值写成清楚的配置:

ORANGE_HSV = {
    "lower": np.array([5, 120, 100], dtype=np.uint8),
    "upper": np.array([25, 255, 255], dtype=np.uint8),
}
2.7-image-011

这张图适合保留,因为它同时展示了原图、HSV、蒙版、结果和滑动条,比单独的代码截图更能说明流程。


← 上一节:2.6 形态学处理 | 下一节:3.1 Canny边缘检测 →

留言区

不收集任何个人信息;昵称只绑定浏览器里的随机 Cookie。