[OpenCV入門教程之十二】OpenCV邊緣檢測:Canny算子,Sobel算子,Laplace算子,Scharr濾波器合輯


http://blog.csdn.net/poem_qianmo/article/details/25560901

 

 

本系列文章由@淺墨_毛星雲 出品,轉載請注明出處。  

 

 

 

 

 

 

文章鏈接: http://blog.csdn.net/poem_qianmo/article/details/25560901

 

作者:毛星雲(淺墨)    微博:http://weibo.com/u/1723155442

知乎:http://www.zhihu.com/people/mao-xing-yun

郵箱: happylifemxy@163.com

寫作當前博文時配套使用的OpenCV版本: 2.4.9

 

 

本篇文章中,我們將一起學習OpenCV中邊緣檢測的各種算子和濾波器——Canny算子,Sobel算子,Laplace算子以及Scharr濾波器。文章中包含了五個淺墨為大家准備的詳細注釋的博文配套源代碼。在介紹四塊知識點的時候分別一個,以及最后的綜合示例中的一個。文章末尾提供配套源代碼的下載。

依然是是放出一些程序運行截圖吧:

 

 

 效果圖看完,我們來嘮嘮嗑。

 

首先,需要說明的是,淺墨這篇文章最后的示例代碼是采用兩周前剛剛發布的2.4.9來書寫的。里面的lib都已經改成了2.4.9版本的。如果大家需要運行的話,要么配置好2.4.9.要么把淺墨在工程中包含的末尾數字為249的各種lib改成之前的248或者你對應的OpenCV版本。

不然會提示: LINK : fatal error LNK1181: 無法打開輸入文件“opencv_calib3d248.lib”之類的錯誤。

OpenCV 2.4.9的配置和之前的2.4.8差不多,如果還是不太清楚,具體可以參考淺墨修改過的對應2.4.9版的配置文章:

 

【OpenCV入門教程之一】 安裝OpenCV:OpenCV 2.4.8或2.4.9 +VS 開發環境配置

 

第二,給大家分享一個OpenCV中寫代碼時節約時間的小常識。其實OpenCV中,不用namedWindow,直接imshow就可以顯示出窗口。大家看下文的示例代碼就可以發現,淺墨在寫代碼的時候並沒有用namedWindow,遇到想顯示出來的Mat變量直接imshow。我們一般是為了規范,才先用namedWindow創建窗口,再imshow出它來,因為我們還有需要用到指定窗口名稱的地方,比如用到trackbar的時候。而一般情況想顯示一個Mat變量的圖片的話,直接imshow就可以啦。

 

OK,開始正文吧~

 

 

 

 

 

一、關於邊緣檢測

       

 

 

在具體介紹之前,先來一起看看邊緣檢測的一般步驟吧。

 

1)濾波:邊緣檢測的算法主要是基於圖像強度的一階和二階導數,但導數通常對噪聲很敏感,因此必須采用濾波器來改善與噪聲有關的邊緣檢測器的性能。常見的濾波方法主要有高斯濾波,即采用離散化的高斯函數產生一組歸一化的高斯核(具體見“高斯濾波原理及其編程離散化實現方法”一文),然后基於高斯核函數對圖像灰度矩陣的每一點進行加權求和(具體程序實現見下文)。

 

        2)增強:增強邊緣的基礎是確定圖像各點鄰域強度的變化值。增強算法可以將圖像灰度點鄰域強度值有顯著變化的點凸顯出來。在具體編程實現時,可通過計算梯度幅值來確定。

 

        3)檢測:經過增強的圖像,往往鄰域中有很多點的梯度值比較大,而在特定的應用中,這些點並不是我們要找的邊緣點,所以應該采用某種方法來對這些點進行取舍。實際工程中,常用的方法是通過閾值化方法來檢測。

 

另外,需要注意,下文中講到的Laplace算子,sobel算子和Scharr算子都是帶方向的,所以,示例中我們分別寫了X方向,Y方向和最終合成的的效果圖。

 

OK,正餐開始,召喚canny算子。:)

 

 

 

 

 

 

 

 

二、canny算子篇

 

 

 

2.1 canny算子相關理論與概念講解

 

2.1.1 canny算子簡介

 

Canny邊緣檢測算子是John F.Canny於 1986 年開發出來的一個多級邊緣檢測算法。更為重要的是 Canny 創立了邊緣檢測計算理論(Computational theory ofedge detection),解釋了這項技術是如何工作的。Canny邊緣檢測算法以Canny的名字命名,被很多人推崇為當今最優的邊緣檢測的算法。

其中,Canny 的目標是找到一個最優的邊緣檢測算法,讓我們看一下最優邊緣檢測的三個主要評價標准:

 

