开运算和闭运算_OpenCV计算机视觉学习(5)——形态学处理(腐蚀膨胀,开闭运算,礼帽黑帽,边缘检测)...
在開(kāi)始學(xué)習(xí)之前推薦大家可以多在FlyAI競(jìng)賽服務(wù)平臺(tái)多參加訓(xùn)練和競(jìng)賽,以此來(lái)提升自己的能力。FlyAI是為AI開(kāi)發(fā)者提供數(shù)據(jù)競(jìng)賽并支持GPU離線(xiàn)訓(xùn)練的一站式服務(wù)平臺(tái)。每周免費(fèi)提供項(xiàng)目開(kāi)源算法樣例,支持算法能力變現(xiàn)以及快速的迭代算法模型。
如果需要處理的原圖及代碼,請(qǐng)移步小編的GitHub地址
傳送門(mén):請(qǐng)點(diǎn)擊我如果點(diǎn)擊有誤:https://github.com/LeBron-Jian/ComputerVisionPractice
形態(tài)學(xué)操作簡(jiǎn)單來(lái)說(shuō),就是改變物體的形狀,下面學(xué)習(xí)一下,首先本文的目錄如下:
- 1,定義結(jié)構(gòu)元素
- 2,腐蝕和膨脹
- 3,開(kāi)運(yùn)算和閉運(yùn)算
- 4,禮帽/頂帽,黑帽算法
- 5,梯度運(yùn)算
- 6,形態(tài)學(xué)運(yùn)算 檢測(cè)邊和角點(diǎn)(1,檢測(cè)邊緣 ; 2,檢測(cè)拐角)
1,定義結(jié)構(gòu)元素
形態(tài)學(xué)操作的原理:在特殊領(lǐng)域運(yùn)算形式——結(jié)構(gòu)元素(Structure Element),在每個(gè)像素位置上與二值圖像對(duì)應(yīng)的區(qū)域進(jìn)行特定的邏輯運(yùn)算。運(yùn)算結(jié)構(gòu)是輸出圖像的相應(yīng)像素。運(yùn)算效果取決于結(jié)構(gòu)元素大小內(nèi)容以及邏輯運(yùn)算性質(zhì)。
結(jié)構(gòu)元素:膨脹和腐蝕操作的最基本組成部分,用于測(cè)試輸出圖像,通常要比待處理的圖像小很多,二維平面結(jié)構(gòu)元素由一個(gè)數(shù)值為0或1的矩陣組成。結(jié)構(gòu)元素的原點(diǎn)指定了圖像中需要處理的像素范圍,結(jié)構(gòu)元素中數(shù)值為1的點(diǎn)決定結(jié)構(gòu)元素的領(lǐng)域像素進(jìn)行膨脹或腐蝕操作時(shí)是否需要參與計(jì)算。
形態(tài)學(xué)處理的核心就是定義結(jié)構(gòu)元素,在OpenCV-Python中,可以使用其自帶的 getStructuringElement 函數(shù),也可以直接使用 Numpy 的 ndarray 來(lái)定義一個(gè)結(jié)構(gòu)元素,形象圖如下:
下面代碼為上圖的十字形,代碼如下:
#_*_coding:utf-8_*_import cv2import numpy as npdef show_element():element_cross = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5))print(element_cross)element_ellipse = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))print(element_ellipse)element_rect = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))print(element_rect)'''[[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]][[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]][[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]]'''def define_cross_structure():NpKernel = np.uint8(np.zeros((5, 5)))for i in range(5):NpKernel[2, i] = 1NpKernel[i, 2] = 1print("NpKernel", NpKernel)'''NpKernel [[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]]'''上面我們自定義了一個(gè)結(jié)構(gòu)元素 kernel,先聲明一個(gè)矩陣,然后對(duì)其進(jìn)行賦值,這種方法靈活但是略顯復(fù)雜。OpenCV提供了一個(gè)函數(shù) 也就是上面展示的,可以獲取常用結(jié)構(gòu)元素的性質(zhì):矩形(包括線(xiàn)形),橢圓(包括圓形)以及十字形。下面具體學(xué)習(xí)一下此方法
1.1 定義一些基本符號(hào)和關(guān)系
1,元素
設(shè)有一幅圖像X,若點(diǎn) a 在 X 的區(qū)域以?xún)?nèi),則稱(chēng) a 為 X 的元素,記做 a 屬于 X,如圖 6.1所示。
2,B包含于X
設(shè)有兩幅圖像 B, X。對(duì)于 B中所有的元素 ai, 都有 ai 屬于 X,則稱(chēng)B包含于 (included in)X ,記做 B 屬于 X,如圖6.2所示。
3,B擊中 X
設(shè)有兩幅圖像B, X。若存在這一一個(gè)點(diǎn),它即是B的元素,又是 X 的元素,則稱(chēng) B 擊中(hit)X,記做 B ↑ X,如圖6.3所示。
4,B不擊中 X
設(shè)有兩幅圖像B, X。若不存在任何一個(gè)點(diǎn),它既是B的元素,又是 X的元素,即 B和 X的交集是空,則稱(chēng) B 不擊中(miss)X,記做 B ∩ X = Φ;其中 ∩ 是集合運(yùn)算相交的符號(hào),Φ 表示空集,如圖6.4所示。
5,補(bǔ)集
設(shè)有一幅圖像 X,所有 X 區(qū)域以外的點(diǎn)構(gòu)成的集合稱(chēng)為 X 的補(bǔ)集,記做 Xc,如下圖所示。顯然,如果B ∩ X = Φ,則 B 在 X的補(bǔ)集內(nèi),即 B 屬于 Xc。
6,結(jié)構(gòu)元素
設(shè)有兩幅圖像B,X。若X是被處理的對(duì)象,而B(niǎo)是用來(lái)處理X的,則稱(chēng)B為結(jié)構(gòu)元素(structure element),又被形象的稱(chēng)作刷子。結(jié)構(gòu)元素通常都是一些比較小的圖像。
7,對(duì)稱(chēng)集
設(shè)有一幅圖像B,將B中所有元素的坐標(biāo)取反,即令(x, y)變?yōu)?#xff08;-x, -y),所有這些點(diǎn)構(gòu)成的新的集合稱(chēng)為B的對(duì)稱(chēng)集,記做 Bv,如下圖6.6所示。
8,平移
設(shè)有一幅圖像B,有一個(gè)點(diǎn)a(x0, y0),將B平移a后的結(jié)果是,把B中所有元素的橫坐標(biāo)加 x0,縱坐標(biāo)加 y0,即令(x, y)變成(x + x0, y+y0),所有這些點(diǎn)構(gòu)成新的集合稱(chēng)為B的平移,記做 Ba,如圖6.7所示。
1.2 getStructuringElement 方法
getStructuringElement 是OpenCV提供的一個(gè)函數(shù),getStructuringElement 的內(nèi)部并沒(méi)有什么優(yōu)化實(shí)現(xiàn),只是封裝了一些功能,其原理同樣是聲明了一個(gè)矩陣,然后求形狀,指定矩陣的值。而我們只需要直接調(diào)用即可。
函數(shù)原型如下:
def getStructuringElement(shape, ksize, anchor=None):參數(shù)的意思:
- shape 表示內(nèi)核的形狀,有三種形狀可以選擇:
——十字形:cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5))
——橢圓:cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5))
——矩形:cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
- ksize 表示內(nèi)核的尺寸(n, n)
- anchor 錨點(diǎn)的位置
此函數(shù)最終會(huì)返回指定形狀和尺寸的結(jié)構(gòu)元素。
下面代碼實(shí)現(xiàn)一下,這里同時(shí)展示一下自己寫(xiě)的:
kernel1 = np.ones((3, 3), np.uint8)kernel2 = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))print(kernel1)print(kernel2)print(kernel1 == kernel2)'''[[1 1 1][1 1 1][1 1 1]][[1 1 1][1 1 1][1 1 1]][[ True True True][ True True True][ True True True]]'''這里其實(shí)再證明一次。
2,腐蝕和膨脹
圖像的膨脹(Dilation)和腐蝕(Erosion)是兩種基本的形態(tài)學(xué)運(yùn)算,主要用來(lái)尋找圖像中的極大區(qū)域和極小區(qū)域。其中膨脹類(lèi)似于“領(lǐng)域擴(kuò)張”,將圖像中的高亮區(qū)域或白色部分進(jìn)行擴(kuò)張,其運(yùn)行結(jié)果圖比原圖的高亮區(qū)域更大;腐蝕類(lèi)似于“領(lǐng)域被蠶食”,將圖像中的高亮區(qū)域或白色部分進(jìn)行縮減細(xì)化,其運(yùn)行結(jié)果圖比原圖的高亮區(qū)域更小。
形態(tài)學(xué)各種功能實(shí)現(xiàn),都?xì)w結(jié)為腐蝕 erode 和 膨脹 dilate 的組合,形象理解一下就是腐蝕等于變瘦,膨脹等于變胖,所以下面學(xué)習(xí)一下腐蝕和膨脹。
注意:腐蝕和膨脹主要針對(duì)二值化圖像的白色部分。
2.1 腐蝕
腐蝕就是把結(jié)構(gòu)元素B平移a后得到Ba,若Ba包含于X,我們記下這個(gè)a點(diǎn),所有滿(mǎn)足上述條件的 a點(diǎn)組成的集合稱(chēng)為X被B腐蝕(Erosion)的結(jié)果。
上圖 X 是被處理的對(duì)象,B是結(jié)構(gòu)元素,不難知道,對(duì)于任意一個(gè)在陰影部分的點(diǎn) a,Ba包含于X,所以 X被B腐蝕的結(jié)果就是那個(gè)陰影部分,陰影部分在 X的范圍之內(nèi),且比 X小,就像 X 被剝掉了一層似的,這0就是為什么叫腐蝕的原因。
腐蝕的運(yùn)算符為 “ - ”,其定義如下:
該公式表示圖像A用卷積模板B來(lái)進(jìn)行腐蝕處理,通過(guò)模板B與圖像A進(jìn)行卷積計(jì)算,得到B覆蓋區(qū)域的像素點(diǎn)最小值,并用這個(gè)最小值來(lái)替代參考點(diǎn)的像素值。如圖所示,將左邊的原始圖像A腐蝕處理為右邊的效果圖A-B。
腐蝕:腐蝕會(huì)把物體的邊界腐蝕掉,卷積核沿著圖像滑動(dòng),如果卷積核對(duì)應(yīng)的原圖的所有像素值為1,那么中心元素就保持原來(lái)的值,否則變?yōu)榱恪V饕獞?yīng)用在去除白噪聲,也可以斷開(kāi)連在一起的物體。
在原圖的每一個(gè)區(qū)域中取最小值,由于是二值化圖像,只要有一個(gè)點(diǎn)為0,則為0,來(lái)達(dá)到瘦身的目的。
腐蝕的作用:
- 1,對(duì)象大小減少1個(gè)像素(3*3)
- 2,平滑對(duì)象邊緣
- 3,弱化或者分割圖像之間的半島型連接
2.2 膨脹(Dilate)
膨脹可以看做是腐蝕的對(duì)偶運(yùn)算,其定義是:把結(jié)構(gòu)元素B平移 a 后得到 Ba,若Ba擊中X,我們記下這個(gè) a 點(diǎn)。所有滿(mǎn)足上述條件的 a點(diǎn)組成的集合稱(chēng)為 X被B膨脹的結(jié)果。
膨脹的方法是:拿B的中心點(diǎn)和X上的點(diǎn)及X周?chē)狞c(diǎn)一個(gè)一個(gè)的對(duì),如果B上有一個(gè)點(diǎn)落在X的范圍內(nèi),則該點(diǎn)為黑,可以看出X的范圍就像X膨脹一圈似的。
圖像膨脹的運(yùn)算符是“⊕”,其定義如下:
這個(gè)公式表示用B來(lái)對(duì)圖像A進(jìn)行膨脹處理,其中B是一個(gè)卷積模板或卷積核,其形狀可以為正方形或圓形,通過(guò)模板B與圖像A進(jìn)行卷及計(jì)算,掃描圖像中的每一個(gè)像素點(diǎn),用模板元素與二值圖像元素做“與”運(yùn)算,如果都為0,那么目標(biāo)像素點(diǎn)為0,否則為1。從而計(jì)算B覆蓋區(qū)域的像素點(diǎn)最大值,并用該值替換參考點(diǎn)的像素值實(shí)現(xiàn)膨脹。下圖是將左邊的原始圖像A膨脹處理為右邊的效果圖 A⊕B。
膨脹:卷積核所對(duì)應(yīng)的原圖像的像素值只要有一個(gè)是1,中心像素值就是1.一般在除噪聲,先腐蝕再膨脹,因?yàn)楦g在去除白噪聲的時(shí)候也會(huì)使圖像縮小,所以我們之后要進(jìn)行膨脹。當(dāng)然也可以用來(lái)將兩者物體分開(kāi)。
膨脹的作用:
- 1,對(duì)象大小增加一個(gè)像素 (3*3)
- 2,平滑對(duì)象邊緣
- 3,減少或者填充對(duì)象之間的距離
2.3 代碼展示
代碼如下:
import cv2import numpy as npdef erode_image(img_path):origin_img = cv2.imread(img_path)gray_img = cv2.cvtColor(origin_img, cv2.COLOR_BGR2GRAY)# OpenCV定義的結(jié)構(gòu)元素kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))# 腐蝕圖像eroded = cv2.erode(gray_img, kernel)# 顯示腐蝕后的圖像cv2.imshow('Origin', origin_img)cv2.imshow('Erode', eroded)cv2.waitKey(0)cv2.destroyAllWindows()def dilate_image(img_path):origin_img = cv2.imread(img_path)gray_img = cv2.cvtColor(origin_img, cv2.COLOR_BGR2GRAY)# OpenCV定義的結(jié)構(gòu)元素kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))# 膨脹圖像dilated = cv2.dilate(gray_img, kernel)# 顯示腐蝕后的圖像cv2.imshow('Dilate', dilated)cv2.waitKey(0)cv2.destroyAllWindows()if __name__ == '__main__':img_path = 'origin.jpg'erode_image(img_path)dilate_image(img_path)如上所示,腐蝕和膨脹的處理很簡(jiǎn)單,只需設(shè)置好結(jié)構(gòu)元素,然后分別調(diào)用 cv2.erode() 和 cv2.dilate()函數(shù),其中第一個(gè)參數(shù)為需要處理的圖像,第二個(gè)是結(jié)構(gòu)元素,返回處理好的圖像。
下圖從左到右依次是 原圖,腐蝕,膨脹(我們可以看出腐蝕是將線(xiàn)條變瘦,膨脹是變胖):
2.4 腐蝕和膨脹的知識(shí)點(diǎn)補(bǔ)充
1,可以看做膨脹是將白色區(qū)域擴(kuò)大,腐蝕是將黑色區(qū)域擴(kuò)大。
2,可以不進(jìn)行灰度處理,對(duì)彩色圖像進(jìn)行處理
腐蝕cv2.erode(src, # 輸入圖像kernel, # 卷積核dst=None,anchor=None,iterations=None, # 迭代次數(shù),默認(rèn)1borderType=None,borderValue=None)膨脹cv2.dilate(src, # 輸入圖像kernel, # 卷積核dst=None,anchor=None,iterations=None, # 迭代次數(shù),默認(rèn)1borderType=None,borderValue=None)3,開(kāi)運(yùn)算和閉運(yùn)算
開(kāi)運(yùn)算和閉運(yùn)算就是將腐蝕和膨脹按照一定的次序進(jìn)行處理。但是這兩者并不是可逆的,即先開(kāi)后閉并不能得到原來(lái)的圖像。
為了獲取圖像中的主要對(duì)象:對(duì)一幅二值圖連續(xù)使用閉運(yùn)算和開(kāi)運(yùn)算,或者消除圖像中的噪聲,也可以對(duì)圖像先用開(kāi)運(yùn)算后用閉運(yùn)算,不過(guò)這樣也會(huì)消除一些破碎的對(duì)象。
- 開(kāi)運(yùn)算:先腐蝕后膨脹,用于移除由圖像噪聲形成的斑點(diǎn)
- 閉運(yùn)算:先膨脹后腐蝕,用來(lái)連接被誤分為許多小塊的對(duì)象
3.1 開(kāi)運(yùn)算
開(kāi)運(yùn)算 = 先腐蝕運(yùn)算,再膨脹運(yùn)算(看上去把細(xì)微連在一起的兩塊目標(biāo)分開(kāi)了)
開(kāi)運(yùn)算的效果圖如下圖所示:
開(kāi)運(yùn)算總結(jié):
- (1)開(kāi)運(yùn)算能夠除去孤立的小點(diǎn),毛刺和小橋,而總的位置和形狀不變。
- (2)開(kāi)運(yùn)算是一個(gè)基于幾何運(yùn)算的濾波器
- (3)結(jié)構(gòu)元素大小的不同將導(dǎo)致濾波效果的不同
- (4)不同的結(jié)構(gòu)元素的選擇導(dǎo)致了不同的分割,即提取出不同的特征。
3.2 閉運(yùn)算
閉運(yùn)算=先膨脹運(yùn)算,再腐蝕運(yùn)算(看上去將兩個(gè)細(xì)微連接的圖封閉在一起)
閉運(yùn)算的效果如下圖所示:
閉運(yùn)算總結(jié):
- (1)閉運(yùn)算能夠填平小湖(即小孔),彌合小裂縫,而總的位置和形狀不變。
- (2)閉運(yùn)算是通過(guò)填充圖像的凹角來(lái)濾波圖像的。
- (3)結(jié)構(gòu)元素大小的不同將導(dǎo)致濾波效果的不同。
- (4)不同結(jié)構(gòu)元素的選擇導(dǎo)致了不同的分割。
代碼如下:
import cv2import numpy as npdef Open_operation(img_path):origin_img = cv2.imread(img_path)gray_img = cv2.cvtColor(origin_img, cv2.COLOR_BGR2GRAY)# OpenCV定義的結(jié)構(gòu)元素kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))# 開(kāi)運(yùn)算open = cv2.morphologyEx(gray_img, cv2.MORPH_OPEN, kernel)# 顯示腐蝕后的圖像cv2.imshow('Open', open)cv2.waitKey(0)cv2.destroyAllWindows()def Closed_operation(img_path):origin_img = cv2.imread(img_path)gray_img = cv2.cvtColor(origin_img, cv2.COLOR_BGR2GRAY)# OpenCV定義的結(jié)構(gòu)元素kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))# 閉運(yùn)算closed = cv2.morphologyEx(gray_img, cv2.MORPH_CLOSE, kernel)# 顯示腐蝕后的圖像cv2.imshow('Closed', closed)cv2.waitKey(0)cv2.destroyAllWindows()def show_origin(origin_path):# img = cv2.imread(origin_path, )# 灰度化img = cv2.imread(origin_path, 0)cv2.imshow('origin', img)cv2.waitKey(0)cv2.destroyAllWindows()if __name__ == '__main__':# 此圖為加了高斯噪聲的圖片img_path = 'butterfly_Gaussian.jpg'show_origin(img_path)Closed_operation(img_path)Open_operation(img_path)import cv2import numpy as npdef Open_operation(img_path):origin_img = cv2.imread(img_path)gray_img = cv2.cvtColor(origin_img, cv2.COLOR_BGR2GRAY)# OpenCV定義的結(jié)構(gòu)元素kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))# 開(kāi)運(yùn)算open = cv2.morphologyEx(gray_img, cv2.MORPH_OPEN, kernel)# 顯示腐蝕后的圖像cv2.imshow('Open', open)cv2.waitKey(0)cv2.destroyAllWindows()def Closed_operation(img_path):origin_img = cv2.imread(img_path)gray_img = cv2.cvtColor(origin_img, cv2.COLOR_BGR2GRAY)# OpenCV定義的結(jié)構(gòu)元素kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))# 閉運(yùn)算closed = cv2.morphologyEx(gray_img, cv2.MORPH_CLOSE, kernel)# 顯示腐蝕后的圖像cv2.imshow('Closed', closed)cv2.waitKey(0)cv2.destroyAllWindows()def show_origin(origin_path):# img = cv2.imread(origin_path, )# 灰度化img = cv2.imread(origin_path, 0)cv2.imshow('origin', img)cv2.waitKey(0)cv2.destroyAllWindows()if __name__ == '__main__':# 此圖為加了高斯噪聲的圖片img_path = 'butterfly_Gaussian.jpg'show_origin(img_path)Closed_operation(img_path)Open_operation(img_path)效果如下:(分布是原圖,開(kāi)運(yùn)算的圖,閉運(yùn)算的圖)
閉運(yùn)算用來(lái)連接被誤分為許多小塊的對(duì)象,而開(kāi)運(yùn)算用于移除由圖像噪聲形成的斑點(diǎn)。因此,某些情況下可以連續(xù)運(yùn)用這兩種運(yùn)算。如對(duì)一幅二值圖連續(xù)使用閉運(yùn)算和開(kāi)運(yùn)算,將獲得圖像中的主要讀寫(xiě)。同樣,如果想消除圖像中噪聲(即圖像中的“小點(diǎn)”),也可以對(duì)圖像先用開(kāi)運(yùn)算后用閉運(yùn)算,不過(guò)這樣也會(huì)消除一些破碎的對(duì)象。
3.3 開(kāi)運(yùn)算和閉運(yùn)算的知識(shí)點(diǎn)補(bǔ)充
這里主要補(bǔ)充函數(shù)原型
開(kāi)運(yùn)算圖像開(kāi)運(yùn)算主要使用的函數(shù)morphologyEx,它是形態(tài)學(xué)擴(kuò)展的一組函數(shù),其參數(shù)cv2.MORPH_OPEN對(duì)應(yīng)開(kāi)運(yùn)算。其原型如下:dst = cv2.morphologyEx(src, cv2.MORPH_OPEN, kernel)參數(shù)dst表示處理的結(jié)果,src表示原圖像,cv2.MORPH_OPEN表示開(kāi)運(yùn)算,kernel表示卷積核閉運(yùn)算圖像閉運(yùn)算主要使用的函數(shù)morphologyEx,其原型如下:dst = cv2.morphologyEx(src, cv2.MORPH_CLOSE, kernel)參數(shù)dst表示處理的結(jié)果,src表示原圖像, cv2.MORPH_CLOSE表示閉運(yùn)算,kernel表示卷積核4,禮帽/頂帽,黑帽算法
禮帽 :原始圖像與其進(jìn)行開(kāi)運(yùn)算后的圖像進(jìn)行一個(gè)差
黑帽:原始圖像與其閉運(yùn)算后的圖像進(jìn)行一個(gè)差
禮帽運(yùn)算 = 原始圖像 - 開(kāi)運(yùn)算
黑帽運(yùn)算 = 閉運(yùn)算 - 原始圖像
代碼如下:
import cv2def hat_algorithm(img_path):original_img0 = cv2.imread(img_path)original_img = cv2.imread(img_path, 0)kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) # 定義矩形結(jié)構(gòu)元素TOPHAT_img = cv2.morphologyEx(original_img, cv2.MORPH_TOPHAT, kernel) # 頂帽運(yùn)算BLACKHAT_img = cv2.morphologyEx(original_img, cv2.MORPH_BLACKHAT, kernel) # 黒帽運(yùn)算# 顯示圖像cv2.imshow("original_img0", original_img0)cv2.imshow("original_img", original_img)cv2.imshow("TOPHAT_img", TOPHAT_img)cv2.imshow("BLACKHAT_img", BLACKHAT_img)cv2.waitKey(0)cv2.destroyAllWindows()if __name__ == '__main__':img_path = 'butterfly_Gaussian.jpg'hat_algorithm(img_path)效果如下:(依次是原圖,灰度圖圖片(兩個(gè)類(lèi)似,是因?yàn)槲覍⒃瓐D做了灰度化高斯處理,所以灰度化之后和原圖類(lèi)似),頂帽圖片,黑帽圖片)
該算法可以用于圖像識(shí)別的預(yù)處理,用于圖像二值化后取出孤立點(diǎn),代碼如下:
import cv2def deal_isolated(img_path):original_img = cv2.imread(img_path, 0)gray_img = cv2.resize(original_img, None, fx=0.8, fy=0.8,interpolation=cv2.INTER_CUBIC) # 圖形太大了縮小一點(diǎn)kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) # 定義矩形結(jié)構(gòu)元素(核大小為3效果好)TOPHAT_img = cv2.morphologyEx(gray_img, cv2.MORPH_TOPHAT, kernel) # 頂帽運(yùn)算BLACKHAT_img = cv2.morphologyEx(gray_img, cv2.MORPH_BLACKHAT, kernel) # 黒帽運(yùn)算bitwiseXor_gray = cv2.bitwise_xor(gray_img, TOPHAT_img)# 顯示如下腐蝕后的圖像cv2.imshow("gray_img", gray_img)cv2.imshow("TOPHAT_img", TOPHAT_img)cv2.imshow("BLACKHAT_img", BLACKHAT_img)cv2.imshow("bitwiseXor_gray", bitwiseXor_gray)cv2.waitKey(0)cv2.destroyAllWindows()if __name__ == '__main__':img_path = 'lena.jpg'deal_isolated(img_path)可以看出,最后效果更加明顯了一些:
5,梯度運(yùn)算
梯度 = 膨脹 - 腐蝕
下面看一個(gè)示例:
import cv2import numpy as npimport matplotlib.pyplot as pltimg = cv2.imread('circle.jpg')kernel = np.ones((7, 7), np.uint8)# kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7))erosion = cv2.erode(img, kernel, iterations = 5)dilation = cv2.dilate(img, kernel, iterations = 3)gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)result = [img, erosion, dilation, gradient]titles = ['origin img', 'erosion img', 'dilate img', 'gradient img']for i in range(4):plt.subplot(2, 2, i+1), plt.imshow(result[i])plt.title(titles[i])plt.xticks([]), plt.yticks([])plt.show()效果如下:
6,用形態(tài)學(xué)運(yùn)算檢測(cè)邊和角點(diǎn)
這里通過(guò)一個(gè)較復(fù)雜的例子學(xué)習(xí)如何用形態(tài)學(xué)算子檢測(cè)圖像中的邊緣和拐角(這里只做形態(tài)學(xué)處理例子,實(shí)際使用請(qǐng)參考Canny和Harris等算法:請(qǐng)參考博文:深入學(xué)習(xí)OpenCV中幾種圖像邊緣檢測(cè)算子)
6.1 檢測(cè)邊緣
形態(tài)學(xué)檢測(cè)邊緣的原理很簡(jiǎn)單,在膨脹時(shí),圖像中的物體會(huì)向周?chē)皵U(kuò)張”;腐蝕時(shí),圖像的額物體會(huì)“收縮”。比較兩幅圖像,由于其變化的區(qū)域只發(fā)生在邊緣。所以這時(shí)將這兩幅圖像相減,得到的就是圖像中的邊緣。這里用的依然是參考資料《Opencv2 Computer Vision Application Programming Cookbook》中相關(guān)章節(jié)的圖片:
代碼如下:
# coding=utf-8import cv2import numpydef detection_edge(img_path):image = cv2.imread(img_path, 0)# 構(gòu)造一個(gè)3×3的結(jié)構(gòu)元素element = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))dilate = cv2.dilate(image, element)erode = cv2.erode(image, element)# 將兩幅圖像相減獲得邊,第一個(gè)參數(shù)是膨脹后的圖像,第二個(gè)參數(shù)是腐蝕后的圖像# cv2.absdiff參數(shù):(膨脹后的圖像,腐蝕后的圖像)result = cv2.absdiff(dilate, erode)# 上面得到的結(jié)果是灰度圖,將其二值化以便更清楚的觀(guān)察結(jié)果retval, result = cv2.threshold(result, 40, 255, cv2.THRESH_BINARY)# 反色,即對(duì)二值圖每個(gè)像素取反result = cv2.bitwise_not(result)# 顯示圖像cv2.imshow("result", result)cv2.waitKey(0)cv2.destroyAllWindows()if __name__ == '__main__':img_path = "building.jpg"detection_edge(img_path)結(jié)果如下:
6.2 檢測(cè)角點(diǎn)(拐角)
與邊緣檢測(cè)不同,拐角的檢測(cè)過(guò)程稍稍有些復(fù)雜。但是原理相同,所不同的是先用十字形的結(jié)構(gòu)元素膨脹像素,這種情況下只會(huì)在邊緣處“擴(kuò)張”,角點(diǎn)不發(fā)生變化。接著用菱形的結(jié)構(gòu)元素腐蝕原圖像,導(dǎo)致只有在拐角處才會(huì)“收縮”,而直線(xiàn)邊緣都未發(fā)生變化。
第二步是用X行膨脹原圖像,焦點(diǎn)膨脹的比邊要多。這樣第二次用方塊腐蝕時(shí),角點(diǎn)恢復(fù)原狀,而邊要腐蝕的更多。所以當(dāng)兩幅圖像相減時(shí),只保留了拐角處,示意圖如下(示意圖來(lái)自參考資料《Opencv2 Computer Vision Application Programming Cookbook》):
代碼如下:
# coding=utf-8import cv2import numpydef detection_inflexion(img_path):image = cv2.imread(img_path, 0)origin = cv2.imread(img_path)# 構(gòu)造5×5的結(jié)構(gòu)元素,分別為十字形、菱形、方形和X型cross = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5))# 菱形結(jié)構(gòu)元素的定義稍麻煩一些diamond = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))diamond[0, 0] = 0diamond[0, 1] = 0diamond[1, 0] = 0diamond[4, 4] = 0diamond[4, 3] = 0diamond[3, 4] = 0diamond[4, 0] = 0diamond[4, 1] = 0diamond[3, 0] = 0diamond[0, 3] = 0diamond[0, 4] = 0diamond[1, 4] = 0square = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))x = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5))# 使用cross膨脹圖像dilate_cross_img = cv2.dilate(image, cross)# 使用菱形腐蝕圖像erode_diamond_img = cv2.erode(dilate_cross_img, diamond)# 使用X膨脹原圖像dilate_x_img = cv2.dilate(image, x)# 使用方形腐蝕圖像erode_square_img = cv2.erode(dilate_x_img, square)# result = result1.copy()# 將兩幅閉運(yùn)算的圖像相減獲得角result = cv2.absdiff(erode_square_img, erode_diamond_img)# 使用閾值獲得二值圖retval, result = cv2.threshold(result, 40, 255, cv2.THRESH_BINARY)# 在原圖上用半徑為5的圓圈將點(diǎn)標(biāo)出。for j in range(result.size):y = int(j / result.shape[0])x = int(j % result.shape[0])if result[x, y] == 255:cv2.circle(image, (y, x), 5, (255, 0, 0))cv2.imshow("Result", image)cv2.waitKey(0)cv2.destroyAllWindows()if __name__ == '__main__':img_path = "building.jpg"detection_inflexion(img_path)通過(guò)上面的代碼就能檢測(cè)出圖像的拐角并標(biāo)出來(lái),效果如下:
當(dāng)然這個(gè)只是一個(gè)示例,效果不是很好。
參考文獻(xiàn):https://www.cnblogs.com/ssyfj/p/9276999.htmlhttps://blog.csdn.net/wsp_1138886114/article/details/82917661https://blog.csdn.net/JohinieLi/article/details/81041276https://blog.csdn.net/hanshanbuleng/article/details/806571481、《Opencv2 Computer Vision Application Programming Cookbook》
2、《OpenCV References Manule》
最初的來(lái)源: https://blog.csdn.net/sunny2038/article/details/9137759https://blog.csdn.net/gbxvip/article/details/50844007
更多精彩內(nèi)容請(qǐng)?jiān)L問(wèn)FlyAI-AI競(jìng)賽服務(wù)平臺(tái);為AI開(kāi)發(fā)者提供數(shù)據(jù)競(jìng)賽并支持GPU離線(xiàn)訓(xùn)練的一站式服務(wù)平臺(tái);每周免費(fèi)提供項(xiàng)目開(kāi)源算法樣例,支持算法能力變現(xiàn)以及快速的迭代算法模型。
挑戰(zhàn)者,都在FlyAI!!!
總結(jié)
以上是生活随笔為你收集整理的开运算和闭运算_OpenCV计算机视觉学习(5)——形态学处理(腐蚀膨胀,开闭运算,礼帽黑帽,边缘检测)...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: clickhouse 子查询_TPCDS
- 下一篇: 铁矿怎么来_铁矿期货今日创出新高908.