轉自:http://blog.csdn.net/xiaqunfeng123/article/details/17302003
Sobel 算子是一個離散微分算子 (discrete differentiation operator)。 它結合了高斯平滑和微分求導,用來計算圖像灰度函數的近似梯度。
圖像邊緣,相素值會發生顯著的變化了。表示這一改變的一個方法是使用 導數 。 梯度值的大變預示着圖像中內容的顯著變化。用更加形象的圖像來解釋,假設我們有一張一維圖形。下圖2中灰度值的”躍升”表示邊緣的存在,圖3中使用一階微分求導我們可以更加清晰的看到邊緣”躍升”的存在。
圖1、lena.jpg
圖2、像素一維圖形
圖3、一階導數
具體是采用卷積的計算方法實現的。假設被作用的圖像為 ,在兩個方向上求導:
水平變化求導:將 與一個奇數大小的內核
進行卷積。比如,當內核大小為3時,
的計算結果為圖4a:
垂直變化求導:將 I 與一個奇數大小的內核 進行卷積。比如,當內核大小為3時,
的計算結果為圖4b:
在圖像的每一點,結合以上兩個結果求出近似 梯度 ,如圖4c:
圖4a
圖4b
圖4c
因為Sobel算子只是求取了導數的近似值,當內核大小為時,以上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_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
當 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