【OpenCV】Rectクラスの完全ガイド|初期化から実践的な使い方まで
はじめに
OpenCV(Open Source Computer Vision Library)は、画像処理やコンピュータビジョンに関する多種多様な機能を提供する、オープンソースのライブラリです。顔検出、物体追跡、画像フィルタリング、機械学習など、その応用範囲は無限大とも言えます。これらの高度な処理を行う上で、最も基本的かつ重要な概念の一つが「領域」の扱いです。特定の顔を切り抜く、画像内のあるオブジェクトを追跡する、特定のエリアだけを加工するなど、多くの処理は画像全体ではなく、ある「矩形領域」を対象とします。
この「矩形領域」をプログラム上で直感的かつ効率的に扱うために、OpenCVが提供しているのがcv::Rect
クラスです。cv::Rect
は、単に矩形の座標とサイズを保持するだけでなく、領域同士の演算や判定など、便利な機能を数多く備えています。このクラスを使いこなすことは、OpenCVプログラミングの効率と品質を飛躍的に向上させる鍵となります。
しかし、多くの入門書やチュートリアルでは、cv::Rect
は「物体の位置を示すもの」として軽く触れられるだけで、その豊富な機能や正しい使い方について深く掘り下げられることは稀です。結果として、多くの開発者がそのポテンシャルを十分に引き出せていないのが現状です。
そこでこの記事では、OpenCVのcv::Rect
クラスに焦点を当て、その基礎から応用までを徹底的に解説する「完全ガイド」をお届けします。
本記事を読み終える頃には、あなたは以下の知識を習得しているでしょう。
cv::Rect
クラスの基本的な構造と役割- 様々な方法で
cv::Rect
オブジェクトを初期化するテクニック - 矩形領域の情報を取得し、判定するための便利なメンバ関数
|
(和集合)や&
(積集合)といった演算子を使った高度な領域操作- ROI(関心領域)の設定、物体検出結果の描画、安全な領域操作といった実践的な応用方法
この記事が、あなたのOpenCVプロジェクトを次のレベルへと引き上げる一助となれば幸いです。さあ、cv::Rect
の奥深い世界へ一緒に旅立ちましょう。
第1章: cv::Rect
クラスの基礎
まずはじめに、cv::Rect
がどのようなもので、どのような構造を持っているのかを理解するところから始めます。
1.1 cv::Rect
とは何か?
cv::Rect
クラスは、2次元平面上の矩形領域を表現するためのデータ構造です。画像処理の世界では、この「矩形領域」が極めて重要な役割を果たします。
- 物体検出 (Object Detection): 顔、人、車などを検出した際、その物体が画像のどの位置にあるかを示すためにバウンディングボックス(bounding box)として
cv::Rect
が使われます。 - ROI (Region of Interest) の設定: 画像全体ではなく、特定の部分(例えば、顔の部分だけ、ナンバープレートの部分だけ)に処理を適用したい場合、その関心領域を
cv::Rect
で指定します。 - 物体追跡 (Object Tracking): 動画内で特定の物体を追いかける際、各フレームにおける物体の位置を
cv::Rect
で管理します。
このように、cv::Rect
は「画像のどこか」を指し示すための基本的なツールなのです。
1.2 Rect
クラスのメンバ変数
cv::Rect
クラスは、矩形を定義するために4つのパブリックなメンバ変数を持っています。
int x
: 矩形の左上の角のx座標int y
: 矩形の左上の角のy座標int width
: 矩形の幅int height
: 矩形の高さ
例えば、cv::Rect(100, 50, 200, 150)
というオブジェクトは、以下のような矩形を表します。
- 始点(左上)の座標: (100, 50)
- 幅: 200ピクセル
- 高さ: 150ピクセル
この矩形の右下の角の座標は (x + width, y + height)
、つまり (100 + 200, 50 + 150) = (300, 200)
となります。ただし、OpenCVの座標系ではピクセルは点ではなく領域を持つため、右下の座標は厳密には (x + width - 1, y + height - 1)
のピクセルまでを含む領域と考えるのが一般的です。この点を覚えておくと、ROIの操作などで混乱を避けられます。
【図解イメージ】
(x, y)
+-------------------------+
| |
| | height
| |
+-------------------------+
width
1.3 Rect
クラスのテンプレートとしての側面
実は、私たちが普段cv::Rect
と呼んでいるものは、C++のテンプレート機能を使ったエイリアス(別名)です。その本体はcv::Rect_
というテンプレートクラスです。
cpp
template<typename _Tp> class Rect_
{
public:
_Tp x, y, width, height;
// ... その他のメンバ関数
};
そして、cv::Rect
は、このテンプレートの型_Tp
にint
を指定したもののtypedef(型定義)なのです。
cpp
typedef Rect_<int> Rect;
なぜこのようなテンプレート構造になっているのでしょうか?それは、整数座標だけでなく、より精度の高い浮動小数点数で矩形を扱いたい場合があるからです。OpenCVには、int
以外の型に対応したRect
のエイリアスも用意されています。
cv::Rect2i
:cv::Rect_<int>
と同じ (つまりcv::Rect
と同じ)cv::Rect2f
:cv::Rect_<float>
(単精度浮動小数点数)cv::Rect2d
:cv::Rect_<double>
(倍精度浮動小数点数)
Rect2f
やRect2d
は、例えば、機械学習モデルの出力がサブピクセル精度でバウンディングボックスを予測する場合や、幾何学的な計算で高精度な座標を扱いたい場合に非常に役立ちます。
この記事では主に最も一般的に使われるcv::Rect
(int
型)に焦点を当てて解説しますが、ここで紹介する機能のほとんどはRect2f
やRect2d
でも同様に利用できます。
第2章: cv::Rect
オブジェクトの初期化と生成
cv::Rect
オブジェクトを生成(初期化)するには、いくつかの方法があります。状況に応じて最適な方法を選択できるように、それぞれの方法をマスターしましょう。
2.1 基本的なコンストラクタ
C++では、オブジェクトを生成する際にコンストラクタが呼び出されます。cv::Rect
には複数のコンストラクタが用意されています。
1. デフォルトコンストラクタ
引数なしでオブジェクトを生成します。すべてのメンバ変数が0で初期化されます。
“`cpp
include
include
int main() {
cv::Rect r1;
std::cout << “r1: ” << r1 << std::endl;
// 出力: r1: [0 x 0 from (0, 0)]
return 0;
}
“`
2. x, y, width, height
を指定するコンストラクタ
最も一般的で直感的な方法です。左上の座標(x, y)と幅(width)、高さ(height)を直接指定します。
“`cpp
include
include
int main() {
cv::Rect r2(100, 50, 200, 150);
std::cout << “r2: ” << r2 << std::endl;
// 出力: r2: [200 x 150 from (100, 50)]
return 0;
}
“`
3. Point
とSize
を指定するコンストラクタ
左上の点をcv::Point
オブジェクトで、サイズをcv::Size
オブジェクトで指定する方法です。コードの可読性が向上する場合があります。
“`cpp
include
include
int main() {
cv::Point top_left(100, 50);
cv::Size size(200, 150);
cv::Rect r3(top_left, size);
std::cout << “r3: ” << r3 << std::endl;
// 出力: r3: [200 x 150 from (100, 50)]
return 0;
}
“`
4. 2つの対角点を指定するコンストラクタ
矩形の対角線上にある2つの点(例えば、左上と右下)をcv::Point
オブジェクトで指定する方法です。このコンストラクタは、2つの点のx, y座標の大小関係を自動的に判断し、正規化された(widthとheightが正の値になる)矩形を生成してくれます。
“`cpp
include
include
int main() {
cv::Point pt1(100, 50); // 左上
cv::Point pt2(300, 200); // 右下
cv::Rect r4(pt1, pt2);
std::cout << “r4: ” << r4 << std::endl;
// 出力: r4: [200 x 150 from (100, 50)]
// 点の順序が逆でもOK
cv::Point pt3(300, 200); // 右下
cv::Point pt4(100, 50); // 左上
cv::Rect r5(pt3, pt4);
std::cout << "r5: " << r5 << std::endl;
// 出力: r5: [200 x 150 from (100, 50)]
return 0;
}
“`
マウスドラッグで領域を選択するようなUIを実装する際に特に便利です。
2.2 コピーコンストラクタと代入演算子
もちろん、既存のRect
オブジェクトから新しいオブジェクトを複製することも簡単です。
“`cpp
include
include
int main() {
cv::Rect original_rect(10, 20, 30, 40);
// コピーコンストラクタ
cv::Rect copied_rect(original_rect);
std::cout << "Copied: " << copied_rect << std::endl;
// 代入演算子
cv::Rect assigned_rect;
assigned_rect = original_rect;
std::cout << "Assigned: " << assigned_rect << std::endl;
return 0;
}
“`
2.3 他のデータ型からの変換
Rect_<T>
テンプレートクラスは、異なる型を持つ別のRect_
オブジェクトから変換するためのコンストラクタも備えています。これにより、例えばRect2f
からRect
へ(またはその逆へ)の変換が可能です。
“`cpp
include
include
int main() {
// float型のRect
cv::Rect2f rect_f(10.5f, 20.2f, 30.8f, 40.4f);
std::cout << “Rect2f: ” << rect_f << std::endl;
// Rect2fからRectへの変換(小数点は切り捨てられる)
cv::Rect rect_i(rect_f);
std::cout << "Rect from Rect2f: " << rect_i << std::endl;
// 出力: Rect from Rect2f: [30 x 40 from (10, 20)]
return 0;
}
“`
このとき、浮動小数点数から整数への変換では小数点以下が切り捨てられることに注意が必要です。四捨五入したい場合は、手動で計算する必要があります。
第3章: cv::Rect
クラスの便利なメンバ関数
cv::Rect
は単なるデータコンテナではありません。矩形に関する様々な計算や判定を簡単に行うためのメンバ関数が用意されています。
3.1 領域情報の取得
area()
: 矩形の面積 (width * height
) を計算して返します。
cpp
cv::Rect r(10, 10, 100, 50);
double a = r.area(); // aは 5000.0 になる
size()
: 矩形のサイズをcv::Size
オブジェクトとして返します。
cpp
cv::Rect r(10, 10, 100, 50);
cv::Size s = r.size(); // s.widthは100, s.heightは50
tl()
(Top-Left): 矩形の左上の角の座標をcv::Point
オブジェクトとして返します。cv::Point(x, y)
と同じです。
cpp
cv::Rect r(10, 10, 100, 50);
cv::Point p = r.tl(); // p.xは10, p.yは10
br()
(Bottom-Right): 矩形の右下の角の座標をcv::Point
オブジェクトとして返します。cv::Point(x + width, y + height)
と同じです。
cpp
cv::Rect r(10, 10, 100, 50);
cv::Point p = r.br(); // p.xは110, p.yは60
empty()
: 矩形が空かどうか(width <= 0
または height <= 0
)を判定します。面積が0以下の場合はtrue
を返します。
“`cpp
cv::Rect r1(10, 10, 100, 50);
bool b1 = r1.empty(); // b1は false
cv::Rect r2(10, 10, 0, 50);
bool b2 = r2.empty(); // b2は true
“`
後述する領域の積集合を計算した後など、結果の矩形が有効かどうかをチェックするのによく使われます。
3.2 領域の判定
contains(const Point& pt)
: 指定した点pt
が、矩形の内側または境界線上にあるかどうかを判定します。
pt.x >= x && pt.x < x + width && pt.y >= y && pt.y < y + height
という条件式と同じ意味になります。戻り値はbool
型です。
“`cpp
include
include
int main() {
cv::Rect button(100, 100, 200, 50); // (100, 100) から (300, 150) の範囲
cv::Point click1(150, 120); // 内側
cv::Point click2(50, 120); // 外側
cv::Point click3(100, 100); // 境界線上 (左上)
std::cout << std::boolalpha; // bool値をtrue/falseで表示
std::cout << "Click1 is inside: " << button.contains(click1) << std::endl;
std::cout << "Click2 is inside: " << button.contains(click2) << std::endl;
std::cout << "Click3 is inside: " << button.contains(click3) << std::endl;
return 0;
}
実行結果:
Click1 is inside: true
Click2 is inside: false
Click3 is inside: true
“`
GUIアプリケーションで、特定のボタンがクリックされたかどうかを判定するような場面で非常に便利です。
第4章: cv::Rect
の演算子オーバーロード
cv::Rect
クラスの真価は、その演算子のオーバーロードにあります。これにより、複数の矩形領域をまるで数値計算のように、直感的かつ簡潔に扱うことができます。
4.1 和集合 (Union): |
演算子と |=
演算子
rect1 | rect2
は、2つの矩形rect1
とrect2
を両方とも完全に含む最小の矩形(和集合、Union)を計算します。
【図解イメージ】
“`
rect1
+——-+
| |
| +—+—+
+—+—+ |
| | rect2
+——-+
↓ rect1 | rect2
+---------------+
| +-------+ |
| | | |
| | +---+---+ |
| +---+---+ | |
| | | |
| +-------+ |
+---------------+
“`
これは、近接する複数の検出領域を1つにまとめたい場合などに非常に役立ちます。
“`cpp
include
include
int main() {
cv::Rect r1(50, 50, 100, 100);
cv::Rect r2(100, 100, 150, 150);
cv::Rect union_rect = r1 | r2;
std::cout << "r1: " << r1 << std::endl;
std::cout << "r2: " << r2 << std::endl;
std::cout << "Union: " << union_rect << std::endl;
// 自己代入バージョン
r1 |= r2;
std::cout << "r1 |= r2: " << r1 << std::endl;
return 0;
}
実行結果:
r1: [100 x 100 from (50, 50)]
r2: [150 x 150 from (100, 100)]
Union: [200 x 200 from (50, 50)]
r1 |= r2: [200 x 200 from (50, 50)]
``
min(r1.x, r2.x)
和集合の左上のx座標は、y座標は
min(r1.y, r2.y)となり、右下のx座標は
max(r1.br().x, r2.br().x)、y座標は
max(r1.br().y, r2.br().y)`となります。
4.2 積集合 (Intersection): &
演算子と &=
演算子
rect1 & rect2
は、2つの矩形rect1
とrect2
が重なっている共通領域(積集合、Intersection)を計算します。もし重なっていない場合、結果は面積が0以下の空の矩形になります。
【図解イメージ】
“`
rect1
+——-+
| |
| +—+—+
+—+—+ |
| | rect2
+——-+
↓ rect1 & rect2
+---+
| |
+---+
“`
これは、2つの領域が重なっているかどうか(衝突判定)、また重なっている領域がどこかを正確に知りたい場合に不可欠です。
“`cpp
include
include
int main() {
cv::Rect r1(50, 50, 100, 100);
cv::Rect r2(100, 100, 150, 150);
cv::Rect r3(300, 300, 50, 50); // r1とは重ならない
cv::Rect intersection_rect = r1 & r2;
std::cout << "Intersection (r1 & r2): " << intersection_rect << std::endl;
std::cout << "Is empty? " << std::boolalpha << intersection_rect.empty() << std::endl;
cv::Rect no_intersection_rect = r1 & r3;
std::cout << "Intersection (r1 & r3): " << no_intersection_rect << std::endl;
std::cout << "Is empty? " << std::boolalpha << no_intersection_rect.empty() << std::endl;
// 自己代入バージョン
r1 &= r2;
std::cout << "r1 &= r2: " << r1 << std::endl;
return 0;
}
実行結果:
Intersection (r1 & r2): [50 x 50 from (100, 100)]
Is empty? false
Intersection (r1 & r3): [0 x 0 from (0, 0)]
Is empty? true
r1 &= r2: [50 x 50 from (100, 100)]
``
empty()`関数と組み合わせることで、領域が重なっているかどうかを簡単に判定できます。
4.3 移動 (Translation): +
と -
(Pointとの組み合わせ)
rect + point
は、矩形を点point
の分だけ平行移動させます。x
座標にpoint.x
が、y
座標にpoint.y
が加算されます。width
とheight
は変わりません。
“`cpp
cv::Rect r(100, 100, 50, 50);
cv::Point offset(10, 20);
cv::Rect moved_rect = r + offset;
// moved_rect は Rect(110, 120, 50, 50) となる
cv::Rect moved_back_rect = moved_rect – offset;
// moved_back_rect は r と同じ Rect(100, 100, 50, 50) となる
``
+=,
-=`演算子も使用可能です。物体追跡で、前フレームの位置から現在の位置を予測する際などに利用できます。
4.4 拡大・縮小 (Scaling): +
と -
(Sizeとの組み合わせ)
rect + size
は、矩形のサイズをsize
の分だけ拡大します。x
, y
座標は変わらず、width
にsize.width
が、height
にsize.height
が加算されます。
“`cpp
cv::Rect r(100, 100, 50, 50);
cv::Size margin(10, 10);
cv::Rect expanded_rect = r + margin;
// expanded_rect は Rect(100, 100, 60, 60) となる
// 注意: 中心は移動する
cv::Rect shrinked_rect = expanded_rect – margin;
// shrinked_rect は r と同じ Rect(100, 100, 50, 50) となる
“`
この演算は、矩形の左上の点を基準に拡大・縮小します。もし矩形の中心を保ったまま拡大・縮小したい場合は、手動で計算する必要があります。
“`cpp
// 中心を維持したまま拡大する例
cv::Rect r(100, 100, 50, 50);
int margin_x = 10;
int margin_y = 10;
cv::Rect centered_expanded_rect(
r.x – margin_x,
r.y – margin_y,
r.width + 2 * margin_x,
r.height + 2 * margin_y
);
// centered_expanded_rectは Rect(90, 90, 70, 70) となる
“`
検出領域に少し余白(マージン)を持たせたい場合に便利です。
4.5 比較演算子: ==
と !=
rect1 == rect2
は、2つの矩形のx, y, width, height
がすべて等しいかどうかを判定します。
“`cpp
cv::Rect r1(10, 20, 30, 40);
cv::Rect r2(10, 20, 30, 40);
cv::Rect r3(0, 0, 0, 0);
if (r1 == r2) { // true
std::cout << “r1 and r2 are identical.” << std::endl;
}
if (r1 != r3) { // true
std::cout << “r1 and r3 are different.” << std::endl;
}
“`
第5章: 実践的な使い方と応用例
理論を学んだところで、次はcv::Rect
を実際の画像処理でどのように活用するのかを見ていきましょう。
5.1 ROI (Region of Interest) の設定
ROI(関心領域)は、画像の一部だけを切り出して処理するための強力な機能です。cv::Mat
オブジェクトの()
演算子にcv::Rect
を渡すことで、簡単にROIを作成できます。
“`cpp
include
int main() {
// 画像を読み込む
cv::Mat image = cv::imread(“path/to/your/image.jpg”);
if (image.empty()) {
std::cerr << “Image not found!” << std::endl;
return -1;
}
// 顔検出器などで検出された顔の領域(ダミー)
cv::Rect face_roi(150, 100, 120, 120);
// Rectを使ってROIを切り出す
cv::Mat face_image = image(face_roi);
// ROIに対して処理を適用(例:グレースケール化)
cv::cvtColor(face_image, face_image, cv::COLOR_BGR2GRAY);
// ↑ この処理は元の 'image' にも反映される!
cv::imshow("Original Image with modified ROI", image);
cv::imshow("Face ROI", face_image);
cv::waitKey(0);
return 0;
}
“`
【重要】ROIはデータのコピーではない
上記のコードで最も注意すべき点は、cv::Mat face_image = image(face_roi);
で作成されたface_image
は、新しい画像データをコピーしたものではなく、元のimage
のデータの一部を指し示すヘッダ情報に過ぎないということです。
したがって、face_image
に対して行われた変更(例:cvtColor
)は、元のimage
にも直接反映されます。これはメモリ効率が良いという利点がありますが、意図しない副作用を生む可能性もあります。
もし、ROI部分を独立したデータとして扱いたい場合は、clone()
またはcopyTo()
メソッドを使って明示的にデータをコピーする必要があります。
cpp
// データをコピーして、元の画像に影響を与えないようにする
cv::Mat face_image_clone = image(face_roi).clone();
cv::GaussianBlur(face_image_clone, face_image_clone, cv::Size(15, 15), 0); // この処理は元のimageに影響しない
5.2 物体検出結果の描画
Haar-cascade分類器や深層学習ベースの物体検出器(YOLO, SSDなど)は、検出した物体の位置をcv::Rect
のリスト(std::vector<cv::Rect>
)として返すのが一般的です。cv::rectangle()
関数を使えば、これらの結果を画像上に簡単に描画できます。
“`cpp
include
include
int main() {
cv::Mat image = cv::imread(“path/to/your/image.jpg”);
if (image.empty()) return -1;
// 物体検出の結果(ダミー)
std::vector<cv::Rect> detected_objects;
detected_objects.push_back(cv::Rect(150, 100, 120, 120));
detected_objects.push_back(cv::Rect(400, 180, 80, 150));
// 検出されたすべてのオブジェクトをループで描画
for (const auto& rect : detected_objects) {
// rectangle(画像, 矩形, 色, 線の太さ)
cv::rectangle(image, rect, cv::Scalar(0, 255, 0), 2);
}
cv::imshow("Detections", image);
cv::waitKey(0);
return 0;
}
``
cv::rectangle()の第2引数に
cv::Rect`オブジェクトをそのまま渡せるのがポイントです。
5.3 矩形領域の安全な操作 (画像外参照の防止)
物体検出のアルゴリズムや、矩形の拡大処理などによって生成されたRect
が、画像の範囲外にはみ出してしまうことがあります。例えば、画像の右端にある顔を検出し、それにマージンを追加した場合などです。
このような範囲外のRect
を使ってROIを切り出そうとすると、プログラムはクラッシュしてしまいます。これを防ぐための、非常に重要でエレガントなテクニックがあります。それは画像全体の矩形と積集合を取ることです。
“`cpp
include
// 矩形を画像境界内に安全にクリッピングする関数
void clipRect(cv::Rect& rect, const cv::Size& img_size) {
// 画像全体の矩形を生成
cv::Rect image_boundary(0, 0, img_size.width, img_size.height);
// 入力矩形と画像境界の積集合を取る
// これにより、はみ出した部分が自動的に切り取られる
rect &= image_boundary;
}
int main() {
cv::Mat image = cv::imread(“path/to/your/image.jpg”);
if (image.empty()) return -1;
// 画像の右端にはみ出すような矩形(ダミー)
// 画像サイズが 640x480 だと仮定
cv::Rect unsafe_rect(600, 100, 100, 100); // x=600, width=100 なので、右に60ピクセルはみ出す
// 安全にクリッピング
clipRect(unsafe_rect, image.size());
// これで unsafe_rect は Rect(600, 100, 40, 100) のようになる
// この矩形を使えば、安全にROIを操作できる
std::cout << "Clipped Rect: " << unsafe_rect << std::endl;
cv::rectangle(image, unsafe_rect, cv::Scalar(0, 0, 255), 2);
cv::imshow("Safely Clipped", image);
cv::waitKey(0);
return 0;
}
``
rect &= cv::Rect(0, 0, img_size.width, img_size.height)`という一行は、OpenCVプログラミングにおける必須のイディオム(慣用句)です。必ず覚えておきましょう。
この
5.4 複数の矩形のマージ
文字認識(OCR)などで、個々の文字として検出された小さな矩形を、単語や行といった大きな矩形にまとめたい場合があります。このようなケースでは、|
(和集合)演算子が活躍します。
“`cpp
include
include
int main() {
// 文字 ‘H’, ‘e’, ‘l’, ‘l’, ‘o’ の検出矩形(ダミー)
std::vector
char_rects.push_back(cv::Rect(100, 100, 20, 30)); // H
char_rects.push_back(cv::Rect(125, 105, 18, 25)); // e
char_rects.push_back(cv::Rect(148, 100, 10, 30)); // l
char_rects.push_back(cv::Rect(162, 100, 10, 30)); // l
char_rects.push_back(cv::Rect(177, 105, 18, 25)); // o
if (char_rects.empty()) {
return 0;
}
// 最初の矩形をベースにマージを開始
cv::Rect word_rect = char_rects[0];
// 2つ目以降の矩形を次々にマージしていく
for (size_t i = 1; i < char_rects.size(); ++i) {
word_rect |= char_rects[i];
}
std::cout << "Merged Word Rect: " << word_rect << std::endl;
// 出力例: Merged Word Rect: [95 x 30 from (100, 100)]
// x, y, width, heightは入力によって変わる
// 画像に描画して確認
cv::Mat canvas = cv::Mat::zeros(cv::Size(400, 200), CV_8UC3);
for (const auto& r : char_rects) {
cv::rectangle(canvas, r, cv::Scalar(255, 0, 0), 1); // 青で個々の文字
}
cv::rectangle(canvas, word_rect, cv::Scalar(0, 255, 0), 2); // 緑でマージされた単語
cv::imshow("Merged Rect", canvas);
cv::waitKey(0);
return 0;
}
“`
第6章: C++以外の言語でのRect
cv::Rect
の概念は、他の言語のOpenCVバインディングでも共通していますが、表現方法が少し異なります。
6.1 Python
PythonのOpenCVでは、Rect
に相当する専用のクラスは通常使われず、(x, y, w, h)
という形式のタプルまたはNumPy配列で矩形を表現するのが一般的です。
x
: 左上のx座標y
: 左上のy座標w
: 幅h
: 高さ
“`python
import cv2
import numpy as np
画像を読み込む
image = cv2.imread(“path/to/your/image.jpg”)
矩形をタプルで表現
face_roi = (150, 100, 120, 120)
(x, y, w, h) = face_roi
ROIの切り出し (NumPyのスライシング)
Pythonでは (y, x) の順序であることに注意!
face_image = image[y:y+h, x:x+w]
矩形の描画
pt1は(x, y), pt2は(x+w, y+h)
cv2.rectangle(image, (x, y), (x+w, y+h), (0, 255, 0), 2)
cv2.imshow(“Image”, image)
cv2.imshow(“Face”, face_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
``
Rect
C++のクラスが持つような演算子(
|,
&`など)は、Pythonのタプルにはありません。これらの操作を行いたい場合は、自分で関数を実装するか、NumPyの機能を駆使して計算する必要があります。
6.2 Java / C
JavaやC#のOpenCVラッパー(OpenCV for Java, Emgu CVなど)では、C++と同様にRect
という名前のクラスが提供されており、メンバ変数や基本的なメソッドもC++版に準じています。演算子のオーバーロードの代わりに、union
やintersection
といった名前のメソッドが用意されていることが一般的です。使い方はC++の考え方を応用できます。
まとめ
この記事では、OpenCVにおける最も基本的で重要なクラスの一つであるcv::Rect
について、その全貌を詳細に解説しました。
- 基礎:
x, y, width, height
という4つのメンバ変数で矩形を定義するシンプルな構造と、Rect2f
などのテンプレートのバリエーションについて学びました。 - 初期化: 4つの値、PointとSize、2つの対角点など、状況に応じた柔軟な初期化方法を見てきました。
- メンバ関数:
area()
,tl()
,br()
,contains()
など、矩形の情報を効率的に扱うための便利な関数を紹介しました。 - 演算子:
|
(和集合)と&
(積集合)を使った強力な領域演算は、Rect
クラスの真骨頂です。これにより、複雑な領域操作を簡潔なコードで実現できます。 - 実践的応用: ROIの(安全な)設定、物体検出結果の描画、複数領域のマージといった、実際のプログラミングで頻出するシナリオでの活用法を具体的なコードと共に示しました。
cv::Rect
は、単なる「四角」ではありません。それは、画像上の「意味のある領域」を定義し、操作し、分析するための洗練されたツールです。このクラスを自在に操れるようになることで、あなたのコードはより堅牢に、より簡潔に、そしてより表現力豊かになるでしょう。
物体検出、画像セグメンテーション、インタラクティブな画像処理アプリケーションなど、あなたがこれから取り組むであろうあらゆるOpenCVプロジェクトにおいて、cv::Rect
は間違いなくあなたの頼れる相棒となるはずです。本記事が、その第一歩を踏み出すための確かな道しるべとなったことを願っています。