有关图像处理前三次的笔记:
OpenCV与图像处理学习三——图像基本操作(1)
OpenCV与图像处理学习四——图像基本操作(2)
OpenCV与图像处理学习五——图像基本操作(3)
这是有关图像基本操作的最后一次笔记,有关图像形态学操作。
形态学,是图像处理中应用最为广泛的技术之一,主要用于从图像中提取对表达和描绘区域形状有意义的图像分量,使后续的识别工作能够抓住目标对象最为本质的形状特征,如边界和连通区域等。
下面会经常用到一个概念,这里先进行说明:
结构元素:设有两幅图像B,X,若X是被处理的对象,而B是用来处理X的,则B称为结构元素(structure element),又被形象地称作刷子。结构元素通常都是一些比较小的图像。
下面将介绍形态学的几种常用操作:腐蚀、膨胀、开运算和闭运算等。
图像的膨胀(Dilation)和腐蚀(Erosion)是两种基本的形态学运算,其中膨胀类似于“领域扩张”,将图像中的白色部分进行扩张,其运行结果图比原图的白色区域更大;而腐蚀类似于“领域被蚕食”,将图像中白色的部分进行缩减细化,其运行结果图比原图的白色区域更小。
把结构元素B平移a后得到Ba,若Ba包含于X,我们记下这个a点,所有满足上述条件的a点组成的集合称作X被B腐蚀(Erosion)的结果。如下图所示:
其中X是被处理的对象,B是结构元素。对于任意一个在阴影部分的点a,Ba包含于X,所以X被B腐蚀的结果就是那个阴影部分。阴影部分在X的范围之内,且比X小,就像X被剥掉了一层似的。
腐蚀后的结果如下图黑色部分所示:
相较于原来的灰色部分,仿佛变瘦了。
OpenCV中的函数为:
dst = cv2.erode( src, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]] )
参数为:
下面看个例子:
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('./image/morphology.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
kernel = np.ones((3, 3), np.uint8)
erosion = cv2.erode(img, kernel, iterations = 1)
plt.subplot(121), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(erosion), plt.title('erosion')
plt.xticks([]), plt.yticks([])
plt.show()
结果如下所示:
若将结构元素的尺寸扩大到7,结果为:
ps:在构造结构元素的时候,可以使用numpy,也可以使用OpenCV提供的函数cv2.getStructuringElement()
函数:
retval = cv2.getStructuringElement( shape, ksize[, anchor] )
参数:
看一下例子:
import numpy as np
import cv2
kernel = np.ones((5, 5), np.uint8)
print(kernel)
[[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]]
kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (7,7))
print(kernel)
[[0 0 0 1 0 0 0]
[0 0 0 1 0 0 0]
[0 0 0 1 0 0 0]
[1 1 1 1 1 1 1]
[0 0 0 1 0 0 0]
[0 0 0 1 0 0 0]
[0 0 0 1 0 0 0]]
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7,7))
print(kernel)
[[0 0 0 1 0 0 0]
[0 1 1 1 1 1 0]
[1 1 1 1 1 1 1]
[1 1 1 1 1 1 1]
[1 1 1 1 1 1 1]
[0 1 1 1 1 1 0]
[0 0 0 1 0 0 0]]
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7,7))
print(kernel)
[[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 1 1 1]
[1 1 1 1 1 1 1]
[1 1 1 1 1 1 1]
[1 1 1 1 1 1 1]]
我们可以用非矩形的结构元素来进行腐蚀操作:
#!/usr/bin/env python3
import cv2
image = cv2.imread("./image/morphology.png")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv2.imshow("Gray Image", gray)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7))
eroded = cv2.erode(gray.copy(), kernel, 10)
# eroded = cv2.erode(gray.copy(), None, 10)
cv2.imshow("Eroded Image", eroded)
cv2.waitKey(0)
cv2.destroyAllWindows()
也是可以达到一定效果的,但是比矩形的那种腐蚀程度低一些些,因为毕竟结构元素里多了一些0。
膨胀(dilation)可以看做是腐蚀的对偶运算,其定义是:把结构元素B平移后得到Ba,若Ba与X有交集,我们记下这个a点。所有满足上述条件的a点组成的集合称作X被B膨胀后的结果,如下图所示:
其中X是被处理的对象,B是结构元素,对于任意一个在阴影部分的点a,Ba与X有交集,所以X被B膨胀后的结果就是那个阴影部分,阴影部分包括X所有范围,就像是X膨胀了一圈似的。
膨胀后的图像,其中绿色是膨胀多出来的部分:
在OpenCV中的函数为:
dst = cv2.dilate( src, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]] )
参数:
看个例子:
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('./image/morphology.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
#kernel = np.ones((3,),np.uint8)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7,7))
dilation = cv2.dilate(img,kernel,iterations = 1)
plt.subplot(121),plt.imshow(img),plt.title('origin')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(dilation),plt.title('dilation')
plt.xticks([]), plt.yticks([])
plt.show()
结果为:
若将运算元素尺寸扩大一点,扩大为11:
原本断开的地方或小孔都被填上了。
开运算 = 先腐蚀运算,再膨胀运算,看上去把细微连在一起的两块目标分开了,开运算的效果图如下所示:
开运算对一些细微的小点,小块,细条等部分是可以消去的,因为先腐蚀消去它们,导致它们消失了无法再通过膨胀变回来,而一些比较大的块通过腐蚀操作只是会变瘦一点,不会被完全抹去,所以可以通过膨胀运算变回来,那么总的效果就是开运算去除了这些孤立的小点,细长的小条。
开运算总结:
开运算和闭运算都用如下函数来表示,这个函数是OpenCV中图像形态学变化的通用函数:
dst = cv2.morphologyEx( src, op, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]] )
参数如下所示:
cv2.getStructuringElement
函数来定义。下面看个例子:
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('./image/open.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
#kernel = np.ones((5,5),np.uint8)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
plt.subplot(121), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(opening), plt.title('opening')
plt.xticks([]), plt.yticks([])
plt.show()
结果如下所示:
一些细小的点被去除了很多,但是开运算的结构元素的尺寸很重要,太小可能去除效果不好,太大可能会得到不想要的结果,如将3改为9,结果将变为:
所以调节这个参数还是很关键的。
闭运算 = 先膨胀运算,再腐蚀运算,看上去将两个细微连接的图块封闭在一起,闭运算的效果图如下图所示:
闭运算总结:
看个例子:
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
img = cv.imread('./image/close.png')
img = cv.cvtColor(img,cv.COLOR_BGR2RGB)
# kernel = np.ones((5,5),np.uint8)
kernel = np.ones((7, 7), np.uint8)
closing = cv.morphologyEx(img, cv.MORPH_CLOSE, kernel)
plt.subplot(121), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(closing), plt.title('closing')
plt.xticks([]), plt.yticks([])
plt.show()
结果如下所示:
一些小孔被填满了。若把尺寸从7改为21,结果为:
就有点过了,把不需要连接和填补的地方也给连接、填补了,所以要合理选择参数。
用cv2.morphologyEx
函数可以实现基础梯度操作,看下面这个例子:
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
img = cv.imread('./image/morphology.png')
img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
kernel = np.ones((3, 3), np.uint8)
gradient = cv.morphologyEx(img, cv.MORPH_GRADIENT, kernel)
plt.subplot(121), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(gradient), plt.title('gradient')
plt.xticks([]), plt.yticks([])
plt.show()
看两个例子:
顶帽:
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
img = cv.imread('./image/morphology.png')
img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
kernel = np.ones((9, 9), np.uint8)
tophat = cv.morphologyEx(img, cv.MORPH_TOPHAT, kernel)
plt.subplot(121), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(tophat), plt.title('tophat')
plt.xticks([]), plt.yticks([])
plt.show()
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
img = cv.imread('./image/morphology.png')
img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
kernel = np.ones((9, 9), np.uint8)
tophat = cv.morphologyEx(img, cv.MORPH_BLACKHAT, kernel)
plt.subplot(121), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(tophat), plt.title('blackhat')
plt.xticks([]), plt.yticks([])
plt.show()
图像处理之图像基本操作的笔记就暂时到这里,后面将学习传统方法进行图像分割,包括阈值分割、边缘检测算法、连通域分析以及一些其他区域生长算法。