学习目标
- 学习不同类型的形态学操作,比如腐蚀(
Erosion
),膨胀(Dilation
),开操作(Opening
),闭操作(Closing
)等 - 学习不同的函数,比如,
cv2.erode()
,cv2.dilate()
,cv2.morphologyEx()
等
理论知识
形态学变换是一系列基于图像形状的简单操作。通常处理二值图像,输入有2个参数,1.输入图像;2.结构化元素或核,形状决定该操作的性质。两个基本的形态学操作为腐蚀和膨胀。他们的变体,比如开操作,闭操作,梯度等。我们将使用下面的图像一一介绍形态学操作。
1. Erosion(腐蚀操作)
腐蚀的基本概念类似与土壤腐蚀,通常腐蚀前景的边界。操作过程:定义的核在图像上滑动,图像当前位置的像素为0或者1,只有核内所有的像素都为1,当前像素位置的值为1,否则就是0.
所以,根据核的大小,边界附近的像素为0。所以腐蚀操作相当于是将目标的边界变细。因此适合去除小的白色噪声。函数使用如下:
dst = cv2.erode(src, kernel, iterations)
def erode(src, kernel, dst=None, anchor=None, iterations=None, borderType=None, borderValue=None):
"""
@brief Erodes an image by using a specific structuring
element(结构化元素,比如卷积核).
The function supports the in-place mode. Erosion can be
applied several ( iterations ) times. In case of multi-
channel images, each channel is processed independently.
@param src input image; the number of channels can be
arbitrary, but the depth should be one of CV_8U, CV_16U,
CV_16S, CV_32F or CV_64F.
@param dst output image of the same size and type as src.
@param kernel structuring element used for erosion; if
`element=Mat()`, a `3 x 3` rectangular structuring element is
used. Kernel can be created using #getStructuringElement.
@param anchor position of the anchor within the element;
default value (-1, -1) means that the anchor is at the
element center.
@param iterations number of times erosion is applied.可以对图像
使用多次腐蚀操作.
@param borderType pixel extrapolation method, see
#BorderTypes
@param borderValue border value in case of a constant border
@sa dilate, morphologyEx, getStructuringElement
"""
下面的例子:
class Morphological:
def __init__(self, image):
self.img = cv2.imread(image, 0)
self.kernel = np.ones((5, 5), np.uint8)
self.kernel_tophat = np.ones((9, 9), np.uint8)
def erosion(self):
erosion = cv2.erode(self.img, self.kernel, iterations=1)
return erosion
def dilate(self):
dilation = cv2.dilate(self.img, self.kernel, iterations=1)
return dilation
def opening(self):
opening = cv2.morphologyEx(self.img, cv2.MORPH_OPEN, self.kernel)
return opening
def closing(self):
closing = cv2.morphologyEx(self.img, cv2.MORPH_CLOSE, self.kernel)
return closing
def morphological_gradient(self):
gradient = cv2.morphologyEx(self.img, cv2.MORPH_GRADIENT, self.kernel)
return gradient
def top_hat(self):
tophat = cv2.morphologyEx(self.img, cv2.MORPH_TOPHAT, self.kernel_tophat)
return tophat
def black_hat(self):
blackhat = cv2.morphologyEx(self.img, cv2.MORPH_BLACKHAT, self.kernel)
return blackhat
def show(self, name, result):
cv2.imshow('origin', self.img)
cv2.imshow(name, result)
cv2.waitKey(0)
if __name__ == '__main__':
image = 'j.png' # j_1.png
mor = Morphological(image)
# result1 = mor.erosion()
# result1 = mor.dilate()
# result1 = mor.opening()
# result1 = mor.closing()
# result1 = mor.morphological_gradient()
# result1 = mor.top_hat()
result1 = mor.black_hat()
mor.show('black_hat', result1)
结果如下:
2. Dilation(膨胀)
相当于腐蚀的反操作,只要图像的至少一个非零元素在核内,那么该位置的像素值置1。通常会增粗边界。一般情况下,在去除噪声时,会先腐蚀后膨胀:腐蚀将白色噪声去除,但是会边界变细,那么膨胀可以补边界。
dilation = cv2.dilate(img,kernel,iterations = 1)
def dilate(src, kernel, dst=None, anchor=None, iterations=None, borderType=None, borderValue=None):
"""
@brief Dilates an image by using a specific structuring
element.
The function supports the in-place mode. Dilation can be
applied several ( iterations ) times. In case of multi-
channel images, each channel is processed independently.
@param src input image; the number of channels can be
arbitrary, but the depth should be one of CV_8U, CV_16U,
CV_16S, CV_32F or CV_64F.
@param dst output image of the same size and type as src.
@param kernel structuring element used for dilation; if
elemenat=Mat(), a 3 x 3 rectangular structuring element is
used. Kernel can be created using #getStructuringElement
@param anchor position of the anchor within the element;
default value (-1, -1) means that the anchor is at the
element center.
@param iterations number of times dilation is applied.
@param borderType pixel extrapolation method, see
#BorderTypes
@param borderValue border value in case of a constant border
@sa erode, morphologyEx, getStructuringElement
"""
示例代码见上述的使用,运行结果如下:
3. Opening
开操作:腐蚀+膨胀的连续处理。常用在去除噪声,下面介绍函数的使用:
cv2.morphologyEx(src, op, kernel)
上面的op
主要包括下面的类型:
"""
enum MorphTypes{
MORPH_ERODE = 0, //腐蚀
MORPH_DILATE = 1, //膨胀
MORPH_OPEN = 2, //开操作
MORPH_CLOSE = 3, //闭操作
MORPH_GRADIENT = 4, //梯度操作
MORPH_TOPHAT = 5, //顶帽操作
MORPH_BLACKHAT = 6, //黑帽操作
MORPH_HITMISS = 7
};
"""
def morphologyEx(src, op, kernel, dst=None, anchor=None, iterations=None, borderType=None, borderValue=None):
"""
@brief Performs advanced morphological transformations.
@param src Source image. The number of channels can be
arbitrary. The depth should be one of CV_8U, CV_16U, CV_16S,
CV_32F or CV_64F.
@param dst Destination image of the same size and type as
source image.
@param op Type of a morphological operation, see #MorphTypes
@param kernel Structuring element. It can be created using
#getStructuringElement.
@param anchor Anchor position with the kernel. Negative
values mean that the anchor is at the kernel center.
@param iterations Number of times erosion and dilation are
applied.
@param borderType Pixel extrapolation method, see
#BorderTypes
@param borderValue Border value in case of a constant
border. The default value has a special meaning.
当iteration大于1时,注意下面的操作顺序:
@note The number of iterations is the number of times
erosion or dilatation operation will be applied.
For instance, an opening operation (#MORPH_OPEN) with two
iterations is equivalent to apply
successively: erode -> erode -> dilate -> dilate (and not
erode -> dilate -> erode -> dilate).
"""
示例代码见上述的使用,运行结果如下:
4. Closing
闭操作是开操作的逆过程:膨胀+腐蚀。常用于去除前景目标里面的小洞,或者目标里面小的黑色点。
代码在上面,运行结果如下:
5. Morphological Gradient(形态学梯度)
具体操作如下:梯度 = 膨胀-腐蚀,结果类似于得到目标的外轮廓。
运行结果如下:
6. Top Hat(顶帽操作)
顶帽 = Img - Opening,输入图像与开操作的差。
代码如上,核的大小为9x9,运行结果如下:
7. Black Hat(黑帽)
黑帽 = 闭运算-原始输入。
Structuring Element(结构化元素)
在上面的例子中,我们可以通过Numpy手动创建结构化元素,它是矩形。但是在一些情况下,我们需要特定形状的核。鉴于此,OpenCV提供了函数,具体的定义和使用如下:
cv2.getStructuringElement(shape, ksize, anchor=None)
"""
@brief: 生成特定形状的核或着结构化元素
@shape:结构化元素的形状
矩形:MORPH_RECT = 0
交叉形::MORPH_CROSS = 1
椭圆形::MORPH_ELLIPSE = 2
@ksize:核的大小
"""
# Rectangular Kernel
a = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
# Elliptical Kernel
b = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
# Cross-shaped Kernel
c = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5))
print(a)
print('=============')
print(b)
print('=============')
print(c)
print('=============')
输出如下:
[[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]]
=============
[[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]]
=============
[[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]]
=============