最小外接矩形(RotatedRect返回角度angle、width、height问题)

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

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

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

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

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

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

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

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

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

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

width>height

width<height

顺便做了个仿射变换:

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

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

实验源码:

#include "opencv2/opencv.hpp"
#include <iostream>
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<vector<Point>> contours;
		findContours(srcImg, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
	     Rect boundRect;  //定义外接矩形
	     RotatedRect box; //定义最小外接矩形

		 vector<vector<Point>>::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<<endl;
				char width[20], height[20];
				sprintf(width, "width=%0.2f", box.size.width);//
				sprintf(height, "height=%0.2f", box.size.height);//
				putText(dstImg, width, box.center, CV_FONT_HERSHEY_COMPLEX_SMALL, 0.85, Scalar(0, 0, 255));
				putText(dstImg, height, box.center + Point2f(0, 20), CV_FONT_HERSHEY_COMPLEX_SMALL, 0.85, Scalar(0, 0, 255));
	     }
	 namedWindow("rec_dst", 0);
	 imshow("rec_dst", dstImg);

		
		//旋转校正
		float angle_rotation;
		if ((box.size.width / box.size.height) < 1)
			angle_rotation = 90 + box.angle;//正数,逆时针旋转
		else
			angle_rotation = box.angle; //负数,顺时针旋转

		double scale = 0.78;//缩放比例
		Mat rot_m = getRotationMatrix2D(box.center,angle_rotation,scale);//获得旋转矩阵
		warpAffine(dstImg, dstImg, rot_m, dstImg.size());//仿射变换

	    namedWindow("dst", 0);
		imshow("dst", dstImg);
		
	     waitKey(0);


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值