OpenCV RotatedRect类中angle参数探究 C++

0.前言
本文主要探讨RotatedRect类angle的实际含义,为后续学者提供一定的参考。

1.官方手册
RotatedRect其一构造函数如下图(图1-1)所示。


在OpenCV图形坐标系中,水平方向向右为x轴正方向,垂直方向向下为y轴正方向,左上角为(0,0)点。
center表示矩形的中心坐标,size中包含了矩形的宽度和高度,angle是矩形顺时针方向的旋转角度。
图片来源地址:https://docs.opencv.org/4.6.0/db/dd6/classcv_1_1RotatedRect.html#aba20dfc8444fff72bd820b616f0297ee

2.angle含义探讨
笔者在学习该参数时参考了一些网络文献,在实际测试时发现实验结果与参考的文献结论有冲突,所以自行做了实验去探究angle的实际含义。

2.1实验设计
笔者从网络上文献中摘取了一幅图像,该图像如下(图2-1)所示。


图片来源连接:https://blog.csdn.net/mailzst1/article/details/83141632
对于图2-1,笔者做出了一些变动,将图2-1缩放到水平为400像素,保持原图像比例不变。得到四个矩阵角点坐标如下所示。

矩阵(1) 矩阵(2) 矩阵(3) 矩阵(4)
Point(43, 30) Point(233, 84) Point(100, 217) Point(264, 222)
Point(129, 30) Point(227, 39) Point(57, 205) Point(225, 145)
Point(129, 77) Point(312, 25) Point(81, 122) Point(265, 125)
Point(43, 77) Point(319, 70) Point(124, 135) Point(305, 201)

使用如下代码运行测试,从中发现规律。

点击查看代码
Mat mSrcImage(400, 400, CV_8UC3, Scalar(0, 0, 0));
int iTypeVal = 0;
vector<Point> vPoints;
void onTypeChange(int, void*)
{
	Mat mResult;
	mSrcImage.copyTo(mResult);
	if (iTypeVal == 0)//boundingRectO函数
	{
		Rect rRes=boundingRect(vPoints);
		rectangle(mResult, rRes, Scalar(0, 0, 255), 1);
	}
	else if (iTypeVal==1)//minAreaRectO函数
	{
		RotatedRect rrRes = minAreaRect(vPoints);
		Point2f pPoint[4];
		rrRes.points(pPoint);
		for (short a = 0; a < 4; ++a)
		{
			line(mResult, pPoint[a], pPoint[(a + 1) % 4], Scalar(0, 0, 255), 1);
		}
		cout << rrRes.center << "," << rrRes.angle << endl;
	}
	else if (iTypeVal == 2)//minEnclosingCircJeO函数
	{
		Point2f pCenter;
		float fR;
		minEnclosingCircle(vPoints, pCenter, fR);
		circle(mResult, pCenter, fR, Scalar(0, 0, 255), 1);
	}
	else if (iTypeVal == 3)//fitEllipseO函数
	{
		RotatedRect rrEllipse = fitEllipse(vPoints);
		ellipse(mResult, rrEllipse, Scalar(0, 0, 255), 1);
	}
	else if(iTypeVal==4)//minEnclosingTriangle函数
	{
		vector<Point2f> vTriangle(3);
		minEnclosingTriangle(vPoints, vTriangle);
		for (short a = 0; a < 3; ++a)
		{
			line(mResult, vTriangle[a], vTriangle[(a + 1) % 3], Scalar(0, 0, 255), 1);
		}
	}
	else//凸包
	{
		vector<Point> vHull;
		convexHull(vPoints, vHull, false, true);
		vector<vector<Point>> vvContours = { vHull };
		drawContours(mResult, vvContours, -1, Scalar(0, 0, 255), 1);
	}
	imshow("轮廓包围", mResult);
}
void openCVdemo::test_61()
{
	//四个矩阵
	vPoints.push_back(Point(43, 30));
	vPoints.push_back(Point(129, 30));
	vPoints.push_back(Point(129, 77));
	vPoints.push_back(Point(43, 77));

	//vPoints.push_back(Point(233, 84));
	//vPoints.push_back(Point(227, 39));
	//vPoints.push_back(Point(312, 25));
	//vPoints.push_back(Point(319, 70));

	//vPoints.push_back(Point(100, 217));
	//vPoints.push_back(Point(57, 205));
	//vPoints.push_back(Point(81, 122));
	//vPoints.push_back(Point(124, 135));

	//vPoints.push_back(Point(264, 222));
	//vPoints.push_back(Point(225, 145));
	//vPoints.push_back(Point(265, 125));
	//vPoints.push_back(Point(305, 201));

	for (Point& p : vPoints)
	{
		circle(mSrcImage, p, 2, Scalar(255, 255, 255), -1, LineTypes::FILLED);
	}
	imshow("原图", mSrcImage);
	namedWindow("轮廓包围", WindowFlags::WINDOW_AUTOSIZE);
	createTrackbar("轮廓类型", "轮廓包围", &iTypeVal, 5, onTypeChange, nullptr);
	onTypeChange(0, nullptr);
}

2.2代码测试
分别测试每个矩阵的四个点,得到如下测试结果(图2-2)。

2.3规律探究
根据控制台输出的结果笔者绘制了下图(图2-3)。


上图标注了每个矩阵的angle值。不难看出,将图像x轴按顺时针方向旋转(旋转角度>0),旋转过程中遇到的第一个与旋转轴平行的边为L,angle的值是L与水平方向的夹角。具体示例如下图(图2-4)所示。

3.结语
本文是笔者探究的规律,若有误,请指正。

热门相关:萌妻鲜嫩:神秘老公晚上见   腹黑冷王:娘子不太乖   浴火重生:恶魔五小姐   废材归来:绝色魔君驭兽后   煌煌天道无上剑宗