Sobel 邊緣檢測算子


轉自:http://blog.csdn.net/xiaqunfeng123/article/details/17302003

    Sobel 算子是一個離散微分算子 (discrete differentiation operator)。 它結合了高斯平滑和微分求導,用來計算圖像灰度函數的近似梯度。

    圖像邊緣,相素值會發生顯著的變化了。表示這一改變的一個方法是使用 導數 。 梯度值的大變預示着圖像中內容的顯著變化。用更加形象的圖像來解釋,假設我們有一張一維圖形。下圖2中灰度值的”躍升”表示邊緣的存在,圖3中使用一階微分求導我們可以更加清晰的看到邊緣”躍升”的存在。

圖1、lena.jpg 

 圖2、像素一維圖形

圖3、一階導數

       具體是采用卷積的計算方法實現的。假設被作用的圖像為I ,在兩個方向上求導:

水平變化求導:將 I 與一個奇數大小的內核 G_{x} 進行卷積。比如,當內核大小為3時, G_{x} 的計算結果為圖4a:

垂直變化求導:將 I 與一個奇數大小的內核 G_{y} 進行卷積。比如,當內核大小為3時, G_{y} 的計算結果為圖4b:

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

圖4a

圖4b

圖4c

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

兩種實現版本:

C 版本:

cvSobel ( const cvArr* src, CvArr* dst, int xorder, int yorder, int aperture_size =3 )

src,  dst 分別是源圖像和目標圖像,xorder ,yorder – 分別為x,y方向導數運算參數,可取0,1,2  。aperture_size是方形濾波器的寬,是小於7的奇數。

具體見《Learning OpenCV》那本書,P.170頁

下面是代碼,比較簡單:

#include <highgui.h>  
#include <cv.h>  
  
using namespace cv;  
using namespace std;  
  
int main(int argc, char ** argv)  
{  
    IplImage* src, *dstx,*dsty,*dst;  
  
    src = cvLoadImage( "car.png",0 );  
    dst = cvCreateImage( cvGetSize( src ), IPL_DEPTH_16S, 1 );  
    dstx = cvCreateImage( cvGetSize( src ), IPL_DEPTH_16S, 1 );  
    dsty = cvCreateImage( cvGetSize( src ), IPL_DEPTH_16S, 1 );  
  
    cvNamedWindow( "src" );  
    cvNamedWindow( "sobel" );  
  
    cvShowImage( "src", src );  
  
    cvSobel( src, dstx, 1, 0, 7 );  //sobel  
    cvSobel( src, dsty, 0, 1, 7 );  
    cvAddWeighted(dstx,0.5,dsty,0.5,0,dst);  
  
    cvShowImage( "sobel", dst );  
  
    cvWaitKey(0);  
    cvReleaseImage( &src );  
    cvReleaseImage( &dst );  
  
    return 0;  
}  

效果圖:

C++版本:

先來看一下C++下 Sobel 的定義

C++:   void Sobel(  InputArray src ,  OutputArray dst,  int ddepth,  int dx,  int dy,  int ksize=3,   

                                           double scale=1,double delta=0,intborderType=BORDER_DEFAULT )

各參數的意義如下:

src – 輸入圖像。dst – 輸出圖像,與輸入圖像同樣大小,擁有同樣個數的通道。

ddepth –輸出圖片深度;下面是輸入圖像支持深度和輸出圖像支持深度的關系:

src.depth() = CV_8Uddepth = -1/CV_16S/CV_32F/CV_64F

src.depth() = CV_16U/CV_16Sddepth = -1/CV_32F/CV_64F

src.depth() = CV_32Fddepth = -1/CV_32F/CV_64F

src.depth() = CV_64Fddepth = -1/CV_64F

當 ddepth為-1時, 輸出圖像將和輸入圖像有相同的深度。輸入8位圖像則會截取頂端的導數。

xorder – x方向導數運算參數。yorder – y方向導數運算參數。

ksize – Sobel內核的大小,可以是:1,3,5,7。  注意:只可以是小於7 的奇數

scale – 可選的縮放導數的比例常數。delta – 可選的增量常數被疊加到導數中。borderType – 用於判斷圖像邊界的模式。

下面是程序:

#include "opencv2/imgproc/imgproc.hpp"  
#include "opencv2/highgui/highgui.hpp"  
#include <stdlib.h>  
#include <stdio.h>  
  
using namespace cv;  
using namespace std;  
  
int main( int argc, char** argv )  
{  
    Mat src, src_gray;  
    Mat grad;  
    char* window_name = "求解梯度";  
    int scale = 1;  
    int delta = 0;  
    int ddepth = CV_16S;  
  
    src = imread( "car.png" );  
    if( !src.data )  
    {   
        return -1;   
    }  
    //高斯模糊  
    GaussianBlur( src, src, Size(3,3), 0, 0, BORDER_DEFAULT );  
    //轉成灰度圖  
    cvtColor( src, src_gray,CV_RGB2GRAY );  
  
    namedWindow( window_name, CV_WINDOW_AUTOSIZE );  
  
    Mat grad_x, grad_y;  
    Mat abs_grad_x, abs_grad_y;  
    //x方向梯度計算  
    Sobel( src_gray, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT );  
    convertScaleAbs( grad_x, abs_grad_x );  
    //y方向梯度計算  
    Sobel( src_gray, grad_y, ddepth, 0, 1, 3, scale, delta, BORDER_DEFAULT );  
    convertScaleAbs( grad_y, abs_grad_y );  
    //加權和  
    addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad );  
  
    imshow( window_name, grad );  
  
    waitKey();  
    return 0;  
}  

如果要用Scharr濾波器的話,把Sobel那行代碼替換掉就好了:

Scharr( src_gray, grad_x, ddepth, 1, 0, scale, delta, BORDER_DEFAULT );  
Scharr( src_gray, grad_x, ddepth, 0, 1, scale, delta, BORDER_DEFAULT );  

效果圖:

參考資料:http://docs.opencv.org/doc/tutorials/imgproc/imgtrans/sobel_derivatives/sobel_derivatives.html

轉載請注明出處:http://blog.csdn.net/xiaqunfeng123


免責聲明!

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



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