形态学处理 主要针对残缺图像、有斑点图像,不同区域有粘连的图像,使其变得丰满,或者去除多余的像素,也有类似去噪的作用;

形态学处理 的方法有 腐蚀、膨胀、开运算、闭运算、顶帽、底帽、形态学梯度;

形态学处理 通常针对 灰度图,确切的说是二值图,最好是 黑底 白景

 

腐蚀

腐蚀逻辑:和滤波一样,也存在一个,只是这个核不是进行卷积运算,而是取核中像素最小值代替锚点的像素值,    【类似 最小 池化】

如果图像为 黑底白景,那么核中都是黑底或者白景时,保持不变,如果核中有黑有白,那锚点就会被替换成黑色,

这样黑色区域就变大了,白色区域就变小了,就像被腐蚀了一样,

反之就是膨胀了

 

先看个实例吧

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]]

 

也可以自己创建核

import cv2 as cv
import numpy as np

img = cv.imread('ren.jpg')
img = cv.resize(img,  None, fx=0.2, fy=0.2)
img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
kernal = np.ones((3, 3))
img2 = cv.erode(img, kernal, iterations=5)
cv.imshow('ren', np.hstack([img, img2]))
cv.waitKey(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学习—形态学处理