C#で画像の輪郭を抽出する
C#でOpenCvSharp4を使って、画像の輪郭を抽出します。
目次
OpenCvSharpをWPFアプリケーションで使う準備
OpenCvSharpをWPFアプリに適用するには、NuGetでいくつかのパッケージをインストールする必要があります。
Visual Studioのプロジェクトメニューの中のNuGetパッケージ管理から、下記を追加してください。
OpenCvSharp4
OpenCvSharp4.runtime.win
OpenCvSharp4.WpfExtensions
輪郭を抽出する手順
OpenCVのfindcontoursの機能を使って、画像から輪郭を抽出します。
大凡の手順は下記です。
画像をMat形式で読み込み、またはMat形式へ変換する。
画像をグレイスケールに変換する。
グレイスケール画像を白黒画像に変換する。
白黒画像から輪郭を抽出する。
輪郭を抽出してみる
実際に輪郭を抽出してみましょう。
サンプルとして、下図の図形の輪郭を抽出してみます。
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メソッドで、輪郭を指定の画像に描画します。
抽出した輪郭を全て表示してみます。
緑色の円の内側のさらに円形に切り抜かれた部分も輪郭が抽出されています。
Cv2.FindContoursメソッドのRetrievalModesをListからExternalに変えると、外側部分だけの輪郭が抽出されます。
Cv2.FindContours(bw_mat, out contours, out hindex, RetrievalModes.External, ContourApproximationModes.ApproxNone);
表示してみると、緑色の円形の内側の切り抜き部分の輪郭が抽出されていないことがわかります。
抽出した輪郭のデータは、輪郭毎にひとまとまりになっています。
Cv2.DrawContoursメソッドの3番目の引数を0にすると、下図のようになります。緑色の円形の輪郭ですね。
Cv2.DrawContoursメソッドの3番目の引数を1にすると、下図のようになります。茶色の棒の輪郭ですね。
主なメソッド
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
のように色名で指定します。
公開日
広告
C#で画像処理カテゴリの投稿
- C#でBitmapImageをByte配列に変換してみた
- C#でBitmapで描いた画像をImageコントロールに表示してみた
- C#でHSBで色指定してラスタ画像を描いてみた
- C#でOpenCVを使う
- C#でラスタ画像を描いてみた
- C#で写真の中の線を抽出してみた
- C#で描いた画像をファイルに保存してみた
- C#で画像の色を反転してみた
- C#で画像の輪郭を抽出する
- C#で画像を描いてみた(WPFでBitmapSource.Create編)
- C#で画像を描いてみた(WPFでWritableBitmap編)
- C#で画像ファイルを表示してみた
- C#のOpenCvSharpでMatとBitmapSourceを変換する
- C#のWPFアプリでPNG形式の画像ファイルの読み込みと書き出しをする