1.低錯誤率: 標識出盡可能多的實際邊緣,同時盡可能的減少噪聲產生的誤報。

 

2.高定位性: 標識出的邊緣要與圖像中的實際邊緣盡可能接近。

 

3.最小響應: 圖像中的邊緣只能標識一次,並且可能存在的圖像噪聲不應標識為邊緣。

 

為了滿足這些要求 Canny 使用了變分法,這是一種尋找滿足特定功能的函數的方法。最優檢測使用四個指數函數項的和表示,但是它非常近似於高斯函數的一階導數。

 

 

 

 

2.1.2 Canny 邊緣檢測的步驟

 

 

1.消除噪聲。 一般情況下,使用高斯平滑濾波器卷積降噪。 如下顯示了一個 size = 5 的高斯內核示例:

 

 

 

 

2.計算梯度幅值和方向。 此處,按照Sobel濾波器的步驟。

 

Ⅰ.運用一對卷積陣列 (分別作用於 x 和 y 方向):

 

 

 

 

 

Ⅱ.使用下列公式計算梯度幅值和方向:

 

 

 

梯度方向近似到四個可能角度之一(一般為0, 45, 90, 135)

 

3.非極大值抑制。 這一步排除非邊緣像素, 僅僅保留了一些細線條(候選邊緣)。

 

 

4.滯后閾值。最后一步,Canny 使用了滯后閾值,滯后閾值需要兩個閾值(高閾值和低閾值):

 

Ⅰ.如果某一像素位置的幅值超過 高 閾值, 該像素被保留為邊緣像素。

Ⅱ.如果某一像素位置的幅值小於 低 閾值, 該像素被排除。

.如果某一像素位置的幅值在兩個閾值之間,該像素僅僅在連接到一個高於 高 閾值的像素時被保留。

 

tips:對於Canny函數的使用,推薦的高低閾值比在2:1到3:1之間。

 

更多的細節,可以參考canny算子的wikipedia:

http://en.wikipedia.org/wiki/Canny_edge_detector

canny邊緣檢測的原理講述,課參看這篇博文:

http://blog.csdn.net/likezhaobin/article/details/6892176

canny算子的中文wikipedia:

http://zh.wikipedia.org/wiki/Canny%E7%AE%97%E5%AD%90

 

 

 

2.2 OpenCV中Canny函數詳解

 

Canny函數利用Canny算法來進行圖像的邊緣檢測。

 

 

[cpp]  view plain  copy
 
  1. C++: 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。

 

 

需要注意的是,這個函數閾值1和閾值2兩者的小者用於邊緣連接,而大者用來控制強邊緣的初始段,推薦的高低閾值比在2:1到3:1之間。

 

調用示例:

 

[cpp]  view plain  copy
 
  1. //載入原始圖   
  2.        Mat src = imread("1.jpg");  //工程目錄下應該有一張名為1.jpg的素材圖  
  3.        Canny(src, src, 3, 9,3 );  
  4.        imshow("【效果圖】Canny邊緣檢測", src);  

 

如上三句,就有結果出來,非常好用。

 

 

 

 

2.3 調用Canny函數的實例代碼

 

 

OpenCV中調用Canny函數的實例代碼如下:

 

