数字图像处理之形态学处理

1.概述

形态学操作是根据图像形状进行的简单操作。一般情况下对二值化图像进行的操作。需要输入两个参数,一个是原始图像,第二个被称为结构化元素或核,它是用来决定操作的性质的。两个基本的形态学操作是腐蚀和膨胀。他们的变体构成了开运算,闭运算,梯度等。

通用函数

1
morphologyEx(Mat src, Mat dst, int op, Mat kernel)
  • src:输入图像
  • dst:输出图像
  • op:模式
  • kernel:卷积核(和上一篇一样的获取方法)

模式

  • MORPH_ERODE = 0:腐蚀
  • MORPH_DILATE = 1:膨胀
  • MORPH_OPEN = 2:开造作
  • MORPH_CLOSE = 3:闭操作
  • MORPH_GRADIENT = 4:基本梯度
  • MORPH_TOPHAT = 5:顶帽
  • MORPH_BLACKHAT = 6:黑帽

2.腐蚀

这个操作会把前景物体的边界腐蚀掉(但是前景仍然是白色)。

根据卷积核的大小靠近前景的所有像素都会被腐蚀掉(变为 0),所以前景物体会变小,整幅图像的白色区域会减少。这对于去除白噪声很有用,也可以用来断开两个连在一块的物体等。

1
2
3
4
5
6
7
8
9
#erode函数
K1 = np.ones((1,3), np.uint8)
img_erode = cv.erode(img, K1)
show(img_erode)

#通用函数
K1 = np.ones((2,3), np.uint8)
img_erode2 = cv.morphologyEx(img, cv.MORPH_ERODE, K1)
show(img_erode2)

3.膨胀

与腐蚀相反。一般在去噪声时先用腐蚀再用膨胀。因为腐蚀在去掉白噪声的同时,也会使前景对象变小。所以我们再对他进行膨胀。这时噪声已经被去除了,不会再回来了,但是前景还在并会增加。膨胀也可以用来连接两个分开的物体。

1
2
3
4
5
6
7
8
9
#dilate函数
K = cv.getStructuringElement(cv.MORPH_RECT, (3,1))
img_dilate = cv.dilate(img, K)

#通用函数
K2 = cv.getStructuringElement(cv.MORPH_RECT, (1,3))
img_dilate2 = cv.morphologyEx(img, cv.MORPH_DILATE, K2)

show(np.hstack([img, img_dilate, img_dilate2]))

4.开运算

先进性腐蚀再进行膨胀就叫做开运算。

image.png

1
2
3
4
5
6
7
K = cv.getStructuringElement(cv.MORPH_RECT, (3,3))
# 使用erode和dilate函数
img_open = cv.dilate(cv.erode(img, K), K)
# 通用函数
img_open2 = cv.morphologyEx(img, cv.MORPH_OPEN, K)

show(np.hstack([img, img_open, img_open2]))

5.闭运算

先膨胀再腐蚀。它经常被用来填充前景物体中的小洞,或者前景物体上的小黑点。

image.png

1
2
3
4
5
6
7
8
9
img = cv.imread('pic/bulk_bin120x100.png', -1)
K = cv.getStructuringElement(cv.MORPH_RECT, (3,3))

# 使用erode和dilate函数
img_close1 = cv.erode(cv.dilate(img, K), K)
#通用函数
img_close = cv.morphologyEx(img, cv.MORPH_CLOSE, K)

show(np.hstack([img, img_close]))

6.形态学梯度

其实就是一幅图像膨胀与腐蚀的差别。

结果看上去就像前景物体的轮廓。

image.png

1
2
3
4
5
6
7
8
9


K = np.ones((3,3), np.uint8)
#morphologyEx函数
img_grad1 = cv.morphologyEx(img, cv.MORPH_GRADIENT, K)
#通用函数
img_grad2 = cv.subtract(cv.dilate(img, K), img)

show(np.hstack([img, img_grad1, img_grad2]))

7.顶帽变换

原始图像与进行开运算之后得到的图像的差。

image.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#使用通用函数
I = cv.imread('pic/page760x900.jpg', 0)
#反转
Ic = 255 - I
#开运算核
kernel = np.ones((21, 21), np.uint8)
#礼帽变换/顶帽变换
tophat = cv.morphologyEx(Ic, cv.MORPH_TOPHAT, kernel)
#二值化
_, Ic_bin = cv.threshold(tophat, 25, 255, 0)
#反转
I_bin = 255 - Ic_bin
show(I_bin)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#完整步骤
# 1读图
I = cv.imread('pic/page760x900.jpg', 0)
# 2反转
Ic = 255 - I
# 3开运算
K = np.ones((21, 21), np.uint8)
Ic_open = cv.morphologyEx(Ic, cv.MORPH_OPEN, K)
# 4帽顶变换(梯度)
Ic_tophat = cv.subtract(Ic, Ic_open)
# 5二值化
_, Ic_bin = cv.threshold(Ic_tophat, 25, 255, 0)
# 6反转变回
I_bin = 255 - Ic_bin

8.底帽变换

进行闭运算之后得到的图像与原始图像的差。

1
2
3
4
5
6
7
8
9
10
11
# 1
I = cv.imread('pic/page760x900.jpg', 0)

K = np.ones((21, 21), np.uint8)
I_blackhat = cv.morphologyEx(I, cv.MORPH_BLACKHAT, K)

_, Ic_bin = cv.threshold(I_blackhat, 25, 255, 0)

I_bin = 255 - Ic_bin

show(I_bin)

处理后的图像

  • 开操作和顶帽 : 开操作有的顶帽没有,顶帽有的开操作没有
  • 闭操作和黑帽 : 闭操作有的黑帽没有,黑帽有的闭操作没有

9.击中击不中变换

  击中击不中变换是形态学中用来检测特定形状所处位置的一个基本工具。它的原理就是使用腐蚀;如果要在一幅图像A上找到B形状的目标:

首先,建立一个比B大的模板W;使用此模板对图像A进行腐蚀,得到图像假设为Process1;

其次,用B减去W,从而得到V模板(W-B);使用V模板对图像A的补集进行腐蚀,得到图像假设为Process2;

然后,Process1与Process2取交集;得到的结果就是B的位置。这里的位置可能不是B的中心位置,要视W-B时对齐的位置而异;

两次腐蚀,然后交集,结果就出来了。

数学公式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
I = cv.imread('pic/rectangle_find35.png', -1)

K = np.zeros((37,37), np.uint8)
K[1:36, 1:36] = 1

IeK = cv.erode(I, K)

Ic = 255 - I
Kc = 1 - K

IceKc = cv.erode(Ic, Kc)

hitmiss = cv.bitwise_and(IeK, IceKc)

show(hitmiss)

待补充……