三种边缘检测算子
3種邊緣檢測(cè)算子
灰度或結(jié)構(gòu)等信息的突變位置是圖像的邊緣,圖像的邊緣有幅度和方向?qū)傩?#xff0c;沿邊緣方向像素變化緩慢,垂直邊緣方向像素變化劇烈。因此,邊緣上的變化能通過(guò)梯度計(jì)算出來(lái)。
一階導(dǎo)數(shù)的梯度算子
對(duì)于二維的圖像,梯度定義為一個(gè)向量,
Gx對(duì)于x方向的梯度,Gy對(duì)應(yīng)y方向的梯度,向量的幅值本來(lái)是?mag(f)?=?(Gx2?+?Gy2)1/2,為簡(jiǎn)化計(jì)算,一般用mag(f)=|Gx|+|Gy|近似,幅值同時(shí)包含了x而后y方向的梯度信息。梯度的方向?yàn)?α?=?arctan(Gx/Gy)?。
由于圖像的數(shù)字離散特性,所以梯度微分運(yùn)算用差分代替,并且用小的空域模板和圖像進(jìn)行卷積近似計(jì)算梯度,由于模板的不同,因此衍生處多種梯度算子:Roberts算子、Sobel算子和Prewitt算子。
Sobel與Prewitt算子模板
平滑模板都有一個(gè)特點(diǎn),即模板內(nèi)所有平滑值的和為0,因此梯度計(jì)算的步驟是:
高斯拉普拉斯算子
上面的一階導(dǎo)數(shù)算子,是各向異性的,因此分x方向和y方向的梯度值,而高斯拉普拉斯算子是對(duì)圖像求二階導(dǎo)數(shù),邊緣對(duì)應(yīng)二階導(dǎo)數(shù)的過(guò)零點(diǎn)。
由上式可知,xy進(jìn)行互換的結(jié)果是一樣的,所以拉普拉斯算子沒(méi)有x方向和y方向的區(qū)分,拉普拉斯算子對(duì)應(yīng)圖像中的差分運(yùn)算是:
也可以通過(guò)卷積模板實(shí)現(xiàn),
LOG算子
相對(duì)于一階導(dǎo)數(shù),高斯拉普拉斯算子(Laplacian of Gaussian, LOG算子)由于求二階導(dǎo)數(shù),很容易將點(diǎn)噪聲判別為邊界,因此常在使用LOG算子前先用高斯平滑濾波器去除正態(tài)分布的噪聲,二維高斯分布為:
其中?σ?為高斯分布標(biāo)準(zhǔn)差,決定高斯濾波器的寬度,用該函數(shù)對(duì)圖像平滑濾波,可以減少椒鹽噪聲對(duì)LOG算子的影響。
Canny算子
1983,MIT,Canny提出的邊緣檢測(cè)三個(gè)標(biāo)準(zhǔn):
Canny算子力圖在抗噪聲干擾與精度之間尋求最佳方案,Canny算子有相關(guān)的復(fù)雜理論,其基本的步驟是:
計(jì)算濾波后圖像的梯度幅值和方向 可以使用Sobel算子計(jì)算Gx與Gy方向的梯度,則梯度幅值和梯度的方向依次為?
使用雙閾值[T1,T2]法檢測(cè)邊緣的起點(diǎn)和終點(diǎn),這樣能形成連接的邊緣。T2>T1,T2用來(lái)找到?jīng)]條線段,T1用來(lái)在這條線段兩端延伸尋找邊緣的斷裂處,并連接這些邊緣。
OpenCV中相關(guān)源碼
Sobel算子及LOG算子的源碼在/modules/imgproc/src/deriv.cpp中,Canny算子實(shí)現(xiàn)在/modules/imgproc/src/canny.cpp中。
經(jīng)過(guò)之前的基礎(chǔ)準(zhǔn)備,感覺(jué)只要知道什么時(shí)候該用什么OpenCV函數(shù),其它的一切都變得簡(jiǎn)單起來(lái)了。于是感覺(jué)學(xué)著去探索OpenCV的源碼對(duì)自己的受益會(huì)更大,就從這里開(kāi)始吧。
deriv.cpp中有Sobel算子的源碼:
<code class="sourceCode c" style="margin: 0px; padding: 0.5em; line-height: normal; font-family: Monaco, 'Courier New', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; color: rgb(51, 51, 51); border: none; display: block; background: rgb(248, 248, 255);"><span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">void</span> cv::Sobel( InputArray _src, OutputArray _dst, <span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">int</span> ddepth, <span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">int</span> dx, <span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">int</span> dy,<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">int</span> ksize, <span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">double</span> scale, <span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">double</span> delta, <span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">int</span> borderType ) {Mat src = _src.getMat(); <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// 從InputArray中提取Mat數(shù)據(jù)結(jié)構(gòu),InputArray只能作為形參的類(lèi)型,但可以傳入Mat類(lèi)型實(shí)參</span><span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span> (ddepth < <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>)ddepth = src.depth(); <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// 像素深度(即像素位數(shù)),有CV_8U=0, CV_8S=1, CV_16U=2, CV_16S=3, CV_32S=4, CV_32F=5, CV_64F=6</span>_dst.create( src.size(), CV_MAKETYPE(ddepth, src.channels()) );Mat dst = _dst.getMat();<span class="ot" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32);">#if defined (HAVE_IPP) && (IPP_VERSION_MAJOR >= 7)</span><span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>(dx < <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">3</span> && dy < <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">3</span> && src.channels() == <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span> && borderType == <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>){<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>(IPPDeriv(src, dst, ddepth, dx, dy, ksize,scale))<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">return</span>;} <span class="ot" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32);">#endif</span><span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">int</span> ktype = std::max(CV_32F, std::max(ddepth, src.depth()));Mat kx, ky;getDerivKernels( kx, ky, dx, dy, ksize, false, ktype ); <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// 創(chuàng)建Sobel算子差分用的卷積模板,結(jié)果放在kx,ky中</span><span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>( scale != <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span> ){<span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// usually the smoothing part is the slowest to compute,</span><span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// so try to scale it instead of the faster differenciating part</span><span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>( dx == <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span> )kx *= scale;<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">else</span>ky *= scale;}sepFilter2D( src, dst, ddepth, kx, ky, Point(-<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>,-<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>), delta, borderType ); <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// 使用卷積核進(jìn)行平滑操作,前面已經(jīng)說(shuō)過(guò),差分轉(zhuǎn)化為卷積操作,而卷積運(yùn)算就是平滑濾波</span> }</code>getSobelKernels是實(shí)際創(chuàng)建卷積模板的函數(shù),被上面的getDerivKernels調(diào)用,不妨看看OpenCV中Sobel創(chuàng)建的卷積模板是啥樣的,下面只是getSobelKernels函數(shù)的一部分,
<code class="sourceCode c" style="margin: 0px; padding: 0.5em; line-height: normal; font-family: Monaco, 'Courier New', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; color: rgb(51, 51, 51); border: none; display: block; background: rgb(248, 248, 255);"><span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">for</span>( <span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">int</span> k = <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>; k < <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">2</span>; k++ ) {Mat* kernel = k == <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span> ? &kx : &ky;<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">int</span> order = k == <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span> ? dx : dy;<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">int</span> ksize = k == <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span> ? ksizeX : ksizeY;CV_Assert( ksize > order );<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>( ksize == <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span> )kerI[<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>] = <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>;<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">else</span> <span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>( ksize == <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">3</span> ){<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>( order == <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span> )kerI[<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>] = <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>, kerI[<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>] = <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">2</span>, kerI[<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">2</span>] = <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>; <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// 只進(jìn)行均值平滑,無(wú)差分作用</span><span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">else</span> <span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>( order == <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span> )kerI[<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>] = -<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>, kerI[<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>] = <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>, kerI[<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">2</span>] = <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>; <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// 差分算子</span><span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">else</span>kerI[<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>] = <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>, kerI[<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>] = -<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">2</span>, kerI[<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">2</span>] = <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>;}<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">else</span>{...}</code>ksize表示卷積核的大小,之前理論分析中取的是3x3的模板,對(duì)應(yīng)到if( ksize == 3 ),order變量確定對(duì)x梯度方向的卷積模板進(jìn)行賦值還是y梯度方向的卷積進(jìn)行賦值,因此,當(dāng)且僅當(dāng)Sobel函數(shù)的輸入實(shí)參中dx=1時(shí)才計(jì)算Gx方向的梯度,dy=1時(shí)才計(jì)算dy方向的梯度。OpenCV沒(méi)有給出Prewiit算子的源碼,但可以自己通過(guò)修改替換getDerivKernels函數(shù)實(shí)現(xiàn)Prewiit的功能。
LOG算子也可以進(jìn)行相同的分析,這里就不寫(xiě)下來(lái)了。再看Canny算子,
<code class="sourceCode c" style="margin: 0px; padding: 0.5em; line-height: normal; font-family: Monaco, 'Courier New', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; color: rgb(51, 51, 51); border: none; display: block; background: rgb(248, 248, 255);"><span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">void</span> cv::Canny( InputArray image, OutputArray _edges,<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">double</span> threshold1, <span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">double</span> threshold2,<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">int</span> apertureSize, bool L2gradient ) {Mat src = image.getMat();_edges.create(src.size(), CV_8U);CvMat c_src = src, c_dst = _edges.getMat();cvCanny( &c_src, &c_dst, threshold1, threshold2,apertureSize + (L2gradient ? CV_CANNY_L2_GRADIENT : <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>)); }</code>C++版本的Canny算子實(shí)際就是調(diào)用原來(lái)C版本中的函數(shù),只是進(jìn)行了下封裝而已,在cvCanny函數(shù)中我看到這么幾行代碼:
<code class="sourceCode c" style="margin: 0px; padding: 0.5em; line-height: normal; font-family: Monaco, 'Courier New', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; color: rgb(51, 51, 51); border: none; display: block; background: rgb(248, 248, 255);">dx = cvCreateMat( size.height, size.width, CV_16SC1 ); dy = cvCreateMat( size.height, size.width, CV_16SC1 ); cvSobel( src, dx, <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>, <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>, aperture_size ); <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// 計(jì)算Gx </span> cvSobel( src, dy, <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>, <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>, aperture_size ); <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// 計(jì)算Gy</span></code>Canny就是調(diào)用Sobel算子計(jì)算x方向的梯度Gx和y方向的梯度Gy。計(jì)算梯度角度和非最大化抑制的代碼有些長(zhǎng),
<code class="sourceCode c" style="margin: 0px; padding: 0.5em; line-height: normal; font-family: Monaco, 'Courier New', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; color: rgb(51, 51, 51); border: none; display: block; background: rgb(248, 248, 255);"><span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// calculate magnitude and angle of gradient, perform non-maxima supression.</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// fill the map with one of the following values:</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// 0 - the pixel might belong to an edge</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// 1 - the pixel can not belong to an edge</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// 2 - the pixel does belong to an edge</span> <span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">for</span>( i = <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>; i <= size.height; i++ ) {<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">int</span>* _mag = mag_buf[(i > <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>) + <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>] + <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>;<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">float</span>* _magf = (<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">float</span>*)_mag;<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">const</span> <span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">short</span>* _dx = (<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">short</span>*)(dx->data.ptr + dx->step*i);<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">const</span> <span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">short</span>* _dy = (<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">short</span>*)(dy->data.ptr + dy->step*i);uchar* _map;<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">int</span> x, y;ptrdiff_t magstep1, magstep2;<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">int</span> prev_flag = <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>;<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>( i < size.height ){_mag[-<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>] = _mag[size.width] = <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>;<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>( !(flags & CV_CANNY_L2_GRADIENT) )<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">for</span>( j = <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>; j < size.width; j++ )_mag[j] = abs(_dx[j]) + abs(_dy[j]);<span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">/*else if( icvFilterSobelVert_8u16s_C1R_p != 0 ) // check for IPP</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> {</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> // use vectorized sqrt</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> mag_row.data.fl = _magf;</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> for( j = 0; j < size.width; j++ )</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> {</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> x = _dx[j]; y = _dy[j];</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> _magf[j] = (float)((double)x*x + (double)y*y);</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> }</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> cvPow( &mag_row, &mag_row, 0.5 );</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> }*/</span><span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">else</span>{<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">for</span>( j = <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>; j < size.width; j++ ){x = _dx[j]; y = _dy[j];_magf[j] = (<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">float</span>)std::sqrt((<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">double</span>)x*x + (<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">double</span>)y*y);}}}<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">else</span>memset( _mag<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">-1</span>, <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>, (size.width + <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">2</span>)*<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">sizeof</span>(<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">int</span>) );<span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// at the very beginning we do not have a complete ring</span><span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// buffer of 3 magnitude rows for non-maxima suppression</span><span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>( i == <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span> )<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">continue</span>;_map = map + mapstep*i + <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>;_map[-<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>] = _map[size.width] = <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>;_mag = mag_buf[<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>] + <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>; <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// take the central row</span>_dx = (<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">short</span>*)(dx->data.ptr + dx->step*(i<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">-1</span>));_dy = (<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">short</span>*)(dy->data.ptr + dy->step*(i<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">-1</span>));magstep1 = mag_buf[<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">2</span>] - mag_buf[<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>];magstep2 = mag_buf[<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>] - mag_buf[<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>];<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>( (stack_top - stack_bottom) + size.width > maxsize ){<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">int</span> sz = (<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">int</span>)(stack_top - stack_bottom);maxsize = MAX( maxsize * <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">3</span>/<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">2</span>, maxsize + <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">8</span> );stack.resize(maxsize);stack_bottom = &stack[<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>];stack_top = stack_bottom + sz;}<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">for</span>( j = <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>; j < size.width; j++ ){<span class="ot" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32);">#define CANNY_SHIFT 15</span><span class="ot" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32);">#define TG22 (int)(0.4142135623730950488016887242097*(1<<CANNY_SHIFT) + 0.5)</span>x = _dx[j];y = _dy[j];<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">int</span> s = x ^ y;<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">int</span> m = _mag[j];x = abs(x);y = abs(y);<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>( m > low ){<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">int</span> tg22x = x * TG22;<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">int</span> tg67x = tg22x + ((x + x) << CANNY_SHIFT);y <<= CANNY_SHIFT;<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>( y < tg22x ){<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>( m > _mag[j<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">-1</span>] && m >= _mag[j<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">+1</span>] ){<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>( m > high && !prev_flag && _map[j-mapstep] != <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">2</span> ){CANNY_PUSH( _map + j );prev_flag = <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>;}<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">else</span>_map[j] = (uchar)<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>;<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">continue</span>;}}<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">else</span> <span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>( y > tg67x ){<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>( m > _mag[j+magstep2] && m >= _mag[j+magstep1] ){<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>( m > high && !prev_flag && _map[j-mapstep] != <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">2</span> ){CANNY_PUSH( _map + j );prev_flag = <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>;}<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">else</span>_map[j] = (uchar)<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>;<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">continue</span>;}}<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">else</span>{s = s < <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span> ? -<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span> : <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>;<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>( m > _mag[j+magstep2-s] && m > _mag[j+magstep1+s] ){<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>( m > high && !prev_flag && _map[j-mapstep] != <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">2</span> ){CANNY_PUSH( _map + j );prev_flag = <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>;}<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">else</span>_map[j] = (uchar)<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>;<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">continue</span>;}}}prev_flag = <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>;_map[j] = (uchar)<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>;}<span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// scroll the ring buffer</span>_mag = mag_buf[<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>];mag_buf[<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>] = mag_buf[<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>];mag_buf[<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>] = mag_buf[<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">2</span>];mag_buf[<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">2</span>] = _mag; }</code>最后就是使用雙閾值跟蹤邊界,形成連續(xù)邊緣,
<code class="sourceCode c" style="margin: 0px; padding: 0.5em; line-height: normal; font-family: Monaco, 'Courier New', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; color: rgb(51, 51, 51); border: none; display: block; background: rgb(248, 248, 255);"><span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// now track the edges (hysteresis thresholding)</span> <span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">while</span>( stack_top > stack_bottom ) {uchar* m;<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>( (stack_top - stack_bottom) + <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">8</span> > maxsize ){<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">int</span> sz = (<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">int</span>)(stack_top - stack_bottom);maxsize = MAX( maxsize * <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">3</span>/<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">2</span>, maxsize + <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">8</span> );stack.resize(maxsize);stack_bottom = &stack[<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>];stack_top = stack_bottom + sz;}CANNY_POP(m);<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>( !m[-<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>] )CANNY_PUSH( m - <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span> );<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>( !m[<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>] )CANNY_PUSH( m + <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span> );<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>( !m[-mapstep<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">-1</span>] )CANNY_PUSH( m - mapstep - <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span> );<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>( !m[-mapstep] )CANNY_PUSH( m - mapstep );<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>( !m[-mapstep<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">+1</span>] )CANNY_PUSH( m - mapstep + <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span> );<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>( !m[mapstep<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">-1</span>] )CANNY_PUSH( m + mapstep - <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span> );<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>( !m[mapstep] )CANNY_PUSH( m + mapstep );<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span>( !m[mapstep<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">+1</span>] )CANNY_PUSH( m + mapstep + <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span> ); }<span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// the final pass, form the final image</span> <span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">for</span>( i = <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>; i < size.height; i++ ) {<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">const</span> uchar* _map = map + mapstep*(i<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">+1</span>) + <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>;uchar* _dst = dst->data.ptr + dst->step*i;<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">for</span>( j = <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>; j < size.width; j++ )_dst[j] = (uchar)-(_map[j] >> <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>); }</code>這其中貌似用到了棧對(duì)鄰域梯度信息進(jìn)行保存,以上詳細(xì)的實(shí)現(xiàn)沒(méi)做太多的分析,但流程就擺在那里了。請(qǐng)注意,OpenCV中的Canny實(shí)現(xiàn)包含了Canny算子的3個(gè)步驟,唯獨(dú)沒(méi)有第一步中的高斯平滑濾波,因此調(diào)用前得先使用高斯平滑濾波。
試試身手
Sobel算子的源碼:
<code class="sourceCode c" style="margin: 0px; padding: 0.5em; line-height: normal; font-family: Monaco, 'Courier New', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; color: rgb(51, 51, 51); border: none; display: block; background: rgb(248, 248, 255);"><span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">/*</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> * FileName : sobel.cpp</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> * Author : xiahouzuoxin @163.com</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> * Version : v1.0</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> * Date : Sun 16 Nov 2014 09:53:16 AM CST</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> * Brief : </span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> * </span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> * Copyright (C) MICL,USTB</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> */</span> <span class="ot" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32);">#include <iostream></span> <span class="ot" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32);">#include "cv.h"</span> <span class="ot" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32);">#include "highgui.h"</span> <span class="ot" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32);">#include "opencv2/imgproc/imgproc.hpp"</span>using namespace std; using namespace cv;<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">int</span> main(<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">int</span> argc, <span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">char</span> *argv[]) {<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span> (argc < <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">2</span>) {cout << <span class="st" style="margin: 0px; padding: 0px; color: rgb(64, 112, 160);">"Usage: ./sobel [image file]"</span> <<endl;<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">return</span> -<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>;}<span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// Read image</span>Mat src = imread(argv[<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>], CV_LOAD_IMAGE_COLOR);<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span> (!src.data) {cout << <span class="st" style="margin: 0px; padding: 0px; color: rgb(64, 112, 160);">"Error: read image"</span> << endl;<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">return</span> -<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>;}cvtColor(src, src, CV_RGB2GRAY);namedWindow(<span class="st" style="margin: 0px; padding: 0px; color: rgb(64, 112, 160);">"Origin"</span>, <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>);imshow(<span class="st" style="margin: 0px; padding: 0px; color: rgb(64, 112, 160);">"Origin"</span>,src);<span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// CV_EXPORTS_W void Sobel( InputArray src, OutputArray dst, int ddepth,</span><span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// int dx, int dy, int ksize=3,</span><span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// double scale=1, double delta=0,</span><span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// int borderType=BORDER_DEFAULT );</span>Mat dst_x;<span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// Gradient X</span>Sobel(src, dst_x, src.depth(), <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>, <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>, <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">3</span>, <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>, <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>, BORDER_DEFAULT);namedWindow(<span class="st" style="margin: 0px; padding: 0px; color: rgb(64, 112, 160);">"Sobel image X gradient"</span>, <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>);imshow(<span class="st" style="margin: 0px; padding: 0px; color: rgb(64, 112, 160);">"Sobel image X gradient"</span>, dst_x);Mat dst_y;<span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// Gradient Y</span>Sobel(src, dst_y, src.depth(), <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>, <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>, <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">3</span>, <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>, <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>, BORDER_DEFAULT);namedWindow(<span class="st" style="margin: 0px; padding: 0px; color: rgb(64, 112, 160);">"Sobel image Y gradient"</span>, <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>);imshow(<span class="st" style="margin: 0px; padding: 0px; color: rgb(64, 112, 160);">"Sobel image Y gradient"</span>, dst_y);Mat dst;<span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// Method one: |G|=|Gx|+|Gy|</span>convertScaleAbs(dst_x, dst_x);convertScaleAbs(dst_y, dst_y);addWeighted(dst_x, <span class="fl" style="float: left; margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0.5</span>, dst_y, <span class="fl" style="float: left; margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0.5</span>, <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>, dst);namedWindow(<span class="st" style="margin: 0px; padding: 0px; color: rgb(64, 112, 160);">"Sobel image XY"</span>, <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>);imshow(<span class="st" style="margin: 0px; padding: 0px; color: rgb(64, 112, 160);">"Sobel image XY"</span>, dst);<span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// Method two:</span>Sobel(src, dst, src.depth(), <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>, <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>, <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">3</span>, <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>, <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>, BORDER_DEFAULT);waitKey();<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">return</span> <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>; }</code>簡(jiǎn)單的換一下函數(shù),就是LOG算子的源碼:
<code class="sourceCode c" style="margin: 0px; padding: 0.5em; line-height: normal; font-family: Monaco, 'Courier New', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; color: rgb(51, 51, 51); border: none; display: block; background: rgb(248, 248, 255);"><span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">/*</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> * FileName : Laplace.cpp</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> * Author : xiahouzuoxin @163.com</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> * Version : v1.0</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> * Date : Sun 16 Nov 2014 10:52:09 AM CST</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> * Brief : </span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> * </span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> * Copyright (C) MICL,USTB</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> */</span> <span class="ot" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32);">#include <iostream></span> <span class="ot" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32);">#include "cv.h" </span> <span class="ot" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32);">#include "highgui.h"</span> <span class="ot" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32);">#include "opencv2/imgproc/imgproc.hpp"</span>using namespace std; using namespace cv;<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">int</span> main(<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">int</span> argc, <span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">char</span> *argv[]) { <span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span> (argc < <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">2</span>) {cout << <span class="st" style="margin: 0px; padding: 0px; color: rgb(64, 112, 160);">"Usage: ./Laplace [image file]"</span> <<endl;<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">return</span> -<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>;}<span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// Read image</span>Mat src = imread(argv[<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>], CV_LOAD_IMAGE_COLOR);<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span> (!src.data) {cout << <span class="st" style="margin: 0px; padding: 0px; color: rgb(64, 112, 160);">"Error: read image"</span> << endl;<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">return</span> -<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>;}cvtColor(src, src, CV_RGB2GRAY);namedWindow(<span class="st" style="margin: 0px; padding: 0px; color: rgb(64, 112, 160);">"Origin"</span>, CV_WINDOW_AUTOSIZE);imshow(<span class="st" style="margin: 0px; padding: 0px; color: rgb(64, 112, 160);">"Origin"</span>,src);Mat dst;<span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// CV_EXPORTS_W void Laplacian( InputArray src, OutputArray dst, int ddepth,</span><span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// int ksize=1, double scale=1, double delta=0,</span><span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// int borderType=BORDER_DEFAULT );</span>Laplacian(src, dst, src.depth(), <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">3</span>, <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>, <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>, BORDER_DEFAULT);namedWindow(<span class="st" style="margin: 0px; padding: 0px; color: rgb(64, 112, 160);">"Laplacian"</span>, CV_WINDOW_AUTOSIZE);imshow(<span class="st" style="margin: 0px; padding: 0px; color: rgb(64, 112, 160);">"Laplacian"</span>,dst);waitKey();<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">return</span> <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>; }</code>Canny算子的源碼也很簡(jiǎn)單,只不過(guò)使用了GaussianBlur進(jìn)行高斯平滑,
<code class="sourceCode c" style="margin: 0px; padding: 0.5em; line-height: normal; font-family: Monaco, 'Courier New', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; color: rgb(51, 51, 51); border: none; display: block; background: rgb(248, 248, 255);"><span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">/*</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> * FileName : canny.cpp</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> * Author : xiahouzuoxin @163.com</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> * Version : v1.0</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> * Date : Sun 16 Nov 2014 10:59:31 AM CST</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> * Brief : </span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> * </span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> * Copyright (C) MICL,USTB</span> <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;"> */</span><span class="ot" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32);">#include <iostream></span> <span class="ot" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32);">#include "cv.h" </span> <span class="ot" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32);">#include "highgui.h"</span> <span class="ot" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32);">#include "opencv2/imgproc/imgproc.hpp"</span>using namespace std; using namespace cv;<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">int</span> main(<span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">int</span> argc, <span class="dt" style="margin: 0px; padding: 0px; color: rgb(144, 32, 0);">char</span> *argv[]) {<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span> (argc < <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">2</span>) {cout << <span class="st" style="margin: 0px; padding: 0px; color: rgb(64, 112, 160);">"Usage: ./canny [image file]"</span> <<endl;<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">return</span> -<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>;}<span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// Read image</span>Mat src = imread(argv[<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>], CV_LOAD_IMAGE_COLOR);<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">if</span> (!src.data) {cout << <span class="st" style="margin: 0px; padding: 0px; color: rgb(64, 112, 160);">"Error: read image"</span> << endl;<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">return</span> -<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">1</span>;}cvtColor(src, src, CV_RGB2GRAY);namedWindow(<span class="st" style="margin: 0px; padding: 0px; color: rgb(64, 112, 160);">"Origin"</span>, CV_WINDOW_AUTOSIZE);imshow(<span class="st" style="margin: 0px; padding: 0px; color: rgb(64, 112, 160);">"Origin"</span>,src);Mat dst;GaussianBlur(src, dst, Size(<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">3</span>,<span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">3</span>), <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>, <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>); <span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// 使用Gaussian濾波器進(jìn)行平滑</span><span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// CV_EXPORTS_W void Canny( InputArray image, OutputArray edges,</span><span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// double threshold1, double threshold2,</span><span class="co" style="margin: 0px; padding: 0px; color: rgb(96, 160, 176); font-style: italic;">// int apertureSize=3, bool L2gradient=false );</span>Canny(dst, dst, <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">50</span>, <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">200</span>, <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">3</span>);namedWindow(<span class="st" style="margin: 0px; padding: 0px; color: rgb(64, 112, 160);">"Canny"</span>, CV_WINDOW_AUTOSIZE);imshow(<span class="st" style="margin: 0px; padding: 0px; color: rgb(64, 112, 160);">"Canny"</span>,dst);waitKey();<span class="kw" style="margin: 0px; padding: 0px; color: rgb(0, 112, 32); font-weight: bold;">return</span> <span class="dv" style="margin: 0px; padding: 0px; color: rgb(64, 160, 112);">0</span>; }</code>請(qǐng)注意,上面的Sobel和LOG算子代碼都沒(méi)有在計(jì)算結(jié)果后使用閾值判斷是否屬于邊界,而直接顯示了邊緣信息。
Sobel邊緣檢測(cè)結(jié)果:右上為x梯度結(jié)果,左下為y梯度結(jié)果,右下為G(x,y)梯度結(jié)果
Laplace邊緣檢測(cè)結(jié)果
總結(jié)
- 上一篇: 【OpenCV入门指南】第五篇轮廓检测
- 下一篇: send(),recv()函数详解