2013年1月4日 星期五

EmguCV Image Process: Filtering the Images part 3

中正大學夕陽
EmguCV Image Process: Filtering the Images

參考OpenCV 2 Computer Vision Application Programming Cookbook第六章

介紹內容如下:

Filtering images using low-pass filters

Eiltering images using a median filter

Applying directional filters to detect edges

Computing the Laplacian of an image

上一篇介紹了non-linear filters

而本篇再回過頭來討論關於linear filters

還記得第一篇關於low pass filters的討論

本篇反過來對於高頻的區塊做強化

也就是所謂的high-pass filters

而在這篇所介紹的high-pass filters可用來實作邊緣偵測(edge detection)

而在本篇介紹的filter稱為:Sobel filter

而因為此filter會根據kernel的設定而被影像中水平或垂直的頻率所影響

也被稱作為:directional filter

這裡以灰階的影像來做討論

要使用sobel的方法非常簡單,如下所示:

Image<Gray, Byte> image = new Image<Gray, Byte>("image.jpg");
//horizontal filter
Image<Gray, float> sobelX = image.Sobel(1, 0, 3);
//vertical filter
Image<Gray, float> sobelY = image.Sobel(0, 1, 3);
透過Sobel內帶入的參數

決定是要做水平偵測還是垂直偵測

而這裡帶的kernal size為3

還有要注意的是Sobel出來的結果是16位元深度的影像

所以要使用Image<Bgr, float>為型別的變數

如果使用sobelX跟sobelY的save方法

結果如下:


 sobelY的影像,水平方向的邊緣偵測

sobelX的影像,垂直方向的邊緣偵測

若是直接轉成8位元深度的影像

程式如下:

Image<Gray, Byte> sobelXByte = sobelX.Convert<Gray, Byte>();
Image<Gray, Byte> sobelYByte = sobelY.Convert<Gray, Byte>();
透過Convert做轉換,從16位元深度轉成8位元深度

這樣轉換出來的影像

會變成:


 sobelYByte影像存圖,有點像是浮雕的感覺~ 

sobelXByte影像存圖

若是直接透過Convert轉換會變得很奇怪

這到底是為什麼呢??

原因就在於16位元深度的sobel影像的最小灰階值並不是為0而是為負的!!

因此若直接做轉換

8位元深度的0會對應到16位元深度的最小值(為負值)

8位元深度的255會對應到16位元深度的最大值(為正值)

因此16位元深度的0反而會對應到8位元深度的中間值(可能會是128等)

所以影像看起來會是灰色的!! 而不是像一開始黑白分明!!

這是很多初學者一開始用OpenCV的Sobel方法容易遇到的問題

而至於為什麼會是負值,後面一點再來討論!


那若是要做邊緣偵測,就得把水平與垂直的部分合起來

先取出sobelX與sobelY的絕對值的影像

在透過運算子"+"就可以將兩張影像做相加的運算

這時候利用一些方法讓Convert的轉換符合我們的需求

程式如下:

//Convert negative values to positive valus
sobelX = sobelX.AbsDiff(new Gray(0));
sobelY = sobelY.AbsDiff(new Gray(0));

Image<Gray, float> sobel = sobelX + sobelY;
//Find sobel min or max value
double[] mins, maxs;
//Find sobel min or max value position
Point[] minLoc, maxLoc;
sobel.MinMax(out mins, out maxs, out minLoc, out maxLoc);
//Conversion to 8-bit image
Image<Gray, Byte> sobelImage = sobel.ConvertScale<byte>(255 / maxs[0], 0);
透過ConvertScale帶入sobel影像中最大值(可能會超過255)

做比較符合我們需求的轉換後

結果如下:


 這是sobel 16位元深度的影像直接存出來的結果

sobelImage 這是轉換成8位元深度的影像結果


這裡為什麼要將sobelX跟sobelY做絕對值在相加呢??

若是sobel是直接透過兩張影像直接相加後

存出來的圖會忽略掉負值(直接給0),也會忽略掉超過255的值(直接給255)

如下圖:


沒有先取絕對值的話,會損失很多細節


而透過ConvertScale方法轉出來的8位元深度影像

就算沒有事前先對兩張影像取絕對值

也會把原先的負值的pixel也轉換成正值!!

並且把超過255的值也限縮到255之內,所以畫面看起來才會比較暗

例如此例子中,sobel影像中最小的值為:-784,最大值為:930

直接相加的存圖就會直接從把灰階值為-784~0之間的pixels設為0

灰階值為256~930之間的pixels設為255


而透過取絕對值在相加的sobel影像的灰階值會落在0~930

則256~930之間的pixels就會被直接設定為255

少了這之間的比較細節


而透過ConvertScale的方法就是利用Scale的方式

會先把灰階值為負的值轉成正的

所以灰階值-784~0的pixels會轉成0~784灰階值

然後再透過mapping的方式把0~930對應到0~255

所以包含的影像細節是比較多的!!!

這點一定要特別注意

這時候只要把結果加上二值化

就成了邊緣偵測的結果了!!

//Get binary image
sobelImage._ThresholdBinary(new Gray(70), new Gray(255));
設定70為閥值來做二值化

結果如下:

偵測到邊緣的部分都以白色做呈現

可以調整閥值來決定呈現邊緣的多寡

而至於Sobel filter為什麼會有負值??

下一篇再來討論嚕!!


4 則留言:

  1. 非常有用。。告诉了我怎么用索贝尔算法做边缘检测。

    回覆刪除
  2. 請問Sobel(1, 0, 3)這段帶入的參數是什麼意思?

    回覆刪除
    回覆
    1. 可參考emgucv的文件:http://www.emgu.com/wiki/files/3.0.0/document/html/6a5bee61-41d2-a4a7-2c32-c489b78b90d9.htm

      前兩個參數主要決定是要用水平方向(x),還是垂直方向(y)的處理,第三個參數就是決定sobel演算法的範圍大小,只能是奇數,一般大小設定為3。

      刪除