OpenCV を用いて画像の輪郭を検出する - C#

C#でOpenCVを用いて画像の輪郭を検出するコードを紹介します。この記事で紹介する方法は、C#からOpenCvSharpeを用いてOpenCVを利用します。

事前準備

OpenCV,OpenCvSharpをインストールします。インストール手順はこちらの記事を参照してください。

プログラム

UI

下図のUIを作成します。TextBoxを1つ、Buttonを2つ配置します。

コード

下記のコードを記述します。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using OpenCvSharp;

namespace OpenCVSnake
{
  public partial class FormMain : Form
  {
    public FormMain()
    {
      InitializeComponent();
    }

    private void button_reference_Click(object sender, EventArgs e)
    {
      if (openFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
        textBox1.Text = openFileDialog1.FileName;
      }
    }

    private void button_Exec_Click(object sender, EventArgs e)
    {
      string ImageFileName = textBox1.Text;

      IplImage src = new IplImage(ImageFileName, LoadMode.GrayScale);
      IplImage dst = new IplImage(src.Size, BitDepth.U8, 3);

      CvPoint[] contour = new CvPoint[100];
      CvPoint center = new CvPoint(src.Width / 2, src.Height / 2);
      for (int i = 0; i < contour.Length; i++) {
        contour[i].X = (int)(center.X * Math.Cos(2 * Math.PI * i / contour.Length) + center.X);
        contour[i].Y = (int)(center.Y * Math.Sin(2 * Math.PI * i / contour.Length) + center.Y);
      }
      
      CvWindow w = new CvWindow();
      while (true) {
        src.SnakeImage(contour, 0.45f, 0.35f, 0.2f, new CvSize(15, 15), new CvTermCriteria(1), true);
        src.CvtColor(dst, ColorConversion.GrayToRgb);
        for (int i = 0; i < contour.Length - 1; i++) {
          dst.Line(contour[i], contour[i + 1], new CvColor(255, 0, 0), 2);
        }
        dst.Line(contour[contour.Length - 1], contour[0], new CvColor(255, 0, 0), 2);
        w.Image = dst;
        int key = CvWindow.WaitKey();
        if (key == 27) {
          break;
        }
      }
    }
  }
}

解説

下記のコードは画像を読み込むIplImageと結果画像を出力するImlImageを作成しています。輪郭検出のため画像はグレースケールで読み込みます。
  IplImage src = new IplImage(ImageFileName, LoadMode.GrayScale);
  IplImage dst = new IplImage(src.Size, BitDepth.U8, 3);

輪郭の多角形の初期状態を設定します。このプログラムでは頂点数を100とし、初期状態は画像全体を囲む円形で設定しています。
  CvPoint[] contour = new CvPoint[100];
  CvPoint center = new CvPoint(src.Width / 2, src.Height / 2);
  for (int i = 0; i < contour.Length; i++) {
    contour[i].X = (int)(center.X * Math.Cos(2 * Math.PI * i / contour.Length) + center.X);
    contour[i].Y = (int)(center.Y * Math.Sin(2 * Math.PI * i / contour.Length) + center.Y);
  }

SnakeImageメソッドを呼び出し、画像の輪郭を検出します。結果を表示するため、結果画像に元画像をRGB画像としてコピーします。その後、結果画像に輪郭の多角形を描画します。
  CvWindow w = new CvWindow();
  while (true) {
    src.SnakeImage(contour, 0.45f, 0.35f, 0.2f, new CvSize(15, 15), new CvTermCriteria(1), true);
    src.CvtColor(dst, ColorConversion.GrayToRgb);
    for (int i = 0; i < contour.Length - 1; i++) {
      dst.Line(contour[i], contour[i + 1], new CvColor(255, 0, 0), 2);
    }
    dst.Line(contour[contour.Length - 1], contour[0], new CvColor(255, 0, 0), 2);

    w.Image = dst;
    int key = CvWindow.WaitKey();
    if (key == 27) {
      break;
    }
  }

下記のコードで輪郭を結果が雑煮描画します。
for (int i = 0; i < contour.Length - 1; i++) {
  dst.Line(contour[i], contour[i + 1], new CvColor(255, 0, 0), 2);
}
dst.Line(contour[contour.Length - 1], contour[0], new CvColor(255, 0, 0), 2);

結果画像をウィンドウに表示し、キー入力を待ちます。Escキーが押された場合は処理を中止します。
w.Image = dst;
int key = CvWindow.WaitKey();
if (key == 27) {
  break;
}

実行結果

プロジェクトを実行します。下図の画面が表示されます。


[参照]ボタンをクリックします。ファイル選択ダイアログボックスが開きます。画像ファイルを選択します。今回はOpenCvSharpのデモのCake.bmpを開きます。


テキストボックスに画像ファイルのパスが設定されました。


[Exec]ボタンをクリックします。ウィンドウが表示され画像の輪郭が赤枠で表示されます。


ウィンドウを閉じるごとに、輪郭検出が進み、より正確な輪郭が検出されます。


補足

初期状態はプログラム実行過程では表示されませんが下図の画像いっぱいの円が初期状態になります。

著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
掲載日: 2014-04-09
iPentec all rights reserverd.