C#で画像の輪郭を抽出する

C#でOpenCvSharp4を使って、画像の輪郭を抽出します。

目次

  1. OpenCvSharpをWPFアプリケーションで使う準備
  2. 輪郭を抽出する手順
  3. 輪郭を抽出してみる
    1. XAML
    2. 変換の実際
  4. 主なメソッド
    1. Cv2.FindContours
    2. Cv2.DrawContours

OpenCvSharpをWPFアプリケーションで使う準備

OpenCvSharpをWPFアプリに適用するには、NuGetでいくつかのパッケージをインストールする必要があります。

Visual Studioのプロジェクトメニューの中のNuGetパッケージ管理から、下記を追加してください。

  • OpenCvSharp4

  • OpenCvSharp4.runtime.win

  • OpenCvSharp4.WpfExtensions

輪郭を抽出する手順

OpenCVのfindcontoursの機能を使って、画像から輪郭を抽出します。

大凡の手順は下記です。

  1. 画像をMat形式で読み込み、またはMat形式へ変換する。

  2. 画像をグレイスケールに変換する。

  3. グレイスケール画像を白黒画像に変換する。

  4. 白黒画像から輪郭を抽出する。

輪郭を抽出してみる

実際に輪郭を抽出してみましょう。

サンプルとして、下図の図形の輪郭を抽出してみます。

サンプル画像

XAML

XAMLは下記のようにします。ImageをStackPanelで横に並べました。

左側に元の画像を、右側に変換後の画像を表示させます。

<Window x:Class="cv2_test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:cv2_test"
        mc:Ignorable="d"
        Title="MainWindow" Height="360" Width="835" Loaded="Window_Loaded">
    <Grid>
        <StackPanel Orientation="Horizontal">
            <Image Name="imgSrc" Stretch="Fill" Width="400" Height="300" Margin="5" />
            <Image Name="imgDst" Stretch="Fill" Width="400" Height="300" Margin="5" />
        </StackPanel>
    </Grid>
</Window>

変換の実際

ではまず、サンプル画像を読み取って、グレイスケールに変換してみましょう。

using OpenCvSharp;
using OpenCvSharp.WpfExtensions;
using System.Windows;

namespace cv2_test
{
    public partial class MainWindow : System.Windows.Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            var src_mat = new Mat("test.png", ImreadModes.Color);

            var src_bitmap = BitmapSourceConverter.ToBitmapSource(src_mat);
            imgSrc.Source = src_bitmap;

            var gray_mat = new Mat();
            Cv2.CvtColor(src_mat, gray_mat, ColorConversionCodes.BGR2GRAY);
            var dst_bitmap = BitmapSourceConverter.ToBitmapSource(gray_mat);
            imgDst.Source = dst_bitmap;
        }
    }
}

WindowがLoadされると、ファイルの読み取りと変換と表示をするというプログラムです。

Matは、OpenCvSharpで使用される一般的な画像の形式(行列)です。

カラー画像をグレイスケールに変換するには、Cv2.CvtColorメソッドを使用します。

結果は、下図のようになります。

グレイスケール

次に、グレイスケール画像を白黒にします。

using OpenCvSharp;
using OpenCvSharp.WpfExtensions;
using System.Windows;

namespace cv2_test
{
    public partial class MainWindow : System.Windows.Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            var src_mat = new Mat("test.png", ImreadModes.Color);

            var src_bitmap = BitmapSourceConverter.ToBitmapSource(src_mat);
            imgSrc.Source = src_bitmap;

            var gray_mat = new Mat();
            Cv2.CvtColor(src_mat, gray_mat, ColorConversionCodes.BGR2GRAY);

            var bw_mat = new Mat();
            Cv2.Threshold(gray_mat, bw_mat, 192, 255, ThresholdTypes.BinaryInv);
            var dst_bitmap = BitmapSourceConverter.ToBitmapSource(bw_mat);
            imgDst.Source = dst_bitmap;
        }
    }
}

グレイスケール画像を白黒2値画像に変換するには、Cv2.Thresholdメソッドを使用します。

結果は、下図のようになります。

白黒

背景が黒色になっていることに注意してください。

では、白黒画像から輪郭を抽出します。抽出した輪郭を、元の画像に赤色の線で重ねます。

using OpenCvSharp;
using OpenCvSharp.WpfExtensions;
using System.Windows;

namespace cv2_test
{
    public partial class MainWindow : System.Windows.Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            var src_mat = new Mat("test.png", ImreadModes.Color);

            var src_bitmap = BitmapSourceConverter.ToBitmapSource(src_mat);
            imgSrc.Source = src_bitmap;

            var gray_mat = new Mat();
            Cv2.CvtColor(src_mat, gray_mat, ColorConversionCodes.BGR2GRAY);

            var bw_mat = new Mat();
            Cv2.Threshold(gray_mat, bw_mat, 192, 255, ThresholdTypes.BinaryInv);

