2012年11月21日 星期三

EmguCV Image Process: Transforming Images with Morphological Operations part 3

20101219030.jpg
EmguCV Image Process: Transforming Images with Morphological Operations

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

介紹內容如下:

Eroding and dilating images using morphological filters

Opening and closing images using morphological filters

Detecting edges and corners using morphological filters

Segmenting images using watersheds

Extracting foreground objects with the GrabCut algorithm


上一篇介紹了透過形態學運算子(閉鎖和斷開)來做一些影像處理

Morphological filters也可以用來擷取影像中特殊的特徵(features)

這裡介紹如何偵測影像中的線段(lines)跟邊角(corners)

所以這次才換了一張有較多線條的影像來作範例

首先來定義一個叫做:MorphoFeatures的類別

class MorphoFeatures
{
    //threshold produce binary image
    private int _Threshold;

    //Set the threshold
    public int Threshold
    {
        get { return this._Threshold; }
        set { this._Threshold = value; }
    }
    /// <summary>
    /// Detecting lines is quite easy using the appropriate filter of MorphologyEx function.
    /// </summary>
    /// <param name="image">source image</param>
    /// <returns>result image</returns>
    public Image<Gray, byte> GetEdges(Image<Gray, byte> image)
    {
        Image<Gray, byte> result = null;

        //Get the gradient image
        result = image.MorphologyEx(null, Emgu.CV.CvEnum.CV_MORPH_OP.CV_MOP_GRADIENT, 1);

        //Apply threshold to obtain a binary image
        ApplyThreshold(result);

        return result;

    }
    /// <summary>
    /// The binary edge image is obtained through a simple private method of the class
    /// </summary>
    /// <param name="image">result image</param>
    void ApplyThreshold(Image<Gray, byte> image)
    {
        //Apply threshold on result
        if (this._Threshold > 0)
        {
            image._ThresholdBinary(new Gray(this._Threshold), new Gray(255));
        }
    }
}
可以設定threshold的值

並透過GetEdges來取得偵測邊緣線段的影像

這時在main function的部分就可以簡單的來取得偵測結果

//Read input image
Image<Gray, Byte> image = new Image<Gray, byte>(@"image.jpg");
//Create the morphological features instance
MorphoFeatures morpho = new MorphoFeatures();
//Set the threshold
morpho.Threshold = 40;
//Get the edges
Image<Gray,byte> result = morpho.GetEdges(image);
先建立一個MorphoFeatures的實體類別

然後設定threshold的值

再把讀取的影像傳入

便可取得偵測結果

結果如下:
偵測出的邊緣線段用白色呈現出來


而偵測邊角的方法則相對來說就複雜很多

在OpenCV跟EmguCV中都沒有直接實作的方法

在這邊將會採用許多非方形的結構元素

實際上需要四種不同的結構元素:方形、菱形、十字、X型

所以必須先建立起所需要的結構元素

//structuring elements used in corner detection
private StructuringElementEx _Cross;
private StructuringElementEx _Diamond;
private StructuringElementEx _Square;
private StructuringElementEx _XSharp;
在MorphoFeatures中加入所需的結構元素

public MorphoFeatures()
{
    this._Threshold = -1;

    //Creating the cross-shaped structuring element
    int[,] cross = new int[5, 5]
                        { {0,0,1,0,0},
                          {0,0,1,0,0},
                          {1,1,1,1,1},
                          {0,0,1,0,0},
                          {0,0,1,0,0}, };
    this._Cross = new StructuringElementEx(cross, 2, 2);

    //Creating the diamond-shaped structuring element
    int[,] diamond = new int[5, 5]
                        { {0,0,1,0,0},
                          {0,1,1,1,0},
                          {1,1,1,1,1},
                          {0,1,1,1,0},
                          {0,0,1,0,0}, };
    this._Diamond = new StructuringElementEx(diamond, 2, 2);

    //Creating the Square-shaped structuring element
    this._Square = new StructuringElementEx(5, 5, 2, 2, Emgu.CV.CvEnum.CV_ELEMENT_SHAPE.CV_SHAPE_RECT);

    //Creating the x-shaped structuring element
    int[,] xSharp = new int[5, 5]
                        { {1,0,0,0,1},
                          {0,1,0,1,0},
                          {0,0,1,0,0},
                          {0,1,0,1,0},
                          {1,0,0,0,1}, };
    this._XSharp = new StructuringElementEx(xSharp, 2, 2);
}
然後在建構式中初始化這些結構元素

