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為什麼會有負值??
下一篇再來討論嚕!!
非常有用。。告诉了我怎么用索贝尔算法做边缘检测。
回覆刪除謝謝你的留言及鼓勵。
刪除請問Sobel(1, 0, 3)這段帶入的參數是什麼意思?
回覆刪除可參考emgucv的文件:http://www.emgu.com/wiki/files/3.0.0/document/html/6a5bee61-41d2-a4a7-2c32-c489b78b90d9.htm
刪除前兩個參數主要決定是要用水平方向(x),還是垂直方向(y)的處理,第三個參數就是決定sobel演算法的範圍大小,只能是奇數,一般大小設定為3。