1. 程式人生 > >7.邊緣檢測:2D運算——Canny邊緣原理、Canny邊緣檢測器、Canny-Matlab實戰_2

7.邊緣檢測:2D運算——Canny邊緣原理、Canny邊緣檢測器、Canny-Matlab實戰_2

目錄

Canny邊緣原理

Canny邊緣檢測器

Canny-Matlab實戰


Canny邊緣原理

既然我們知道了如何計算光滑導數和梯度,我們就可以回到如何找到邊的問題上。

基本上這是一個多步驟的過程,對吧?

1.你要建立平滑的導數來抑制一些噪聲我們要計算一些主要的梯度。

2.我們要對梯度進行閾值以找到一些重要的變化區域。

3.我們將這些領域,我們要做所謂的變薄。

(下面所示的一個例子,我們將進一步討論,這樣脂肪邊緣變成一個輪廓,然後說這就是優勢)

4.最後,我們要以某種方式連線這些畫素的,如果你真的想要一個連線輪廓。

所以我想說的邊緣運算元是John Canny做的,實際上是他的碩士論文,當我還在那裡上學的時候,

他在他的博士論文中繼續做了一些很酷的東西,但是大家都知道因為它被稱為Canny邊緣運算元。

Canny運算元的工作原理是這樣的:

1.首先你用高斯函式的導數對影象進行過濾;

2.找出幅度和方向;

3.進行非極大值抑制,也就是這種細化,我們等下會講到;

4.有一個連線操作,在這裡您將不僅要定義一個閾值,還要定義兩個閾值,並且您將使用高的閾值開始邊緣化;

但是您將使用更低的閾值來繼續;(我們將詳細討論這個,這是真正的酷的洞察力)

順便說一下,MATLAB會處理Canny邊緣,所以你可以通過呼叫邊緣函式來獲取Canny邊緣。

我鼓勵你看MATLAB的相關邊緣文件。

MATLAB中影象處理工具箱的文件,幫助你學習MATLAB中的所有邊緣檢測,它實際上是影象處理資訊的主要來源。

Canny邊緣檢測器

為了說明這個有趣的邊緣探測器,我要用Lena的圖片:

有個人,他剪掉了1972年男性雜誌的頂部照片,那是Lena Soderberg,我想是她的名字。

這是她最近的照片:

所以我們都變了。

我們還是使用最上面那張用於影象處理的圖片。

如果我們取梯度的大小,我們得到的值是這樣的:

你看它接近邊緣,但實際上,你和我都看到了邊緣,對吧?

它只是一個梯度影象,對吧?

然後我們就可以閾值梯度:

現在你可以看到這些東西都消失了,所以任何梯度不高的東西都被去掉了。

然後我們做一個調整叫影象細化, 稍後我們會詳細討論這個。

這個奇特的名字叫非極大抑制。

基本上就是說,如果我有一些點在區域性超過閾值,讓我只取出超過它最多的點。

我們馬上會學習到。

當你這樣做的時候,你會得到這樣的結果:

兩張對比,你可以看到這是它的細化版本:

簡單說明一下,為什麼要細化?

我們來看看這個小區域:

你可以看到有一個厚的部分超過了臨界值。

我們想說的是中間有一條邊:

你想想,如果你來到這裡,你可能會看到一個輪廓線從低到高,對吧?

如果你對它求導,你會發現這個區域的導數高於這個閾值:

這就得到了這條粗邊:

我們想把它變成一條細邊。

因此,非最大抑制的方式,細化是在canny運算元中完成的,如下所示:

你不需要實施這個,我只是想讓你知道發生了什麼。

基本上它會找到高梯度的區域,它會沿著梯度的方向看過去,它只會找到那裡的峰值:

然後這裡也是一樣,你會在這裡找到峰值:

這裡的梯度是這個方向,所以會找到峰值:

有時候,有時候你需要插值,這就是這張圖顯示:

