Cv2.BoundingRect / Cv2.MinAreaRect の使い方【OpenCV/C#(OpenCvSharp)】〜外接矩形を求める〜

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

Cv2.BoundingRect / Cv2.MinAreaRect の使い方【OpenCV/C#(OpenCvSharp)】〜外接矩形を求める〜

輪郭(contour)に対して外接矩形を求める関数が Cv2.BoundingRect と Cv2.MinAreaRect です。前者は軸平行(水平・垂直)な矩形、後者は回転を考慮した最小面積の矩形を返します。物体検出やトリミング処理の前処理として頻繁に使われます。

// 軸平行外接矩形
Rect rect = Cv2.BoundingRect(contour);

// 最小面積外接矩形(回転あり)
RotatedRect rotRect = Cv2.MinAreaRect(contour);

動作環境

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

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

基本の使い方

以下のサンプルでは、バイナリ画像から輪郭を検出し、BoundingRect(青)と MinAreaRect(緑)をそれぞれ描画して保存します。

using OpenCvSharp;

// バイナリ画像を生成(白い矩形を斜めに傾けて描画)
using var src = new Mat(400, 600, MatType.CV_8UC3, Scalar.Black);

// 傾いた多角形を描画して「傾いた物体」を模擬する
Point[] polygon =
[
    new Point(150, 80),
    new Point(350, 50),
    new Point(420, 200),
    new Point(220, 230),
];
Cv2.FillConvexPoly(src, polygon, Scalar.White);

// グレースケール化 → 二値化
using var gray = new Mat();
Cv2.CvtColor(src, gray, ColorConversionCodes.BGR2GRAY);

using var binary = new Mat();
Cv2.Threshold(gray, binary, 128, 255, ThresholdTypes.Binary);

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

using var dst = src.Clone();

foreach (var contour in contours)
{
    // --- BoundingRect: 軸平行外接矩形 ---
    Rect boundingRect = Cv2.BoundingRect(contour);
    Cv2.Rectangle(dst, boundingRect, Scalar.Blue, 2);

    // --- MinAreaRect: 最小面積外接矩形(回転あり)---
    RotatedRect rotatedRect = Cv2.MinAreaRect(contour);

    // RotatedRect の4頂点を取得して描画
    Point2f[] boxPoints = Cv2.BoxPoints(rotatedRect);
    // float → Point に変換してポリライン描画
    Point[] boxPointsInt = Array.ConvertAll(boxPoints, p => new Point((int)p.X, (int)p.Y));
    Cv2.Polylines(dst, [boxPointsInt], isClosed: true, Scalar.LimeGreen, 2);
}

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

実行すると result.png が保存され、傾いた白い図形に対して 青の軸平行矩形(BoundingRect)と 緑の回転矩形(MinAreaRect)が重ねて描画された画像が得られます。MinAreaRect の方が物体にぴったりフィットしていることが確認できます。

result.png を保存しました。

コードの要点

行の処理 ポイント
Cv2.FindContours 外部輪郭のみ取得(RetrievalModes.External)
Cv2.BoundingRect(contour) Rect を返す。X・Y・Width・Height をそのまま使える
Cv2.MinAreaRect(contour) RotatedRect を返す。Center・Size・Angle を持つ
Cv2.BoxPoints(rotatedRect) RotatedRect の4頂点(Point2f[])を取得
Cv2.Polylines 4頂点を線で結んで回転矩形を描画

引数と戻り値

Cv2.BoundingRect

引数/戻り値 型 説明
array IEnumerable<Point> 対象の輪郭点列
戻り値 Rect 軸平行外接矩形(X, Y, Width, Height)

Cv2.MinAreaRect

引数/戻り値 型 説明
points IEnumerable<Point2f> または IEnumerable<Point> 対象の輪郭点列
戻り値 RotatedRect 回転外接矩形(Center, Size, Angle)

RotatedRect のプロパティ

プロパティ 型 説明
Center Point2f 矩形の中心座標
Size Size2f 矩形の幅と高さ
Angle float 回転角度(度)

実践例

物体ごとにバウンディングボックスを切り出す

物体検出後のROI(Region of Interest)切り出しは BoundingRect が便利です。

