一、Canny算法介紹
Canny 的目標是找到一個最優的邊緣檢測算法,最優邊緣檢測的含義是:
好的檢測- 算法能夠盡可能多地標識出圖像中的實際邊緣。
好的定位- 標識出的邊緣要盡可能與實際圖像中的實際邊緣盡可能接近。
最小響應- 圖像中的邊緣只能標識一次,並且可能存在的圖像噪聲不應標識為邊緣。
1、canny算法步驟
1.高斯模糊--GaussianBlur 消除噪聲。 一般情況下,使用高斯平滑濾波器卷積降噪。,因為canny是對噪聲敏感的算法,所以先降噪,但是降噪不要太過,以免丟失
2.灰度轉換--cvtColor
3.計算梯度--Sobel/Scharr
4.非最大信號抑制
5.高低閾值輸出二值圖像
2、非最大信號抑制
圖像梯度幅值矩陣中的元素值越大,說明圖像中該點的梯度值越大,但這不不能說明該點就是邊緣(這僅僅是屬於圖像增強的過程)。在Canny算法中,非極大值抑制是進行邊緣檢測的重要步驟,通俗意義上是指尋找像素點局部最大值,將非極大值點所對應的灰度值置為0,這樣可以剔除掉一大部分非邊緣的點(這是本人的理解)。
根據圖可知,要進行非極大值抑制,就首先要確定像素點C的灰度值在其8值鄰域內是否為最大。圖中藍色的線條方向為C點的梯度方向,這樣就可以確定其局部的最大值肯定分布在這條線上,也即出了C點外,梯度方向的交點dTmp1和dTmp2這兩個點的值也可能會是局部最大值。因此,判斷C點灰度與這兩個點灰度大小即可判斷C點是否為其鄰域內的局部最大灰度點。如果經過判斷,C點灰度值小於這兩個點中的任一個,那就說明C點不是局部極大值,那么則可以排除C點為邊緣。這就是非極大值抑制的工作原理。
注意以下兩點:
1)中非最大抑制是回答這樣一個問題:“當前的梯度值在梯度方向上是一個局部最大值嗎?” 所以,要把當前位置的梯度值與梯度方向上兩側的梯度值進行比較;
2)梯度方向垂直於邊緣方向。
但實際上,我們只能得到C點鄰域的8個點的值,而dTmp1和dTmp2並不在其中,要得到這兩個值就需要對該兩個點兩端的已知灰度進行線性插值,也即根據圖中的g1和g2對dTmp1進行插值,根據g3和g4對dTmp2進行插值,這要用到其梯度方向,這是Canny算法中要求解梯度方向矩陣Thita的原因。
完成非極大值抑制后,會得到一個二值圖像,非邊緣的點灰度值均為0,可能為邊緣的局部灰度極大值點可設置其灰度為128。根據下文的具體測試圖像可以看出,這樣一個檢測結果還是包含了很多由噪聲及其他原因造成的假邊緣。因此還需要進一步的處理。
3、高低閾值輸出二值圖像
二、代碼實現兩種canny方法
1、需要我們求出梯度,這里用sobel計算梯度
Canny(dx, dy, threshold1, threshold2[, edges[, L2gradient]]) -> edges
使用帶自定義圖像漸變的Canny算法在圖像中查找邊緣,
dx參數表示輸入圖像的x導數(x導數滿足16位,選擇CV_16SC1或CV_16SC3)
dy參數表示輸入圖像的y導數(y導數滿足16位,選擇CV_16SC1或CV_16SC3)。
threshold1參數表示設置的低閾值。
threshold2參數表示設置的高閾值,一般設定為低閾值的3倍 (根據Canny算法的推薦)。
edges參數表示輸出邊緣圖像,單通道8位圖像。
L2gradient參數表示L2gradient參數表示一個布爾值,如果為真,則使用更精確的L2范數進行計算(即兩個方向的倒數的平方和再開方),否則使用L1范數(直接將兩個方向導數的絕對值相加)。
代碼實現
1 import cv2 as cv 2 3 def edge_demo(image): 4 #1、高斯模糊 去噪聲 5 #arg:輸入圖像 6 #arg:高斯核(可以是(0,0),這樣會自動根據sigmax計算得來) 7 #age:sigmax(高斯核函數在X方向的的標准偏差) 8 gaosiblur = cv.GaussianBlur(image,(3,3),0) 9 10 #2、灰度轉換 11 gray = cv.cvtColor(gaosiblur,cv.COLOR_BGR2GRAY) 12 13 #3、計算梯度 14 xgrad = cv.Sobel(gray,cv.CV_16SC1,1,0) #canny方法API要求不允許使用浮點數 15 ygrad = cv.Sobel(gray,cv.CV_16SC1,0,1) 16 17 #4.Canny方法中包含非最大信號抑制和雙閾值輸出 18 edge_output = cv.Canny(xgrad,ygrad,50,150) #50是低閾值,150是高閾值 19 cv.imshow('canny_edge',edge_output) 20 21 # 相與,獲取顏色(掩模就是canny_edge,兩個imag先與之后,最后在與mask與,mask的黑色部分用於剔除,白色部分用於保留) 22 dst = cv.bitwise_and(image,image,mask=edge_output) 23 cv.imshow('color edge',dst) 24 25 img = cv.imread('1.jpg') 26 cv.imshow('input image',img) 27 edge_demo(img) 28 cv.waitKey(0) 29 cv.destroyAllWindows()
2、直接調用Canny算法在單通道灰度圖像中查找邊緣
def Canny(image, threshold1, threshold2, edges=None, apertureSize=None, L2gradient=None):
image參數表示8位輸入圖像。
threshold1參數表示設置的低閾值。
threshold2參數表示設置的高閾值,一般設定為低閾值的3倍 (根據Canny算法的推薦)。
edges參數表示輸出邊緣圖像,單通道8位圖像。
apertureSize參數表示Sobel算子的大小。
L2gradient參數表示一個布爾值,如果為真,則使用更精確的L2范數進行計算(即兩個方向的倒數的平方和再開方),否則使用L1范數(直接將兩個方向導數的絕對值相加)。
代碼實現
1 import cv2 as cv 2 3 def edge_demo(image): 4 5 #1、高斯模糊 去噪聲 6 #arg:輸入圖像 7 #arg:高斯核(可以是(0,0),這樣會自動根據sigmax計算得來) 8 #age:sigmax(高斯核函數在X方向的的標准偏差) 9 gaosiblur = cv.GaussianBlur(image,(3,3),0) 10 11 #2、灰度轉換 12 gray = cv.cvtColor(gaosiblur,cv.COLOR_BGR2GRAY) 13 14 #3、直接傳入灰度圖像,Canny方法中包含計算梯度,菲最大信號抑制和雙閾值輸出 15 edge_output = cv.Canny(gray,50,150) #50是低閾值,150是高閾值 16 cv.imshow('canny_edge',edge_output) 17 dst = cv.bitwise_and(image,image,mask=edge_output) #相與,獲取顏色 18 cv.imshow('color edge',dst) 19 img = cv.imread('1.jpg') 20 cv.imshow('input image',img) 21 edge_demo(img) 22 cv.waitKey(0) 23 cv.destroyAllWindows()
三、掩膜mask
在有些圖像處理的函數中有的參數里面會有mask參數,即此函數支持掩膜操作,首先何為掩膜以及有什么用,如下:
數字圖像處理中的掩膜的概念是借鑒於PCB制版的過程,在半導體制造中,許多芯片工藝步驟采用光刻技術,用於這些步驟的圖形“底片”稱為掩膜(也稱作“掩模”),其作用是:在硅片上選定的區域中對一個不透明的圖形模板遮蓋,繼而下面的腐蝕或擴散將只影響選定的區域以外的區域。
圖像掩膜與其類似,用選定的圖像、圖形或物體,對處理的圖像(全部或局部)進行遮擋,來控制圖像處理的區域或處理過程。
數字圖像處理中,掩模為二維矩陣數組,有時也用多值圖像,圖像掩模主要用於:
①提取感興趣區,用預先制作的感興趣區掩模與待處理圖像相乘,得到感興趣區圖像,感興趣區內圖像值保持不變,而區外圖像值都為0。
②屏蔽作用,用掩模對圖像上某些區域作屏蔽,使其不參加處理或不參加處理參數的計算,或僅對屏蔽區作處理或統計。
③結構特征提取,用相似性變量或圖像匹配方法檢測和提取圖像中與掩模相似的結構特征。
④特殊形狀圖像的制作。
在所有圖像基本運算的操作函數中,凡是帶有掩膜(mask)的處理函數,其掩膜都參與運算(輸入圖像運算完之后再與掩膜圖像或矩陣運算)。
利用掩膜(mask)進行“與”操作,即掩膜圖像白色區域是對需要處理圖像像素的保留,黑色區域是對需要處理圖像像素的剔除,其余按位操作原理類似只是效果不同而已。