实验中使用到最小外接矩阵角度的内容,写博客记录。
本篇主要参考了如下四个博客:
https://blog.csdn.net/u013925378/article/details/84563011
https://blog.csdn.net/weixin_40647819/article/details/80884594
https://www.cnblogs.com/little-monkey/p/7429579.html
https://blog.csdn.net/bemy1008/article/details/86237961
第一篇博客主要介绍opencv外接矩阵boundingRect与最小外接矩阵minAreaRect的用法区别,后三篇博客结合可对最小外接旋转角度有较为清晰的认识。
下面对知识点做总结记录:
1.外接矩阵与最小外接矩阵的差别
Rect boundingRect(InputArray points)得到包覆此轮廓的最小正矩形,RotatedRect minAreaRect(InputArray points)得到包覆轮廓的最小矩阵。具体可以查阅第一个链接博客
2.RotatedRect有三个属性1)矩形中心点(质心)2)边长(长和宽)3)旋转角度(返回的值是角度值而非弧度值)
旋转角度的范围是[-90,0),当矩形水平或者竖直时均返回-90。(下图来源上面链接2博客)
在opencv中,坐标的原点在左上角,与x轴平行的方向角度为0,逆时针旋转角度为负,顺时针旋转角度为正(在x轴和y轴的 象限里的角度为正的原则吧)。minAreaRect类中的角度值是水平轴(x轴)逆时针旋转与碰到的第一个边的夹角。因此, 角度值必然是负值,取值范围为[-90,0)。同时,以这样的旋转方式,碰到的第一个边被定义为width。由于是以这样的方 式判断width,因此存在width<height的情况。
3. Mat getRotationMatrix2D(Point2f center, double angle, double scale)(注意它中的角度与最小外接矩阵角度的差异)
该函数中的角度:如果该角度为正值,则为逆时针旋转
如果该角度为负值,则为顺时针旋转
下面是一个测试代码,主要是展现上边三个函数的使用方法:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
RNG rng(12345);
int main()
{
Mat srcImg = imread("timg.jpg");
Mat dstImg = srcImg.clone();
cvtColor(srcImg, srcImg, CV_BGR2GRAY);
threshold(srcImg, srcImg, 0, 255.0, THRESH_BINARY_INV | CV_THRESH_OTSU);
Mat element = getStructuringElement(MORPH_RECT, Size(11, 11), Point(-1, -1)); //定义结构元素
dilate(srcImg, srcImg, element);
vector<vector<Point>> contours;
vector<Vec4i> hierarcy;
findContours(srcImg, contours, hierarcy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE, Point(0, 0));
Rect boundRect;//只绘制最大轮廓对应的外接矩阵
RotatedRect box;//只绘制最大轮廓对应的最小外接矩阵
Point2f rect[4];
Mat drawing = Mat::zeros(srcImg.size(), CV_8UC3);
int len = 0;
for (int i = 0; i<contours.size(); i++)
{
//绘制轮廓
/*Scalar color = Scalar(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256));
drawContours(drawing, contours, (int)i, color, 2, 8, hierarcy);*/
cout << contours[i].size() << endl;
if (contours[i].size()>len)
{
len = contours[i].size();
box = minAreaRect(Mat(contours[i]));//最小外接矩阵
boundRect = boundingRect(Mat(contours[i]));//外接矩阵
}
}
//绘制外接矩阵绿色框绘制
rectangle(dstImg, Point(boundRect.x, boundRect.y), Point(boundRect.x + boundRect.width, boundRect.y + boundRect.height), Scalar(0, 255, 0), 2, 8);
box.points(rect);
//绘制最小外接矩阵红色框绘制
for (int j = 0; j < 4; j++)
{
line(dstImg, rect[j], rect[(j + 1) % 4], Scalar(0, 0, 255), 2, 8);//
}
imshow("外接矩阵", dstImg);
//最小外接矩阵的角度,根据角度对最小外接矩阵旋转
float boxangle = box.angle;
cout << boxangle << endl;
cout << "width:" << box.size.width << '\t' << "height:" << box.size.height << endl;
//根据角度对图像进行旋转
if (0< abs(boxangle) && abs(boxangle) <= 45)
boxangle = boxangle;
else if (45< abs(boxangle) && abs(boxangle)<90)
boxangle = 90 - abs(boxangle);
Point2f center = box.center; //定义旋转中心坐标
double angle0 = boxangle;
double scale = 1;
Mat roateM = getRotationMatrix2D(center, angle0, scale);//获得旋转矩阵,顺时针为负,逆时针为正
warpAffine(dstImg, dstImg, roateM, dstImg.size());//仿射变换
imshow("根据角度旋转之后图像", dstImg);
waitKey(0);
return 0;
}
对下面图像(图像来源网络)使用上述方法得到其外接矩阵(绿色)、最小外接矩阵(红色)、及其根据最小外接矩阵角度使用仿射变换获得的旋转图像。
结果图:
文中若有错误或不妥之处,还望指出,以便共同学习!