using OpenCvSharp;

using var src = Cv2.ImRead("objects.png");
if (src.Empty()) { Console.WriteLine("画像を読み込めませんでした。"); return; }

using var gray = new Mat();
Cv2.CvtColor(src, gray, ColorConversionCodes.BGR2GRAY);

using var binary = new Mat();
Cv2.Threshold(gray, binary, 0, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu);

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

int index = 0;
foreach (var contour in contours)
{
    // 小さなノイズ輪郭を除外
    if (Cv2.ContourArea(contour) < 500) continue;

    Rect roi = Cv2.BoundingRect(contour);

    // Mat のインデクサで ROI を切り出し
    using var cropped = new Mat(src, roi);
    Cv2.ImWrite($"crop_{index++}.png", cropped);
}

Console.WriteLine($"{index} 個の物体を切り出しました。");

回転角度を利用した傾き補正

MinAreaRect の Angle を使って画像の傾きを補正するケースです。

using OpenCvSharp;

using var src = Cv2.ImRead("tilted_text.png");
if (src.Empty()) { Console.WriteLine("画像を読み込めませんでした。"); return; }

using var gray = new Mat();
Cv2.CvtColor(src, gray, ColorConversionCodes.BGR2GRAY);

using var binary = new Mat();
Cv2.Threshold(gray, binary, 0, 255, ThresholdTypes.BinaryInv | ThresholdTypes.Otsu);

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

if (contours.Length == 0) { Console.WriteLine("輪郭が検出されませんでした。"); return; }

// 最大面積の輪郭を対象にする
var largest = contours.MaxBy(c => Cv2.ContourArea(c))!;
RotatedRect rotRect = Cv2.MinAreaRect(largest);

double angle = rotRect.Angle;
// OpenCV の Angle 定義: Width < Height のとき -90〜0 度
if (rotRect.Size.Width < rotRect.Size.Height)
    angle += 90.0;

// 回転行列を生成して傾き補正
using var rotMat = Cv2.GetRotationMatrix2D(rotRect.Center, angle, 1.0);
using var corrected = new Mat();
Cv2.WarpAffine(src, corrected, rotMat, src.Size(), InterpolationFlags.Cubic);

Cv2.ImWrite("corrected.png", corrected);
Console.WriteLine($"補正角度: {angle:F2} 度 → corrected.png を保存しました。");

つまずきポイント

⚠️ MinAreaRect の Angle の解釈に注意

RotatedRect.Angle は OpenCV 4.x では −90 〜 0 度の範囲で返ります。矩形の幅と高さの大小関係によって「どちらが長辺か」が変わるため、傾き補正時は Width < Height かどうかを確認して角度を調整する必要があります。上記の実践例を参考にしてください。

⚠️ BoxPoints の戻り値は Point2f[] であり整数座標ではない

Cv2.BoxPoints は Point2f[] を返します。Cv2.Polylines や Cv2.FillPoly に渡すには Point[](整数)へ変換が必要です。そのまま渡そうとするとコンパイルエラーになるか、意図しない丸め誤差が生じます。

// NG: Point2f[] をそのまま Polylines に渡せない
// OK: Array.ConvertAll で Point に変換する
Point[] pts = Array.ConvertAll(boxPoints, p => new Point((int)p.X, (int)p.Y));

⚠️ 空の輪郭に対して呼び出すと例外が発生する

FindContours の結果が空配列のまま BoundingRect や MinAreaRect を呼ぶと実行時例外になります。contours.Length == 0 のチェックと、面積によるフィルタリング(Cv2.ContourArea(contour) < 閾値)を必ず入れましょう。

関連する関数

その他、Cv2.MinEnclosingCircle(最小包含円)・Cv2.ConvexHull(凸包)も輪郭の形状記述に関連します。

まとめ

  • Cv2.BoundingRect は軸平行の外接矩形を Rect で返す。ROI 切り出しや座標取得に最適。
  • Cv2.MinAreaRect は回転を考慮した最小面積矩形を RotatedRect で返す。傾き補正や精密な形状解析に使う。
  • Angle の解釈と BoxPoints の型変換は C#(OpenCvSharp)固有の落とし穴なので要注意。

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

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

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

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