cv::approxPolyDP の使い方【OpenCV/C++】〜輪郭を多角形近似する〜

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

cv::approxPolyDP の使い方【OpenCV/C++】〜輪郭を多角形近似する〜

cv::approxPolyDP は、輪郭(点列)を少ない頂点の多角形に近似する関数です。cv::findContours で取得した輪郭に適用し、三角形・四角形・多角形の検出や、輪郭の簡略化に使います。

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

std::vector<cv::Point> approx;
cv::approxPolyDP(contour, approx, epsilon, true);

動作環境

  • OpenCV 4.x
  • コンパイル例: g++ -std=c++17 main.cpp $(pkg-config --cflags --libs opencv4)

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


基本の使い方

二値画像から輪郭を抽出し、各輪郭を多角形近似して頂点数を表示する例です。

#include <iostream>
#include <vector>
#include <opencv2/opencv.hpp>

int main()
{
    // グレースケールで読み込み
    cv::Mat src = cv::imread("shapes.png", cv::IMREAD_GRAYSCALE);
    if (src.empty()) {
        std::cerr << "画像を読み込めませんでした" << std::endl;
        return 1;
    }

    // 二値化
    cv::Mat binary;
    cv::threshold(src, binary, 127, 255, cv::THRESH_BINARY);

    // 輪郭抽出
    std::vector<std::vector<cv::Point>> contours;
    cv::findContours(binary, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);

    cv::Mat dst;
    cv::cvtColor(src, dst, cv::COLOR_GRAY2BGR);

    for (size_t i = 0; i < contours.size(); ++i) {
        // epsilon = 輪郭周長の 2% を目安に設定
        double epsilon = 0.02 * cv::arcLength(contours[i], true);
        std::vector<cv::Point> approx;
        cv::approxPolyDP(contours[i], approx, epsilon, true);

        // 近似後の頂点数を表示
        std::cout << "輪郭 " << i << ": " << approx.size() << " 頂点" << std::endl;

        // 近似多角形を描画
        cv::polylines(dst, approx, true, cv::Scalar(0, 0, 255), 2);
    }

    cv::imwrite("result.png", dst);
    return 0;
}

実行結果

輪郭 0: 3 頂点
輪郭 1: 4 頂点
輪郭 2: 8 頂点

shapes.png に三角形・四角形・円(近似で多角形)が含まれている場合、上のような出力が得られます。result.png には各輪郭の近似多角形が赤線で描画されます。

各処理の解説

内容
cv::threshold 二値化。輪郭抽出の前処理として必須
cv::findContours 輪郭の点列を取得
cv::arcLength(contours[i], true) 輪郭の周長を計算。epsilon の基準値として使う
cv::approxPolyDP(contours[i], approx, epsilon, true) 輪郭を多角形近似。結果は approx に格納される
cv::polylines 近似後の多角形を描画

引数と戻り値

