Sobel運算元原理及OpenCv實現

2021-07-02 00:17:59 字數 3587 閱讀 7457

索貝爾運算元

(sobeloperator

)主要用於獲得數字影象的一階梯度,常見的應用和物理意義是邊緣檢測。

在技術上,它是乙個離散的一階

差分運算元,用來計算影象亮度函式的一階梯度之近似值,

用來運算影象亮度函式的灰度值近似值。

在影象的任何一點使用此運算元,將會產生該點對應的梯度向量或是其法向量。

一、sobel

邊緣檢測運算元

在討論邊緣運算元之前,首先給出一些術語的定義:

(1)邊緣:灰度或結構等資訊的突變處,邊緣是乙個區域的結束,也是另乙個區域的開始,利用該特徵可以分割影象。

(2)邊緣點:影象中具有座標[x,

y],且處在強度顯著變化的位置上的點。

(3)邊緣段:對應於邊緣點座標[x,

y]及其方位

,邊緣的方位可能是梯度角。

二、sobel

運算元的基本原理

sobel

運算元是一階導數的邊緣檢測運算元,在演算法實現過程中,通過3×

3模板作為核與影象中的每個畫素點做卷積和運算,然後選取合適的閾值以提取邊緣。

採用3×3

鄰域可以避免在畫素之間內插點上計算梯度。

sobel

運算元也是一種梯度幅值,即:

其中的偏導數sx和sy

可用卷積模板來實現。

運算元演算法的優點是計算簡單,速度快。

但是由於只採用了2

個方向的模板,只能檢測水平和垂直方向的邊緣,因此這種演算法

對於紋理較為複雜的影象,其邊緣檢測效果就不是很理想。

該演算法認為:凡灰度新值大於或等於閾值的畫素點時都是邊緣點。這種判斷欠合理,會造成邊緣點的誤判,因為許多雜訊點的灰度值也很大。

以灰度影象為例,它的理論基礎是這樣的,如果出現乙個邊緣,那麼影象的灰度就會有一定的變化,為了方便假設由黑漸變為白代表乙個邊界,那麼對其灰度分析,在邊緣的灰度函式就是乙個一次函式y=kx

,對其求一階導數就是其斜率k

,就是說邊緣的一階導數是乙個常數,而由於非邊緣的一階導數為零,這樣通過求一階導數就能初步判斷影象的邊緣了。通常是x

方向和y

方向的導數,也就是梯度。理論上計算機就是通過這種方式來獲得影象的邊緣。

但是,具體應用到影象中你會發現這個導數是求不了的,因為沒乙個準確的函式去求導,而且計算機在求解析解要比求數值解麻煩得多,所以就想到了一種替代的方式來求導數。就是用乙個3×3

的視窗來對影象進行近似求導。拿對x

方向求導為例,某一點的導數為第三列的元素之和減去第一列元素之和,這樣就求得了某一點的近似導數。其實也很好理解為什麼它就近似代表導數,導數就代表乙個變化率,從第一列變為第三列,灰度值相減,當然就是乙個變化率了。這就是所謂的prewitt

運算元。這樣近似x

方向導數就求出來了。y

方向導數與x

方向導數求法相似,只不過是用第三行元素之和減去第一行元素之和。x

方向和y

方向導數有了,那麼梯度也就出來了。這樣就可以找出一幅圖中的邊緣了。

中心點的導數,所以給第二列加了乙個權重,它的權重為2

,第一列和第三列的權重為1

,好了,這就是sobel

運算元了。相比prewitt

運算元,sobel

的抗噪能力更強。如圖所示:這樣,中心點的y

方向導數就求出來了。

舉個例子吧。

點以sobel

方式求導數δx=1×50+2×30+1×50

-(1×50+2×30+1×50

)=0。這樣可以看出這個點不是邊界。

好了,了解了基本理論之後,我們看看opencv

下的sobel

函式吧voidcvsobel( const cvarr* src, cvarr* dst, int xorder, int yorder, intaperture_size=3 );

src:輸入影象;dst

:輸出影象;xorder

:x方向上的差分階數;yorder

:y方向上的差分階數;

aperture_size

擴充套件sobel

核的大小(既視窗階數),必須是

1(注意這是乙個3×1

或1×3

向量而不是乙個方陣),3, 5 或7

。**實現:

#include

#include

voidmain()

執行,你會發現出錯,仔細看看沒有問題啊。其實,這裡是問題的,因為以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

voidmain()

問題:cvsobel(gray,sobel,1,0,3);

表示對影象x

方向進行一階差分,得到的影象垂直的邊緣比較明顯,而cvsobel(gray,sobel,0,1,3);

表示對影象y

方向進行一階差分,得到的影象水平的邊緣比較明顯,如果使用cvsobel(gray,sobel,1,1,3);

兩個方向的邊緣反而都不清楚了。

感謝:

Sobel運算元及cvSobel

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

Sobel運算元及cvSobel

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

Sobel運算元及cvSobel

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