[cpp]  view plain  copy
 
  1. //-----------------------------------【頭文件包含部分】---------------------------------------  
  2. //            描述:包含程序所依賴的頭文件  
  3. //----------------------------------------------------------------------------------------------  
  4. #include <opencv2/opencv.hpp>  
  5. #include<opencv2/highgui/highgui.hpp>  
  6. #include<opencv2/imgproc/imgproc.hpp>  
  7.   
  8. //-----------------------------------【命名空間聲明部分】---------------------------------------  
  9. //            描述:包含程序所使用的命名空間  
  10. //-----------------------------------------------------------------------------------------------  
  11. using namespace cv;  
  12. //-----------------------------------【main( )函數】--------------------------------------------  
  13. //            描述:控制台應用程序的入口函數,我們的程序從這里開始  
  14. //-----------------------------------------------------------------------------------------------  
  15. int main( )  
  16. {  
  17.     //載入原始圖    
  18.     Mat src = imread("1.jpg");  //工程目錄下應該有一張名為1.jpg的素材圖  
  19.     Mat src1=src.clone();  
  20.   
  21.     //顯示原始圖   
  22.     imshow("【原始圖】Canny邊緣檢測", src);   
  23.   
  24.     //----------------------------------------------------------------------------------  
  25.     //  一、最簡單的canny用法,拿到原圖后直接用。  
  26.     //----------------------------------------------------------------------------------  
  27.     Canny( src, src, 150, 100,3 );  
  28.     imshow("【效果圖】Canny邊緣檢測", src);   
  29.   
  30.       
  31.     //----------------------------------------------------------------------------------  
  32.     //  二、高階的canny用法,轉成灰度圖,降噪,用canny,最后將得到的邊緣作為掩碼,拷貝原圖到效果圖上,得到彩色的邊緣圖  
  33.     //----------------------------------------------------------------------------------  
  34.     Mat dst,edge,gray;  
  35.   
  36.     // 【1】創建與src同類型和大小的矩陣(dst)  
  37.     dst.create( src1.size(), src1.type() );  
  38.   
  39.     // 【2】將原圖像轉換為灰度圖像  
  40.     cvtColor( src1, gray, CV_BGR2GRAY );  
  41.   
  42.     // 【3】先用使用 3x3內核來降噪  
  43.     blur( gray, edge, Size(3,3) );  
  44.   
  45.     // 【4】運行Canny算子  
  46.     Canny( edge, edge, 3, 9,3 );  
  47.   
  48.     //【5】將g_dstImage內的所有元素設置為0   
  49.     dst = Scalar::all(0);  
  50.   
  51.     //【6】使用Canny算子輸出的邊緣圖g_cannyDetectedEdges作為掩碼,來將原圖g_srcImage拷到目標圖g_dstImage中  
  52.     src1.copyTo( dst, edge);  
  53.   
  54.     //【7】顯示效果圖   
  55.     imshow("【效果圖】Canny邊緣檢測2", dst);   
  56.   
  57.   
  58.     waitKey(0);   
  59.   
  60.     return 0;   
  61. }  

 


運行效果圖:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

三、sobel算子篇

 
 
 
 

3.1 sobel算子相關理論與概念講解

 
 
 

3.1.1 基本概念

 

Sobel 算子是一個主要用作邊緣檢測的離散微分算子 (discrete differentiation operator)。 它Sobel算子結合了高斯平滑和微分求導,用來計算圖像灰度函數的近似梯度。在圖像的任何一點使用此算子,將會產生對應的梯度矢量或是其法矢量。

 

sobel算子的wikipedia:

http://zh.wikipedia.org/wiki/%E7%B4%A2%E8%B2%9D%E7%88%BE%E7%AE%97%E5%AD%90

 

sobel算子相關概念,還可以參看這篇博文:

http://www.cnblogs.com/lancidie/archive/2011/07/17/2108885.html

 

 

 

3.1.2 sobel算子的計算過程

 

 

我們假設被作用圖像為 I.然后進行如下的操作:

 

1.分別在x和y兩個方向求導。

 

Ⅰ.水平變化: 將 I 與一個奇數大小的內核進行卷積。比如,當內核大小為3時, 的計算結果為:

 

 

 

 

Ⅱ.垂直變化: 將: I 與一個奇數大小的內核進行卷積。比如,當內核大小為3時,  的計算結果為:

 

 

 

2.在圖像的每一點,結合以上兩個結果求出近似梯度:

 

 

另外有時,也可用下面更簡單公式代替:

 

 

 

 

 

3.2 OpenCV中Sobel函數詳解

 

 

Sobel函數使用擴展的 Sobel 算子,來計算一階、二階、三階或混合圖像差分。

 

 

[cpp]  view plain  copy
 
  1. C++: void Sobel (  
  2. InputArray src,//輸入圖  
  3.  OutputArray dst,//輸出圖  
  4.  int ddepth,//輸出圖像的深度  
  5.  int dx,  
  6.  int dy,  
  7.  int ksize=3,  
  8.  double scale=1,  
  9.  double delta=0,  
  10.  int borderType=BORDER_DEFAULT );  

 


 

  • 第一個參數,InputArray 類型的src,為輸入圖像,填Mat類型即可。
  • 第二個參數,OutputArray類型的dst,即目標圖像,函數的輸出參數,需要和源圖片有一樣的尺寸和類型。
  • 第三個參數,int類型的ddepth,輸出圖像的深度,支持如下src.depth()和ddepth的組合:
  • 若src.depth() = CV_8U, 取ddepth =-1/CV_16S/CV_32F/CV_64F
  • 若src.depth() = CV_16U/CV_16S, 取ddepth =-1/CV_32F/CV_64F
  • 若src.depth() = CV_32F, 取ddepth =-1/CV_32F/CV_64F
  • 若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處得到更詳細的信息。

 

 

一般情況下,都是用ksize x ksize內核來計算導數的。然而,有一種特殊情況——當ksize為1時,往往會使用3 x 1或者1 x 3的內核。且這種情況下,並沒有進行高斯平滑操作。

 

一些補充說明:

 

