今回は画像の中にあるオブジェクトの輪郭を抽出し、抽出した輪郭の面積を求める方法をご紹介します。
画像からオブジェクトの輪郭の抽出や面積を出すことにより
・輪郭の照合による判定
・面積による判定
といった場合に活用できると思いますので、そういった課題がある方に読んで頂けたらと思います。
「ココナラ」でC#、OpenCvSharpを使った画像処理アプリの作成を行っています。
もし、作成依頼やお困りごとがあればお気軽にご相談ください!

前準備
Visual Studio 2019のインストール
下記にインストール方法をまとめたので参考にしてください。

OpenCvSharpのインストール
下記にインストール方法をまとめたので参考にしてください。前準備にインストール方法が記載してあります

ソースコードと解説
処理内容
①画像読込み
②グレースケール化
③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;
using OpenCvSharp.Extensions;
using System.IO;
namespace ImageProcessing
{
public partial class Form1 : Form
{
private Mat _srcMat;
private Mat _thresholdImege;
public Form1()
{
InitializeComponent();
//PictureBoxのサイズに合わせて表示
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
pictureBox2.SizeMode = PictureBoxSizeMode.StretchImage;
}
private void button1_Click_1(object sender, EventArgs e)
{
//画像の読込み
_srcMat = Cv2.ImRead(@"C:\Users\uchiy\Desktop\輪郭抽出.jpg");
pictureBox1.Image = BitmapConverter.ToBitmap(_srcMat);
//グレースケール
var grayMat = _srcMat.CvtColor(ColorConversionCodes.BGR2GRAY);
//2値化
_thresholdImege = grayMat.Threshold(230, 255, ThresholdTypes.BinaryInv);
pictureBox2.Image = BitmapConverter.ToBitmap(_thresholdImege);
}
private void button2_Click_1(object sender, EventArgs e)
{
//ジャグ配列
OpenCvSharp.Point[][] contours;
OpenCvSharp.HierarchyIndex[] hierarchyIndexes;
//輪郭抽出
_thresholdImege.FindContours(out contours, out hierarchyIndexes, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
//輪郭を赤枠で囲う
_srcMat.DrawContours(contours, -1, Scalar.Red,2);
pictureBox2.Image = BitmapConverter.ToBitmap(_srcMat);
string areas = null;
var count = 1;
foreach (var contour in contours)
{
//面積を算出
var area = Cv2.ContourArea(contour);
if (count < contours.Count())
{
areas = areas + area + " ";
}
else
{
areas = areas + area;
}
count++;
}
textBox1.Text = areas;
}
}
}
次に詳細の解説をしていきます。
画像の呼び出しから2値化まで
OpenCvSharpにて輪郭を抽出する際には下記の条件で検出が行われています。
背景:黒
オブジェクト:白
そのため、2値化をする際に背景が黒、検出したいオブジェクトが白になるように2値化しなければなりません。
今回の画像で通常のBinaryで2値化をすると背景が白、オブジェクトが黒となり、画像の外枠が検出されてしまうため、BinaryInvにて2値化を行います。
【 Binary 】

【 BinaryInv 】

private void button1_Click_1(object sender, EventArgs e)
{
//画像の読込み
_srcMat = Cv2.ImRead(@"C:\Users\uchiy\Desktop\輪郭抽出.jpg");
pictureBox1.Image = BitmapConverter.ToBitmap(_srcMat);
//グレースケール
var grayMat = _srcMat.CvtColor(ColorConversionCodes.BGR2GRAY);
//2値化
_thresholdImege = grayMat.Threshold(230, 255, ThresholdTypes.BinaryInv);
pictureBox2.Image = BitmapConverter.ToBitmap(_thresholdImege);
}
輪郭抽出と面積算出
輪郭座標抽出:FindContours
輪郭描画:DrawContours
面積算出:ContourArea
①FindContours にて全ての輪郭を抽出します。
今回は外枠だけを抽出したかったため、RetrievalModesをExternalにしましたが、
内側の輪郭も抽出したい場合は RetrievalModes.Listにすることにより四角の内側の
輪郭も抽出することができます。
②DrawContours にて抽出した全ての輪郭を描画します。
③ContourArea にて抽出した輪郭の面積を算出します。
今回は配列に主力された輪郭座標をforeachにて順番に面積計算をさせています。

private void button2_Click_1(object sender, EventArgs e)
{
//ジャグ配列
OpenCvSharp.Point[][] contours;
OpenCvSharp.HierarchyIndex[] hierarchyIndexes;
//輪郭抽出
_thresholdImege.FindContours(out contours, out hierarchyIndexes, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
//輪郭を赤枠で囲う
_srcMat.DrawContours(contours, -1, Scalar.Red,2);
pictureBox2.Image = BitmapConverter.ToBitmap(_srcMat);
string areas = null;
var count = 1;
foreach (var contour in contours)
{
//面積を算出
var area = Cv2.ContourArea(contour);
if (count < contours.Count())
{
areas = areas + area + " ";
}
else
{
areas = areas + area;
}
count++;
}
textBox1.Text = areas;
}
まとめ
以上が、 画像の中にあるオブジェクトの輪郭を抽出し、抽出した輪郭の面積を求める方法 となります。
輪郭を検出させることにより、2値化をし面積を求めるといったやり方では対応できなかった部分(外乱などによる色合いのばらつき)など、解決できる幅が広がると思います。
今回は絵の検出をしました、次回はWebカメラを接続させ、物の検出を行いたいと思います。
ご興味がある方は、今後も是非ご覧いただけたらと思います。
最後までご覧いただきありがとうございました!
C#で画像処理を学ぶためのおススメの書籍
C#でOpenCvを扱う方法などを詳しく解説してくれています。
C#で画像処理を解説してくれている本がほとんどない中、こちらの書籍はいろいろなメソッドの使い方等を事例を交えて解説してくれているため、非常に参考になります。
私はこちらの書籍を参考に画像処理を実装しました。
是非参考にしてみてください。
C#の基本が学習したいという方におススメのスクール:侍エンジニア塾のエキスパートコース