            OpenCvSharp.Point[][] contours;
            OpenCvSharp.HierarchyIndex[] hindex;
            Cv2.FindContours(bw_mat, out contours, out hindex, RetrievalModes.List, ContourApproximationModes.ApproxNone);
            Cv2.DrawContours(src_mat, contours, -1, new Scalar(0,0,255), 2);
            var dst_bitmap = BitmapSourceConverter.ToBitmapSource(src_mat);
            imgDst.Source = dst_bitmap;
        }
    }
}

Cv2.FindContoursメソッドで白黒画像から輪郭を抽出します。輪郭を表す点の座標は、輪郭毎にジャグ配列となります。

Cv2.DrawContoursメソッドで、輪郭を指定の画像に描画します。

抽出した輪郭を全て表示してみます。

Listで輪郭抽出

緑色の円の内側のさらに円形に切り抜かれた部分も輪郭が抽出されています。

Cv2.FindContoursメソッドのRetrievalModesをListからExternalに変えると、外側部分だけの輪郭が抽出されます。

Cv2.FindContours(bw_mat, out contours, out hindex, RetrievalModes.External, ContourApproximationModes.ApproxNone);

表示してみると、緑色の円形の内側の切り抜き部分の輪郭が抽出されていないことがわかります。

Externalで輪郭抽出

抽出した輪郭のデータは、輪郭毎にひとまとまりになっています。

Cv2.DrawContoursメソッドの3番目の引数を0にすると、下図のようになります。緑色の円形の輪郭ですね。

1つ目

Cv2.DrawContoursメソッドの3番目の引数を1にすると、下図のようになります。茶色の棒の輪郭ですね。

2つ目

主なメソッド

BitmapSourceConverter.ToBitmapSourceについては、MatとBitmapSourceを変換する投稿を参照してください。

Cv2.FindContours

Cv2.FindContours(image, out contours, out hierarchy, mode, method, [offset]);

変数

内容

image

Mat

8bit シングルチャネル画像データ

contours

OpenCvSharp.Point[][]

輪郭を表す点のデータ

hierarchy

OpenCvSharp.HierarchyIndex[]

輪郭の階層に関する情報

mode

OpenCvSharp.RetrievalModes

輪郭の検索モード

method

OpenCvSharp.ContourApproximationModes

輪郭の表現方法

offset

System.Nullable<Point>

省略可。規定値はnull。

画像の輪郭を検索します。

戻り値はありません。contoursとhierarchyに指定した配列に、検索の結果が入力されます。これらの引数にoutパラメータ修飾子を指定します。

輪郭の検索モードには、下記の種類があります。図の外側だけを検索するならExternalで良いですが、図の内側だとか図がネストされているとかいうことになると、ListやTreeなどが使われます。contour[i][]の階層情報はhierarchy[i]に入力されます。

メンバ

内容

External

0

最も外側の輪郭を検索する

List

1

階層に関係なく全ての輪郭を検索する

CComp

2

全ての輪郭を検索し、2つの階層で表す

Tree

3

全ての輪郭を検索し、ネストされた階層で表す

FloodFill

4

輪郭の表現方法には下記の種類があります。Noneは輪郭を表す全ての点を返します。Simpleは端点など代表的な点を返します。

メンバ

内容

ApproxNone

1

全ての点を返す

ApproxSimple

2

端点など、代表となる点だけを返す

ApproxTC89L1

3

TC89L1アルゴリズムで近似する

ApproxTC89KCOS

4

TC89KCOSアルゴリズムで近似する

Cv2.DrawContours

Cv2.DrawContours(image, contours, contourIdx, color, [thickness], [lineType], [hierarchy], [maxLevel], [offset]);

変数

内容

image

Mat

輪郭を書き込む画像データ(出力先)

contours

OpenCvSharp.Point[][]

輪郭を表す点のデータ

contourIdx

Int32

画像に書き込む輪郭のインデックス。負の値の場合は全ての輪郭。

color

OpenCvSharp.Scalar

輪郭を書き込むときの色

thickness

Int32

省略可。規定値は1。輪郭線の幅。

lineType

OpenCvSharp.LineTypes

省略可。規定値はLineTypes.Link8。線の種類。

hierarchy

Mat

省略可。規定値はnull。

maxLevel

Int32

省略可。規定値は2147483647。

offset

Nullable<Point>

省略可。規定値はnull。

imageに指定したMat画像データに、contoursに指定した輪郭を書き込みます。imageに指定した画像そのものが書き換わりますので注意してください。contourIdxを-1にすると、全てのcontoursが書き込まれます。

colorには、OpenCvSharp.Scalarクラスのインスタンスを指定します。

new OpenCvSharp.Scalar(0,0,255)

のようにBGRの色を数値で指定するか、またはScalarクラスのフィールドに色名が規定されているので

OpenCvSharp.Scalar.Red

のように色名で指定します。

公開日

広告