Cv2.FindContours の使い方【OpenCV/C#(OpenCvSharp)】〜輪郭を検出する〜

OpenCV for C#
📌 準備: OpenCV の環境構築がまだの方はこちら → C++ 環境構築ガイド / C#(OpenCvSharp)セットアップ

Cv2.FindContours の使い方【OpenCV/C#(OpenCvSharp)】〜輪郭を検出する〜

Cv2.FindContours は、二値化画像から物体の輪郭(境界線)を検出する関数です。検出した輪郭を使えば、物体の個数カウント・バウンディングボックスの取得・形状フィルタリングなど、多くの実務処理につなげられます。

最小の呼び出し形は次のとおりです。

Cv2.FindContours(binary, out Point[][] contours, out HierarchyIndex[] hierarchy,
    RetrievalModes.External, ContourApproximationModes.ApproxSimple);

動作環境

  • OpenCV 4.x(OpenCvSharp4)
  • .NET 8 コンソールアプリ
  • NuGet: OpenCvSharp4 + OpenCvSharp4.runtime.win(Windows 実行時)

環境構築がまだの方は環境構築ガイドを先にどうぞ。


基本の使い方

下記サンプルでは、白い矩形が複数描かれたグレー画像を生成し、輪郭を検出して元画像に描画します。

using OpenCvSharp;

// 描画先のカラー画像と、輪郭検出に使う二値化画像を用意する
using var src = new Mat(400, 600, MatType.CV_8UC3, Scalar.Black);

// いくつかの白い矩形を描いてテスト用シーンを作成
Cv2.Rectangle(src, new Rect(50,  50,  150, 100), Scalar.White, -1);
Cv2.Rectangle(src, new Rect(260, 80,  100, 200), Scalar.White, -1);
Cv2.Rectangle(src, new Rect(420, 200, 130, 150), Scalar.White, -1);

// グレースケール変換 → 二値化(FindContours の入力は 8UC1 二値画像)
using var gray   = new Mat();
using var binary = new Mat();
Cv2.CvtColor(src, gray, ColorConversionCodes.BGR2GRAY);
Cv2.Threshold(gray, binary, 127, 255, ThresholdTypes.Binary);

// 輪郭検出
// RetrievalModes.External   : 最外郭のみ取得(入れ子の内側は無視)
// ContourApproximationModes.ApproxSimple : 直線部分の中間点を省略して頂点のみ保持
Cv2.FindContours(
    binary,
    out Point[][] contours,
    out HierarchyIndex[] hierarchy,
    RetrievalModes.External,
    ContourApproximationModes.ApproxSimple);

Console.WriteLine($"検出した輪郭の数: {contours.Length}");

// 検出した全輪郭を緑色で描画(-1 を指定すると全インデックスを一括描画)
using var dst = src.Clone();
Cv2.DrawContours(dst, contours, -1, Scalar.LimeGreen, 2);

// 各輪郭の面積とバウンディングボックスをコンソールに出力
for (int i = 0; i < contours.Length; i++)
{
    double area = Cv2.ContourArea(contours[i]);
    Rect   bbox = Cv2.BoundingRect(contours[i]);
    Console.WriteLine($"  [{i}] 面積={area:F0}  bbox={bbox}");
}

Cv2.ImWrite("result.png", dst);
Console.WriteLine("result.png に保存しました。");

実行結果(コンソール):

検出した輪郭の数: 3
  [0] 面積=15000  bbox={X=50,Y=50,Width=150,Height=100}
  [1] 面積=20000  bbox={X=260,Y=80,Width=100,Height=200}
  [2] 面積=19500  bbox={X=420,Y=200,Width=130,Height=150}
result.png に保存しました。

result.png には、3 つの矩形の外周に緑色の輪郭線が描かれた画像が出力されます。

コードの要点

行の目的 ポイント
CvtColor でグレースケール変換 FindContours の入力は CV_8UC1(グレー or 二値)でなければならない
Threshold で二値化 背景が黒・物体が白になるよう値を設定する
RetrievalModes.External 最外郭のみ。内側に穴がある形状でも外側のみ返る
ContourApproximationModes.ApproxSimple 矩形なら4点だけ返る。ApproxNone にすると全画素が返り点数が増える
out Point[][] contours 各輪郭が Point[] として格納された配列
out HierarchyIndex[] hierarchy 輪郭同士の親子・兄弟関係(External のみ検出時は不要なことが多い)

引数と戻り値

void Cv2.FindContours(
    InputArray  image,
    out Point[][] contours,
    out HierarchyIndex[] hierarchy,
    RetrievalModes       mode,
    ContourApproximationModes method,
    Point?               offset = null)
引数 型 説明
image InputArray 入力二値画像(CV_8UC1)。処理後に内容が変更される場合があるため Clone を渡すことを推奨
contours out Point[][] 検出された輪郭の配列。各要素が1輪郭の点列
hierarchy out HierarchyIndex[] 輪郭の階層情報(Next, Previous, FirstChild, Parent)
mode RetrievalModes 輪郭の取得モード(下表参照)
method ContourApproximationModes 輪郭の近似方法(下表参照)
offset Point? 輪郭点に加算するオフセット(ROI 処理時に使用)

RetrievalModes の主な選択肢:

