目标
- 掌握不同的形态学操作,例如图像腐蚀(erosion)、图像膨胀(dilation)、图像开运算(opening)和图像闭运算(closing)等
- 掌握几个函数:cv2.erode(), cv2.dilate(), cv2.morphologyEx()等
理论
形态学变换是一些基于图像形状而展开的简单操作,通常是对二值图进行操作。形态学变换需要两个输入,第一个输入是源图像,第二个输入称为结构单元或者卷积核用于确定操作的类型。图像腐蚀和图像膨胀是两类基本的形态学操作。通过组合两类操作可以实现其他复杂操作,比如图像开运算、图像闭运算和图像梯度等。下面我们将通过案例介绍每一种操作的具体实现方法,案例将采用下面的图像进行操作。
1、图像腐蚀
图像腐蚀的基本概念如同土壤侵蚀一样,对前景对象(使用使前景对象为白色)的边缘进行腐蚀。具体操作过程为,使用卷积核的中心点逐个像素扫描原始图像,被扫描到的原始图像中的像素点,只有当卷积核对应的元素值均为1时,其值才为1,否则值为0(腐蚀为0)。
进行腐蚀操作,将使所有边缘区域的像素点根据卷积核尺寸确定是否去除。因此,前景对象的厚度或者尺寸将减少(图像中白色区域减少)。图像腐蚀操作对于去除图像中的小的白色噪声点和分开两个相连区域等操作十分有用。
下面的案例中将使用一个5×5卷积核对图像进行腐蚀操作,其中,卷积核的单元数均为1。
import cv2 as cv
import numpy as np
img = cv.imread('j.png',0)
# 生成5×5卷积核
kernel = np.ones((5,5),np.uint8)
# 图像腐蚀操作
erosion = cv.erode(img,kernel,iterations = 1)
结果如下图,可见前景对象字母变细了,边缘被腐蚀掉了。
2、图像膨胀
图像膨胀和图像腐蚀正好相反。同样,使用卷积核的中心点逐个像素扫描原始图像,被扫描到的原始图像中的像素点,当卷积核对应的元素值只要一个为1时,其值就为1,否则值为0。因此,图像膨胀操作将增加图像中的白色区域(增加前景对象的尺寸)。通常情况下,图像腐蚀后,除去了噪声,但是会压缩图像,对腐蚀过的图像进程膨胀处理,可以去除噪声并保持原有形状。
dilation = cv.dilate(img,kernel,iterations = 1)
结果如下,函数中的第三个参数iterations=1,表示对图像进行一次膨胀操作。
3、图像开运算
图像开运算的另一个名字是先腐蚀再膨胀,该操作应用与去除噪声。
opening = cv.morphologyEx(img, cv.MORPH_OPEN, kernel)
结果如下所示,可以看到左边图像中的噪声点都被除去,同时字母形状基本没有变化。
核心代码为dst = cv2.morphologyEx(src, op, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]])
其中,src为源图像,图像可以是任意通道数的,但是图像深度必须是CV_8U, CV_16U, CV_16S, CV_32F or CV_64F的一个;dst为目标图像,和源图像大小类型相同;op为形态学操作的类型;kernel为卷积核;anchor为卷积核的锚点位置,负值表示锚点在卷积核中心;iteration为进行开运算的次数;borderType为像素外推方法。
4、图像闭运算
图像闭运算与图像开运算相反,是先膨胀处理再腐蚀处理。它有助于关闭前景物体内部的小孔,或者是物体山的小黑点。
closing = cv.morphologyEx(img, cv.MORPH_CLOSE, kernel)
结果如下图所示,可以看到左边字母中的小孔都被填上了。
5、形态学梯度
梯度操作不同于腐蚀图像和膨胀图像,是获得对象的轮廓。
gradient = cv.morphologyEx(img, cv.MORPH_GRADIENT, kernel)
结果如下所示,通过梯度操作获得了字母的轮廓。
6、图像顶帽
顶帽图像是将原始图像减去开运算图像,得到的噪声图像。
import cv2
import numpy as np
o = cv2.imread('image\\tophat.bmp',cv2.IMREAD_UNCHANGED)
k = np.ones((5,5),np.uint8)
r = cv2.morphologyEx(o,cv2.MORPH_TOPHAT,k)
cv2.imshow('original',o)
cv2.imshow('result',r)
cv2.waitKey()
cv2.destroyAllWindows()
结果如下所示,顶帽操作获得图像的噪声图像。
7、图像黑帽
黑帽图像是闭运算图像减去原始图像,可以得到图像内部的小孔或者前景色中的小黑点。
import cv2
import numpy as np
o = cv2.imread('image\\blackhat.bmp',cv2.IMREAD_UNCHANGED)
k = np.ones((5,5),np.uint8)
r = cv2.morphologyEx(o,cv2.MORPH_BLACKHAT,k)
cv2.imshow('original',o)
cv2.imshow('result',r)
cv2.waitKey()
cv2.destroyAllWindows()
结果如下所示,黑帽操作可以获得前景对象中的小孔。
结构单元
在前面的章节中,我们通过Numpy创建方形的结构单元。但是在一些案例中,我们可能需要一些椭圆或者圆形的卷积核。为了实现这个需求,OpenCV提供了一个函数,cv2.getStructuringElement()。输入需要的形状和尺寸就可以获得想要的卷积核。
# 方形卷积核
>>> cv.getStructuringElement(cv.MORPH_RECT,(5,5))
array([[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1]], dtype=uint8)
# 椭圆卷积核
>>> cv.getStructuringElement(cv.MORPH_ELLIPSE,(5,5))
array([[0, 0, 1, 0, 0],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[0, 0, 1, 0, 0]], dtype=uint8)
# 十字卷积核
>>> cv.getStructuringElement(cv.MORPH_CROSS,(5,5))
array([[0, 0, 1, 0, 0],
[0, 0, 1, 0, 0],
[1, 1, 1, 1, 1],
[0, 0, 1, 0, 0],
[0, 0, 1, 0, 0]], dtype=uint8)
参考资料