在使用opencv的过程中经常会使用minAreaRect返回一个轮廓的外接矩形,它返回的是一个RotatedRect的类:
class CV_EXPORTS RotatedRect
{
public:
//! various constructors
RotatedRect();
RotatedRect(const Point2f& center, const Size2f& size, float angle);
RotatedRect(const CvBox2D& box);
//! returns 4 vertices of the rectangle
void points(Point2f pts[]) const;
//! returns the minimal up-right rectangle containing the rotated rectangle
Rect boundingRect() const;
//! conversion to the old-style CvBox2D structure
operator CvBox2D() const;
Point2f center; //< the rectangle mass center
Size2f size; //< width and height of the rectangle
float angle; //< the rotation angle. When the angle is 0, 90, 180, 270 etc., the rectangle becomes an up-right rectangle.
};
这个类中包含了外接矩形的中心center、大小size以及角度angle。为了更好的理解这几个参数的意义,请看下图:
在opencv中,坐标的原点在左上角,与x轴平行的方向为角度为0,逆时针旋转角度为负,顺时针旋转角度为正。而RotatedRect类是以矩形的哪一条边与x轴的夹角作为角度的呢?参考http://blog.csdn.net/mine1024/article/details/6044856的观点:angle 是水平轴(x轴)逆时针旋转,与碰到的第一个边的夹角,而opencv默认把这个边的边长作为width。由前面所说,angle的取值范围必然是负的,实际angle的取值范围为(-90,0]。利用成员函数void points(Point2f pts[]) const;可以计算出矩形的四个角点。计算的原理很简单:
图中的θ=-angle。从图中可知p[0].x=center.x-a
其中a=0.5*width*cosθ - b, b=heigth*sinθ,且cosθ=cos(angle),sinθ=sin(-angle)=-sin(angle)
那么p[0].x=center.x - 0.5*(width*cos(angle) + heigth*sin(angle)),对于p[0].y也可以用同样的原理计算,对应opencv中的源代码如下:
void RotatedRect::points(Point2f pt[]) const
{
double _angle = angle*CV_PI/180.;
float b = (float)cos(_angle)*0.5f;
float a = (float)sin(_angle)*0.5f;
pt[0].x = center.x - a*size.height - b*size.width;
pt[0].y = center.y + b*size.height - a*size.width;
pt[1].x = center.x + a*size.height - b*size.width;
pt[1].y = center.y - b*size.height - a*size.width;
pt[2].x = 2*center.x - pt[0].x;
pt[2].y = 2*center.y - pt[0].y;
pt[3].x = 2*center.x - pt[1].x;
pt[3].y = 2*center.y - pt[1].y;
}
由此可知道,在opencv中,RotatedRect的角度实际上就是水平轴x与矩形宽width的夹角,而在利用minAreaRect函数去求一个外接矩形时,函数对于矩形长和宽的选择取决于这个外接矩形的边与水平轴的角度距离,即将水平轴逆时针旋转,最先与其平行的边的长度作为宽度width,另外一条边则为height,而角度则直接取该边与水平轴的夹角。应该意识到,在minAreaRect函数中,RotatedRect的width和height的选取跟矩形的尺寸无关,并非长的就是height,短的就是width。