1.當內核大小為 3 時, 我們的Sobel內核可能產生比較明顯的誤差(畢竟,Sobel算子只是求取了導數的近似值而已)。 為解決這一問題,OpenCV提供了Scharr 函數,但該函數僅作用於大小為3的內核。該函數的運算與Sobel函數一樣快,但結果卻更加精確,其內核是這樣的:

 

 

 

 

2.因為Sobel算子結合了高斯平滑和分化(differentiation),因此結果會具有更多的抗噪性。大多數情況下,我們使用sobel函數時,取【xorder = 1,yorder = 0,ksize = 3】來計算圖像X方向的導數,【xorder = 0,yorder = 1,ksize = 3】來計算圖像y方向的導數。

計算圖像X方向的導數,取【xorder= 1,yorder = 0,ksize = 3】情況對應的內核:

 

 

 

 

而計算圖像Y方向的導數,取【xorder= 0,yorder = 1,ksize = 3】對應的內核:

 

 

 

 

3.3 調用Sobel函數的實例代碼

 

 

調用Sobel函數的實例代碼如下。這里只是教大家如何使用Sobel函數,就沒有先用一句cvtColor將原圖;轉化為灰度圖,而是直接用彩色圖操作。

 

[cpp]  view plain  copy
 
  1. //-----------------------------------【頭文件包含部分】---------------------------------------  
  2. //            描述:包含程序所依賴的頭文件  
  3. //----------------------------------------------------------------------------------------------  
  4. #include <opencv2/opencv.hpp>  
  5. #include<opencv2/highgui/highgui.hpp>  
  6. #include<opencv2/imgproc/imgproc.hpp>  
  7.   
  8. //-----------------------------------【命名空間聲明部分】---------------------------------------  
  9. //            描述:包含程序所使用的命名空間  
  10. //-----------------------------------------------------------------------------------------------  
  11. using namespace cv;  
  12. //-----------------------------------【main( )函數】--------------------------------------------  
  13. //            描述:控制台應用程序的入口函數,我們的程序從這里開始  
  14. //-----------------------------------------------------------------------------------------------  
  15. int main( )  
  16. {  
  17.     //【0】創建 grad_x 和 grad_y 矩陣  
  18.     Mat grad_x, grad_y;  
  19.     Mat abs_grad_x, abs_grad_y,dst;  
  20.   
  21.     //【1】載入原始圖    
  22.     Mat src = imread("1.jpg");  //工程目錄下應該有一張名為1.jpg的素材圖  
  23.   
  24.     //【2】顯示原始圖   
  25.     imshow("【原始圖】sobel邊緣檢測", src);   
  26.   
  27.     //【3】求 X方向梯度  
  28.     Sobel( src, grad_x, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT );  
  29.     convertScaleAbs( grad_x, abs_grad_x );  
  30.     imshow("【效果圖】 X方向Sobel", abs_grad_x);   
  31.   
  32.     //【4】求Y方向梯度  
  33.     Sobel( src, grad_y, CV_16S, 0, 1, 3, 1, 1, BORDER_DEFAULT );  
  34.     convertScaleAbs( grad_y, abs_grad_y );  
  35.     imshow("【效果圖】Y方向Sobel", abs_grad_y);   
  36.   
  37.     //【5】合並梯度(近似)  
  38.     addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dst );  
  39.     imshow("【效果圖】整體方向Sobel", dst);   
  40.   
  41.     waitKey(0);   
  42.     return 0;   
  43. }  

 

 

運行截圖如下:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

四、Laplace算子篇

 

 

 

4.1 Laplace算子相關理論與概念講解

 

Laplacian 算子是n維歐幾里德空間中的一個二階微分算子,定義為梯度grad()的散度div()。因此如果f是二階可微的實函數,則f的拉普拉斯算子定義為:

 

(1) f的拉普拉斯算子也是笛卡兒坐標系xi中的所有非混合二階偏導數求和:

(2) 作為一個二階微分算子,拉普拉斯算子把C函數映射到C函數,對於k ≥ 2。表達式(1)(或(2))定義了一個算子Δ :C(R) → C(R),或更一般地,定義了一個算子Δ : C(Ω) → C(Ω),對於任何開集Ω。

 

根據圖像處理的原理我們知道,二階導數可以用來進行檢測邊緣 。 因為圖像是 “二維”, 我們需要在兩個方向進行求導。使用Laplacian算子將會使求導過程變得簡單。

 

Laplacian 算子的定義:

 

 

 

 

需要點破的是,由於 Laplacian使用了圖像梯度,它內部的代碼其實是調用了 Sobel 算子的。

 

另附一個小tips:讓一幅圖像減去它的Laplacian可以增強對比度。

 

 

