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.DrawContours — 検出した輪郭を画像に描画する(本記事でも使用)
- Cv2.ContourArea — 輪郭の面積を計算する
- Cv2.BoundingRect — 輪郭を囲む軸平行な矩形を取得する
- Cv2.Threshold — FindContours の前処理として必須の二値化関数。詳しくは Cv2.Threshold の使い方【OpenCV/C#(OpenCvSharp)】〜画像を二値化する(大津の方法含む)〜 を参照
- Cv2.AdaptiveThreshold — 照明ムラがある場面での前処理に有効。詳しくは Cv2.AdaptiveThreshold の使い方【OpenCV/C#(OpenCvSharp)】 を参照
- ノイズが多い画像で輪郭を安定させたいときは、前段に Cv2.GaussianBlur や Cv2.BilateralFilter を挟むとよいです
まとめ
Cv2.FindContours は二値化画像から物体の輪郭点列を取得する関数です。RetrievalModes と ContourApproximationModes を目的に合わせて選ぶことで、最外郭のみの取得や点数の削減が可能です。取得した輪郭は ContourArea・BoundingRect と組み合わせることで、面積フィルタや位置取得といった実務処理に即座に応用できます。
🛠 画像処理のプロが開発するSDK/API
本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/APIを開発しています。SDK は C#/.NET 8 に正式対応しており、OpenCvSharp を使うプロジェクトからそのまま組み込めます。
→ 顔認証のなりすまし対策 設計チェックリスト(当ブログ)
→ なりすまし判定SDK/APIの詳細を見る
→ API仕様書・サンプルコード

