1、Sobel邊緣檢測算子html
l 在討論邊緣算子以前,首先給出一些術語的定義:算法
l (1)邊緣:灰度或結構等信息的突變處,邊緣是一個區域的結束,也是另外一個區域的開始,利用該特徵能夠分割圖像。函數
l (2)邊緣點:圖像中具備座標[x,y],且處在強度顯著變化的位置上的點。ui
l (3)邊緣段:對應於邊緣點座標[x,y]及其方位 ,邊緣的方位多是梯度角。url
幻燈片3spa
2、Sobel算子的基本原理視頻
l Sobel算子是一階導數的邊緣檢測算子,在算法實現過程當中,經過3×3模板做爲核與圖像中的每一個像素點作卷積和運算,而後選取合適的閾值以提取邊緣。htm
l 採用3×3鄰域能夠避免在像素之間內插點上計算梯度。Sobel算子也是一種梯度幅值,即:blog
l 其中的偏導數Sx 和Sy可用卷積模板來實現。內存
幻燈片4
Sx=(Z1+2Z2+Z3)-(Z7+2Z8+Z9)
Sy=(Z1+2Z4+Z7)-(Z3+2Z6+Z9)
幻燈片5
l Sobel算子算法的優勢是計算簡單,速度快。可是因爲只採用了2個方向的模板,只能檢測水平和垂直方向的邊緣,所以這種算法對於紋理較爲複雜的圖像,其邊緣檢測效果就不是很理想。該算法認爲:凡灰度新值大於或等於閾值的像素點時都是邊緣點。這種判斷欠合理,會形成邊緣點的誤判,由於許多噪聲點的灰度值也很大。
幻燈片6
3、模板方向的改變
幻燈片7
幻燈片8
4、Sobel算子圖像邊緣檢測的MATLB程序實現
l >> f=imread('peppers.png');
l >> f=rgb2gray(f);
l >> f=im2double(f);
l >> figure,imshow(f),title('原始圖像');
l >> [SFST Threshold] =edge(f,'sobel','horizontal');
l >> figure,imshow(SFST),title(' 水平圖像邊緣檢測');
l >> [VSFAT Threshold]=edge(f,'sobel','vertical');
l >> figure,imshow(VSFAT),title('垂直圖像邊緣檢測');
l >> s45=[-2 -1 0;-1 0 1;0 1 2];
l >> SFST45=imfilter(f,s45,'replicate');
l >> SFST45=SFST45>=Threshold;
l >> figure,imshow(SFST45),title('45度角圖像邊緣檢測');
l >> s135=[0 -1 -2;1 0 -1;2 1 0];
l >> SFST135=imfilter(f,s135,'replicate');
l >> SFST135=SFST135>=Threshold;
l >> figure,imshow(SFST135),title('135度角圖像邊緣檢測');
l >>
幻燈片9
幻燈片10
幻燈片11
幻燈片12
幻燈片13
因爲項目裏要用到邊緣檢測,因此今天研究了一下最簡單的梯度的方法。
首先,咱們來開一下計算機是如何檢測邊緣的。以灰度圖像爲例,它的理論基礎是這樣的,若是出現一個邊緣,那麼圖像的灰度就會有必定的變化,爲了方便假設由黑漸變爲白表明一個邊界,那麼對其灰度分析,在邊緣的灰度函數就是一個一次函數y=kx,對其求一階導數就是其斜率k,就是說邊緣的一階導數是一個常數,而因爲非邊緣的一階導數爲零,這樣經過求一階導數就能初步判斷圖像的邊緣了。一般是X方向和Y方向的導數,也就是梯度。理論上計算機就是經過這種方式來得到圖像的邊緣。
可是,具體應用到圖像中你會發現這個導數是求不了的,由於沒一個準確的函數讓你去求導,並且計算機在求解析解要比求數值解麻煩得多,因此就想到了一種替代的方式來求導數。就是用一個3×3的窗口來對圖像進行近似求導。拿對X方向求導爲例,某一點的導數爲第三列的元素之和減去第一列元素之和,這樣就求得了某一點的近似導數。其實也很好理解爲何它就近似表明導數,導數就表明一個變化率,從第一列變爲第三列,灰度值相減,固然就是一個變化率了。這就是所謂的Prewitt算子。這樣近似X方向導數就求出來了。Y方向導數與X方向導數求法類似,只不過是用第三行元素之和減去第一行元素之和。X方向和Y方向導數有了,那麼梯度也就出來了。這樣就能夠找出一幅圖中的邊緣了。
還有一個問題,因爲求的是3×3中心點的導數,因此給第二列加了一個權重,它的權重爲2,第一列和第三列的權重爲1,好了,這就是Sobel算子了。相比Prewitt算子,Sobel的抗噪能力更強。如圖所示:這樣,中心點的Y方向導數就求出來了。
舉個例子吧。,X點以Sobel方式求導數ΔX=1×50+2×30+1×50-(1×50+2×30+1×50)=0。這樣能夠看出這個點不是邊界。
好了,瞭解了基本理論以後,咱們看看OpenCv下的Sobel函數吧,void cvSobel( const CvArr* src, CvArr* dst, int xorder, int yorder, int aperture_size=3 );src:輸入圖像;dst:輸出圖像;xorder:x 方向上的差分階數;yorder:y 方向上的差分階數;aperture_size 擴展 Sobel 核的大小(既窗口階數),必須是 1(注意這是一個3×1或1×3向量而不是一個方陣), 3, 5 或 7。
下面編寫一個Sobel邊緣檢測的程序吧,平臺是VS08,創建Win32控制檯應用程序。
#include <cv.h>
#include <highgui.h>
void main()
{
IplImage *frame,*gray,*sobel;
frame=cvLoadImage("lena.jpg");//加載圖像
gray=cvCreateImage(cvGetSize(frame),frame->depth,1);//分配圖像空間
sobel=cvCreateImage(cvGetSize(frame),frame->depth,1);
cvNamedWindow("frame");
cvNamedWindow("gray");
cvNamedWindow("sobel");
cvCvtColor(frame,gray,CV_BGR2GRAY);//轉爲灰度
cvSobel(gray,sobel,1,0,3);
cvShowImage("frame",frame);//顯示圖像
cvShowImage("gray",gray);
cvShowImage("sobel",sobel);
cvWaitKey(0);//等待
cvReleaseImage(&frame);//釋放空間(對視頻處理很重要,不釋放會形成內存泄露)
cvReleaseImage(&gray);
cvReleaseImage(&sobel);
cvDestroyWindow("frame");
cvDestroyWindow("gray");
cvDestroyWindow("sobel");
}
運行,你會發現出錯,仔細看看沒有問題啊。其實,這裏是問題的,由於以Sobel方式求完導數後會有負值,還有會大於255的值而你建的Sobel的圖像是 IPL_DEPTH_8U,也就是8位無符號數,因此Sobel創建的圖像位數不夠,要16位有符號的,也就是 IPL_DEPTH_16S。把創建圖像這句改成
sobel=cvCreateImage(cvGetSize(frame),IPL_DEPTH_16S,1);運行,發現不報錯了,可是Sobel圖像顯示不出來,這是什麼緣由呢?原來圖像顯示是以8位無符號顯示的,如今是16位有符號,固然顯示會出問題了。因此還要將Sobel轉爲8位無符號。OpenCv裏提供了一個函數,就是cvConvertScaleAbs( const CvArr* src, CvArr* dst, double scale=1, double shift=0 );src:源圖像;dst:目標圖像;scale:轉化前乘的係數;shift轉化前加的係數。這樣新建一個無符號圖像再轉換就能夠實現了。
IplImage *sobel8u=cvCreateImage(cvGetSize(sobel),IPL_DEPTH_8U,1);
再在顯示圖像前加上cvConvertScaleAbs(sobel,sobel8u,1,0);這樣就能夠看到cvSobel的效果了。能夠看X方向或Y方向求導是什麼效果。
爲了方便你們,我把改好後的程序也放上來了。
#include <cv.h>
#include <highgui.h>
void main()
{
IplImage *frame,*gray,*sobel;
frame=cvLoadImage("e:/p1.jpg");//加載圖像
gray=cvCreateImage(cvGetSize(frame),frame->depth,1);//分配圖像空間
sobel=cvCreateImage(cvGetSize(frame),IPL_DEPTH_16S,1);
cvNamedWindow("frame");
cvNamedWindow("gray");
cvNamedWindow("sobel");
cvCvtColor(frame,gray,CV_BGR2GRAY);//轉爲灰度
cvSobel(gray,sobel,1,0,3);
IplImage *sobel8u=cvCreateImage(cvGetSize(sobel),IPL_DEPTH_8U,1);
cvConvertScaleAbs(sobel,sobel8u,1,0);
cvShowImage("frame",frame);//顯示圖像
cvShowImage("gray",gray);
cvShowImage("sobel",sobel8u);
cvWaitKey(0);//等待
cvReleaseImage(&frame);//釋放空間(對視頻處理很重要,不釋放會形成內存泄露)
cvReleaseImage(&gray);
cvReleaseImage(&sobel);
cvDestroyWindow("frame");
cvDestroyWindow("gray");
cvDestroyWindow("sobel");
}