【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个物体被很好的识别出来了,但是另一个物体并没有,而且背景中的暗部也被识别了。

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

相关推荐
丢爸1 小时前
网络学习-eNSP配置NAT
linux·网络·学习
宜向华1 小时前
opencv 实现两个图片的拼接去重功能
人工智能·opencv·计算机视觉
YHPsophie1 小时前
AT3340-6T杭州中科微BDS定位授时板卡性能指标
经验分享·笔记·学习·车载系统·信息与通信
robot_大菜鸟2 小时前
python_openCV_计算图片中的区域的黑色比例
开发语言·python·opencv
6230_2 小时前
关于HTTP通讯流程知识点补充—常见状态码及常见请求方式
前端·javascript·网络·网络协议·学习·http·html
日记成书3 小时前
【无线通信发展史⑨】1791年路易吉·伽伐尼-关于动物电的研究与1800年亚历山大·伏打伯爵-电池:伏打电池
网络·人工智能·学习·职场和发展·信息与通信
贾saisai3 小时前
Xilinx系FPGA学习笔记(四)VIO、ISSP(Altera)及串口学习
笔记·学习·fpga开发
月夕花晨3743 小时前
C++学习笔记(13)
c++·笔记·学习
吃什么芹菜卷4 小时前
机器学习:opencv--图像边缘检测
人工智能·opencv·计算机视觉
probably1214 小时前
学习记录之Java学习笔记3
java·笔记·学习