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引数の取り違え
closed を false にすると、始点と終点を結ぶ辺が計算に含まれません。findContours で取得した閉じた輪郭に対して false を渡すと、実際の周長より短い値が返ります。輪郭の周長を求める場合は 必ず true を指定してください。
⚠️ oriented=true にした場合の符号
cv::contourArea(c, true) は輪郭が反時計回りなら正、時計回りなら負の値を返します。面積として使いたい場合は std::abs() を忘れずに。単純に大小比較するコードで符号付きのまま使うとフィルタリングが意図通りに動きません。
関連する関数
- cv::findContours — 輪郭の取得元。本記事のコードと常にセットで使用します
- cv::drawContours — 取得・フィルタリングした輪郭を可視化する際に使います
- cv::Canny の使い方【OpenCV/C++】〜エッジ検出の基本と閾値の決め方〜 — 二値化の前処理としてエッジ画像を使う場合に参照してください
- cv::morphologyEx の使い方【OpenCV/C++】〜オープニング・クロージングでノイズ除去〜 — 二値化後の前処理で輪郭品質を上げたい場合に組み合わせます
- cv::erode / cv::dilate の使い方【OpenCV/C++】〜収縮・膨張処理の基本と実践〜 — 小さなノイズ輪郭を事前に消す場合の定番処理です
まとめ
cv::contourAreaで輪郭の面積、cv::arcLengthで周長をピクセル単位で取得できます- 面積閾値によるノイズ除去、面積と周長を組み合わせた真円度フィルタリングが実務でよく使われます
arcLengthのclosed引数とcontourAreaのoriented引数の挙動を把握しておくと意図しないバグを防げます
🛠 画像処理のプロが開発するSDK/API
本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/API(C++製・OpenCV 4.10)を開発しています。顔認証システムへの組み込み実績多数。

