前置
形态学主要是从图像中提取分量信息,该分量信息通常是图像理解时所使用的最本质的形状特征,对于表达和描绘图像的形状有重要意义。
大体就是通过一系列操作让图像信息中的关键信息更加凸出。同时,形态学的操作都是基于灰度图进行。
相关操作最主要的2种操作为腐蚀/膨胀,后面又延伸了综合操作-开运算/闭运算/形态学梯度/礼帽/黑帽等等。
腐蚀
腐蚀是最基本的形态学操作之一,能够去除图像的边界点,使图像沿着边界向内收缩,对于小于指定结构元的部分会去除。
所以,通过腐蚀可以达到去除一些外部噪音、元素分割等功能。
腐蚀原理说明 :
结构元 : 就是拥有一个中心位置并且有一定范围的结构,说白了就是一个二维数组。
扫描图像中的每一个像素点,结构元和扫描点重合,用结构元元素与覆盖的二值图做"与"运算,都为1则图像的像素值不变,否则改为0。
图示如下:
a图为原图,b图为结构元,当扫描到第2行,由于第一行存在2/3/4列的数据为0,所以经过腐蚀后,第2行的2/3/4列的1都变成了0,同理第4行。
扫描到第三行的2/3/4列时,由于都是1,第3行的2/3/4列分别不变,最终结果如d图所示。
函数语法说明: dst = cv2.erode( src,kernel,anchor,iterations,borderType,borderValue)
- kernel : 腐蚀时使用的结构体,可以自己生成,也可以通过cv2.getStructuringElement()生成,就是个二维数组空间
- anchor : 锚点位置,默认为(-1,-1),在核的中心位置
- iterations : 腐蚀操作迭代次数,默认为1,只进行1次腐蚀操作
- borderType : 边界处理方式,一般采用默认BORDER_CONSTANT
- borderValue : 边界填充值,一般采用默认值
所以经常简化为 dst = cv2.erode( src ,kernel )
程序实例如下:
import cv2 as cv
import numpy as np
origin = cv.imread("erode.bmp")
# 使用了一个3*3的结构元,结构元面积越大,腐蚀的越厉害,结果图就越小
kernel_3 = np.ones((3, 3), np.uint8)
# 不大明显,但有些触角已经变短了
erode_3 = cv.erode(origin, kernel_3)
# 反复刷了三次,图像进一步腐蚀减小,边角已经腐蚀掉了
erode_3_3 = cv.erode(origin, kernel_3, iterations=3)
cv.imshow("origin", origin)
cv.imshow("erode_3", erode_3)
cv.imshow("erode_3_3", erode_3_3)
cv.waitKey()
cv.destroyAllWindows()
运行结果如下:
膨胀
膨胀操作同样是形态学的一种基本操作,膨胀和腐蚀的作用是相反的,膨胀操作能对边界进行扩张。膨胀操作可以将较近的2个对象连通在一起,也有利于填补
图片分割后图像内的空白处。
膨胀原理说明:
使用结构元扫描图像中的每一个像素点,结构元和扫描点重合,用结构元元素与覆盖的二值图做"与"运算,都为0则图像的像素值为0,否则改为1,这样图片边缘位置就会紧跟结构元进行扩张。
函数语法说明: dst = cv2.dilate( src ,kernel,anchor,iterations,borderType,borderValue)
函数参数与腐蚀参数完全一致,不做过多解释。方法可以简化为 dst = cv2.dilate( src,kernel )
程序实例如下:
import cv2 as cv
import numpy as np
origin = cv.imread("dilation.png")
# 使用了一个3*3的结构元,结构元面积越大,膨胀的越厉害,结果图就越大
kernel = np.ones((3, 3), np.uint8)
erode_3 = cv.dilate(origin, kernel)
# 连续膨胀3次
erode_3_3 = cv.dilate(origin, kernel, iterations=9)
cv.imshow("origin", origin)
cv.imshow("dilation_3", erode_3)
cv.imshow("dilation_3_3", erode_3_3)
cv.waitKey()
cv.destroyAllWindows()
运行如下:
开运算
开运算是先进行腐蚀操作,后再进行膨胀操作。进行腐蚀操作可以把图像中目标外的噪声去掉,膨胀后再恢复目标的大小。
函数语法说明: dst = cv2.morphologyEx( src,cv2.MORPH_OPEN,kernel)
src,dst分别是原始图像和处理后的结果图像
cv2.MORPH_OPEN : 做开运算的标识
kernel : 运算使用的结构元
程序如下:
import cv2 as cv
import numpy as np
origin = cv.imread("open_pic.png", 0)
kernel = np.ones((5, 5), np.uint8)
open_pic = cv.morphologyEx(origin, cv.MORPH_OPEN, kernel) # 开运算
cv.imshow("origin", origin)
cv.imshow("open", open_pic)
cv.waitKey()
cv.destroyAllWindows()
运行如下:
闭运算
闭运算是先进行膨胀操作,后再进行腐蚀操作。主要针对情景为图像中关键信息内部有小洞,膨胀操作会填充小洞。
函数语法说明: dst = cv2.morphologyEx( src,cv2.MORPH_CLOSE,kernel)
闭运算和开运算使用一样的函数,仅通过标识不同来实现不同操作。
cv2.MORPH_OPEN : 做闭运算的标识
程序如下:
import cv2 as cv
import numpy as np
origin = cv.imread("close_pic.png", 0)
kernel = np.ones((5, 5), np.uint8)
open_pic = cv.morphologyEx(origin, cv.MORPH_CLOSE, kernel) # 闭运算
cv.imshow("origin", origin)
cv.imshow("open", open_pic)
cv.waitKey()
cv.destroyAllWindows()
运行结果如下:
形态学梯度
其实就是一副图像膨胀和腐蚀的差别,看起来就是前景物体的轮廓。
函数语法说明:dst = cv2.morphologyEx( src,cv2.MORPH_GRADIENT,kernel)
函数同开闭运算一样,只有样式不一样。
cv2.MORPH_GRADIENT : 形态学梯度的关键字
程序如下:
import cv2 as cv
import numpy as np
origin = cv.imread("abcdefg.png")
kernel = np.ones((5, 5), np.uint8)
open_pic = cv.morphologyEx(origin, cv.MORPH_GRADIENT, kernel) # 形态学梯度
cv.imshow("origin", origin)
cv.imshow("gradient", open_pic)
cv.waitKey()
cv.destroyAllWindows()
效果图如下:
礼帽
开运算是先腐蚀再膨胀,会消除图像中的噪声,而礼帽是原始图片与进行开运算后的得到的图像的差,也就是消除掉的噪声。
函数语法说明: dst = cv2.morphologyEx( src,cv2.MORPH_TOPHAT,kernel)
import cv2 as cv
import numpy as np
origin = cv.imread("open_pic.png")
kernel = np.ones((5, 5), np.uint8)
open_pic = cv.morphologyEx(origin, cv.MORPH_TOPHAT, kernel) # 礼帽
cv.imshow("origin", origin)
cv.imshow("tophat", open_pic)
cv.waitKey()
cv.destroyAllWindows()
运行如下:
黑帽
闭运算是先膨胀再腐蚀,具有填充内部小洞的功能,而黑帽是进行闭运算得到的图片与原始图像的差,所以显示的是之前补上的小洞图片。
函数语法说明: dst = cv2.morphologyEx( src,cv2.MORPH_BLACKHAT,kernel)
程序如下:
import cv2 as cv
import numpy as np
origin = cv.imread("close_pic.png")
kernel = np.ones((5, 5), np.uint8)
open_pic = cv.morphologyEx(origin, cv.MORPH_BLACKHAT, kernel) # 黑帽
cv.imshow("origin", origin)
cv.imshow("blackhat", open_pic)
cv.waitKey()
cv.destroyAllWindows()
运行如下: