通過閾值化分割可以得到二值圖,但往往會出現圖像中物體形態不完整,變的殘缺,可以通過形態學處理,使其變得豐滿,或者去除掉多余的像素。常用的形態學處理算法包括:腐蝕,膨脹,開運算,閉運算,形態學梯度,頂帽運算和底帽運算。
1. 腐蝕
腐蝕操作類似於中值平滑,也有一個核,但不進行卷積運算,而是取核中像素值的最小值代替錨點位置的像素值,這樣就會使圖像中較暗的區域面積增大,較亮的的區域面積減小。如果是一張黑底,白色前景的二值圖,就會使白色的前景物體顏色變小,就像被腐蝕了一樣。
進行腐蝕操作的核,不僅可以是矩形,還可以是十字形和橢圓形,opencv提供getStructuringElement()函數來獲得核,其參數如下:
kernel=cv2.getStructuringElement(shape,ksize,anchor) shape:核的形狀 cv2.MORPH_RECT: 矩形 cv2.MORPH_CROSS: 十字形(以矩形的錨點為中心的十字架) cv2.MORPH_ELLIPSE:橢圓(矩形的內切橢圓) ksize: 核的大小,矩形的寬,高格式為(width,height) anchor: 核的錨點,默認值為(-1,-1),即核的中心點
opencv提供erode()函數進行腐蝕操作,其對應參數如下:
dst=cv2.erode(src,kernel,anchor,iterations,borderType,borderValue): src: 輸入圖像對象矩陣,為二值化圖像 kernel:進行腐蝕操作的核,可以通過函數getStructuringElement()獲得 anchor:錨點,默認為(-1,-1) iterations:腐蝕操作的次數,默認為1 borderType: 邊界種類,有默認值 borderValue:邊界值,有默認值
腐蝕操作的代碼和效果如下:
可以看到二值化圖像中白色的opencv字體面積變小了,就像被腐蝕了。注意這是黑底白字,如果是白底黑字,效果會相反,字體反而會膨脹。

#coding:utf-8 import cv2 as cv img = cv.imread(r"C:\Users\Administrator\Desktop\logo.png") img_cvt = cv.cvtColor(img,cv.COLOR_BGR2GRAY) ret,img_thr = cv.threshold(img_cvt,200,255,cv.THRESH_BINARY_INV) kernel = cv.getStructuringElement(cv.MORPH_RECT,(3,5)) 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()
2.膨脹
膨脹操作和腐蝕操作正好相反,是取核中像素值的最大值代替錨點位置的像素值,這樣會使圖像中較亮的區域增大,較暗的區域減小。如果是一張黑底,白色前景的二值圖,就會使白色的前景物體顏色面積變大,就像膨脹了一樣
opencv提供dilate()函數進行膨脹操作,其對應參數如下:
dst = cv2.dilate(src,kernel,anchor,iterations,borderType,borderValue) src: 輸入圖像對象矩陣,為二值化圖像 kernel:進行腐蝕操作的核,可以通過函數getStructuringElement()獲得 anchor:錨點,默認為(-1,-1) iterations:腐蝕操作的次數,默認為1 borderType: 邊界種類 borderValue:邊界值
膨脹操作的代碼和效果如下:
可以看到二值化圖像中白色的opencv字體面積變大了,就像膨脹了

#coding:utf-8 import cv2 as cv img = cv.imread(r"C:\Users\Administrator\Desktop\logo.png") img_cvt = cv.cvtColor(img,cv.COLOR_BGR2GRAY) ret,img_thr = cv.threshold(img_cvt,200,255,cv.THRESH_BINARY_INV) kernel = cv.getStructuringElement(cv.MORPH_RECT,(3,5)) dst = cv.dilate(img_thr,kernel,iterations=1) cv.imshow("img",img) cv.imshow("img_thr",img_thr) cv.imshow("dst",dst) cv.waitKey(0) cv.destroyAllWindows()
3.開運算,閉運算,頂帽,頂帽
開運算:先進行腐蝕操作,后進行膨脹操作,主要用來去除一些較亮的部分,即先腐蝕掉不要的部分,再進行膨脹。
閉運算:先進行膨脹操作,后進行腐蝕操作,主要用來去除一些較暗的部分。
形態學梯度:膨脹運算結果減去腐蝕運算結果,可以拿到輪廓信息。
頂帽運算:原圖像減去開運算結果。
底帽運算:原圖像減去閉運算結果。
進行開運算,閉運算,頂帽運算,底帽運算,形態學梯度,opencv提供了一個統一的函數cv2.morphologyEx(),其對應參數如下:
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:邊界值
使用代碼和對應效果如下:

#coding:utf-8 import cv2 as cv import matplotlib.pyplot as plt img = cv.imread(r"C:\Users\Administrator\Desktop\logo.png") img_cvt = cv.cvtColor(img,cv.COLOR_BGR2GRAY) ret,img_thr = cv.threshold(img_cvt,200,255,cv.THRESH_BINARY_INV) kernel = cv.getStructuringElement(cv.MORPH_RECT,(3,5)) 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()
4.應用實例
有如下一張中文圖片,當我們進行字符切割時,常需要知道其中的漢字是否帶下划線,方便進行后續處理。
我們首先想到的可能是使用霍夫直線檢測算法,但是直接檢測時,會有很多干擾。我們可以通過采用一個橫向的矩陣核,來腐蝕字體,使圖片中只剩下下划線,然后再進行霍夫直線檢測,這樣干擾小,准確度會高很多。具體實現代碼和效果如下:
#coding:utf-8 import cv2 as cv img = cv.imread(r"C:\Users\Administrator\Desktop\chinese.png") img_cvt = cv.cvtColor(img,cv.COLOR_BGR2GRAY) ret,img_thr = cv.threshold(img_cvt,100,255,cv.THRESH_BINARY) kernel = cv.getStructuringElement(cv.MORPH_RECT,(30,1)) #由於是1*30的矩陣,字體會被橫向空隙的白色腐蝕掉,而下划線橫向都是黑色,不會腐蝕 dst = cv.dilate(img_thr,kernel,iterations=1) #由於是白底黑字,所有進行膨脹操作來去除黑色字體 cv.imshow("img_thr",img_thr) cv.imshow("dst",dst) cv.waitKey(0) cv.destroyAllWindows()