Cv2.ContourArea / Cv2.ArcLength の使い方【OpenCV/C#(OpenCvSharp)】〜輪郭の面積・周長を求める〜

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

Cv2.ContourArea / Cv2.ArcLength の使い方【OpenCV/C#(OpenCvSharp)】〜輪郭の面積・周長を求める〜

Cv2.ContourArea は輪郭の面積(ピクセル数)、Cv2.ArcLength は輪郭の周長を返します。Cv2.FindContours で取得した輪郭をフィルタリングする際に最もよく使う関数です。最小限のコードは次のとおりです。

double area   = Cv2.ContourArea(contour);
double length = Cv2.ArcLength(contour, closed: true);

動作環境

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

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


基本の使い方

下記プログラムは、グレースケール画像を二値化して輪郭を検出し、各輪郭の面積と周長をコンソールに出力します。

using OpenCvSharp;

// --- 入力画像の準備(グレースケール)---
using var src = new Mat("input.png", ImreadModes.Grayscale);

// --- 二値化 ---
using var bin = new Mat();
Cv2.Threshold(src, bin, thresh: 128, maxval: 255, type: ThresholdTypes.Binary);

// --- 輪郭検出 ---
Cv2.FindContours(
    bin,
    out Point[][] contours,
    out HierarchyIndex[] hierarchy,
    RetrievalModes.External,       // 外側の輪郭のみ
    ContourApproximationModes.ApproxSimple);

// --- 面積・周長を取得して出力 ---
for (int i = 0; i < contours.Length; i++)
{
    // ContourArea: 輪郭に囲まれた領域の面積(ピクセル単位)
    double area = Cv2.ContourArea(contours[i]);

    // ArcLength: 輪郭の周長。第2引数 true で閉じた輪郭として計算
    double length = Cv2.ArcLength(contours[i], closed: true);

    Console.WriteLine($"輪郭[{i}]: 面積={area:F1} px²  周長={length:F1} px");
}

実行結果(例):

輪郭[0]: 面積=4823.5 px²  周長=278.3 px
輪郭[1]: 面積=312.0 px²  周長=72.4 px
輪郭[2]: 面積=51.0 px²  周長=28.3 px

行ごとの解説:

処理 説明
Cv2.Threshold 輪郭検出の前処理として二値化。照明が不均一な場合は Cv2.AdaptiveThreshold も検討
Cv2.FindContours 二値画像から輪郭点列を取得。RetrievalModes.External で最外郭のみ取得
Cv2.ContourArea Point[] を受け取り面積を double で返す
Cv2.ArcLength 第2引数 closed が true のとき始点と終点を結んで周長を計算

引数と戻り値

Cv2.ContourArea

引数 型 説明
contour InputArray(Point[] 等) 輪郭点列
oriented bool(省略可・既定 false) true にすると符号付き面積を返す(向きの判定に使用)
戻り値 double 輪郭の面積(ピクセル単位)

Cv2.ArcLength

引数 型 説明
curve InputArray(Point[] 等) 輪郭点列
closed bool true: 閉じた曲線として計算、false: 開いた曲線
戻り値 double 周長(ピクセル単位)

⚠️ ContourArea は輪郭の面積であり「輪郭上のピクセル数」ではありません。countNonZero とは異なります。


実践例

小さなノイズ輪郭を除去して描画する

実務では、二値化後に生じる小さなノイズ輪郭を面積で除外するのが典型的な使い方です。

using OpenCvSharp;

using var src = new Mat("input.png", ImreadModes.Grayscale);
using var bin = new Mat();
Cv2.Threshold(src, bin, 128, 255, ThresholdTypes.Binary);

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

// カラー画像に変換して輪郭を描画する
using var dst = new Mat();
Cv2.CvtColor(src, dst, ColorConversionCodes.GRAY2BGR);

for (int i = 0; i < contours.Length; i++)
{
    double area = Cv2.ContourArea(contours[i]);

    // 面積が 500 px² 未満の輪郭はノイズとして無視
    if (area < 500.0) continue;

    // 残った輪郭を緑で描画
    Cv2.DrawContours(dst, contours, i, color: Scalar.Green, thickness: 2);
    Console.WriteLine($"有効輪郭[{i}]: 面積={area:F1}");
}

Cv2.ImWrite("output.png", dst);

真円度(Circularity)で形状をフィルタリングする

面積と周長を組み合わせると「どれだけ円に近いか」を数値化できます。

circularity = 4π × area / (arcLength²)

完全な円のとき 1.0 に近づき、細長い形状ほど小さくなります。

using OpenCvSharp;

using var src = new Mat("shapes.png", ImreadModes.Grayscale);
using var bin = new Mat();
Cv2.Threshold(src, bin, 128, 255, ThresholdTypes.Binary);

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

using var dst = new Mat();
Cv2.CvtColor(src, dst, ColorConversionCodes.GRAY2BGR);

foreach (var contour in contours)
{
    double area   = Cv2.ContourArea(contour);
    double length = Cv2.ArcLength(contour, closed: true);

    if (area < 100.0 || length < 1.0) continue;

    // 真円度を計算
    double circularity = 4.0 * Math.PI * area / (length * length);

    // 0.8 以上なら円とみなして赤で描画
    Scalar color = circularity >= 0.8 ? Scalar.Red : Scalar.Blue;
    Cv2.DrawContours(dst, new[] { contour }, 0, color, thickness: 2);
    Console.WriteLine($"面積={area:F1}  周長={length:F1}  真円度={circularity:F3}");
}

Cv2.ImWrite("circularity_result.png", dst);

実行すると、円形の輪郭が赤・それ以外が青で描画された画像が circularity_result.png に保存されます。


つまずきポイント

① ArcLength の closed 引数を間違える

Cv2.FindContours で取得した輪郭は通常「閉じた輪郭」です。closed: false で呼ぶと始点〜終点間の距離が含まれず、周長が短く計算されます。FindContours の結果を使う場合は closed: true が正解です。

② 面積 0 の輪郭が混入する

点や線分など面積が 0 の輪郭が検出されることがあります。真円度の計算のように面積や周長で割り算をするときは、if (area < 1.0 || length < 1.0) continue; のようなガードを必ず入れてください。ゼロ除算で NaN や Infinity が混入します。

③ out Point[][] contours の Mat バージョンとの混同

OpenCvSharp では FindContours のオーバーロードに out Mat[] contours 版もあります。こちらを使った場合、各 Mat を Dispose する必要があります。Point[][] 版はマネージド配列なので GC 任せで問題ありませんが、Mat[] 版を使う際は using や foreach での Dispose を忘れないでください。


関連する関数

  • Cv2.FindContours — 輪郭点列を取得する。ContourArea / ArcLength と常にセットで使う
  • Cv2.DrawContours — 取得した輪郭を画像上に描画する
  • Cv2.BoundingRect — 輪郭の外接矩形を取得する(面積フィルタと組み合わせて ROI 切り出しに使う)
  • Cv2.Threshold / Cv2.AdaptiveThreshold — 輪郭検出の前処理。二値化の精度が輪郭品質に直結する。Cv2.Threshold の使い方・Cv2.AdaptiveThreshold の使い方も参照

まとめ

  • Cv2.ContourArea で輪郭面積、Cv2.ArcLength で周長を double で取得できる。
  • 面積による小ノイズ除去と、面積÷周長²による真円度フィルタリングが実務の代表的な用途。
  • ArcLength の closed 引数・ゼロ除算ガード・Mat[] 版の Dispose に注意する。

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

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

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

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