形态学处理 主要针对 有残缺图像、有斑点图像,不同区域有粘连的图像,使其变得丰满,或者去除多余的像素,也有类似去噪的作用;
形态学处理 的方法有 腐蚀、膨胀、开运算、闭运算、顶帽、底帽、形态学梯度;
形态学处理 通常针对 灰度图,确切的说是二值图,最好是 黑底 白景
腐蚀
腐蚀逻辑:和滤波一样,也存在一个核,只是这个核不是进行卷积运算,而是取核中像素最小值代替锚点的像素值, 【类似 最小 池化】
如果图像为 黑底白景,那么核中都是黑底或者白景时,保持不变,如果核中有黑有白,那锚点就会被替换成黑色,
这样黑色区域就变大了,白色区域就变小了,就像被腐蚀了一样,
反之就是膨胀了
先看个实例吧
img = cv.imread('imgs/fs3.jpg') img_cvt = cv.cvtColor(img, cv.COLOR_BGR2GRAY) ret, img_thr = cv.threshold(img_cvt, 200, 255, cv.THRESH_BINARY) kernel = cv.getStructuringElement(cv.MORPH_CROSS, (3, 3)) # MORPH_CROSS MORPH_RECT MORPH_ELLIPSE dst = cv.erode(img_thr, kernel, iterations=1) cv.imshow("img",img) cv.imshow("img_thr",img_thr) cv.imshow("dst",dst) cv.waitKey(0) cv.destroyAllWindows()
效果图
可以看到 字体 模糊了,像腐蚀了一样
可以看到 字体变小了,且模糊了,像腐蚀了一样
创建 核
前面提到 腐蚀操作 需要一个 核,opencv 提供 getStructuringElement() 函数来创建核;
可以创建 矩形核,十字核,椭圆核
kernel=cv2.getStructuringElement(shape,ksize,anchor)
shape:核的形状
cv2.MORPH_RECT: 矩形
cv2.MORPH_CROSS: 十字形(以矩形的锚点为中心的十字架)
cv2.MORPH_ELLIPSE:椭圆(矩形的内切椭圆)
ksize: 核的大小,矩形的宽,高格式为(width,height)
anchor: 核的锚点,默认值为(-1,-1),即核的中心点
示例
k = cv.getStructuringElement(cv.MORPH_RECT, (5, 5)) # 矩形核 print(k) # [[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]] k = cv.getStructuringElement(cv.MORPH_ELLIPSE, (5, 5)) # 椭圆核 print(k) # [[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]] k = cv.getStructuringElement(cv.MORPH_CROSS, (5, 5)) # 十字核 print(k) # [[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]]
腐蚀函数
dst=cv2.erode(src,kernel,anchor,iterations,borderType,borderValue): src: 输入图像对象矩阵,为二值化图像 kernel:进行腐蚀操作的核,可以通过函数getStructuringElement()获得 anchor:锚点,默认为(-1,-1) iterations:腐蚀操作的次数,默认为1 borderType: 边界种类,有默认值 borderValue:边界值,有默认值
膨胀
膨胀逻辑:与腐蚀相反,也存在一个核,只是取核中最大的像素值替代锚点的像素值, 【类似 最大 池化】
如果是 黑底白景,那么 黑色区域 会变小,白色区域 会变大,相当于膨胀了
膨胀函数
dst = cv2.dilate(src,kernel,anchor,iterations,borderType,borderValue) src: 输入图像对象矩阵,为二值化图像 kernel:进行腐蚀操作的核,可以通过函数getStructuringElement()获得 anchor:锚点,默认为(-1,-1) iterations:腐蚀操作的次数,默认为1 borderType: 边界种类 borderValue:边界值
示例
img = cv.imread('imgs/fs3.jpg') img_cvt = cv.cvtColor(img, cv.COLOR_BGR2GRAY) ret, img_thr = cv.threshold(img_cvt, 200, 255,cv.THRESH_BINARY) kernel = cv.getStructuringElement(cv.MORPH_RECT, (2, 2)) dst = cv.dilate(img_thr, kernel, iterations=3) # 膨胀 3 次看看效果,很明显 cv.imshow("img",img) cv.imshow("img_thr",img_thr) cv.imshow("dst",dst) cv.waitKey(0) cv.destroyAllWindows()
效果图
可以看到 字体变粗了,膨胀 3 次,效果明显
开运算、闭运算、顶帽、底帽、形态学梯度
- 开运算:先进行腐蚀操作,后进行膨胀操作,主要用来去除一些较亮的部分,即先腐蚀掉不要的部分,再进行膨胀。
- 闭运算:先进行膨胀操作,后进行腐蚀操作,主要用来去除一些较暗的部分。
- 形态学梯度:膨胀运算结果减去腐蚀运算结果,可以拿到轮廓信息。
- 顶帽运算:原图像减去开运算结果。
- 底帽运算:原图像减去闭运算结果。
函数
dst = cv2.morphologyEx(src,op,kernel,anchor,iterations,borderType,borderValue) src: 输入图像对象矩阵,为二值化图像 op: 形态学操作类型 cv2.MORPH_OPEN 开运算 cv2.MORPH_CLOSE 闭运算 cv2.MORPH_GRADIENT 形态梯度 cv2.MORPH_TOPHAT 顶帽运算 cv2.MORPH_BLACKHAT 底帽运算 kernel:进行腐蚀操作的核,可以通过函数getStructuringElement()获得 anchor:锚点,默认为(-1,-1) iterations:腐蚀操作的次数,默认为1 borderType: 边界种类 borderValue:边界值
示例
img = cv.imread('imgs/fs3.jpg') img_cvt = cv.cvtColor(img, cv.COLOR_BGR2GRAY) ret, img_thr = cv.threshold(img_cvt, 200, 255, cv.THRESH_BINARY) kernel = cv.getStructuringElement(cv.MORPH_RECT, (2, 2)) open = cv.morphologyEx(img_thr, cv.MORPH_OPEN, kernel, iterations=1) # 开运算 close = cv.morphologyEx(img_thr, cv.MORPH_CLOSE, kernel, iterations=1) # 闭运算 gradient = cv.morphologyEx(img_thr, cv.MORPH_GRADIENT, kernel, iterations=1) # 形态学梯度 tophat = cv.morphologyEx(img_thr, cv.MORPH_TOPHAT, kernel, iterations=1) # 顶帽 blackhat = cv.morphologyEx(img_thr, cv.MORPH_BLACKHAT, kernel, iterations=1) # 底帽 images = [img_thr, open, close, gradient, tophat, blackhat] titles = ["img_thr", "open", "close", "gradient", "tophat", "blackhat"] for i in range(6): plt.subplot(2, 3, i+1), plt.imshow(images[i], "gray") plt.title(titles[i]), plt.xticks([]), plt.yticks([]) plt.show()
效果图
应用案例
有如下一张中文图片,当我们进行字符切割时,常需要知道其中的汉字是否带下划线,方便进行后续处理。
我们首先想到的可能是使用霍夫直线检测算法,但是直接检测时,会有很多干扰。
我们可以通过采用一个横向的矩阵核,来腐蚀字体,使图片中只剩下下划线,然后再进行霍夫直线检测,这样干扰小,准确度会高很多
代码如下
img = cv.imread('imgs/fs.png') img_cvt = cv.cvtColor(img, cv.COLOR_BGR2GRAY) # 膨胀 # ret, img_thr = cv.threshold(img_cvt, 100, 255, cv.THRESH_BINARY) # # 由于是1*30的矩阵,字体会被横向空隙的白色腐蚀掉,而下划线横向都是黑色,不会腐蚀 # kernel = cv.getStructuringElement(cv.MORPH_RECT, (30, 1)) # dst = cv.dilate(img_thr, kernel, iterations=1) # 由于是白底黑字,所有进行膨胀操作来去除黑色字体 # 腐蚀 ret, img_thr = cv.threshold(img_cvt, 100, 255, cv.THRESH_BINARY_INV) kernel = cv.getStructuringElement(cv.MORPH_RECT, (30, 1)) dst = cv.erode(img_thr, kernel, iterations=1) cv.imshow("img_thr", img_thr) cv.imshow("dst", dst) cv.waitKey(0) cv.destroyAllWindows()
效果图
参考资料:
https://www.cnblogs.com/silence-cho/p/11069903.html OpenCV-Python学习—形态学处理