關於Laplace算子的相關概念闡述,可以參看這篇博文:

http://www.cnblogs.com/xfzhang/archive/2011/01/19/1939020.html

Laplace算子的wikipedia:

http://zh.wikipedia.org/wiki/%E6%8B%89%E6%99%AE%E6%8B%89%E6%96%AF%E7%AE%97%E5%AD%90

 

 

 

4.2 OpenCV中Laplacian函數詳解

 

 

Laplacian函數可以計算出圖像經過拉普拉斯變換后的結果。

 

 

[cpp]  view plain  copy
 
  1. C++: 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()處得到更詳細的信息。

 

 

Laplacian( )函數其實主要是利用sobel算子的運算。它通過加上sobel算子運算出的圖像x方向和y方向上的導數,來得到我們載入圖像的拉普拉斯變換結果。

其中,sobel算子(ksize>1)如下:

 

 

而當ksize=1時,Laplacian()函數采用以下3x3的孔徑:

 

 

 

 

 

 

4.3 調用Laplacian函數的實例代碼

 

 

讓我們看一看調用實例:

 

[cpp]  view plain  copy
 
  1. //-----------------------------------【頭文件包含部分】---------------------------------------  
  2. //            描述:包含程序所依賴的頭文件  
  3. //----------------------------------------------------------------------------------------------  
  4. #include <opencv2/opencv.hpp>  
  5. #include<opencv2/highgui/highgui.hpp>  
  6. #include<opencv2/imgproc/imgproc.hpp>  
  7.   
  8. //-----------------------------------【命名空間聲明部分】---------------------------------------  
  9. //            描述:包含程序所使用的命名空間  
  10. //-----------------------------------------------------------------------------------------------  
  11. using namespace cv;  
  12.   
  13.   
  14. //-----------------------------------【main( )函數】--------------------------------------------  
  15. //            描述:控制台應用程序的入口函數,我們的程序從這里開始  
  16. //-----------------------------------------------------------------------------------------------  
  17. int main( )  
  18. {  
  19.     //【0】變量的定義  
  20.     Mat src,src_gray,dst, abs_dst;  
  21.   
  22.     //【1】載入原始圖    
  23.     src = imread("1.jpg");  //工程目錄下應該有一張名為1.jpg的素材圖  
  24.   
  25.     //【2】顯示原始圖   
  26.     imshow("【原始圖】圖像Laplace變換", src);   
  27.   
  28.     //【3】使用高斯濾波消除噪聲  
  29.     GaussianBlur( src, src, Size(3,3), 0, 0, BORDER_DEFAULT );  
  30.   
  31.     //【4】轉換為灰度圖  
  32.     cvtColor( src, src_gray, CV_RGB2GRAY );  
  33.   
  34.     //【5】使用Laplace函數  
  35.     Laplacian( src_gray, dst, CV_16S, 3, 1, 0, BORDER_DEFAULT );  
  36.   
  37.     //【6】計算絕對值,並將結果轉換成8位  
  38.     convertScaleAbs( dst, abs_dst );  
  39.   
  40.     //【7】顯示效果圖  
  41.     imshow( "【效果圖】圖像Laplace變換", abs_dst );  
  42.   
  43.     waitKey(0);   
  44.   
  45.     return 0;   
  46. }  

 

示例效果圖:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

五、scharr濾波器篇

 

 

scharr一般我就直接稱它為濾波器,而不是算子。上文我們已經講到,它在OpenCV中主要是配合Sobel算子的運算而存在的,一個萬年備胎。讓我們直接來看看函數講解吧。

 

 

 

5.1 OpenCV中Scharr函數詳解

 

 

使用Scharr濾波器運算符計算x或y方向的圖像差分。其實它的參數變量和Sobel基本上是一樣的,除了沒有ksize核的大小。

 

 

[cpp]  view plain  copy
 
  1. C++: void Scharr(  
  2. InputArray src, //源圖  
  3.  OutputArray dst, //目標圖  
  4.  int ddepth,//圖像深度  
  5.  int dx,// x方向上的差分階數  
  6.  int dy,//y方向上的差分階數  
  7.  double scale=1,//縮放因子  
  8.  double delta=0,// delta值  
  9.  intborderType=BORDER_DEFAULT )// 邊界模式  


 

 

  • 第一個參數,InputArray 類型的src,為輸入圖像,填Mat類型即可。
  • 第二個參數,OutputArray類型的dst,即目標圖像,函數的輸出參數,需要和源圖片有一樣的尺寸和類型。
  • 第三個參數,int類型的ddepth,輸出圖像的深度,支持如下src.depth()和ddepth的組合:
  • 若src.depth() = CV_8U, 取ddepth =-1/CV_16S/CV_32F/CV_64F
  • 若src.depth() = CV_16U/CV_16S, 取ddepth =-1/CV_32F/CV_64F
  • 若src.depth() = CV_32F, 取ddepth =-1/CV_32F/CV_64F
  • 若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處得到更詳細的信息。

 

 

