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。
你永遠不知道一個或兩個影象中隱藏著什麼。
——學會編寫自己的程式碼,才能練出真功夫。