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
上一篇介紹了sobel的基本原理
而Laplacian是另一種high-pass linear filter
透過計算二階導數來測量影像的curvature
在EmguCV中要使用Laplacian的function非常簡單
就像Sobel function一樣
image.Laplace(this._Aperture);引數中傳入Laplacian的kernel大小
就會回傳Laplacian的處理結果
這裡介紹如何把這個方法包裝起來
建立一個簡單的類別
封裝一些關於Laplacian並且有用的操作
class LaplacianZC { // Original image private Image<Gray, Byte> _Image; //32-bit float image containing the Laplacian private Image<Gray, float> _Laplace; //Aperture size of the laplacian kernel private int _Aperture; /// <summary> /// Initial the aperture size /// </summary> public LaplacianZC() { this._Aperture = 3; } /// <summary> /// Set the aperture size of the kernel /// </summary> /// <param name="size">the aperture size</param> public void SetAperture(int size) { this._Aperture = size; } /// <summary> /// Compute the floating point Laplacian /// </summary> /// <param name="image">the original image</param> /// <returns>the image containing the Laplacian</returns> public Image<Gray,float> ComputeLaplacain(Image<Gray,Byte> image) { //Keep local copy of the image this._Image = image.Clone(); //Compute Laplacian this._Laplace = image.Laplace(this._Aperture); return this._Laplace; } }這個簡單的類別可以設定Laplacian的aperture size
使用的方法也很簡單
建立此類別後
設定kernel size
再把原始影像帶入ComputeLaplacain的方法即可
Image<Gray, Byte> image = new Image<Gray, Byte>("image.jpg"); //Compute Laplacian using LaplacianZC class LaplacianZC laplacianZC = new LaplacianZC(); laplacianZC.SetAperture(5); Image<Gray, float> laplacian = laplacianZC.ComputeLaplacain(image);簡單操作
結果如下:
直接把結果存成圖片的結果!
那Laplacian會遇到跟之前討論Sobel時一樣的問題
結果的影像的值有正有負
所以還要在LalpacianZC的類別中加入一個方法
就跟之前處理Sobel一樣,要把結果影像從32-bit轉成8-bit的影像
/// <summary> /// Get the Laplacian result in 8-bit image /// The max value will be scaled to intensity 255 /// You must call ComputeLaplacian before calling this /// </summary> /// <returns>the Laplacian result in 8-bit image</returns> public Image<Gray, Byte> GetLaplacianImage() { double scale = 0; //Find sobel min or max value double[] mins, maxs; //Find sobel min or max value position Point[] minLoc, maxLoc; this._Laplace.MinMax(out mins, out maxs, out minLoc, out maxLoc); //Conversion to 8-bit image scale = Math.Max(Math.Abs(mins[0]), maxs[0]); Image<Gray, Byte> lapImage = this._Laplace.ConvertScale<byte>(255 / scale, 0); return lapImage; }透過這個方法就可以取得8-bit的結果影像
所以要在
Image<Gray, float> laplacian = laplacianZC.ComputeLaplacain(image);
之後再執行
Image<Gray, Byte> laplacian8bit = laplacianZC.GetLaplacianImage();
laplacian8bit的結果如下:
8-bit的結果影像
Laplacian的2D function定義為二階導數的加總
基本的3X3 kernel如下:
相較於Sobel operator
Laplacian operator比較容易受到影像中的雜訊的影響
注意到Laplacian的kernel的值相加的總合為:0
因此在intensity(亮度)部分比較平滑的部位
計算出來的值會接近0
使用Laplacian operator主要也是用來做邊緣偵測
當計算到邊緣的時候
(假設是從暗到亮)
計算出來的curvature變化會從"正"逐漸過渡到"負"
這個轉換就是在正值與負值之間,也就是邊界的位置
由於以上的特性,要找出邊界可以利用一種方法:Zero-crossing
簡單的示意圖如下:
(取自維基百科)
找出Laplacian結果影像中所有Zero-crossing的點
這些點就會是邊界的位置
由於影像是2D,所以要找出水平方向、垂直方向的Zero-crossing的位置
透過與前一個pixel的value做相乘,若是負值,則表示為Zero-crossing
(正負相乘,或負正相乘)
則把binary影像的這個位置的value設為255
再透過一個threshold來做過濾,降低敏感度
方法一樣寫在LaplacianZC的類別之中
/// <summary> /// Get a binary image of two zero-crossings /// if the product of the two adjascent pixels is /// less than threshold then this zero-crossing /// will be ignored /// </summary> /// <param name="threshold">the threshold</param> /// <returns>the binary image of two zero-crossings</returns> public Image<Gray, Byte> GetZeroCrossings(int threshold) { Image<Gray, Byte> binary = new Image<Gray, byte>(this._Laplace.Width, this._Laplace.Height); threshold *= -1; for (int h = 1; h < this._Laplace.Height; h++) { for (int w = 1; w < this._Laplace.Width; w++) { //if the product of two adjascent pixel is negative //then there is a sign change if ((this._Laplace[h, w].Intensity * this._Laplace[h - 1, w].Intensity) < threshold) { binary[h, w] = new Gray(255);//horizontal zero=crossing } if ((this._Laplace[h, w].Intensity * this._Laplace[h, w - 1].Intensity) < threshold) { binary[h, w] = new Gray(255);//vertical zero-crossing } } } return binary; }操作的方法就是帶入threshold
這裡要注意的是
由於是透過兩個pixel的值相乘來做計算
所以threshold的值要設定的高一點才會有作用
Image<Gray, Byte> binary = laplacianZC.GetZeroCrossings(100000);
帶入100000的結果:
Zero-crossing的結果
可以觀察到
透過Zero-crossing幾乎可以偵測出所有的邊界
但卻不容易區隔比較強的邊界,或是比較弱的邊界
而Laplacian對於雜訊是非常敏感的
基於以上兩點
就知道為什麼透過這個operator會偵測出這麼多的邊緣了!!
沒有留言:
張貼留言