【OpenCV C++20 学习笔记】范围阈值操作

范围阈值操作

原理

HSV颜色空间

HSV(色相hue, 饱和度sarturation, 色明度value)颜色空间与RGB颜色空间相似。hue色相通道代表颜色类型;saturation饱和度通道代表颜色的饱和度,即颜色的浓淡或深浅、value通道代表颜色的亮度。下面的圆柱体展示了HSV颜色空间的模型:

RGB与HSV颜色空间之间的转换

RGB颜色空间是通过红、绿、蓝3种基本颜色的比例来确定颜色。RGB颜色空间模型如下:

OpenCV默认的是BGR颜色空间(相当于RGB),所以需要将RGB颜色空间转换成HSV颜色空间。在OpenCV中可以使用cv::cvtColor函数来进行不同颜色空间之间的转换。每个通道之间的具体转换方法如下:
V ← m a x ( R , G , B ) V \leftarrow max(R,G,B) \\ V←max(R,G,B)
S ← { V − m i n ( R , G , B ) V if V ≠ 0 0 otherwise S \leftarrow \begin{cases} \frac{V-min(R,G,B)}{V} & \text{if} \quad V \neq 0 \\ 0 & \text{otherwise} \end{cases} \\ S←{VV−min(R,G,B)0ifV=0otherwise
H ← { 60 ( G − B ) / ( V − m i n ( R , G , B ) if V = R 120 + 60 ( B − R ) / ( V − m i n ( R , G , B ) if V = G 240 + 60 ( R − G ) / ( V − m i n ( R , G , ) if V = B 0 if R = G = B H \leftarrow \begin{cases} 60(G-B)/(V-min(R,G,B) & \text{if} \quad V=R \\ 120+60(B-R)/(V-min(R,G,B) & \text{if} \quad V=G \\ 240+60(R-G)/(V-min(R,G,) & \text{if} \quad V=B \\ 0 & \text{if} \quad R=G=B \end{cases} H←⎩ ⎨ ⎧60(G−B)/(V−min(R,G,B)120+60(B−R)/(V−min(R,G,B)240+60(R−G)/(V−min(R,G,)0ifV=RifV=GifV=BifR=G=B

  • 如果计算出的 H < 0 H<0 H<0,则 H ← H + 360 H \leftarrow H+360 H←H+360;
  • 计算出的H, S, V的范围分别是: 0 ≤ H ≤ 360 0 \leq H \leq 360 0≤H≤360, 0 ≤ S ≤ 1 0 \leq S \leq 1 0≤S≤1, 0 ≤ V ≤ 255 0 \leq V \leq 255 0≤V≤255

因为转换后的3个通道的颜色值可能会超出数据类型的最大值,所以还需要进行值域的转换

  • 如果数据是8位: S ← 255 S , H ← H / 2 S \leftarrow 255S, H \leftarrow H/2 S←255S,H←H/2,即最终H, S, V的范围分别是: 0 ≤ H ≤ 180 0 \leq H \leq 180 0≤H≤180, 0 ≤ S ≤ 255 0 \leq S \leq 255 0≤S≤255, 0 ≤ V ≤ 255 0 \leq V \leq 255 0≤V≤255
  • 如果数据是16位:目前还不支持
  • 如果数据是32位:不需要转换

用RGB中的白色来举例。在8位的RGB颜色中,3个通道分别是 R = 255 R=255 R=255, G = 255 G=255 G=255, B = 255 B=255 B=255。根据上面的转换公式,转换成HSV颜色空间,则有:
V ← m a x ( R , G , B ) = 255 V \leftarrow max(R, G, B) = 255 V←max(R,G,B)=255
V ≠ 0 V \neq 0 V=0,所以有:
S ← V − m i n ( R , G , B ) V = 0 S \leftarrow \frac{V-min(R,G,B)}{V} = 0 S←VV−min(R,G,B)=0

因为 R = G = B R = G = B R=G=B,所以有:
H ← 0 H \leftarrow 0 H←0

最后进行值域转换得:
S ← 255 S = 0 , H ← H / 2 = 127 S \leftarrow 255S = 0, H \leftarrow H/2 = 127 S←255S=0,H←H/2=127

所以, R G B ( 255 , 255 , 255 ) RGB(255, 255, 255) RGB(255,255,255)转换成 H S V 为: HSV为: HSV为:HSV(0, 0, 127)$。

下面将展示一个用HSV颜色空间来分离图片中的对象的例子:

代码实现

这里使用的图片是来自下载的OpenCV中自带的示例图片:...\opencv\sources\samples\data\stuff.jpg。原图如下:

将该图片导入为src:

cpp 复制代码
Mat src{ imread("stuff.jpg") };

颜色空间的转换

由于导入的图片默认是BGR模式,所以要使用cv::cvtColor函数进行颜色空间的转换:

cpp 复制代码
Mat src_HSV;
cvtColor(src,		//原图
	src_HSV,		//结果图
	COLOR_BGR2HSV);	//颜色空间转换类型:BGR转为HSV

转换后的效果如下:

范围阈值操作

范围阈值操作由inRange()函数完成,其函数原型如下:

cpp 复制代码
void cv::inRange(InputArray	src,
				InputArray	lowerb,
				InputArray	upperb,
				OutputArray	dst)
  • src:原图
  • lowerb:数组或Scalar类型,表示范围的最小边界
  • upperb:数组或Scalar类型,表示范围的最大边界
  • dst:输出图(与原图尺寸相同,CV_8U类型)

这个函数检查图片中的每个值,看它们是否落在其定义的范围内。其检查方法具体如下:

  • 对于单通道图片
    d s t ( I ) = l o w e r b ( I ) 0 ≤ s r c ( I ) 0 ≤ u p p e r b ( I ) 0 dst(I)=lowerb(I)_0 \leq src(I)_0 \leq upperb(I)_0 dst(I)=lowerb(I)0≤src(I)0≤upperb(I)0
  • 对于双通道图片:
    d s t ( I ) = l o w e r b ( I ) 0 ≤ s r c ( I ) 0 ≤ u p p e r b ( I ) 0 ∧ l o w e r b ( I ) 1 ≤ s r c ( I ) 1 ≤ u p p e r b ( I ) 1 dst(I)=lowerb(I)_0 \leq src(I)_0 \leq upperb(I)_0 \land lowerb(I)_1 \leq src(I)_1 \leq upperb(I)_1 dst(I)=lowerb(I)0≤src(I)0≤upperb(I)0∧lowerb(I)1≤src(I)1≤upperb(I)1
  • 对于3通道图片:由上类推
    d s t ( I ) = l o w e r b ( I ) 0 ≤ s r c ( I ) 0 ≤ u p p e r b ( I ) 0 ∧ l o w e r b ( I ) 1 ≤ s r c ( I ) 1 ≤ u p p e r b ( I ) 1 ∧ l o w e r b ( I ) 2 ≤ s r c ( I ) 2 ≤ u p p e r b ( I ) 2 dst(I)=lowerb(I)_0 \leq src(I)_0 \leq upperb(I)_0 \land \\ \qquad lowerb(I)_1 \leq src(I)_1 \leq upperb(I)_1 \land \\ \qquad lowerb(I)_2 \leq src(I)_2 \leq upperb(I)_2 dst(I)=lowerb(I)0≤src(I)0≤upperb(I)0∧lowerb(I)1≤src(I)1≤upperb(I)1∧lowerb(I)2≤src(I)2≤upperb(I)2
    所以,无论是多少通道的图片,指定的范围都是一个闭区间。如果图片中的某个值低于范围的下边界,那就会被赋予下边界值;同理,如果超过上边界,就会被赋予上边界的值。
    当上下边界是用Scalar类型定义的时候,上面几个公式就不用加小括号来表示数组的下标了。
    注意,转换成HSV格式之后,颜色值的最大值变成了Scalar(180, 255, 255).
    本例中使用最大值为上边界,最大值的1/6为下边界:
cpp 复制代码
Mat dst;
inRange(src_HSV,			//HSV图
	Scalar(30, 37, 37),		//下边界
	Scalar(180, 255, 255),	//上边界
	dst);					//输出图

最终效果如下:

可以看到3个物体被很好的识别出来了,但是另一个物体并没有,而且背景中的暗部也被识别了。

可以变换上下边界,查看不同的效果。

相关推荐
西岸行者5 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意5 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码5 天前
嵌入式学习路线
学习
毛小茛5 天前
计算机系统概论——校验码
学习
babe小鑫5 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms5 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下5 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。5 天前
2026.2.25监控学习
学习
im_AMBER5 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J5 天前
从“Hello World“ 开始 C++
c语言·c++·学习