Sobel運算元及cvSobel

2021-05-25 04:29:13 字數 3355 閱讀 9390

由於專案裡要用到邊緣檢測,所以今天研究了一下最簡單的梯度的方法。

首先,我們來開一下計算機是如何檢測邊緣的。以灰度影象為例,它的理論基礎是這樣的,如果出現乙個邊緣,那麼影象的灰度就會有一定的變化,為了方便假設由黑漸變為白代表乙個邊界,那麼對其灰度分析,在邊緣的灰度函式就是乙個一次函式y=kx,對其求一階導數就是其斜率k,就是說邊緣的一階導數是乙個常數,而由於非邊緣的一階導數為零,這樣通過求一階導數就能初步判斷影象的邊緣了。通常是x方向和y方向的導數,也就是梯度。理論上計算機就是通過這種方式來獲得影象的邊緣。

但是,具體應用到影象中你會發現這個導數是求不了的,因為沒乙個準確的函式讓你去求導,而且計算機在求解析解要比求數值解麻煩得多,所以就想到了一種替代的方式來求導數。就是用乙個3×3的視窗來對影象進行近似求導。拿對x方向求導為例,某一點的導數為第三行的元素之和減去第一行元素之和,這樣就求得了某一點的近似導數。其實也很好理解為什麼它就近似代表導數,導數就代表乙個變化率,從第一行變為第三行,灰度值相減,當然就是乙個變化率了。這就是所謂的prewitt運算元。這樣近似x方向導數就求出來了。y方向導數與x方向導數求法相似,只不過是用第三列元素之和減去第一列元素之和。x方向和y方向導數有了,那麼梯度也就出來了。這樣就可以找出一幅圖中的邊緣了。

還有乙個問題,由於求的是3×3中心點的導數,所以給第二列加了乙個權重,它的權重為2,第一列和第三列的權重為1,好了,這就是sobel運算元了。相比prewitt運算元,sobel的抗噪能力更強。如圖所示:

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

#include

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(&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

#include

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(&gray);

cvreleaseimage(&sobel);

cvdestroywindow("frame");

cvdestroywindow("gray");

cvdestroywindow("sobel");

[** 感謝無為的提供]

Sobel運算元及cvSobel

由於專案裡要用到邊緣檢測,所以今天研究了一下最簡單的梯度的方法。首先,我們來開一下計算機是如何檢測邊緣的。以灰度影象為例,它的理論基礎是這樣的,如果出現乙個邊緣,那麼影象的灰度就會有一定的變化,為了方便假設由黑漸變為白代表乙個邊界,那麼對其灰度分析,在邊緣的灰度函式就是乙個一次函式y kx,對其求一...

Sobel運算元及cvSobel

由於專案裡要用到邊緣檢測,所以今天研究了一下最簡單的梯度的方法。首先,我們來開一下計算機是如何檢測邊緣的。以灰度影象為例,它的理論基礎是這樣的,如果出現乙個邊緣,那麼影象的灰度就會有一定的變化,為了方便假設由黑漸變為白代表乙個邊界,那麼對其灰度分析,在邊緣的灰度函式就是乙個一次函式y kx,對其求一...

CV Sobel運算元簡介

sobel運算元是一種常用的邊緣檢測演算法,是一種離散性差分運算元,用差分近似代替梯度。對x求1階差分用來檢測豎直邊緣,同樣的對y求1階差分用來檢測水平邊緣。sobel運算元對垂直和水平方向上的排列表達的較好,但對於其他角度的表達往往不夠準確。sobel運算元根據畫素點上下 左右鄰點灰度加權差,在邊...