【C#】用 EmguCV 改寫 bwareaopen 函數

  • 1702
  • 0
  • 2018-01-15

在 MATLAB 內有個很好用的函數,叫做 bwareaopen。
功能是移除二值影像中小於指定閾值區塊。
可是在 EmguCV 內沒有這個傢伙,所以又到了要自己寫函數的時候了。
程式碼本身並不難,只是為了要看懂其他人在寫什麼,花了本恩許多時間。

話不多說,先來個簡單的畫面吧。

版本概要:
EmguCV 版本:3.2.0.2682
編譯器版本: Visual Studio 2017 Community 
方案平台: x64

這個畫面的主程式如下。

using System;
using System.Windows.Forms;

using Emgu.CV;
using Emgu.CV.Structure;
using Emgu.CV.CvEnum;
using Emgu.CV.Util;

namespace Test
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void button1_Click(object sender, EventArgs e)
        {
            Image<Gray, byte> I = new Image<Gray, byte>(@"D:\Test\img1.jpg");
            Image<Gray, byte> Ic1 = I.Clone();
            Image<Gray, byte> Ic2 = I.Clone();

            CvInvoke.Threshold(I, I, 0, 255, ThresholdType.Otsu);

            MyCV.Bwareaopen(I, Ic1, 50000, MyCV.BwMode.smaller);
            pictureBox1.Image = Ic1.Bitmap;

            MyCV.Bwareaopen(I, Ic2, 50000, MyCV.BwMode.bigger);
            pictureBox2.Image = Ic2.Bitmap;
        }  
    }
}

接著是自定義的類別。

public class MyCV
{
    public enum BwMode
    {
        smaller = 1,
        bigger = 2,
    }

    public static void Bwareaopen(Image<Gray, byte> src, 
                                  Image<Gray, byte> dst, 
                                  int size, BwMode mode)
    {
        Image<Gray, byte> CannyImage = src.Clone();

        // 在 Canny 函數中需要放兩個閾值(第3、4項參數)來找尋影像邊界,
        // 由於現在預設輸入的影像為二值影像,因此這兩個值給多少不影響結果。
        // 值得一提的是最後一項參數預設為 false,若使用預設值,則有些曲折
        // 的邊界會斷開,例如星形或雲形。
        // 另外若使用 Image<Gray, byte> 類別本身的 Canny 函數,用法像是
        // src.Canny(th1, th2),則無法對最後一項參數做更改。
        CvInvoke.Canny(src, CannyImage, 255, 255, 3, true);

        dst.Bitmap = src.Bitmap;
        using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint())
        {
            CvInvoke.FindContours(CannyImage, contours, null, RetrType.List, 
                                  ChainApproxMethod.ChainApproxSimple);

            int count = contours.Size;
            for (int i = 0; i < count; i++)
            {
                using (VectorOfPoint contour = contours[i])
                {
                    // 使用者可以自行決定需求,採用兩種不同的 BwMode。
                    // DrawContours 的第四項參數 MCvScalar(0) 表黑色
                    // DrawContours 的第五項參數 -1 表塗滿區域
                    if ((int)mode == 1)
                    {  
                        if (CvInvoke.ContourArea(contour) < size)
                            CvInvoke.DrawContours(dst, contours, i, 
                                                  new MCvScalar(0), -1);
                    }
                    else
                    {
                        if (CvInvoke.ContourArea(contour) > size)
                            CvInvoke.DrawContours(dst, contours, i, 
                                                  new MCvScalar(0), -1);
                    }
                }
            }
        }
    }
}

以下是程式測試結果。
首先是原圖,這是夏恩藝術家使用小畫家的即興創作。

最後是程式執行結果:

後記:

MATLAB 的 bwareaopen 還可以選擇是 4 連通區域或是 8 連通區域,這意味著
他們所使用的方法和我應該是不一樣的。雖然我也試著使用連通區域標記的方
法,但最後仍沒有成功。也許之後有機會再來挑戰看看。另外一點就是在 C# 內
有爆炸多的類別,在 EmguCV 內也是,因為我只是剛接觸這些東西,所以大部分
的時間都花在找尋合用類別上,希望這種情況會隨著我愈來愈熟練後有所改善。

參考資料:

1.【转载】OpenCV中bwareaopen()轮廓检测函数
2. findContours函數參數說明及相關函數