第八章: 形态学操作
-
什么是形态学操作?
形态学,morphology, 形态学最初是生物学中研究动物和植物结构的一个分支,被引入图像处理领域后,图像形态学就指以形态为基础对图像进行分析的一种方法或技术。 -
图像形态学操作的核心思想是:从图像中提取 用于表达或描绘图像形状 的信息。
-
图像形态学操作的目的是:使计算机更够更好的对图像进行识别和理解。因为图像形态学处理后可以简化图像数据,同时保存了它们基本的形状特性,去除了不相干的结构。
-
关键点:图像形态学操作主要是对二值图像进行操作的,来连接相邻的元素或分离成独立的元素。其次是灰度图像,但处理彩色图像几乎没有意义!
-
图像形态学操作主要有:膨胀、腐蚀、开运算、闭运算、梯度运算、礼帽运算、黑帽运算,击中与击不中变换 等等。
其中膨胀和腐蚀是基本操作,后面的操作都是在膨胀和腐蚀的基础上延申而来的。 -
图像形态学操作有哪些应用领域?由于对图像进行形态学操作后,可以实现比如消除噪声、提取边界、填充区域、提取连通分量、凸壳、细化、粗化等; 还可以分割出独立的图像元素,或者图像中相邻的元素;求取图像中明显的极大值区域和极小值区域;求取图像梯度等等, 所以在视觉检测、图像理解、文字识别、医学图像处理、图像压缩编码等领域有非常重要的应用。
一、图像腐蚀
顾名思义,是将物体的边缘加以腐蚀。具体的操作是拿一个宽m高n的矩形作为kernel,对图像中的每一个像素进行扫描,扫描后的图像的每个像素点和原图的位置对应,同时和扫描时kernel的中心点对应。用kernel遍历原图的所有像素,每遍历一次就将对应位置的像素点更改为kernel中的最小值。这样原图所有像素遍历一轮后,原图中突出的像素点就会被置为最小值,人类视觉看起来就是图像被腐蚀了。
思考题:为什么上面右图腐蚀后的效果是字变胖了?
- API: cv2.erode(img, kernel [, anchor, iterations, borderType ])
#例8.1 观察cv2.erode()函数效果 import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread(r'C:\Users\25584\Desktop\erode.bmp', -1) kernel1 = np.ones((5,5), np.uint8) kernel2 = np.ones((9,9), np.uint8) img_erode_kernel1 = cv2.erode(img, kernel1) img_erode_kernel2 = cv2.erode(img, kernel2) fig, axes = plt.subplots(1,3, figsize=(10,5), dpi=100) axes[0].imshow(img) axes[1].imshow(img_erode_kernel1) axes[2].imshow(img_erode_kernel2) plt.show()
二、图像膨胀
膨胀和腐蚀是一对儿相反的操作,膨胀能对图像的边界进行扩展,就是将图像的轮廓加以膨胀。
操作方法与腐蚀操作一样,也是拿一个kernel,对图像的每个像素做遍历处理。不同之处在于生成的像素值不是所有像素中最小的值,而是最大的值。这样操作的结果会将图像外围的突出点连接并向外延伸。思考题:为什么上面右图膨胀后的效果是字变瘦了?
- API: cv2.dilate(img, kernel [, anchor, iterations, borderType ])
#例8.2 观察cv2.dilate()函数效果 import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread(r'C:\Users\25584\Desktop\dilation.bmp', -1) kernel1 = np.ones((5,5), np.uint8) kernel2 = np.ones((9,9), np.uint8) img_dilate_kernel1 = cv2.dilate(img, kernel1) img_dilate_kernel2 = cv2.dilate(img, kernel2) fig, axes = plt.subplots(1,3, figsize=(10,5), dpi=100) axes[0].imshow(img) axes[1].imshow(img_dilate_kernel1) axes[2].imshow(img_dilate_kernel2) plt.show()
三、图像开运算、闭运算、梯度运算、礼帽运算、黑帽运算、击中不击中变换
腐蚀和膨胀是图像形态学运算的基础,将膨胀和腐蚀进行组合就得出开闭运算、梯度、礼帽、黑帽等不同形式的运算:
- 开运算cv2.MORPH_OPEN:先腐蚀后膨胀--dilate(erode(img))
- 闭运算cv2.MORPH_CLOSE:先膨胀后腐蚀--erode(dilate(img))
- 形态学梯度cv2.MORPH_GRADIENT:膨胀图-腐蚀图--dilate(img)-erode(img)
- 礼帽运算cv2.MORPH_TOPHAT:也叫顶帽运算,原始图像-开运算结果 -- img-open(img)
- 黑帽运算cv2.MORPH_BLACKHAT: 闭运算结果-原始图像 -- close(img)-img
- 击中不击中cv2.MORPH_HITMISS: 前景、背景腐蚀运算的交集 -- intersection(erode(img), erode(notimg))
API: cv2.morphologyEx(img, op, kernel, anchor, iterations, borderType)
其中参数op就是上面的几种类型。1、开运算:
-
#例8.3 使用函数cv2.morphologyEx()实现开运算 import cv2 import numpy as np import matplotlib.pyplot as plt img1 = cv2.imread(r'C:\Users\25584\Desktop\opening.bmp') img2 = cv2.imread(r'C:\Users\25584\Desktop\opening2.bmp') kernel = np.ones((10,10), np.uint8) img_open1 = cv2.morphologyEx(img1, cv2.MORPH_OPEN, kernel) img_open2 = cv2.morphologyEx(img2, cv2.MORPH_OPEN, kernel) fig, axes = plt.subplots(1,4, figsize=(10,5), dpi=100) axes[0].imshow(img1) axes[1].imshow(img2) axes[2].imshow(img_open1) axes[3].imshow(img_open2) plt.show()
说明:开运算可以去噪、计数等。比如识别一张图像中有几个人,要先把人和人重叠的部分分开,然后再计数。
2、闭运算:
-
#例8.4 使用函数cv2.morphologyEx()实现闭运算 import cv2 import numpy as np import matplotlib.pyplot as plt img1 = cv2.imread(r'C:\Users\25584\Desktop\closing.bmp') img2 = cv2.imread(r'C:\Users\25584\Desktop\closing2.bmp') kernel = np.ones((10,10), np.uint8) img_close1 = cv2.morphologyEx(img1, cv2.MORPH_CLOSE, kernel, iterations=3) img_close2 = cv2.morphologyEx(img2, cv2.MORPH_CLOSE, kernel, iterations=3) fig, axes = plt.subplots(1,4, figsize=(10,5), dpi=100) axes[0].imshow(img1) axes[1].imshow(img2) axes[2].imshow(img_close1) axes[3].imshow(img_close2) plt.show()
说明:闭运算可以去除前景物内部的黑点,还可以将不同前景图像进行连接,就是实现前景图像的连接。
3、形态学梯度运算: 该操作可以获取前景图像的边缘
用膨胀图像(扩张亮度)-腐蚀图像(收缩亮度),就可以获取原始图像中的前景图像的边缘。#例8.5 使用函数cv2.morphologyEx()实现梯度运算 import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread(r'C:\Users\25584\Desktop\gradient.bmp') kernel = np.ones((10,10), np.uint8) img_gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel) plt.subplot(121), plt.imshow(img) plt.subplot(122), plt.imshow(img_gradient) plt.show()
4、礼帽运算:
礼帽运算是原图-开运算图,所以礼帽运算可以获得图像的噪声信息,或者得到比原始图的边缘更亮的边缘信息。#例8.6 使用函数cv2.morphologyEx()实现礼帽运算 import cv2 import numpy as np import matplotlib.pyplot as plt img1 = cv2.imread(r'C:\Users\25584\Desktop\tophat.bmp') img2 = cv2.imread(r'C:\Users\25584\Desktop\lena.bmp') kernel = np.ones((10,10), np.uint8) img1_tophat = cv2.morphologyEx(img1, cv2.MORPH_TOPHAT, kernel) img2_tophat = cv2.morphologyEx(img2, cv2.MORPH_TOPHAT, kernel) fig, axes = plt.subplots(1,4, figsize=(10,5), dpi=100) axes[0].imshow(img1) axes[1].imshow(img2) axes[2].imshow(img1_tophat) axes[3].imshow(img2_tophat) plt.show()
5、黑帽运算:
黑帽运算是闭运算图像-原始算图,所以黑帽运算可以获得图像内部的噪音,或者得到比原始图的边缘更暗的边缘信息。#例8.7 使用函数cv2.morphologyEx()实现黑帽运算 import cv2 import numpy as np import matplotlib.pyplot as plt img1 = cv2.imread(r'C:\Users\25584\Desktop\blackhat.bmp') img2 = cv2.imread(r'C:\Users\25584\Desktop\lena.bmp') kernel = np.ones((10,10), np.uint8) img1_blackhat = cv2.morphologyEx(img1, cv2.MORPH_BLACKHAT, kernel) img2_blackhat = cv2.morphologyEx(img2, cv2.MORPH_BLACKHAT, kernel) fig, axes = plt.subplots(1,4, figsize=(10,5), dpi=100) axes[0].imshow(img1) axes[1].imshow(img2) axes[2].imshow(img1_blackhat) axes[3].imshow(img2_blackhat) plt.show()
四、自定义核进行形态操作
- 自定义核的API: cv2.getStructuringElement(shape, ksize, anchor)
shape:表示核的形状。
shape=cv2.MORPH_RECT,表示生成一个矩形结构的核,所有元素值都是1。
shape=cv2.MORPH_CROSS,表示生成一个十字形结构元素,对角线元素值为1。
shape=cv2.MORPH_ELLIPSE表示生成一个椭圆形结构的核。
ksize:核的尺寸
anchor:核的锚点位置。#例8.8 使用函数cv2.getStructuringElement()生成不同的核,并观察不同核对图像的处理效果 import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread(r'C:\Users\25584\Desktop\kernel.bmp') kernel1 = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) kernel2 = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5)) kernel3 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) print(kernel1, '\n\n', kernel2, '\n\n', kernel3) img_dilate1 = cv2.dilate(img, kernel1, iterations=17) img_dilate2 = cv2.dilate(img, kernel2, iterations=8) img_dilate3 = cv2.dilate(img, kernel3, iterations=20) fig, axes = plt.subplots(1,4, figsize=(10,5), dpi=100) axes[0].imshow(img) axes[1].imshow(img_dilate1) axes[2].imshow(img_dilate2) axes[3].imshow(img_dilate3) plt.show()
[[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] [0 0 1 0 0] [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] [1 1 1 1 1] [1 1 1 1 1] [0 0 1 0 0]]