图像处理
图像阈值
图像阈值分割是一种广泛应用的技术,利用图像中要提取的目标区域与其背景在灰度特性上的差异,把图像看作具有不同灰度级的两类区域的组合(目标区域和背景区域),选取一个合理的阈值,从而选取目标区域,但是两个区域的灰度值差异得明显才能处理,否则会导致错误划分。
图像的阈值处理又称为二值化,阈值也称临界值,即为了确定像素点范围,在这个范围内使用同种方法处理,范围外保持不变。
主要使用的函数:
ret,dst=cv2.threshold(src,thresh,maxval,type)
- 返回值有两个,第一个为ret即二值化处理时的阈值的数值,dst即二值化处理后的图像
- 参数有四个,src表示输入图,一般只能为单通道的灰度图,但三通道的RGB图像也可以进行处理(不过只能根据第一个通道的大小进行处理);thresh表示设定的阈值;maxval表示像素值超过阈值或小于阈值时,所赋予的值;type表示操作类型,总共有5种类型。
- cv2.THRESH_BINARY:大于阈值部分取maxval,否则取0
- cv2.THRESH_BINARY_INV:小于阈值部分取maxval,否则取0
- cv2.THRESH_TRUNC:大于阈值部分设为阈值,否则不变
- cv2.THRESH_TOZERO:大于阈值部分不改变,否则设为0
- cv2.THRESH_TOZERO_INV:小于阈值部分不改变,否则设为0
图像平滑
由于亮度变化过大,图像上会出现一些亮点,即噪声(多余的干扰信息),为了去除图像中的噪声使得图像看起来更平滑,因此有了图像平滑处理,其原理是将噪声所在像素点处理为其周围临近像素点的近似值。主要有如下方法:
均值滤波
均值滤波的时候需要考虑周围多少个像素平均值,即确定卷积核的大小,通常情况下都是以当前像素点为中心,读相同的奇数行和奇数列求平均值,结果作为当前像素点的取值。
如上图,当所求像素点为32时,卷积核为3*3,计算值为:
(
25
+
106
+
178
+
43
+
32
+
167
+
178
+
198
+
200
)
/
9
=
125
(25+106+178+43+32+167+178+198+200)/9=125
(25+106+178+43+32+167+178+198+200)/9=125
计算值得到的125为新值,卷积核为:
[
1
1
1
1
1
1
1
1
1
]
/
(
3
×
3
)
\begin{bmatrix} 1&1&1\\ 1&1&1\\ 1&1&1 \end{bmatrix}/(3\times3)
111111111
/(3×3)
若取值为23,则计算公式为
(
23
+
158
+
199
+
25
)
/
4
(23+158+199+25)/4
(23+158+199+25)/4,即边缘像素点只需计算在图像内部的临近点。
均值滤波通过blur函数实现,blur函数语法格式如下:
blur = cv2.blur(img, ksize,anchor,borderType)
- blur是返回值,表示你均值滤波后得到的结果
- img是输入的原始图像
- ksize表示卷积核的大小
- anchor为锚点,一般默认即可
- borderType是边界样式,即对边界的处理方式
- 一般形式:blur = cv2.blur(img,ksize)
方框滤波
方框滤波是均值滤波的一般形式,基本和均值一样,多了一个normalize参数表示是否归一化,它的取值为True或者1时,表示要进行归一化处理,即需要用临近像素点的和除以临近元素的数量总和(和均值滤波计算一样),反之则不用除,直接加法就可,若临近像素点的和大于255则取值255。
高斯滤波
高斯滤波采用的是对图像进行加权平均的过程,每一个像素点都由其本身和邻域内的其他像素值经过加权平均后得到,均值滤波的邻域内每个像素点的权重值都相同,而高斯滤波中,中心点的权重值最大,远离中心点的权重值减小。其3x3的卷积核可如下所示:
[
0.05
0.1
0.05
0.1
0.4
0.1
0.05
0.1
0.05
]
\begin{bmatrix} 0.05&0.1&0.05\\ 0.1&0.4&0.1\\ 0.05&0.1&0.05 \end{bmatrix}
0.050.10.050.10.40.10.050.10.05
借用均值滤波中的图,则计算公式为:
(
25
∗
0.05
+
106
∗
0.1
+
178
∗
0.05
+
43
∗
0.1
+
32
∗
0.4
+
167
∗
0.1
+
178
∗
0.05
+
198
∗
0.1
+
200
∗
0.05
)
=
93.25
(25*0.05+106*0.1+178*0.05+43*0.1+32*0.4+167*0.1+178*0.05+198*0.1+200*0.05)=93.25
(25∗0.05+106∗0.1+178∗0.05+43∗0.1+32∗0.4+167∗0.1+178∗0.05+198∗0.1+200∗0.05)=93.25
则新值为93.25,这里的卷积核的长和宽可以不同,但是必须是奇数
高斯滤波使用GaussianBlur函数,该函数语法如下:
dst=cv2.GaussianBlur(src,ksize,sigmaX,sigmaY,borderType)
- dst为高斯滤波后得到的图像
- src是输入的图像
- ksize滤波卷积核,例如:(3,3)
- sigmaX,sigmaY分别是卷积在水平方向和垂直方向的标准差
- borderType是边界类型
- 一般形式为:aussian = cv2.GaussianBlur(img,(3,3),0,0)
中值滤波
中值滤波将每一像素点灰度值设置为该点临近区域内所有像素点灰度值的中值(即将周围像素点排列取中位数)。借用上面均值滤波的图,取卷积核为3x3,得值为32的像素点的新值为167
中值滤波的实现函数为medianBlur,其基本语法格式为:
dst = cv2.medianBlur( src, ksize)
- dst为返回的图像
- src为输入的图像
- ksize为卷积核大小,必须为奇数
- 一般形式为median = cv2.medianBlur(img, 5),表示5x5的卷积核
中值滤波可以几乎不影响原有图像的情况下去除所有噪声,但是由于需要排序,因此需要的运算较大。
这里可以使用hstack函数将四种滤波后的图像横着拼接到一起,进行对比,如下分别为均值、高斯、中值滤波拼接到一起的代码,res为拼接后的图像,np为numpy库。
res = np.hstack((blur,aussian,median))
形态学操作
形态学操作的目的是为了提取图像中主要的分量信息,通常用于表达和描绘图像的形状。主要包括如下操作:
腐蚀操作
腐蚀操作能够将图像的边界点消除,使图像沿着边界向内收缩,也可以将小于指定结构体元素的部分去除。主要用来“细化”二值图像中的前景,借此实现去除噪声、元素分割等功能。如下为进行了一次腐蚀操作
在腐蚀过程中,通常使用一个结构体(即核)逐个像素的扫描要被腐蚀的图像,并根据结构元和被腐蚀的图像关系来确定腐蚀结果
如上图,根据两种不同关系来决定腐蚀结果图像中的结构元中心点所对应位置的像素点的像素值。红色为结构元,黄色为结构元中心,白色为前景,黑色为背景。
- 左图,结构元完全处于前景图像中,就将结构元中心点所对应的腐蚀结果图像中的像素点的像素值处理为前景色(白色)
- 右图,结构元未完全处于前景图像中(部分在或者完全不在前景图像中,一般为边界像素点),就将结构元中心点所对应的像素值处理为背景色(黑色)
腐蚀操作通过cv2.erode函数实现,语法格式如下:
dst = cv2.erode(src, kernel, anchor, iterations, borderType, borderValue)
- dst为返回的图像
- src为输入图像
- kernal代表腐蚀操作采用的结构元的大小,结构元越大,一次腐蚀面积越大,也可以自定义,例如:kernel = np.ones((30,30),np.uint8)
- anchor表示锚点的位置,一般默认即可
- iterations腐蚀操作迭代的次数,默认为1,表示一次腐蚀操作
- borderType代表边界样式,一般采用BORDER_CONSTANT为默认值。
- borderValue表示边界值,默认即可
- 一般形式为:erosion_1 = cv2.erode(pie,kernel,iterations = 1)
膨胀操作
膨胀操作可以说是腐蚀操作的逆运算,也是用一个结构元来逐个像素的扫描。
- 如果结构元中任意一点在前景图像中,就将结构元对应的像素点处理为前景色
- 如果结果元完全处于背景图像中,就将膨胀图像中对应像素点处理为背景色
膨胀操作主要由dilate函数实现,基本语法如下:
dst = cv2.dilate(src, kernel, anchor, iterations, borderType, borderValue)
- 各个函数的含义和腐蚀函数erode相同
- 一般形式为:dige_dilate = cv2.dilate(dige_erosion,kernel,iterations = 1)
注:腐蚀一次后再膨胀,与原图不一样,因为腐蚀会改变图像像素点。
腐蚀和膨胀都是形态学的基础,两者的组合可以实现多种运算,OpenCV通过morphologyEx函数实现组合运算,基本语法如下:
dst=cv2.morphologyEx(src,op,kernel,anchor,iterations,borderType,borderValue)
- dst表示经过形态学处理后输出的目标图像
- src表示输入的图像
- op表示操作类型,具体如下
类型 | 说明 | 含义 |
---|---|---|
cv2.MORPH_ERODE | 腐蚀 | 腐蚀 |
cv2.MORPH_DILATE | 膨胀 | 膨胀 |
cv2.MORPH_OPEN | 开运算 | 先腐蚀后膨胀 |
cv2.MORPH_CLOSE | 闭运算 | 先膨胀后腐蚀 |
cv2.MORPH_GRADIENT | 形态学梯度运算 | 膨胀图减腐蚀图 |
cv2.MORPH_TOPHAT | 顶帽运算 | 原始图像减开运算 |
cv2.MORPH_BLACKHAT | 黑帽运算 | 闭运算所得图像减原始图像 |
- 其他参数与erode函数一致
开运算
开运算进行的操作是先将图像腐蚀,再对腐蚀的结果进行膨胀,可以用于去噪、计数(即分割连接的图像),如下图所示:
闭运算
闭运算进行的操作是先将图像膨胀,再对膨胀的结果进行腐蚀,可以用于去除图像上的黑点,也可将独立的图像连接到一起,如下图所示:
梯度运算
图像梯度运算是由图像的膨胀图像减去图像的腐蚀图像,该操作可以获取图像的前景图像的边缘。如下图,左为膨胀图像,右为腐蚀图像
梯度运算后得到的图像为:
礼帽运算
礼貌运算是由原始图像减去开运算图像的操作。礼貌运算可以获得图像的噪声信息,或者得到比原始图像的边缘更亮的边缘信息,如下图:
黑帽运算
黑帽运算是由闭运算图像减去原始图像的操作。黑帽运算能够获取图像内部的小孔,或者前景色中的小黑点,或者得到比原始图像的边缘更暗的边缘部分。
图像梯度算子
图像梯度是指图画强度或色彩的方向改动,对于一副图像,其边缘部分两侧灰度值相差大,梯度值大,而图像梯度一般通过计算像素差值来得到梯度的近似值。
Sobel算子
sobel算子可以计算图像梯度,用于提取边界
以上分别为sobel的三阶卷积核,Gx为计算水平梯度时的卷积核,即右边减左边,当目标点(即A的中心点)左右差别特别大的时候,目标的值会很大,说明该点为边界,同理对于Gy,计算的则是垂直方向梯度,垂直方向上有较大的差值,就说明该点为边界。
sobel算子函数语法如下:
dst = cv2.Sobel(src, ddepth, dx, dy, ksize)
- **src为输入图像
- ddepth表示图像的深度
- dx、dy分别表示水平和竖直方向,取1表示取该方向,0表示不计算该方向
- ksize表示算子的卷积核大小
以下图为例,右边为原图:
计算水平方向的梯度,由于根据sobel算子,可以看成是右减左,即在前景(白色圆圈)的左边缘为白(255)减黑(0)等于正,则为白色;右边缘为黑减白等于负,但是sobel算子对所有负数会被截断成0,即为黑色,因此计算后的图像左边有白色边界,右边只有黑色,为此我们需要在计算减法时取绝对值。如下代码:
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
sobelx = cv2.convertScaleAbs(sobelx) #convertScaleAbs即用来取绝对值
cv_show(sobelx,'sobelx')
可得如下图:
求出水平边界后,还需要垂直边界,但是这里通过比较发现,水平和垂直边界最好分开计算,再进行图像融合,而同时计算水平和垂直边界的结果并不是很好,如下图所示:
左边为分开计算,右边为同时计算,明显左边的边界优于右边。
scharr算子
scharr与sobel算子思想一样,只是卷积核的系数不同,scharr算子提取边界也更加灵敏,能提取到更小的边界,但是也容易造成边界的误判
该算子使用Scharr函数,语法和Sobel函数相同
Laplacian算子
laplacian算子也是用来提取边界的,但是和前面两个不同,该算子本身是一个二阶算子,它的运算规则就是在水平方向运算两次,垂直方向上运算两次,两个结果叠加替换中心点的像素值。算子如下:
该算子使用laplacian函数,不需要拆分xy,即
laplacian = cv2.Laplacian(img,cv2.CV_64F)
laplacian = cv2.convertScaleAbs(laplacian)
该算子对噪声比较敏感,不会单独使用,下图为三个算子的对比:
总结
图像处理有很强力的Python工具包,但是了解底层原理还是非常有必要的