4.5. 形态变换
理论
形态学转换是基于图像形状的一些简单操作。它通常在二进制图像上执行。它需要两个输入参数,一个是我们的原始图像,第二个是称为结构元素或核,它决定了操作的性质。腐蚀和膨胀是两个基本的形态学运算符。然后它的变体形式如开运算,闭运算,梯度等也发挥作用。
腐蚀
腐蚀的基本思想就像土壤侵蚀一样,它会腐蚀前景物体的边界(总是试图保持前景为白色)。它是如何做到的呢?卷积核在图像中滑动(如在2D卷积中),只有当卷积核下的所有像素都是1时,原始图像中的像素(1或0)才会被认为是1,否则它会被腐蚀(变为零)。
所以腐蚀作用后,边界附近的所有像素都将被丢弃,具体取决于卷积核的大小。因此,前景对象的厚度或大小减小,或者图像中的白色区域减小。它有助于消除小的白噪声(正如我们在色彩空间章节中看到的那样),或者分离两个连接的对象等。
膨胀
它恰好与腐蚀相反。这里,如果卷积核下的像素至少一个像素为“1”,则像素元素为“1”。因此它增加了图像中的白色区域或前景对象的大小。通常,在去除噪音的情况下,侵蚀之后是扩张。因为,侵蚀会消除白噪声,但它也会缩小我们的物体,所以我们扩大它。由于噪音消失了,它们不会再回来,但我们的物体区域会增加。它也可用于连接对象的破碎部分。
在这里,作为一个例子,我将使用一个5x5卷积核,其中包含完整的卷积核。让我们看看它是如何工作的:
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread(r'C:\Users\yuyalong\Pictures\Saved Pictures\i.jpg', 0)
kernel = np.ones((5, 5), np.uint8)
# 腐蚀
erosion = cv.erode(img, kernel, iterations=1)
# 膨胀
dilation = cv.dilate(img, kernel, iterations=1)
plt.subplot(1, 3, 1), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(1, 3, 2), plt.imshow(erosion), plt.title('Erosion')
plt.xticks([]), plt.yticks([])
plt.subplot(1, 3, 3), plt.imshow(dilation), plt.title('Dilation')
plt.xticks([]), plt.yticks([])
plt.show()
开运算
开运算是先腐蚀后膨胀的合成步骤。如上所述,它有助于消除噪音。这里我们使用函数cv.morphologyEx()
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread(r'C:\Users\yuyalong\Pictures\Saved Pictures\i1.jpg', 0)
kernel = np.ones((5, 5), np.uint8)
# 开运算
opening = cv.morphologyEx(img, cv.MORPH_OPEN, kernel)
plt.subplot(1, 2, 1), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(1, 2, 2), plt.imshow(opening), plt.title('Opening')
plt.xticks([]), plt.yticks([])
plt.show()
闭运算
闭运算与开运算相反,他是先膨胀后腐蚀的操作。它可用于过滤前景对象内的小孔或对象上的小黑点。
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread(r'C:\Users\yuyalong\Pictures\Saved Pictures\i2.jpg', 0)
kernel = np.ones((5, 5), np.uint8)
# 闭运算
closing = cv.morphologyEx(img, cv.MORPH_CLOSE, kernel)
plt.subplot(1, 2, 1), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(1, 2, 2), plt.imshow(closing), plt.title('Closing')
plt.xticks([]), plt.yticks([])
plt.show()
形态学梯度
它的处理结果是显示膨胀和腐蚀之间的差异。
结果看起来像对象的轮廓。
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread(r'C:\Users\yuyalong\Pictures\Saved Pictures\i.jpg', 0)
kernel = np.ones((5, 5), np.uint8)
# 腐蚀
erosion = cv.erode(img, kernel, iterations=1)
# 膨胀
dilation = cv.dilate(img, kernel, iterations=1)
# 形态学梯度
gradient = cv.morphologyEx(img, cv.MORPH_GRADIENT, kernel)
plt.subplot(1, 4, 1), plt.imshow(img), plt.title('original')
plt.xticks([]), plt.yticks([])
plt.subplot(1, 4, 2), plt.imshow(erosion), plt.title('erosion')
plt.xticks([]), plt.yticks([])
plt.subplot(1, 4, 3), plt.imshow(dilation), plt.title('dilation')
plt.xticks([]), plt.yticks([])
plt.subplot(1, 4, 4), plt.imshow(gradient), plt.title('gradient')
plt.xticks([]), plt.yticks([])
plt.show()
礼貌
它的处理结果是输入图像和开运算之间的区别。下面的示例是针对9x9卷积核完成的。
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread(r'C:\Users\yuyalong\Pictures\Saved Pictures\i1.jpg', 0)
kernel = np.ones((5, 5), np.uint8)
# 开运算
opening = cv.morphologyEx(img, cv.MORPH_OPEN, kernel)
# 礼貌
tophat = cv.morphologyEx(img, cv.MORPH_TOPHAT, kernel)
plt.subplot(1, 3, 1), plt.imshow(img), plt.title('original')
plt.xticks([]), plt.yticks([])
plt.subplot(1, 3, 2), plt.imshow(opening), plt.title('opening')
plt.xticks([]), plt.yticks([])
plt.subplot(1, 3, 3), plt.imshow(tophat), plt.title('tophat')
plt.xticks([]), plt.yticks([])
plt.show()
黑帽
它是输入图像闭运算和输入图像之间的差异。
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread(r'C:\Users\yuyalong\Pictures\Saved Pictures\i2.jpg', 0)
kernel = np.ones((5, 5), np.uint8)
# 闭运算
closing = cv.morphologyEx(img, cv.MORPH_CLOSE, kernel)
# 黑帽
blackhat = cv.morphologyEx(img, cv.MORPH_BLACKHAT, kernel)
plt.subplot(1, 3, 1), plt.imshow(img), plt.title('original')
plt.xticks([]), plt.yticks([])
plt.subplot(1, 3, 2), plt.imshow(closing), plt.title('closing')
plt.xticks([]), plt.yticks([])
plt.subplot(1, 3, 3), plt.imshow(blackhat), plt.title('blackhat')
plt.xticks([]), plt.yticks([])
plt.show()
结构元素
我们在Numpy的帮助下手动创建了前面示例中的结构元素。它是正方形的,但在某些情况下可能需要椭圆或圆形卷积核。所以为此,OpenCV有一个函数cv.getStructuringElement()。只需传递卷积核的形状和大小,即可获得所需的卷积核。
# 矩形内核
cv.getStructuringElement(cv.MORPH_RECT,(5,5))
# 椭圆内核
cv.getStructuringElement(cv.MORPH_ELLIPSE,(5,5))
# 十字形内核
cv.getStructuringElement(cv.MORPH_CROSS,(5,5))