'''
形态学操作是根据图像形状进行的简单操作。一般情况下对二值化图像进行的操作。需要输入两个参数,
一个是原始图像,第二个被称为结构化元素或核,它是用来决定操作的性质的。两个基本的形态学操作是
腐蚀和膨胀。他们的变体构成了开运算,闭运算,梯度等。
腐蚀
就像土壤侵蚀一样,这个操作会把前景物体的边界腐蚀掉(但是前景仍然是白色)。这是怎么做到的呢?
卷积核沿着图像滑动,如果与卷积核对应的原图像的所有像素值都是 1,那么中心元素就保持原来的像素
值,否则就变为零。这会产生什么影响呢?根据卷积核的大小靠近前景的所有像素都会被腐蚀掉(变为0),
所以前景物体会变小,整幅图像的白色区域会减少。这对于去除白噪声很有用,也可以用来断开两个连在
一块的物体等。
膨胀
与腐蚀相反,与卷积核对应的原图像的像素值中只要有一个是 1,中心元素的像素值就是 1。所以这个操
作会增加图像中的白色区域(前景)。一般在去噪声时先用腐蚀再用膨胀。因为腐蚀在去掉白噪声的同时,
也会使前景对象变小。所以我们再对他进行膨胀。这时噪声已经被去除了,不会再回来了,但是前景还在
并会增加。膨胀也可以用来连接两个分开的物体。
开运算
先进性腐蚀再进行膨胀就叫做开运算。就像我们上面介绍的那样,它被用来去除噪声。这里我们用到的函
数是 cv2.morphologyEx()。
闭运算
先膨胀再腐蚀。它经常被用来填充前景物体中的小洞,或者前景物体上的小黑点。
形态学梯度
其实就是一幅图像膨胀与腐蚀的差别。结果看上去就像前景物体的轮廓。
礼帽
原始图像与进行开运算之后得到的图像的差。下面的例子是用一个 9x9 的核进行礼帽操作的结果。
黑帽
进行闭运算之后得到的图像与原始图像的差。
'''
import cv2 as cv
import numpy as np
import matplotlib
from matplotlib import pyplot as plt
matplotlib.rcParams['font.family'] = 'SimHei'
img = cv.imread('j.png',0)
kernel = np.ones((5,5),np.uint8)
plt.subplot(231),plt.imshow(img),plt.title(u'原图')
plt.xticks([]), plt.yticks([])
erosion = cv.erode(img,kernel,iterations = 1)
plt.subplot(232),plt.imshow(erosion),plt.title(u'腐蚀')
plt.xticks([]), plt.yticks([])
dilation = cv.dilate(img,kernel,iterations = 1)
plt.subplot(233),plt.imshow(dilation),plt.title(u'膨胀')
plt.xticks([]), plt.yticks([])
gradient = cv.morphologyEx(img, cv.MORPH_GRADIENT, kernel)
plt.subplot(234),plt.imshow(gradient),plt.title(u'形态学梯度')
plt.xticks([]), plt.yticks([])
kernel = np.ones((9,9),np.uint8)
tophat = cv.morphologyEx(img, cv.MORPH_TOPHAT, kernel)
plt.subplot(235),plt.imshow(tophat),plt.title(u'礼帽')
plt.xticks([]), plt.yticks([])
blackhat = cv.morphologyEx(img, cv.MORPH_BLACKHAT, kernel)
plt.subplot(236),plt.imshow(blackhat),plt.title(u'黑帽')
plt.xticks([]), plt.yticks([])
plt.show()
img_open = cv.imread('j-open.png',0)
kernel = np.ones((5,5),np.uint8)
plt.subplot(121),plt.imshow(img_open),plt.title(u'原图')
plt.xticks([]), plt.yticks([])
opening = cv.morphologyEx(img_open, cv.MORPH_OPEN, kernel)
plt.subplot(122),plt.imshow(opening),plt.title(u'开运算')
plt.xticks([]), plt.yticks([])
img_open = cv.imread('j-open.png',0)
kernel = np.ones((5,5),np.uint8)
plt.subplot(121),plt.imshow(img_open),plt.title(u'原图')
plt.xticks([]), plt.yticks([])
opening = cv.morphologyEx(img_open, cv.MORPH_OPEN, kernel)
plt.subplot(122),plt.imshow(opening),plt.title(u'开运算')
plt.xticks([]), plt.yticks([])
plt.show()
img_close = cv.imread('j-close.png',0)
kernel = np.ones((5,5),np.uint8)
plt.subplot(121),plt.imshow(img_close),plt.title(u'原图')
plt.xticks([]), plt.yticks([])
closeing = cv.morphologyEx(img_close, cv.MORPH_OPEN, kernel)
plt.subplot(122),plt.imshow(closeing),plt.title(u'闭运算')
plt.xticks([]), plt.yticks([])
plt.show()



结构化元素
在前面的例子中我们使用 Numpy 构建了结构化元素,它是正方形的。但有时我们需要构建一个椭圆形/圆形的核。
为了实现这种要求,提供了 OpenCV函数 cv2.getStructuringElement()。你只需要告诉他你需要的核的形状和大小。
>>> cv2.getStructuringElement(cv2.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)
>>> cv2.getStructuringElement(cv2.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)
>>> cv2.getStructuringElement(cv2.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)