在偵測邊角時只需要透過這幾個結構元素來做形態學的運算

就能取得邊角位置:
/// <summary>
/// In the detection of corner features, all of these structuring elements
/// are applied in cascade to obtain the resulting corner map.
/// </summary>
/// <param name="image">source image</param>
/// <returns>result image</returns>
public Image<Gray, byte> GetCorners(Image<Gray, byte> image)
{
    Image<Gray, byte> result = image.CopyBlank();
    //Dilate with a cross
    CvInvoke.cvDilate(image, result, this._Cross, 1);
    //Erode with a diamond
    CvInvoke.cvErode(result, result, this._Diamond, 1);

    Image<Gray, byte> result2 = image.CopyBlank();
    //Dilate with a X-shaped
    CvInvoke.cvDilate(image, result2, this._XSharp, 1);
    //Erode with a square
    CvInvoke.cvErode(result2, result2, this._Square, 1);

    //Corners are obtained by differencing
    //the two closed images
    result=result.AbsDiff(result2);
    //Apply threshold to obtain a binary image
    ApplyThreshold(result);

    return result;
}
透過兩張不同的形態運算結果

result透過一次十字擴張、菱形侵蝕

result2透過一次X擴張、矩形侵蝕

在經過差值運算(AbsDiff)後便可得到結果

使用方式與之前相同,透過GetCorners的方法來取得結果

result = morpho.GetCorners(image);

便可得到下圖:
這樣或許看不太出來!?

可以透過不一樣的方式來呈現所抓取的位置

/// <summary>
/// The method draws a circle on the image at each detected point on the
/// binary map.
/// </summary>
/// <param name="binary">corner map binary image</param>
/// <param name="image">source image</param>
public void DrawOnImage(Image<Gray, byte> binary, Image<Gray, byte> image)
{
    //for each pixel
    for (int h = 0; h < binary.Height; h++)
    {
        for (int w = 0; w < binary.Width; w++)
        {
            if (binary[h, w].Intensity != 0)
            {
                CircleF circle = new CircleF(new PointF(w,h),5);
                image.Draw(circle, new Gray(255), 1);
            }
        }
    }
}
對每一個所偵測出來到的點位

畫上一個5X5的小圈圈

直接把邊角偵測的結果跟來源影像一起丟入此方法

result = morpho.GetCorners(image);
morpho.DrawOnImage(result, image);
就會把邊角點位畫在來源影像上

因此來源影像就會變成:
白色圈圈,圈出邊角位置

邊角的角度越直角、越明顯,偵測效果當然就越好

也可以透過structuring element的大小和threshold的值來控制輸出的結果

大家可能會覺得很神奇~怎麼會這樣!!

這就牽扯到形態學的基本運算

利用一般的影像很難看出其中的端倪

若是透過一個簡單的影像來做測試就比較容易明白

這就是測試影像

以上圖當作輸入的來源影像

在GetCorners中,把result跟result2的結果呈現出來


result,仔細看方塊的四個邊角似乎凹陷進去了!!


result2,似乎與原圖相同,四個邊角正常呈現


result=result.AbsDiff(result2); 相減之後的結果就是把四個邊角凸顯出來

有看出其中的端倪了嗎??

在result的處理中

透過一次十字結構的擴張,及一次菱形結構的侵蝕

導致邊角被侵蝕掉

而在result2的處理中

透過一次X結構的擴張,及一次矩形結構的侵蝕

導致邊角的擴張凸顯

兩張影像的相差結果就是四個邊角!!

由此便可偵測出邊角位置

是不是很神奇呢!!!


下一篇如何利用分水嶺演算法來切割影像前景

沒有留言:

張貼留言