void cv::approxPolyDP(
    InputArray  curve,       // 入力輪郭(点列)
    OutputArray approxCurve, // 近似後の点列(出力)
    double      epsilon,     // 近似精度(許容距離)
    bool        closed       // 輪郭を閉じるか
);
引数 説明
curve InputArray 入力の点列。std::vector<cv::Point> が典型
approxCurve OutputArray 近似後の点列。型は入力に合わせて自動決定
epsilon double 元の輪郭と近似曲線の最大距離。小さいほど忠実・大きいほど粗い
closed bool true で閉じた輪郭として処理(輪郭検出結果には通常 true

戻り値はありません(void)。近似結果は approxCurve に書き込まれます。

epsilon の選び方

epsilon は「元の輪郭上の点と近似後の線分との最大許容距離(ピクセル)」です。固定値で指定することもできますが、cv::arcLength の割合で指定するのが実務での定番です。

double epsilon = 0.02 * cv::arcLength(contour, true);  // 周長の 2%
  • 割合を小さくする(0.01 以下): 元の輪郭に忠実・頂点数が多い
  • 割合を大きくする(0.05 以上): 粗い近似・頂点数が少ない
  • 矩形検出なら 0.02〜0.05 が目安

実践例

矩形(四角形)の検出

輪郭を近似したあと頂点数が 4 なら四角形と判定するパターンです。

#include <iostream>
#include <vector>
#include <opencv2/opencv.hpp>

int main()
{
    cv::Mat src = cv::imread("shapes.png", cv::IMREAD_GRAYSCALE);
    if (src.empty()) {
        std::cerr << "画像を読み込めませんでした" << std::endl;
        return 1;
    }

    cv::Mat binary;
    cv::threshold(src, binary, 127, 255, cv::THRESH_BINARY);

    std::vector<std::vector<cv::Point>> contours;
    cv::findContours(binary, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);

    cv::Mat dst;
    cv::cvtColor(src, dst, cv::COLOR_GRAY2BGR);

    for (const auto& contour : contours) {
        // 面積が小さいノイズは除外
        if (cv::contourArea(contour) < 500.0) continue;

        double epsilon = 0.03 * cv::arcLength(contour, true);
        std::vector<cv::Point> approx;
        cv::approxPolyDP(contour, approx, epsilon, true);

        if (approx.size() == 4) {
            // 四角形として緑で描画
            cv::polylines(dst, approx, true, cv::Scalar(0, 255, 0), 2);
            std::cout << "四角形を検出しました" << std::endl;
        } else if (approx.size() == 3) {
            // 三角形として青で描画
            cv::polylines(dst, approx, true, cv::Scalar(255, 0, 0), 2);
            std::cout << "三角形を検出しました" << std::endl;
        }
    }

    cv::imwrite("detected.png", dst);
    return 0;
}

cv::contourArea で面積を事前フィルタリングすることで、ノイズ由来の微小輪郭を除外できます。エッジ検出を前処理に使う場合はcv::Canny の使い方【OpenCV/C++】も参照してください。

輪郭の前処理としてモルフォロジー処理を使う

実画像では輪郭がノイズで乱れていることが多く、cv::approxPolyDP の前にモルフォロジー処理で輪郭を整えると検出精度が上がります。詳しくはcv::morphologyEx の使い方【OpenCV/C++】をご覧ください。


つまずきポイント

⚠️ epsilon に 0 や負の値を指定するとクラッシュする

epsilon に 0 以下の値を渡すと assertion エラーで落ちます。cv::arcLength の結果が 0 になる(点数が 1〜2 の退化した輪郭)ケースで起こりやすいので、呼び出し前に輪郭の点数や面積を確認してください。

if (contour.size() < 3 || cv::contourArea(contour) < 1.0) continue;

⚠️ approx の型を std::vector<cv::Point2f> と std::vector<cv::Point> で混在させる

cv::findContours の出力は std::vector<cv::Point>(整数座標)です。approxPolyDP の入出力型は入力に合わせて推論されるため、入力が cv::Point なら出力も cv::Point になります。cv::Point2f で受け取りたい場合は入力も std::vector<cv::Point2f> に変換してから渡す必要があります。型の不一致は実行時 assertion で検出されます。

⚠️ closed = false で渡すと開いた折れ線として近似される

輪郭検出結果(閉曲線)に closed = false を渡すと、始点と終点を結ぶ辺が考慮されず頂点数の判定が変わります。cv::findContours の結果には必ず closed = true を使ってください。


関連する関数


まとめ

cv::approxPolyDPcv::findContours とセットで使い、輪郭の頂点数から図形を判別する実装の定番です。epsilon は周長の 2〜3% を起点に調整し、面積フィルタと組み合わせることでノイズに強い検出が実現できます。

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

本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/API(C++製・OpenCV 4.10)を開発しています。顔認証システムへの組み込み実績多数。

→ なりすまし判定SDK/APIの詳細を見る
→ API仕様書・サンプルコード

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