cv::contourArea / cv::arcLength の使い方【OpenCV/C++】〜輪郭の面積・周長を求める〜

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

cv::contourArea / cv::arcLength の使い方【OpenCV/C++】〜輪郭の面積・周長を求める〜

cv::contourArea は輪郭が囲む面積を、cv::arcLength は輪郭の周長(弧長)をピクセル単位で返す関数です。cv::findContours で取得した輪郭に対してこの2関数を組み合わせることで、微小ノイズの除去や対象物体の絞り込みを数行で実装できます。

double area   = cv::contourArea(contour);
double length = cv::arcLength(contour, true); // true = 閉じた輪郭

動作環境

  • OpenCV 4.x
  • C++17
  • ビルド例: g++ -std=c++17 main.cpp $(pkg-config --cflags --libs opencv4) -o main

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


基本の使い方

以下のプログラムは、二値化画像から輪郭を抽出し、各輪郭の面積と周長を標準出力に表示します。

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

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

    // 二値化
    cv::Mat bin;
    cv::threshold(src, bin, 128, 255, cv::THRESH_BINARY);

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

    for (std::size_t i = 0; i < contours.size(); ++i) {
        // 面積(ピクセル数ベース)
        double area = cv::contourArea(contours[i]);

        // 周長(第2引数 true = 閉じた輪郭として計算)
        double len = cv::arcLength(contours[i], true);

        std::cout << "輪郭[" << i << "] 面積=" << area
                  << " px²  周長=" << len << " px" << std::endl;
    }

    return 0;
}

実行結果

輪郭[0] 面積=3247.5 px²  周長=234.87 px
輪郭[1] 面積=512.0  px²  周長=92.28  px
輪郭[2] 面積=88.5   px²  周長=38.18  px

実際の値は入力画像に依存しますが、このように輪郭ごとの面積・周長が列挙されます。

行ごとの解説

処理 ポイント
cv::threshold 二値化しないと findContours が正しく動作しない
cv::RETR_EXTERNAL 外側の輪郭のみ取得。入れ子の輪郭が不要な場合に指定
cv::CHAIN_APPROX_SIMPLE 直線部分の中間点を省略して輪郭点数を削減
cv::contourArea 輪郭内部の面積をシューレースの公式で計算。戻り値は double
cv::arcLength 輪郭の折れ線長を合計。第2引数 true で始点と終点を結んで閉じる

引数と戻り値

cv::contourArea

double cv::contourArea(InputArray contour, bool oriented = false)
引数 説明
contour InputArray 輪郭点列(vector<Point> or vector<Point2f>
oriented bool true にすると符号付き面積を返す。輪郭の向き(時計回り/反時計回り)判定に利用
戻り値 double 面積(ピクセル²)。oriented=false なら常に正の値

cv::arcLength

double cv::arcLength(InputArray curve, bool closed)
引数 説明
curve InputArray 輪郭点列(vector<Point> or vector<Point2f>
closed bool true = 閉じた曲線として計算(始点〜終点間の距離も加算)。false = 開いた折れ線
戻り値 double 周長(ピクセル)

実践例

面積による小物体フィルタリング

検査システムなどでは、面積が小さすぎる輪郭をノイズとして除外するのが定石です。

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

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

    cv::Mat bin;
    cv::threshold(src, bin, 128, 255, cv::THRESH_BINARY);

    std::vector<std::vector<cv::Point>> contours;
    std::vector<cv::Vec4i> hierarchy;
    cv::findContours(bin, contours, hierarchy,
                     cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);

    // 面積でフィルタリング(100px² 未満を除去)
    const double MIN_AREA = 100.0;
    std::vector<std::vector<cv::Point>> filtered;
    for (const auto& c : contours) {
        if (cv::contourArea(c) >= MIN_AREA) {
            filtered.push_back(c);
        }
    }

    // 可視化用にカラー画像に変換
    cv::Mat vis;
    cv::cvtColor(src, vis, cv::COLOR_GRAY2BGR);
    cv::drawContours(vis, filtered, -1, cv::Scalar(0, 255, 0), 2);

    cv::imwrite("result.png", vis);
    std::cout << "有効輪郭数: " << filtered.size() << std::endl;

    return 0;
}

真円度による形状フィルタリング

面積と周長を組み合わせると「真円度」を計算できます。真円度が高い輪郭だけを残すことで、円形の物体を選別できます。

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

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

    cv::Mat bin;
    cv::threshold(src, bin, 128, 255, cv::THRESH_BINARY);

    std::vector<std::vector<cv::Point>> contours;
    std::vector<cv::Vec4i> hierarchy;
    cv::findContours(bin, contours, hierarchy,
                     cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);

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

    for (const auto& c : contours) {
        double area = cv::contourArea(c);
        double len  = cv::arcLength(c, true);

        if (len < 1.0) continue; // ゼロ除算防止

        // 真円度 = 4π × 面積 / 周長²(理想的な円で 1.0 に近づく)
        double circularity = 4.0 * CV_PI * area / (len * len);

        if (circularity > 0.8 && area > 50.0) {
            // 真円度が高い輪郭を赤で描画
            std::vector<std::vector<cv::Point>> tmp = {c};
            cv::drawContours(vis, tmp, -1, cv::Scalar(0, 0, 255), 2);
            std::cout << "円形検出: 面積=" << area
                      << " 真円度=" << circularity << std::endl;
        }
    }

    cv::imwrite("circularity_result.png", vis);
    return 0;
}

真円度の式 4π × 面積 / 周長² は、完全な円では 1.0、細長い形状ほど 0 に近づきます。閾値 0.8 は用途に応じて調整してください。


つまずきポイント

⚠️ contourArea が 0 を返す

findContours に渡す画像が二値化されていない、またはチャンネルが 1 でない場合、輪郭が正しく抽出されず面積が 0 になります。CV_Assert エラーが出なくても結果が空になるケースがあるため、src.type() == CV_8UC1 を確認する習慣をつけてください。

⚠️ arcLength の第2引数の取り違え

closedfalse にすると、始点と終点を結ぶ辺が計算に含まれません。findContours で取得した閉じた輪郭に対して false を渡すと、実際の周長より短い値が返ります。輪郭の周長を求める場合は 必ず true を指定してください。

⚠️ oriented=true にした場合の符号

cv::contourArea(c, true) は輪郭が反時計回りなら正、時計回りなら負の値を返します。面積として使いたい場合は std::abs() を忘れずに。単純に大小比較するコードで符号付きのまま使うとフィルタリングが意図通りに動きません。


関連する関数


まとめ

  • cv::contourArea で輪郭の面積、cv::arcLength で周長をピクセル単位で取得できます
  • 面積閾値によるノイズ除去、面積と周長を組み合わせた真円度フィルタリングが実務でよく使われます
  • arcLengthclosed 引数と contourAreaoriented 引数の挙動を把握しておくと意図しないバグを防げます

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

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

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

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