机器视觉opencv教程(四):图像颜色识别与颜色替换
图像颜色识别与颜色替换
前言
图像颜色识别与替换是计算机视觉的基础应用(如交通灯识别、产品分拣、图像美化等),核心是通过HSV 颜色空间实现更直观的颜色定位,并借助掩膜(Mask) 选择性提取或修改目标颜色。相较于 RGB 颜色空间,HSV 更符合人类对颜色的感知逻辑,是颜色相关任务的首选方案。
一、核心基础:HSV 颜色空间
HSV
(Hue - 色调、Saturation - 饱和度、Value - 亮度)是一种基于人眼对颜色感知的颜色模型,与 RGB
(红、绿、蓝)的 “三原色叠加” 逻辑完全不同,更适合颜色分割与识别。
1.1 HSV 与 RGB 的核心区别
维度 | RGB 颜色空间 | HSV 颜色空间 |
---|---|---|
表示逻辑 | 加色法模型,通过红、绿、蓝三通道强度叠加表示颜色 | 基于 “颜色属性”,通过色调、饱和度、亮度三维度描述颜色 |
人类感知匹配 | 不直观(调整红色需同时修改 R、G、B 通道) | 高度匹配(人类对颜色的认知就是 “什么色、艳不艳、亮不亮”) |
颜色分割难度 | 高(通道耦合性强,易受光照影响) | 低(色调 H 独立区分颜色,光照影响可通过亮度 V 调整) |
适用场景 | 图像显示(屏幕、相机) | 颜色识别、分割、替换(计算机视觉任务) |
1.2 HSV 三分量详解
HSV 通过三个独立分量精准描述颜色,各分量的定义、取值范围(含 OpenCV 适配)及意义如下:
分量 | 中文名称 | 定义(核心作用) | 标准取值范围 | OpenCV 取值范围(8 位像素) | 关键说明 |
---|---|---|---|---|---|
H | 色调 | 区分颜色种类(如红、黄、绿) | 0°~360° | 0~179(360° 压缩为 0~179) | 0 = 红,60 = 黄,120 = 绿,180 = 青,240 = 蓝,300 = 紫 |
S | 饱和度 | 描述颜色纯度(鲜艳程度) | 0%~100% | 0~255 | S=0→灰度(无色),S=255→纯色(最鲜艳) |
V | 亮度 | 描述颜色明暗程度 | 0%~100% | 0~255 | V=0→黑色,V=255→最亮(白色或纯色最亮状态) |
例:纯红色的 HSV 值(标准)为 (0°, 100%, 100%),在 OpenCV 中对应 (0, 255, 255);浅红色(偏灰、较暗)可能为 (0, 100, 150)。
1.3 HSV 色域图
HSV 色域可理解为“圆锥体”
结构:
- 圆锥的高度对应亮度 V(从下到上:0→255,黑色→白色);
- 圆锥的半径对应饱和度 S(从中心到边缘:0→255,灰度→纯色);
- 圆锥的圆周角度对应色调 H(0°→360°,红→黄→绿→蓝→紫→红)。
二、关键操作:掩膜(Mask)
掩膜
是颜色识别与替换的 “筛选工具”,通过二值化
图像实现对目标区域的选择性操作。
2.1 掩膜的定义与特点
-
定义:与原始图像尺寸完全相同的二值化图像(仅含 0 和 255 两个像素值)。
-
核心特点:
- 白色区域(像素值 = 255):“保留区”—— 后续操作仅作用于该区域;
- 黑色区域(像素值 = 0):“遮挡区”—— 后续操作忽略该区域。
2.2 掩膜的核心作用
- 选择性提取:仅保留图像中符合条件的区域(如 “只提取黄色区域”);
- 选择性修改:仅修改图像中符合条件的区域(如 “只把红色区域换成蓝色”);
- 噪声过滤:结合形态学操作(如开运算),去除掩膜中的细小杂点。
三、实战 1:图像颜色识别(提取目标颜色)
通过 “HSV 转换→生成掩膜→位与运算” 三步,从图像中精准提取指定颜色(以提取黄色为例)。
# 图像颜色识别:提取指定颜色(黄色)
import cv2
import numpy as np# 步骤1:图像读取与预处理
# cv2.imread:读取图像(OpenCV默认读取为BGR格式,非RGB)
image_np = cv2.imread('./color.png')
# cv2.resize:调整图像大小(700x700),方便观察(非必需,按需调整)
image_np = cv2.resize(image_np, (700, 700))# 步骤2:将BGR图像转换为HSV图像(颜色识别的关键一步)
# cv2.COLOR_BGR2HSV:OpenCV专用的BGR→HSV转换标识(必须用这个,不能用COLOR_RGB2HSV)
hsv_image_np = cv2.cvtColor(image_np, cv2.COLOR_BGR2HSV)# 步骤3:定义目标颜色(黄色)的HSV范围,生成掩膜
# lowerb:HSV范围下限(H=26, S=43, V=46)——黄色的最低阈值
lowerb = np.array([26, 43, 46])
# upperb:HSV范围上限(H=34, S=255, V=255)——黄色的最高阈值
upperb = np.array([34, 255, 255])
# cv2.inRange:生成掩膜——HSV值在[lowerb, upperb]内的像素设为255(白),否则设为0(黑)
mask_image_np = cv2.inRange(hsv_image_np, lowerb, upperb)# 步骤4:位与运算——用掩膜提取目标颜色
# cv2.bitwise_and:仅保留掩膜中白色区域(255)的原图像素,其他区域设为0(黑)
color_image_np = cv2.bitwise_and(image_np, image_np, mask=mask_image_np)# 步骤5:显示结果
cv2.imshow("掩膜(Mask)", mask_image_np) # 显示掩膜
cv2.imshow("提取的黄色区域", color_image_np) # 显示提取的目标颜色
cv2.waitKey(0) # 等待任意按键关闭窗口(0表示无限等待)
cv2.destroyAllWindows() # 释放窗口资源
3.2 关键步骤解析
(1)图像读取:注意 BGR 格式
- OpenCV 的
cv2.imread()
默认将图像读取为BGR 格式(红和蓝通道颠倒),后续转 HSV 必须用cv2.COLOR_BGR2HSV
,而非COLOR_RGB2HSV
(否则颜色转换错误)。
(2)HSV 范围确定:核心难点
- 目标颜色的 HSV 范围(
lowerb
和upperb
)需根据实际需求调整,可通过 “颜色拾取工具” 或 “调试法” 确定(例:红色的 HSV 范围可能是[0,43,46]~[10,255,255]
)。 - 参考常用颜色的 OpenCV-HSV 范围:
红色:[0,43,46]~[10,255,255]
| 绿色:[35,43,46]~[77,255,255]
| 蓝色:[100,43,46]~[124,255,255]
(3)位与运算:掩膜的应用
cv2.bitwise_and(src1, src2, mask)
:当src1=src2=原图
时,掩膜mask
会筛选出原图中需保留的区域 —— 仅mask=255
的位置保留src1&src2
(即原图像素),mask=0
的位置结果为 0(黑色)。
四、实战 2:图片颜色替换(修改目标颜色)
在颜色识别的基础上,增加 “掩膜优化(开运算去噪)” 和 “颜色替换” 步骤,将指定颜色修改为目标颜色(以 “红色→蓝色” 为例)。
4.1 完整代码
# 图片颜色替换:将红色区域替换为蓝色
import cv2
import numpy as np# 步骤1:图像读取与初始显示
image_np = cv2.imread('./color.png')
cv2.imshow("原始图像", image_np) # 显示原图# 步骤2:将BGR图像转换为HSV图像(减少光照干扰,便于颜色定位)
image_hsv = cv2.cvtColor(image_np, cv2.COLOR_BGR2HSV)# 步骤3:制作目标颜色(红色)的掩膜,并优化(开运算去噪)
# 3.1 定义红色的HSV范围
color_lower = np.array([0, 43, 46]) # 红色HSV下限
color_high = np.array([10, 255, 255]) # 红色HSV上限
# 3.2 生成初始掩膜
image_mask = cv2.inRange(image_hsv, color_lower, color_high)
# 3.3 开运算:先腐蚀再膨胀,去除掩膜中的细小白色噪点(避免替换时误改杂点)
# 构建3x3矩形核(结构化元素)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
# 执行开运算(cv2.MORPH_OPEN表示开运算)
image_mask_open = cv2.morphologyEx(image_mask, cv2.MORPH_OPEN, kernel)# 步骤4:执行颜色替换(核心步骤)
# 方式1:数组索引(高效,推荐)——直接将掩膜白色区域的像素设为蓝色(BGR格式:(255,0,0))
image_np[image_mask_open == 255] = (255, 0, 0) # 注意:OpenCV中蓝色是(255,0,0),非(0,0,255)# 方式2:循环遍历(低效,不推荐)——适合理解原理,大图像耗时
# for i in range(image_mask_open.shape[0]): # 遍历行(高度)
# for j in range(image_mask_open.shape[1]): # 遍历列(宽度)
# if image_mask_open[i, j] == 255: # 若为掩膜白色区域
# image_np[i, j] = (255, 0, 0) # 替换为蓝色# 步骤5:显示结果
cv2.imshow("优化后的掩膜", image_mask_open) # 显示去噪后的掩膜
cv2.imshow("颜色替换后图像", image_np) # 显示替换结果
cv2.waitKey(0)
cv2.destroyAllWindows()
4.2 关键步骤解析
(1)掩膜优化:开运算的作用
- 初始掩膜(
image_mask
)可能包含细小白色噪点(如图像中的灰尘、光斑),若直接用于替换,会导致 “误改无关区域”。 - 开运算(
cv2.MORPH_OPEN
):先腐蚀(消除小噪点)再膨胀(恢复目标区域大小),既能去除噪点,又不影响目标区域的整体形状。
(2)颜色替换:注意 BGR 通道顺序
- OpenCV 中颜色用BGR 格式表示,而非 RGB:
蓝色 → (255, 0, 0)(B=255,G=0,R=0);
红色 → (0, 0, 255)(B=0,G=0,R=255);
绿色 → (0, 255, 0)(B=0,G=255,R=0)。 - 数组索引方式(
image_np[image_mask_open == 255] = (255,0,0)
)比循环遍历高效 10~100 倍,尤其适合大图像。
五、关键总结与注意事项
5.1 常见问题与解决方案
- 颜色提取错误:
- 原因:HSV 范围设置不当,或用了
COLOR_RGB2HSV
(而非COLOR_BGR2HSV
)。 - 解决:调整
lowerb
和upperb
(参考常用颜色范围),确保转换标识正确。
- 原因:HSV 范围设置不当,或用了
- 替换后有杂色:
- 原因:掩膜含细小噪点,导致误替换。
- 解决:增加开运算(
cv2.MORPH_OPEN
),或调大核尺寸(如(5,5)
)。
- 颜色替换后显示异常:
- 原因:混淆 BGR 和 RGB 格式(如将蓝色设为
(0,0,255)
)。 - 解决:牢记 OpenCV 用 BGR,蓝色是
(255,0,0)
,红色是(0,0,255)
。
- 原因:混淆 BGR 和 RGB 格式(如将蓝色设为
5.2 操作流程梳理
任务 | 核心流程 | 关键差异 |
---|---|---|
颜色识别 | 读取图像→BGR 转 HSV→生成掩膜→位与提取→显示 | 无需形态学操作(简单场景),需位与运算 |
颜色替换 | 读取图像→BGR 转 HSV→掩膜制作→开运算去噪→颜色替换→显示 | 需掩膜优化(开运算),需数组索引替换 |