你找到梯度,你說:Ok,我認為它在兩個畫素之間,所以你可以得到亞畫素(sub-pixel)精度。

但關鍵是,它看起來垂直於梯度向量,為了找到最大值。

這個細化的部分,這就是怎麼做的,現在有一個非常聰明的細節要看。

如果你看一下下巴下面的這個點,你會看到一些畫素沒有通過閾值:

這是一個問題,你可以說我們的閥值設定的太高了。

然後會出現一大堆我們並不關心的東西。

問題是如何處理這個問題:

1.我們做的第一件事是應用高閾值來檢測邊緣,強邊緣畫素。閾值會拉出一些畫素。

2.我們要做的就是把這些強邊畫素連線起來形成強邊。

3.我們現在使用一個低閾值來找到弱的,但可能的邊緣畫素。

4.接著我們沿著弱畫素點擴充套件強邊。

這意味著如果一條邊上只有弱畫素,它就不會被發現是一條邊。

只有當某些畫素是強邊緣畫素時,才能找到邊緣。這裡的假設是,我關心的所有邊都有一些強畫素。

然後,我可能不得不繼續連線一個閥值較低的區域,但邊緣是由高閥值畫素的檢測開創的。

Canny-Matlab實戰

我有兩個圖片給你,曲發和轎車。

>> pkg load image;
>>
>> frizzy = imread('frizzy.png');
>> froomer = imread('froomer.png');
>> imshow(frizzy);
>> imshow(froomer);
>>
>> % TODO: Find edges in frizzy and froomer images
>>
>> % TODO: Display common edge pixels

程式碼執行,顯示兩張圖片: 

我希望您在這些影象中找到邊緣,然後顯示兩者之間共有的邊緣畫素。

如果你能做也不錯,我們來一起做做吧:

現在我們來看看是怎麼得到這個的。

首先,將影象從顏色轉換為灰度,使用rgb2gray()函式:

然後使用邊緣函式分別計算每個影象的邊緣。請注意,我明確地使用了'canny'。預設是我認為索貝爾,它不起作用。

Canny接受了附加引數、滯後性值和平滑sigma值。隨意玩他們。

>> pkg load image;
>>
>> frizzy = imread('frizzy.png');
>> froomer = imread('froomer.png');
>> imshow(frizzy);
>> imshow(froomer);
>>
>> % TODO: Find edges in frizzy and froomer images
>> frizzy_gray = rgb2gray(frizzy);
>> froomer_gray = rgb2gray(froomer);
>>
>> frizzy_edges = edge(frizzy_gray, 'canny');
>> froomer_edges = edge(froomer_gray, 'canny');
>> imshow(frizzy_edges);
>> imshow(froomer_edges);
>>
>> % TODO: Display common edge pixels

程式碼執行如下:

這是frizzy的邊緣。這幾乎是完美的。

注意在嘴巴和鼻子周圍,我們有很厚的輪廓,我們有雙重邊緣。

這是預期的。

這是froomer的邊緣,也很好。

好的,現在怎麼辦呢?

注意,邊緣影象是二進位制影象。

這意味著你可以和它們一起找到共同的邊緣畫素。

>> pkg load image;
>>
>> frizzy = imread('frizzy.png');
>> froomer = imread('froomer.png');
>> imshow(frizzy);
>> imshow(froomer);
>>
>> % TODO: Find edges in frizzy and froomer images
>> frizzy_gray = rgb2gray(frizzy);
>> froomer_gray = rgb2gray(froomer);
>>
>> frizzy_edges = edge(frizzy_gray, 'canny');
>> froomer_edges = edge(froomer_gray, 'canny');
>> imshow(frizzy_edges);
>> imshow(froomer_edges);
>>
>> % TODO: Display common edge pixels
>> imshow(frizzy_edges & froomer_edges); % binary AND

程式碼執行,結果:

OK。

你永遠不知道一個或兩個影象中隱藏著什麼。


——學會編寫自己的程式碼,才能練出真功夫。