Cv2.ApproxPolyDP の使い方【OpenCV/C#(OpenCvSharp)】〜輪郭を多角形近似する〜

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

Cv2.ApproxPolyDP は、輪郭(点列)をより少ない頂点の多角形に近似する関数です。ノイズで凸凹した輪郭を「三角形」「四角形」として扱いたいときに不可欠です。最小限の呼び出しは次のとおりです。

Point[] approx = Cv2.ApproxPolyDP(contour, epsilon, closed: true);

動作環境

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

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

基本の使い方

下記プログラムは白い矩形を描いた画像を用意し、その輪郭を FindContours で取得してから ApproxPolyDP で多角形近似します。

using OpenCvSharp;

// 512×512 の黒画像に白い矩形を描く
using var src = new Mat(512, 512, MatType.CV_8UC1, Scalar.Black);
Cv2.Rectangle(src, new Rect(100, 100, 300, 200), Scalar.White, thickness: -1);

// 輪郭検出
Cv2.FindContours(
    src,
    out Point[][] contours,
    out HierarchyIndex[] hierarchy,
    RetrievalModes.External,
    ContourApproximationModes.ApproxSimple);

if (contours.Length == 0)
{
    Console.WriteLine("輪郭が見つかりませんでした。");
    return;
}

// 最初の輪郭を多角形近似
Point[] contour = contours[0];

// epsilon = 輪郭周長の 2% を目安にする
double epsilon = 0.02 * Cv2.ArcLength(contour, closed: true);
Point[] approx = Cv2.ApproxPolyDP(contour, epsilon, closed: true);

Console.WriteLine($"元の頂点数: {contour.Length}");
Console.WriteLine($"近似後の頂点数: {approx.Length}");

// 近似結果を描画して確認
using var dst = new Mat(512, 512, MatType.CV_8UC3, Scalar.Black);
// 近似多角形を描画(InputArray.Create で Point[] を渡す)
Cv2.Polylines(dst, new[] { approx }, isClosed: true, color: Scalar.LimeGreen, thickness: 2);
Cv2.ImWrite("approx_result.png", dst);
Console.WriteLine("approx_result.png を保存しました。");

実行すると次のように表示されます。

元の頂点数: 4
近似後の頂点数: 4
approx_result.png を保存しました。

矩形なので元から 4 頂点ですが、実際のカメラ画像や複雑な輪郭では頂点数が大幅に削減されます。

行ごとの解説

箇所 説明
Cv2.ArcLength(contour, closed: true) 輪郭の周長を取得。epsilon 算出の基準に使う
epsilon = 0.02 * arcLength 周長の 2% が定番の初期値。値が大きいほど粗い近似になる
Cv2.ApproxPolyDP(contour, epsilon, true) Douglas-Peucker アルゴリズムで点列を間引く
approx.Length 近似後の頂点数。4 なら四角形、3 なら三角形と判定できる

引数と戻り値

引数 型 説明
curve IEnumerable<Point> 近似対象の点列(輪郭)
epsilon double 近似精度。元の曲線と近似曲線の最大距離(px)。値が大きいほど粗くなる
closed bool true で閉じた多角形として扱う

戻り値: Point[] — 近似後の頂点配列

⚠️ epsilon は絶対値(ピクセル数)です。固定値を使うと解像度変化に弱くなるため、ArcLength の割合で指定するのが実務での定番です。

実践例

四角形・三角形の自動判定

実際の業務でよく使うパターンです。検出した輪郭を多角形近似し、頂点数で形状を分類します。

using OpenCvSharp;

// テスト画像: 三角形と四角形を描画
using var src = new Mat(512, 512, MatType.CV_8UC1, Scalar.Black);
// 四角形
Cv2.Rectangle(src, new Rect(50, 50, 150, 100), Scalar.White, thickness: -1);
// 三角形
Point[] triangle = [new Point(300, 400), new Point(450, 400), new Point(375, 250)];
Cv2.FillPoly(src, new[] { triangle }, Scalar.White);

// 輪郭検出
Cv2.FindContours(
    src,
    out Point[][] contours,
    out HierarchyIndex[] _,
    RetrievalModes.External,
    ContourApproximationModes.ApproxSimple);

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

foreach (Point[] contour in contours)
{
    // 小さすぎる輪郭はノイズとして除外
    if (Cv2.ContourArea(contour) < 500) continue;

    double epsilon = 0.02 * Cv2.ArcLength(contour, closed: true);
    Point[] approx = Cv2.ApproxPolyDP(contour, epsilon, closed: true);

    string label = approx.Length switch
    {
        3 => "Triangle",
        4 => "Rectangle",
        _ => $"Polygon({approx.Length})"
    };

    // 重心を求めてラベル表示
    var m = Cv2.Moments(contour);
    int cx = (int)(m.M10 / m.M00);
    int cy = (int)(m.M01 / m.M00);

    Cv2.Polylines(dst, new[] { approx }, isClosed: true, color: Scalar.LimeGreen, thickness: 2);
    Cv2.PutText(dst, label, new Point(cx - 40, cy),
        HersheyFonts.HersheySimplex, 0.6, Scalar.Yellow, 2);

    Console.WriteLine($"{label}: 頂点={approx.Length}, 面積={Cv2.ContourArea(contour):F0}px²");
}

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

実行すると次のように表示されます。

Rectangle: 頂点=4, 面積=15000px²
Triangle: 頂点=3, 面積=11250px²
shape_detection.png を保存しました。

形状ラベルが緑の多角形と黄色テキストで描画された画像が出力されます。

つまずきポイント

✅ epsilon の単位はピクセル(絶対値)

epsilon はピクセル単位の絶対距離です。0.02 などの小数をそのまま渡しても ほぼ 0px として扱われ、元の輪郭とほぼ変わらない結果になります。必ず Cv2.ArcLength と掛け合わせた値を渡してください。

// NG: epsilon が実質 0 に近い
Point[] approx = Cv2.ApproxPolyDP(contour, 0.02, true);

// OK: 周長の 2% をピクセル数として渡す
double epsilon = 0.02 * Cv2.ArcLength(contour, closed: true);
Point[] approx = Cv2.ApproxPolyDP(contour, epsilon, true);

✅ InputArray / OutputArray の型変換

C++ では std::vector<cv::Point> を直接渡せますが、OpenCvSharp では Point[] や IEnumerable<Point> を受け取るオーバーロードが用意されています。Mat を渡そうとすると実行時エラーになるため、FindContours で得た Point[][] の要素をそのまま渡すのが最も安全です。

✅ closed の指定を忘れない

closed: false にすると開いた曲線として近似され、輪郭の始点と終点が接続されません。物体の外形を扱う場合は必ず true を指定してください。ArcLength の closed 引数と合わせておかないと epsilon の計算値もずれます。

関連する関数

まとめ

Cv2.ApproxPolyDP は輪郭の頂点を Douglas-Peucker アルゴリズムで間引き、シンプルな多角形に変換します。epsilon は ArcLength の割合(2〜5% が目安)で指定するのが実務の定番です。頂点数を見るだけで三角形・四角形などの形状判定ができるため、物体検出や帳票認識のパイプラインで頻繁に活躍します。

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

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

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

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