计算点集的最小外接矩形——OpenCV的minAreaRect函数
函数原型
输入一系列二维点,返回其最小外接矩形。
RotatedRect minAreaRect( InputArray points );
根据函数原型,输入的数据可以是vector<Point>
类型,包含1个以上的点;
返回值是RotatedRect
类型,该类型的定义如下:
class CV_EXPORTS RotatedRect
{
public:
//! default constructor
RotatedRect();
/** full constructor
@param center 矩形的质心
@param size 矩形的宽和高
@param angle 顺时针定义的旋转角。当值为0, 90, 180, 270等90的整数倍时,该矩形为直立矩形(底边水平)
*/
RotatedRect(const Point2f& center, const Size2f& size, float angle);
RotatedRect(const Point2f& point1, const Point2f& point2, const Point2f& point3);
/** returns 返回矩形的四个顶点
@param pts 顺序是原始矩形的左下、左上、右上、右下顶点(顺时针顺序)。
*/
void points(Point2f pts[]) const;
//! returns 返回当前矩形的最小外接直立矩形(坐标为整数)
Rect boundingRect() const;
//! returns 返回包含当前矩形的最小外接矩形(坐标为浮点数),不适合用于图像
Rect_<float> boundingRect2f() const;
//! returns 质心
Point2f center;
//! returns 宽、高
Size2f size;
//! returns 矩形相对于直立矩形的旋转角。
float angle;
};
对于用户而言,最重要的是三个属性:center
、size
、angle
。其中size
和angle
需要着重介绍一下:
RotatedRect的size成员变量
某些用户可能认为宽width<高height,但是size成员变量中,并非如此!宽width表示矩形底边的长度,高height表示矩形竖直边的长度。
void points()输出的点坐标顺序
官方文档定义,points输出的点在原始(未旋转)矩形
中依次是左下、左上、右上、右下点,说的有些不明不白;
实际上,序号为0的点是minAreaRect返回的矩形最左侧的点,1\2\3号点依次按顺时针确定。请参照下一节的图示。
RotatedRect的angle成员变量
该变量描述了矩阵从直立旋转到当前状态顺时针转过的角度值,其取值范围是[0,90]
。
换句话说,angle
是points()输出的0号点与1号点构成的线段与竖直方向的夹角。请参照下一节的图示。
矩形顶点编号与角度图示
测试代码
给出一段测试代码,方便读者理解该函数:
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;
static void help()
{
cout << "This program demonstrates finding the minimum enclosing box of a set\n"
<< "of points using function: minAreaRect().\n"
<< "Random points are generated and then enclosed.\n\n"
<< "Press ESC, 'q' or 'Q' to exit and any other key to regenerate the set of points.\n\n";
}
int main(int /*argc*/, char** /*argv*/)
{
help();
Mat img(500, 500, CV_8UC3, Scalar::all(0));
RNG& rng = theRNG();
for (;;)
{
int i, count = rng.uniform(1, 101);
vector<Point> points;
// Generate a random set of points
for (i = 0; i < count; i++)
{
Point pt;
pt.x = rng.uniform(img.cols / 4, img.cols * 3 / 4);
pt.y = rng.uniform(img.rows / 4, img.rows * 3 / 4);
points.push_back(pt);
}
// Find the minimum area enclosing bounding box
Point2f vtx[4];
RotatedRect box = minAreaRect(points);
box.points(vtx);
img = Scalar::all(0);
// Draw the points
for (i = 0; i < count; i++)
circle(img, points[i], 3, Scalar(0, 0, 255), FILLED, LINE_AA);
// 定义圆弧的参数
cv::Point center = vtx[0];
cv::Size axes(10, 10);
double angle = 0;
double startAngle = -90; // 圆弧起始角度(以度为单位)
double endAngle = box.angle-90; // 圆弧结束角度(以度为单位)
cv::Scalar color(0, 255, 0); // 绿色
// 在图像上绘制表示角度的圆弧
cv::ellipse(img, center, axes, angle, startAngle, endAngle, color,2);
line(img, vtx[0], cv::Point(vtx[0].x,48), Scalar(255, 255, 255), 1, LINE_AA);
// Draw the bounding box
for (i = 0; i < 4; i++)
{
line(img, vtx[i], vtx[(i + 1) % 4], Scalar(0, 255, 0), 1, LINE_AA);
putText(img, to_string(i), vtx[i]-cv::Point2f(16,-32), FONT_HERSHEY_SIMPLEX, 1, Scalar(255, 255, 255));
}
// Show the text info about the rectangle box.
cv::String strRectInfo = "The angle is: ";
strRectInfo += std::to_string(box.angle);
cv::putText(img, strRectInfo, cv::Point(0, 32), cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(255,255,255));
imshow("Rectangle, triangle & circle", img);
char key = (char)waitKey();
if (key == 27 || key == 'q' || key == 'Q') // 'ESC'
break;
}
return 0;
}
参考
opencv官方文档