一、概論
下面將學習opencv中邊緣檢測的各種算子和濾波器:
包括canny算子,sobel算子,scharr算子。
什麼叫做邊緣檢測呢?
邊緣檢測的目標是標識數字圖像中亮度變化明顯的點。圖像屬性中的顯著變化通常反應了屬性的重要事件和變化,包括:
(1) 、深度上的不連續
(2) 、表面方向的不連續
(3) 、物質屬性變化
(4) 、場景照明變化
邊緣檢測剔除了大量認爲與圖像特徵不相關的數據,只保留了圖像中較爲重要的屬性。邊緣檢測和視角有關,視角不一樣,邊緣檢測的結果就不一樣。
檢測方法:
許多邊緣檢測的方法,可以分爲兩類:基於搜索和基於零交叉
對於基於搜索的邊緣檢測,首先計算邊緣強度,通常用一階導數表示,用計算值估計邊緣的局部方向。
對於零交叉的方法,是找到由圖像得到的二階導數的零交叉點來定位邊緣。
許多邊緣檢測的方法依賴於圖像梯度的計算,用不同種類的濾波器來估計x方向和y方向的梯度。
計算一階導數:許多邊緣檢測都是基於高亮的一階導數,這樣就可以得到原始圖像亮度的梯度。用這個梯度我們可以在圖像的亮度梯度中尋找峯值,我們用l(x)表示點x的亮度,l’(x)表示點x的一階導數(亮度梯度),我們可以用下面的方法來計算亮梯度:
計算二階導數:其他的邊緣檢測操作如基於亮度的二階導數,這在數學上就是求一階導數的變化率,也就是圖像亮度的變化率,在二階導數中檢測過零點將得到梯度中的最大值,在二階導數中的峯值檢測是邊線檢測,邊線是雙重邊緣,這樣我們就可以在邊緣的一邊看到一個亮度梯度,在另一邊看到相反的梯度,這樣如果圖像中有邊線出現的話,我們就能在亮度梯度上看到非常大的變化,我們可以通過在二階導數中尋找過零點來尋找邊線。我們用I(x)來表示點x的亮度,I``(x)表示點x的亮度的二階導數,那麼可以這樣計算:
所以,下面介紹邊緣檢測的一般步驟 :
(1) 、濾波:邊緣檢測算法是基於圖像亮度的一階導數和二階導數,但是導數的計算對噪聲很敏感,所以需要濾波器來改善圖像,但是我們需要注意,大多數濾波器都會在降低噪聲的同時導致邊緣強度的減弱。
(2) 、增強:增強邊緣的基礎是確定圖像各個點的鄰域強度的變化值,可以將鄰域強度值有顯著變化的點突出出來,便於安裝增強一般是通過計算梯度幅度來完成的。
(3) 、檢測:在圖像中有許多點的梯度幅度比較大,但是不都是我們需要的邊緣,所以應該檢測哪些點是邊緣,可以通過設定梯度幅度閾值來檢測。
(4) 、定位:確定邊緣位置,但是大多數應用只需要指出邊緣出現在圖像的哪些區域,不需要指出圖像邊緣的精確位置或者方向。
二、幾種邊緣檢測算子
1、canny算子
特點:
(1)、低錯誤率:標識出儘可能多的實際邊緣,同時儘可能的減少噪聲產生的干擾
(2)、高定位性:標誌出的邊緣和圖像中的邊緣儘可能的接近
(3)、最小響應:圖像中的邊緣只能標誌一次,並且可能存在的噪聲不應該標誌爲邊緣
Canny邊緣檢測的步驟:
(1)、消除噪聲:可以使用高斯平滑濾波器卷積降噪
(2)、計算梯度幅值和方向
<1>、運用一對卷積陣列(分別作用於x方向和y方向)
<2>、使用下面的公式計算梯度幅值和方向
梯度方向將近似到四個可能的角度:0,45,90,135
(3)、非極大值抑制:僅僅保留了一些有可能是邊緣的線條
(4)、滯後閾值:這是最後一步,滯後閾值需要兩個閾值,一個高閾值一個低閾值
<1>、如果某一像素的幅值超過高閾值,保留
<2>、如果小於低閾值,不保留
<3>、在中間,只有當這個像素連接到一個高於高閾值的像素時才保留
----------高低閾值比大概應該在<2:1 or 3:1>--------
(5)、函數詳解:
```
void Canny(InputArray image,OutputArray edges, double threshold1, double threshold2, int apertureSize=3,bool L2gradient=false )
```
· 第一個參數,InputArray類型的image,輸入圖像,即源圖像,填Mat類的對象即可,且需爲單通道8位圖像。
· 第二個參數,OutputArray類型的edges,輸出的邊緣圖,需要和源圖片有一樣的尺寸和類型。
· 第三個參數,double類型的threshold1,第一個滯後性閾值。
· 第四個參數,double類型的threshold2,第二個滯後性閾值。
· 第五個參數,int類型的apertureSize,表示應用Sobel算子的孔徑大小,其有默認值3。
· 第六個參數,bool類型的L2gradient,一個計算圖像梯度幅值的標識,有默認值false。
2 、sobel算子
(1)、分別在x和y兩個方向求導
<1>、水平變化,用I與一個奇數大小的內核做卷積,比如當內核大小爲3x3時,可以這樣就算Gx:
<2>、垂直變化,用I與一個奇數大小的內核做卷積,比如當內核大小爲3x3時,可以這樣計算Gy:
(2)、在圖像的每一個點,結合上面兩個方向上的計算結果求出近似梯度:
也可以使用更簡單的公式來計算:
(2)、函數詳解:
void Sobel (
InputArray src,//輸入圖
OutputArray dst,//輸出圖
int ddepth,//輸出圖像的深度
int dx,
int dy,
int ksize=3,
double scale=1,
double delta=0,
int borderType=BORDER_DEFAULT );
· 第一個參數,InputArray 類型的src,爲輸入圖像,填Mat類型即可。
· 第二個參數,OutputArray類型的dst,即目標圖像,函數的輸出參數,需要和源圖片有一樣的尺寸和類型。
· 第三個參數,int類型的ddepth,輸出圖像的深度,支持如下src.depth()和ddepth的組合:
o 若src.depth() = CV_8U, 取ddepth =-1/CV_16S/CV_32F/CV_64F
o 若src.depth() = CV_16U/CV_16S, 取ddepth =-1/CV_32F/CV_64F
o 若src.depth() = CV_32F, 取ddepth =-1/CV_32F/CV_64F
o 若src.depth() = CV_64F, 取ddepth = -1/CV_64F
· 第四個參數,int類型dx,x 方向上的差分階數。
· 第五個參數,int類型dy,y方向上的差分階數。
· 第六個參數,int類型ksize,有默認值3,表示Sobel核的大小;必須取1,3,5或7。
· 第七個參數,double類型的scale,計算導數值時可選的縮放因子,默認值是1,表示默認情況下是沒有應用縮放的。我們可以在文檔中查閱getDerivKernels的相關介紹,來得到這個參數的更多信息。
· 第八個參數,double類型的delta,表示在結果存入目標圖(第二個參數dst)之前可選的delta值,有默認值0。
· 第九個參數, int類型的borderType,我們的老朋友了(萬年是最後一個參數),邊界模式,默認值爲BORDER_DEFAULT。這個參數可以在官方文檔中borderInterpolate處得到更詳細的信息。
3、laplace算子
定義:
函數詳解:
void Laplacian(InputArray src,OutputArray dst, int ddepth, int ksize=1, double scale=1, double delta=0, intborderType=BORDER_DEFAULT );
· 第一個參數,InputArray類型的image,輸入圖像,即源圖像,填Mat類的對象即可,且需爲單通道8位圖像。
· 第二個參數,OutputArray類型的edges,輸出的邊緣圖,需要和源圖片有一樣的尺寸和通道數。
· 第三個參數,int類型的ddept,目標圖像的深度。
· 第四個參數,int類型的ksize,用於計算二階導數的濾波器的孔徑尺寸,大小必須爲正奇數,且有默認值1。
· 第五個參數,double類型的scale,計算拉普拉斯值的時候可選的比例因子,有默認值1。
· 第六個參數,double類型的delta,表示在結果存入目標圖(第二個參數dst)之前可選的delta值,有默認值0。
· 第七個參數, int類型的borderType,邊界模式,默認值爲BORDER_DEFAULT。這個參數可以在官方文檔中borderInterpolate()處得到更詳細的信息。
4、濾波器
void Scharr(
InputArray src, //源圖
OutputArray dst, //目標圖
int ddepth,//圖像深度
int dx,// x方向上的差分階數
int dy,//y方向上的差分階數
double scale=1,//縮放因子
double delta=0,// delta值
intborderType=BORDER_DEFAULT )// 邊界模式
· 第一個參數,InputArray 類型的src,爲輸入圖像,填Mat類型即可。
· 第二個參數,OutputArray類型的dst,即目標圖像,函數的輸出參數,需要和源圖片有一樣的尺寸和類型。
· 第三個參數,int類型的ddepth,輸出圖像的深度,支持如下src.depth()和ddepth的組合:
o 若src.depth() = CV_8U, 取ddepth =-1/CV_16S/CV_32F/CV_64F
o 若src.depth() = CV_16U/CV_16S, 取ddepth =-1/CV_32F/CV_64F
o 若src.depth() = CV_32F, 取ddepth =-1/CV_32F/CV_64F
o 若src.depth() = CV_64F, 取ddepth = -1/CV_64F
· 第四個參數,int類型dx,x方向上的差分階數。
· 第五個參數,int類型dy,y方向上的差分階數。
· 第六個參數,double類型的scale,計算導數值時可選的縮放因子,默認值是1,表示默認情況下是沒有應用縮放的。我們可以在文檔中查閱getDerivKernels的相關介紹,來得到這個參數的更多信息。
· 第七個參數,double類型的delta,表示在結果存入目標圖(第二個參數dst)之前可選的delta值,有默認值0。
· 第八個參數, int類型的borderType,我們的老朋友了(萬年是最後一個參數),邊界模式,默認值爲BORDER_DEFAULT。這個參數可以在官方文檔中borderInterpolate處得到更詳細的信息。