不難理解,如下兩者是等價的:

 

[cpp]  view plain  copy
 
  1. Scharr(src, dst, ddepth, dx, dy, scale,delta, borderType);  

 

 

[cpp]  view plain  copy
 
  1. Sobel(src, dst, ddepth, dx, dy, CV_SCHARR,scale, delta, borderType);  


 

 

 

5.2 調用Scharr函數的實例代碼

 

 

[cpp]  view plain  copy
 
  1. //-----------------------------------【頭文件包含部分】---------------------------------------  
  2. //            描述:包含程序所依賴的頭文件  
  3. //----------------------------------------------------------------------------------------------  
  4. #include <opencv2/opencv.hpp>  
  5. #include<opencv2/highgui/highgui.hpp>  
  6. #include<opencv2/imgproc/imgproc.hpp>  
  7.   
  8. //-----------------------------------【命名空間聲明部分】---------------------------------------  
  9. //            描述:包含程序所使用的命名空間  
  10. //-----------------------------------------------------------------------------------------------  
  11. using namespace cv;  
  12. //-----------------------------------【main( )函數】--------------------------------------------  
  13. //            描述:控制台應用程序的入口函數,我們的程序從這里開始  
  14. //-----------------------------------------------------------------------------------------------  
  15. int main( )  
  16. {  
  17.     //【0】創建 grad_x 和 grad_y 矩陣  
  18.     Mat grad_x, grad_y;  
  19.     Mat abs_grad_x, abs_grad_y,dst;  
  20.   
  21.     //【1】載入原始圖    
  22.     Mat src = imread("1.jpg");  //工程目錄下應該有一張名為1.jpg的素材圖  
  23.   
  24.     //【2】顯示原始圖   
  25.     imshow("【原始圖】Scharr濾波器", src);   
  26.   
  27.     //【3】求 X方向梯度  
  28.     Scharr( src, grad_x, CV_16S, 1, 0, 1, 0, BORDER_DEFAULT );  
  29.     convertScaleAbs( grad_x, abs_grad_x );  
  30.     imshow("【效果圖】 X方向Scharr", abs_grad_x);   
  31.   
  32.     //【4】求Y方向梯度  
  33.     Scharr( src, grad_y, CV_16S, 0, 1, 1, 0, BORDER_DEFAULT );  
  34.     convertScaleAbs( grad_y, abs_grad_y );  
  35.     imshow("【效果圖】Y方向Scharr", abs_grad_y);   
  36.   
  37.     //【5】合並梯度(近似)  
  38.     addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dst );  
  39.   
  40.     //【6】顯示效果圖  
  41.     imshow("【效果圖】合並梯度后Scharr", dst);   
  42.   
  43.     waitKey(0);   
  44.     return 0;   
  45. }  

 

 

運行效果圖:

 

 

 

 

 

 

 

 

六、綜合示例篇——在實戰中熟稔

 

 

 

依然是每篇文章都會配給大家的一個詳細注釋的博文配套示例程序,把這篇文章中介紹的知識點以代碼為載體,展現給大家。

這個示例程序中,分別演示了canny邊緣檢測,sobel邊緣檢測,scharr濾波器的使用,那么,上詳細注釋的代碼吧:

 