値 意味
External 最外郭のみ取得
List 全輪郭を取得(階層なし)
CComp 2階層(外郭と穴)で取得
Tree 全階層を再帰的に取得

ContourApproximationModes の主な選択肢:

値 意味
ApproxNone 輪郭上の全点を保持
ApproxSimple 直線部分の端点のみ保持(点数が少なくなる)
ApproxTC89L1 Teh-Chin チェーン近似

実践例

実践例 1: 面積フィルタで小さなノイズ輪郭を除去する

using OpenCvSharp;

// 実画像の読み込みを想定(ここではテスト用に生成)
using var src = new Mat(300, 400, MatType.CV_8UC3, Scalar.Black);
Cv2.Rectangle(src, new Rect(20,  20,  200, 150), Scalar.White, -1);
Cv2.Rectangle(src, new Rect(250, 200, 10,  10),  Scalar.White, -1); // 小さなノイズ

using var gray   = new Mat();
using var binary = new Mat();
Cv2.CvtColor(src, gray, ColorConversionCodes.BGR2GRAY);
Cv2.Threshold(gray, binary, 127, 255, ThresholdTypes.Binary);

Cv2.FindContours(binary, out Point[][] contours, out _,
    RetrievalModes.External, ContourApproximationModes.ApproxSimple);

// 面積が 500px² 以上の輪郭だけを残す
double minArea = 500.0;
var filtered = contours.Where(c => Cv2.ContourArea(c) >= minArea).ToArray();

using var dst = src.Clone();
Cv2.DrawContours(dst, filtered, -1, Scalar.Cyan, 2);
Cv2.ImWrite("filtered.png", dst);
Console.WriteLine($"全輪郭: {contours.Length}  フィルタ後: {filtered.Length}");
全輪郭: 2  フィルタ後: 1

面積 500px² 未満のノイズ輪郭を除去し、大きな矩形だけが残ります。


実践例 2: 各輪郭にバウンディングボックスを描画して番号を振る

using OpenCvSharp;

using var src = new Mat(400, 600, MatType.CV_8UC3, Scalar.Black);
Cv2.Circle(src,  new Point(100, 200), 80,  Scalar.White, -1);
Cv2.Circle(src,  new Point(350, 150), 60,  Scalar.White, -1);
Cv2.Rectangle(src, new Rect(430, 250, 120, 100), Scalar.White, -1);

using var gray   = new Mat();
using var binary = new Mat();
Cv2.CvtColor(src, gray, ColorConversionCodes.BGR2GRAY);
Cv2.Threshold(gray, binary, 127, 255, ThresholdTypes.Binary);

Cv2.FindContours(binary, out Point[][] contours, out _,
    RetrievalModes.External, ContourApproximationModes.ApproxSimple);

using var dst = src.Clone();
for (int i = 0; i < contours.Length; i++)
{
    Rect bbox = Cv2.BoundingRect(contours[i]);
    // バウンディングボックスを黄色で描画
    Cv2.Rectangle(dst, bbox, Scalar.Yellow, 2);
    // 番号をバウンディングボックスの左上に描画
    Cv2.PutText(dst, $"#{i}", bbox.TopLeft,
        HersheyFonts.HersheySimplex, 0.7, Scalar.Yellow, 2);
}

Cv2.ImWrite("bbox.png", dst);
Console.WriteLine($"検出数: {contours.Length}");
検出数: 3

円2つと矩形1つにそれぞれ番号付きのバウンディングボックスが描かれた bbox.png が出力されます。


つまずきポイント

⚠️ 入力画像が CV_8UC1 でなければならない

FindContours の入力は必ずグレースケールまたは二値化済みの CV_8UC1 画像です。カラー画像(CV_8UC3)をそのまま渡すと実行時エラーが発生します。CvtColor → Threshold(または AdaptiveThreshold)の順で前処理してから渡してください。

⚠️ out Point[][] のメモリ解放は不要だが Mat の using は必須

contours や hierarchy は C# の管理メモリ上に展開されるため Dispose は不要です。一方、処理中に使う Mat(gray・binary・dst など)は using で確実に解放してください。Mat の解放漏れはネイティブメモリリークに直結します。

⚠️ 元画像が書き換えられる場合がある

OpenCV 4.x 時点の実装では FindContours が入力 image の内容を内部で変更することがあります。元画像を描画等に流用したい場合は、binary.Clone() を渡すか、処理後に再度二値化し直すのが安全です。


関連する関数


まとめ

Cv2.FindContours は二値化画像から物体の輪郭点列を取得する関数です。RetrievalModes と ContourApproximationModes を目的に合わせて選ぶことで、最外郭のみの取得や点数の削減が可能です。取得した輪郭は ContourArea・BoundingRect と組み合わせることで、面積フィルタや位置取得といった実務処理に即座に応用できます。

🛠 画像処理のプロが開発するSDK/API

本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/APIを開発しています。SDK は C#/.NET 8 に正式対応しており、OpenCvSharp を使うプロジェクトからそのまま組み込めます。

→ 顔認証のなりすまし対策 設計チェックリスト(当ブログ)
→ なりすまし判定SDK/APIの詳細を見る
→ API仕様書・サンプルコード

タイトルとURLをコピーしました