opencv学习笔记之十五——最小外接矩形(RotatedRect返回角度angle问题)

最近在看一个车牌识别开源项目时,对其中RotatedRect的角度属性理解得不清楚,也查找了大量博客,得出了基本结论,最后通过实验进一步进行了验证。

RotatedRect该类表示平面上的旋转矩形,有三个属性:

  1. 矩形中心点(质心)
  2. 边长(长和宽)
  3. 旋转角度

旋转角度angle的范围为[-90,0),当矩形水平或竖直时均返回-90,请看下图:

opencv学习笔记之十五——最小外接矩形(RotatedRect返回角度angle问题)_第1张图片

来源:https://blog.csdn.net/qq_24237837/article/details/77850496

看了这幅图,我明白了一些,但还是有疑虑,这个角度如何产生?究竟是那条边(宽?高?)与哪条坐标系的角度呢?矩形的宽和高究竟如何确定?

带着这个疑问,继续查找,下面的博客给出了解释(图均来自该博客):

opencv学习笔记之十五——最小外接矩形(RotatedRect返回角度angle问题)_第2张图片

也就是说,opencv的原点在左上角,横向为x轴,纵向为y轴,将x轴逆时针转动,碰到的第一条边(延长线)就为宽(Winth),而与之所成角度就为angle。那么问题又来了,矩形宽不等于高,若宽>高,当x轴逆时针转动碰到的第一条边是短边,将这个短边作为宽Winth,那不是和 宽>高 这个实际情况矛盾么?且看下图:

opencv学习笔记之十五——最小外接矩形(RotatedRect返回角度angle问题)_第3张图片

角度确实已经确定了,但是width“有问题”,当我们算宽高比时,若不加判断,有些情况下(宽>高的实际情况)就会出错(结果相反),那如何解决呢?

当然,opencv这样不区分短长边就是为了照顾正方形。所以计算时我们首先要判断:width/height <1?,若是,则需要交换width和height 的值,这样在后续操作(车牌矫正,数学运算)才不会出错。

为了验证是否是这样我做个小实验,结果如下:

width>height

opencv学习笔记之十五——最小外接矩形(RotatedRect返回角度angle问题)_第4张图片

 

width

opencv学习笔记之十五——最小外接矩形(RotatedRect返回角度angle问题)_第5张图片

 

顺便做了个仿射变换:

opencv学习笔记之十五——最小外接矩形(RotatedRect返回角度angle问题)_第6张图片

 

综合上面的内容,角度和边长的确定已经弄清楚了。

我发现学习openCV,仅仅会用函数,只知道参数和属性还不行啊,太容易忘了,两年后真不一定记得呢,何况真正的开发都是要挖源码的,所以我决定以后在调用函数的同时,要看opencv的源码,理解它的实现过程,看这些大牛是如何写代码的,同时会记录在博客上,方便以后查看,嗯,就这样吧。

实验源码:

#include "opencv2/opencv.hpp"
#include 
using namespace cv;
using namespace std;
 void main()
 {
	    //轮廓最小外接矩形的绘制
		Mat srcImg = imread("pen4.jpg");
		//srcImg = srcImg(Rect(10,10,1000,690));
		namedWindow("scr", 0);
		imshow("scr", srcImg);
		Mat dstImg = srcImg.clone();
	    cvtColor(srcImg, srcImg, CV_BGR2GRAY);
	    threshold(srcImg, srcImg, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY_INV); //二值化
	    //medianBlur(srcImg, srcImg, 9);
	    imshow("threshold", srcImg);
	
	    vector> contours;
		findContours(srcImg, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
	     Rect boundRect;  //定义外接矩形
	     RotatedRect box; //定义最小外接矩形

		 vector>::iterator itc = contours.begin();
	     Point2f rect_point[4];
		 int i = 0;
     for (; itc!=contours.end(); itc++)
		    {  
		 
		         box = minAreaRect(Mat(*itc));  //计算每个轮廓最小外接矩形(旋转)
				 boundRect = box.boundingRect();
				 //boundRect = boundingRect(Mat(*itc));
		         circle(dstImg, Point(box.center.x, box.center.y), 5, Scalar(255,0, 0), -1, 8);  //绘制最小外接矩形的中心点
				// rectangle(dstImg, Point(boundRect.x, boundRect.y), Point(boundRect.x + boundRect.width, boundRect.y + boundRect.height), Scalar(0, 255, 0), 2, 8);
				 rectangle(dstImg, boundRect.tl(), boundRect.br() , Scalar(0, 255, 0), 3, 8);
				 box.points(rect_point);  //把最小外接矩形四个端点复制给rect数组
		        for (int j = 0; j<4; j++)
			         {
					line(dstImg, rect_point[j], rect_point[(j + 1) % 4], Scalar(0, 0, 255), 3, 8);  //绘制最小外接矩形每条边
		             }
				cout << "angle " << i << " :" << box.angle << endl;
				cout << "width " << i << " :" << box.size.width << endl;
				cout << "height " << i << " :" << box.size.height << endl<

你可能感兴趣的:(【OpenCV】,【C++解惑】)