本篇学习笔记主要内容在图像的形态学操作,图像梯度内容。
获取更多可以查看本栏目其他文章。
往期内容:
OpenCV-Python图像处理学习笔记(一)——认识、安装、环境测试
OpenCV-Python图像处理学习笔记(二)——图像/视频读取保存、分割及边界填充
OpenCV-Python图像处理学习笔记(三)——数值运算、图像阈值、图像平滑(滤波)
OpenCV-Python图像处理学习笔记(四)——形态学操作、图像梯度
OpenCV-Python图像处理学习笔记(五)——Canny 边缘检测、图像金字塔、轮廓检测(一)OpenCV-Python图像处理学习笔记(六)——轮廓检测(二)、模板匹配
OpenCV-Python图像处理学习笔记(七)——直方图、图像变换
目录
导入必要Python包
import cv2
import numpy as np
import matplotlib.pyplot as plt
1 形态学操作
形态学操作是根据图像形状进行的简单操作。一般情况下对二值化图像进行的操作。两个基本的形态学操作是腐蚀和膨胀。他们的变体构成了开运算,闭运算,梯度等。
1.1 腐蚀
就像土壤侵蚀一样,这个操作会把图像边界腐蚀掉。具体操作是卷积核沿着图像滑动,如果与卷积核对应的原图像的所有像素值都是 1,那么中心元素就保持原来的像素值,否则就变为零。
img = cv2.imread('j.png', 0)
kernel = np.ones((5,5))
erosion = cv2.erode(img, kernel, iterations=1) # 参数iterations表示腐蚀效果,数值越大腐蚀逐渐增强
经过上述代码执行后,下列左侧图将会变为右侧图:
1.2 膨胀
膨胀和腐蚀像是反方向的操作,与卷积核对应的原图像的像素值中只要有一个是1,中心元素的像素值就是 1。所以这个操作会增加图像中的白色区域(前景)。一般在去噪声时先用腐蚀再用膨胀。因为腐蚀在去掉白噪声的同时,也会使前景对象变小。所以我们再对他进行膨胀。这时噪声已经被去除了,不会再回来了,但是前景还在并会增加。膨胀也可以用来连接两个分开的物体。
dilation = cv2.dilate(img, kernel, iterations = 1)
上述代码执行完后,原图像将变成下图效果:
1.3 开运算
先进性腐蚀再进行膨胀就叫做开运算。就像我们上面介绍的那样,它被用 来去除噪声。
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
cv2.MORPH_OPEN表示进行开运算。
1.4 闭运算
先膨胀再腐蚀。它经常被用来填充前景物体中的小洞,或者前景物体上的 小黑点。
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
cv2.MORPH_CLOSE表示进行开运算。
1.5 梯度计算
梯度 = 膨胀 - 腐蚀
即得到结果类似图像的轮廓。
gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)
cv2.MORPH_GRADIENT表示进行开运算。
1.6 礼帽与黑帽
- 礼帽 = 原始图像 - 开运算输出图像
- 黑帽 = 闭运算输出图像 - 原始图像
# 礼帽
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
# 黑帽
blackhat = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel)
- cv2.MORPH_TOPHAT表示进行礼帽。
- cv2.MORPH_BLACKHAT表示进行黑帽。
1.7 形态学操作之间的关系
- 开运算:dst = open(src, element) = dilate(erode(src, element), element)
- 闭运算:dst = close(src, element) = erode(dilate(src, element), element)
- 梯度计算:dst = morph_grad(src, element) = dilate(src, element) - erode(src, element)
- 礼帽:dst = tophat(src, element)=src - open(src, element)
- 黑帽:dst = blackhat(src, element) =close(src, element) - src
2 图像梯度
梯度简单来说就是求导。
OpenCV 提供了三种不同的梯度滤波器,或者说高通滤波器:Sobel, Scharr 和 Laplacian。
- Sobel,Scharr 其实就是求一阶或二阶导数
- Scharr 是对Sobel(使用小的卷积核求解求解梯度角度时)的优化
- Laplacian 是求二阶导数
2.1 Sobel算子
Sobel算子是高斯平滑与微分操作的结合体,所以它的抗噪声能力很好。
3x3 的Sobel滤波器卷积核如下:
右 - 左 或 上 - 下
img=cv2.imread('dave.jpg')
laplacian = cv2.Laplacian(img, cv2.CV_64F)
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=5)
sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=5)
- cv2.CV_64F 输出图像的深度(数据类型),可以使用-1, 与原图像保持一致。一个从黑到白的边界 的导数是整数,而一个从白到黑的边界点导数却是负数。如果原图像的深度是 np.int8 时,所有的负值都会被截断变成 0,换句话说就是把把边界丢失掉。
- 参数 1,0 为只在水平方向求一阶导数,最大可以求 2 阶导数。
- 参数 0,1 为只在竖直方向求一阶导数,最大可以求 2 阶导数。
- ksize是sobel算子大小。
根据上述方式计算的结果往往不尽人意,或者说图像不是完整的,那么可以通过下来方式,得到完整的梯度图像。
首先有一个原始图像如下:
先在水平方向进行梯度计算,再在竖直方向进行计算,然后分别对它们结果进行abs求绝对值,此时再将两组结果进行加权求和。
img = cv2.imread('lena.jpg', cv2.IMREAD_GRAYSCALE)
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3)
sobelx = cv2.convertScaleAbs(sobelx)
sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3)
sobely=cv2.convertScaleAbs(sobely)
sobelxy = cv2.addWeighted(sobelx, 0.5, sobely, 0.5, 0)
cv_show(sobelxy,' sobelky')
最终结果如下图:
2.2 Scharr算子
如果 ksize=-1,会使用 3x3 的 Scharr 滤波器,它的的效果要比3x3的Sobel滤波器好,而且速度相同,3x3 的Scharr滤波器卷积核如下:
计算类似Sobel算子。
2.3 Laplacian算子
拉普拉斯算子可以使用二阶导数的形式定义,可假设其离散实现类似于二 阶Sobel导数,公式为:
拉普拉斯滤波器使用的卷积核:
拉普拉斯算子一般情况对噪音点较为敏感,效果不是很好,通常会结合其他方法进行使用,不单独使用。
2.4 梯度计算方法
不同算子计算方法差异:
img = cv2.imread('lena.jpg', cv2.IMREAD_GRAYSCALE)
sobelx = cv2.Sobel(img, cv2.CV_64F,1,0,ksize=3)
sobely = cv2.Sobel(img, cv2.CV_64F,0,1,ksize=3)
sobelx = cv2.convertScaleAbs(sobelx)
sobely = cv2.convertScaleAbs(sobely)
sobelxy = cv2.addWeighted(sobelx, 0.5, sobely, 0.5, 0)
scharrx = cv2.Scharr(img, cv2.CV_64F, 1, 0)
scharry = cv2.Scharr(img, cv2.CV_64F, 0, 1)
scharrx = cv2.convertScaleAbs(scharrx)
scharry = cv2.convertScaleAbs(scharry)
scharrxy = cv2.addWeighted(scharrx, 0.5, scharry, 0.5, 0)
laplacian =cv2.Laplacian(img, cv2.CV_64F)
laplacian = cv2.convertScaleAbs(laplacian)
res = np.hstack((sobelxy, scharrxy, laplacian))
cv_show(res, 'res')
下图为上述三种算子的输出结果:
不难看出,Scharr算子相对敏感,能够捕捉较为丰富的信息。
3 小结
下期将继续同步OpenCV边缘检测等内容。