OpenCV 外接矩形与最小外接矩形
由于项目的需要,在找目标时需要将目标图像“抠”下来,作为下一步骤的输入图像。当目标区域的最小外接矩形没有倾斜时还好,直接用OpenCV的cv::Rect(关于没有倾斜角度矩形ROI的提取,见:图像处理之定位_yishuihanq的博客-CSDN博客_图像定位处理),取ROI就可以了;但是如果目标区域的最小外接矩形有倾斜角度时,就不好搞了,OpenCV中没有这样的函数,需要自己想办法去提取。
需要将图1中的目标“书”,提取出来,提取后的结果图像,如图2。
图1 原图src
图2 ROI图像
对此有以下几种方法:
case 1、根据目标ROI的倾斜角度angle,对原图进行旋转,再提取出ROI区域;
case 2、获取目标ROI的外接矩形rect,对外接矩形rect 旋转angle角度,再提取出ROI区域;
步骤:
1、求得书本的轮廓;
2、获取书本轮廓的最小外接矩形rotatedRect(主要是获取旋转中心、旋转角度);
3、进行旋转校正,提取目标区域。
#if(1)
//方法1:对整个大图进行旋转,然后提取目标ROI.
cv::Mat dstTemp1;
cv::Mat result1;
cv::Size2f rotatedRectSize = rotatedRect.size;
//cv::Size rect_size = minRect.size();
cv::Point2f center = rotatedRect.center;//最小外接矩形中心点坐标
//获取旋转矩阵,绕中心进行旋转. m_resize缩放倍数
cv::Mat M = cv::getRotationMatrix2D(center, rotatedRect.angle, 1);
cv::warpAffine(src, dstTemp1, M, src.size());
//提取ROI
result1 = dstTemp1( cv::Rect(center.x - (rotatedRectSize.width / 2),
center.y - (rotatedRectSize.height / 2),
rotatedRectSize.width, rotatedRectSize.height) );
cv::imwrite("../dstTemp1.jpg", dstTemp1);
cv::namedWindow("dstTemp1", 0);
cv::imshow("dstTemp1", dstTemp1);
cv::imwrite("../result1.jpg", result1);
cv::namedWindow("result1", 0);
cv::imshow("result1", result1);
cv::waitKey(0);
#endif
case 2:
#if(1)
//方法2:对目标ROI区域倾斜矩形的外接矩形进行旋转,然后提取目标ROI.
cv::Rect boundingRect = rotatedRect.boundingRect();//返回包含旋转矩形的最小矩形
cv::Mat newSrc = srcCopy(boundingRect);
//ROI区域倾斜矩形的外接矩形,的旋转中心.
cv::Point newCenter;
newCenter.x = rotatedRect.center.x - boundingRect.x;
newCenter.y = rotatedRect.center.y - boundingRect.y;
//获取旋转矩阵,绕中心进行旋转. m_resize缩放倍数
cv::Mat newM2 = cv::getRotationMatrix2D(newCenter, rotatedRect.angle, 1);
cv::Mat newDstTemp2;
cv::warpAffine(newSrc, newDstTemp2, newM2, newSrc.size());
//提取ROI
cv::Mat newResult2;
newResult2 = ( newDstTemp2(cv::Rect(newCenter.x - (rotatedRect.size.width / 2),
newCenter.y - (rotatedRect.size.height / 2),
rotatedRect.size.width, rotatedRect.size.height)) );
//cv::putText(newResult2, std::to_string(newResult2.size()),
// cv::Point(160, 100),
// cv::FONT_HERSHEY_PLAIN, 6, cv::Scalar(0, 0, 255), 10);
//方法2:对目标ROI区域倾斜矩形的外接矩形进行旋转,然后提取目标ROI.
cv::imwrite("../newDstTemp2.jpg", newDstTemp2);
cv::namedWindow("newDstTemp2", 0);
cv::imshow("newDstTemp2", newDstTemp2);
//cv::waitKey(0);
cv::imwrite("../newResult2.jpg", newResult2);
cv::namedWindow("newResult2", 0);
cv::imshow("newResult2", newResult2);
cv::waitKey(0);
#endif
最终结果图像如下:
case 3:
// 3.OpenCV中的方法
//contours.at(i):目标轮廓的最小外接矩形
cv::RotatedRect rect = cv::minAreaRect(contours.at(i));
cv::Mat M, rotated;
//cv::Mat cropped;
// get angle and size from the bounding box
double angle = static_cast(rect.angle);
cv::Size rect_size = rect.size;
if (angle < -45.0)
{
angle += 90.0;
cv::swap(rect_size.width, rect_size.height);
}
// get the rotation matrix
M = getRotationMatrix2D(rect.center, angle, 1.0);
// perform the affine transformation
warpAffine(src, rotated, M, src.size(), cv::INTER_CUBIC);
// crop the resulting image
getRectSubPix(rotated, rect_size, rect.center, cropped);
//cv::imwrite("../testImage/RotatedRectCropped.jpg", cropped);
cv::namedWindow("crop", 0);
cv::imshow("crop", cropped);
cv::waitKey(0);
cv::boxpoints函数的作用:获取矩形的四个顶点坐标。
# 获取最小外接矩阵,中心点坐标,宽高,旋转角度
cv::RotatedRect rect = cv::minAreaRect(contours)
# 获取矩形四个顶点坐标,浮点型
std::vector boxPts;
boxPts = cv::boxPoints(rect)
# 取整
boxPts = np.int0(boxPts)
在OpenCV的坐标体系下,纵坐标最小的是top_point,纵坐标最大的是bottom_point, 横坐标最小的是left_point,横坐标最大的是right_point。
===================================================================
-.-.-.-.-以下内容原文链接:https://blog.csdn.net/Dorwin666/article/details/109518925-.-.-.-.-.
RotatedRect minAreaRect(InputArray points)
RotatedRect表示平面上的旋转矩形
class CV_EXPORTS RotatedRect
{
public:
//构造函数
RotatedRect();
RotatedRect(const Point2f& center, const Size2f& size, float angle);//矩形中心点(质心)、边长(长和宽)、旋转角度
RotatedRect(const CvBox2D& box);
void points(Point2f pts[]) const;//!返回矩形的4个顶点
Rect boundingRect() const; //返回包含旋转矩形的最小矩形
operator CvBox2D() const; //!转换到旧式的cvbox2d结构
Point2f center; //矩形的质心
Size2f size; //矩形的边长
float angle; //旋转角度,当角度为0、90、180、270等时,矩形就成了一个直立的矩形
};
这个类中包含了外接矩形的中心center、大小size以及角度angle,首先center是很好理解的,就是这个矩形的中心点位置,而angle代表的是旋转角度,size包含一个width和一个height,代表的是矩形的宽和高。
矩形的Width与Height定义:矩形坐标系中X轴逆时针旋转,碰到的第一条边为Width,另一条边为Height,其中矩形坐标系的原点为图像坐标系下矩形四个顶点中y值最大的点,如图所示:
根据上图,说明以下几点:
顶点p[0]的位置可以这样理解:
ⓐ 如果没有边与坐标轴平行,则Y坐标最大的点为p[0]点,如矩形(2)(3)(4);
ⓑ 如果有边与坐标轴平行,则有两个Y坐标最大的点。此时,左侧的点为p[0]点。如矩形(1)。
即:Y坐标最大的点为p[0]。如果有两个最大的Y坐标,则左侧点(X坐标较小)为p[0]。
但是opencv版本从4.1以后minAreaRect()返回的顶点和角的定义就改变了,p[0]定义为四个顶点中x值最小点,然后p[1]、p[2]、p[3]按顺时针旋转顺序定义,角度为90°减去上个版本原定义角度。width与height的定义应该没变,p[0]与p[1]之间的长度为height,另一边为width。额,测试了三个图型后得到的结论貌似是这样,还有待进一步验证。。。
-.-.-.-.-以上内容原文链接:https://blog.csdn.net/Dorwin666/article/details/109518925-.-.-.-.-.
====================================================================