[cpp]  view plain  copy
 
  1. //-----------------------------------【程序說明】----------------------------------------------  
  2. //      程序名稱::《【OpenCV入門教程之十二】OpenCV邊緣檢測:Canny算子,Sobel算子,Laplace算子,Scharr濾波器合輯合輯》 博文配套源碼   
  3. //      開發所用IDE版本:Visual Studio 2010  
  4. //      <span style="white-space:pre">  </span>開發所用OpenCV版本:    2.4.9  
  5. //      2014年5月11日 Create by 淺墨  
  6. //      淺墨的微博:@淺墨_毛星雲 http://weibo.com/1723155442/profile?topnav=1&wvr=5&user=1  
  7. //      淺墨的知乎:http://www.zhihu.com/people/mao-xing-yun  
  8. //      淺墨的豆瓣:http://www.douban.com/people/53426472/  
  9. //----------------------------------------------------------------------------------------------  
  10.   
  11.   
  12.   
  13. //-----------------------------------【頭文件包含部分】---------------------------------------  
  14. //      描述:包含程序所依賴的頭文件  
  15. //----------------------------------------------------------------------------------------------   
  16. #include <opencv2/highgui/highgui.hpp>  
  17. #include <opencv2/imgproc/imgproc.hpp>  
  18.   
  19. //-----------------------------------【命名空間聲明部分】--------------------------------------  
  20. //      描述:包含程序所使用的命名空間  
  21. //-----------------------------------------------------------------------------------------------   
  22. using namespace cv;  
  23.   
  24.   
  25. //-----------------------------------【全局變量聲明部分】--------------------------------------  
  26. //      描述:全局變量聲明  
  27. //-----------------------------------------------------------------------------------------------  
  28. //原圖,原圖的灰度版,目標圖  
  29. Mat g_srcImage, g_srcGrayImage,g_dstImage;  
  30.   
  31. //Canny邊緣檢測相關變量  
  32. Mat g_cannyDetectedEdges;  
  33. int g_cannyLowThreshold=1;//TrackBar位置參數    
  34.   
  35. //Sobel邊緣檢測相關變量  
  36. Mat g_sobelGradient_X, g_sobelGradient_Y;  
  37. Mat g_sobelAbsGradient_X, g_sobelAbsGradient_Y;  
  38. int g_sobelKernelSize=1;//TrackBar位置參數    
  39.   
  40. //Scharr濾波器相關變量  
  41. Mat g_scharrGradient_X, g_scharrGradient_Y;  
  42. Mat g_scharrAbsGradient_X, g_scharrAbsGradient_Y;  
  43.   
  44.   
  45. //-----------------------------------【全局函數聲明部分】--------------------------------------  
  46. //      描述:全局函數聲明  
  47. //-----------------------------------------------------------------------------------------------  
  48. static void ShowHelpText( );  
  49. static void on_Canny(int, void*);//Canny邊緣檢測窗口滾動條的回調函數  
  50. static void on_Sobel(int, void*);//Sobel邊緣檢測窗口滾動條的回調函數  
  51. void Scharr( );//封裝了Scharr邊緣檢測相關代碼的函數  
  52.   
  53.   
  54. //-----------------------------------【main( )函數】--------------------------------------------  
  55. //      描述:控制台應用程序的入口函數,我們的程序從這里開始  
  56. //-----------------------------------------------------------------------------------------------  
  57. int main( int argc, char** argv )  
  58. {  
  59.     //改變console字體顏色  
  60.     system("color 2F");    
  61.   
  62.     //顯示歡迎語  
  63.     ShowHelpText();  
  64.   
  65.     //載入原圖  
  66.     g_srcImage = imread("1.jpg");  
  67.     if( !g_srcImage.data ) { printf("Oh,no,讀取srcImage錯誤~! \n"); return false; }  
  68.   
  69.     //顯示原始圖  
  70.     namedWindow("【原始圖】");  
  71.     imshow("【原始圖】", g_srcImage);  
  72.   
  73.     // 創建與src同類型和大小的矩陣(dst)  
  74.     g_dstImage.create( g_srcImage.size(), g_srcImage.type() );  
  75.   
  76.     // 將原圖像轉換為灰度圖像  
  77.     cvtColor( g_srcImage, g_srcGrayImage, CV_BGR2GRAY );  
  78.   
  79.     // 創建顯示窗口  
  80.     namedWindow( "【效果圖】Canny邊緣檢測", CV_WINDOW_AUTOSIZE );  
  81.     namedWindow( "【效果圖】Sobel邊緣檢測", CV_WINDOW_AUTOSIZE );  
  82.   
  83.     // 創建trackbar  
  84.     createTrackbar( "參數值:", "【效果圖】Canny邊緣檢測", &g_cannyLowThreshold, 120, on_Canny );  
  85.     createTrackbar( "參數值:", "【效果圖】Sobel邊緣檢測", &g_sobelKernelSize, 3, on_Sobel );  
  86.   
  87.     // 調用回調函數  
  88.     on_Canny(0, 0);  
  89.     on_Sobel(0, 0);  
  90.   
  91.     //調用封裝了Scharr邊緣檢測代碼的函數  
  92.     Scharr( );  
  93.   
  94.     //輪詢獲取按鍵信息,若按下Q,程序退出  
  95.     while((char(waitKey(1)) != 'q')) {}  
  96.   
  97.     return 0;  
  98. }  
  99.   
  100.   
  101. //-----------------------------------【ShowHelpText( )函數】----------------------------------  
  102. //      描述:輸出一些幫助信息  
  103. //----------------------------------------------------------------------------------------------  
  104. static void ShowHelpText()  
  105. {  
  106.     //輸出一些幫助信息  
  107.     printf( "\n\n\t嗯。運行成功,請調整滾動條觀察圖像效果~\n\n"  
  108.         "\t按下“q”鍵時,程序退出~!\n"  
  109.         "\n\n\t\t\t\t by淺墨" );  
  110. }  
  111.   
  112.   
  113. //-----------------------------------【on_Canny( )函數】----------------------------------  
  114. //      描述:Canny邊緣檢測窗口滾動條的回調函數  
  115. //-----------------------------------------------------------------------------------------------  
  116. void on_Canny(int, void*)  
  117. {  
  118.     // 先使用 3x3內核來降噪  
  119.     blur( g_srcGrayImage, g_cannyDetectedEdges, Size(3,3) );  
  120.   
  121.     // 運行我們的Canny算子  
  122.     Canny( g_cannyDetectedEdges, g_cannyDetectedEdges, g_cannyLowThreshold, g_cannyLowThreshold*3, 3 );  
  123.   
  124.     //先將g_dstImage內的所有元素設置為0   
  125.     g_dstImage = Scalar::all(0);  
  126.   
  127.     //使用Canny算子輸出的邊緣圖g_cannyDetectedEdges作為掩碼,來將原圖g_srcImage拷到目標圖g_dstImage中  
  128.     g_srcImage.copyTo( g_dstImage, g_cannyDetectedEdges);  
  129.   
  130.     //顯示效果圖  
  131.     imshow( "【效果圖】Canny邊緣檢測", g_dstImage );  
  132. }  
  133.   
  134.   
  135.   
  136. //-----------------------------------【on_Sobel( )函數】----------------------------------  
  137. //      描述:Sobel邊緣檢測窗口滾動條的回調函數  
  138. //-----------------------------------------------------------------------------------------  
  139. void on_Sobel(int, void*)  
  140. {  
  141.     // 求 X方向梯度  
  142.     Sobel( g_srcImage, g_sobelGradient_X, CV_16S, 1, 0, (2*g_sobelKernelSize+1), 1, 1, BORDER_DEFAULT );  
  143.     convertScaleAbs( g_sobelGradient_X, g_sobelAbsGradient_X );//計算絕對值,並將結果轉換成8位  
  144.   
  145.     // 求Y方向梯度  
  146.     Sobel( g_srcImage, g_sobelGradient_Y, CV_16S, 0, 1, (2*g_sobelKernelSize+1), 1, 1, BORDER_DEFAULT );  
  147.     convertScaleAbs( g_sobelGradient_Y, g_sobelAbsGradient_Y );//計算絕對值,並將結果轉換成8位  
  148.   
  149.     // 合並梯度  
  150.     addWeighted( g_sobelAbsGradient_X, 0.5, g_sobelAbsGradient_Y, 0.5, 0, g_dstImage );  
  151.   
  152.     //顯示效果圖  
  153.     imshow("【效果圖】Sobel邊緣檢測", g_dstImage);   
  154.   
  155. }  
  156.   
  157.   
  158. //-----------------------------------【Scharr( )函數】----------------------------------  
  159. //      描述:封裝了Scharr邊緣檢測相關代碼的函數  
  160. //-----------------------------------------------------------------------------------------  
  161. void Scharr( )  
  162. {  
  163.     // 求 X方向梯度  
  164.     Scharr( g_srcImage, g_scharrGradient_X, CV_16S, 1, 0, 1, 0, BORDER_DEFAULT );  
  165.     convertScaleAbs( g_scharrGradient_X, g_scharrAbsGradient_X );//計算絕對值,並將結果轉換成8位  
  166.   
  167.     // 求Y方向梯度  
  168.     Scharr( g_srcImage, g_scharrGradient_Y, CV_16S, 0, 1, 1, 0, BORDER_DEFAULT );  
  169.     convertScaleAbs( g_scharrGradient_Y, g_scharrAbsGradient_Y );//計算絕對值,並將結果轉換成8位  
  170.   
  171.     // 合並梯度  
  172.     addWeighted( g_scharrAbsGradient_X, 0.5, g_scharrAbsGradient_Y, 0.5, 0, g_dstImage );  
  173.   
  174.     //顯示效果圖  
  175.     imshow("【效果圖】Scharr濾波器", g_dstImage);   
  176. }  

 

 

放出一些運行效果圖:

 

canny邊緣檢測效果圖:

 

 

Sobel邊緣檢測:

 

Scharr濾波器:

 

好的,就放出這些效果圖吧,具體更多的運行效果大家就自己下載示例程序回去玩~

 

本篇文章的配套源代碼請點擊這里下載:

 

【淺墨OpenCV入門教程之十二】配套源代碼下載

 

 

OK,今天的內容大概就是這些,我們下篇文章見:)

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM