OpenCV学习笔记(十九)——矩形形状的拟合以及周长、面积的计算

前言:

    本文我们来学习矩形形状的拟合以及周长、面积的计算。

一、点集的最小外包

        点集是指坐标点的集。已知二维笛卡尔坐标系中的很多坐标点,需要找到包围这些坐标点的最小外包四边形或者圆,在这里最小指的是最小面积。如下图所示:


      在OpenCV中,通过一系列的点(即点集)去找到这三类最小外包几何单元都有相应的函数可以实现。

1.1 最小外包旋转矩形

         OpenCV提供了两个关于矩形的类:一个是关于直立矩形的Rect;另一个是关于旋转矩形的RotatedRect,它们的内部定义如下所示:

typedef struct Rect
{
	int x;  // 方形的最左角的 x坐标 
	int y;  // 方形的最上或者最下角的 y坐标 
	int width;  // 宽 
	int height; // 高 
}
Rect;

以及

class CV_EXPORTS RotatedRect
{
public:
	//构造函数  
	RotatedRect();
	RotatedRect(const Point2f& center, const Size2f& size, float angle);
	RotatedRect(const CvBox2D& box);

	//返回矩形的4个顶点  
	void points(Point2f pts[]) const;
	//返回包含旋转矩形的最小矩形  
	Rect boundingRect() const;
	//转换到旧式的cvbox2d结构  
	operator CvBox2D() const;

	Point2f center; //矩形的质心  
	Size2f size;    //矩形的边长  
	float angle;    //旋转角度,当角度为0、90、180、270等时,矩形就成了一个直立的矩形  
};

      因此从源代码中可以看出来,只需要三个要素就可以确定一个旋转矩形,它们是中心坐标、尺寸(宽、高)和旋转角度。对于RotatedRect,OpenCV并没有提供类似于画直立矩形的函数rectangle,可以通过画四条边的方式画出该旋转矩形。但是要想画出矩形的四条边,就得知道其四个顶点的坐标,OpenCV提供了函数:

RotatedRect minAreaRect(InputArray points)

详细描述如下:


      返回输入点集points的最小外包旋转矩形。对于该函数的C++API,参数points接收三种点集形式,其中第一种是N*2的Mat类型,指每一行代表一个点的坐标且数据类型只能是CV_32S或者CV_32F;第二种输入类型是vector<Point>或者vector<Point2f>,即多个点组成的向量;第三种是N*1的双通道Mat类型。

     举例:求五个坐标点(4,2)、(1,1)、(5,2)、(8,2)、(10,6)的最小外包旋转矩形。C++API的使用代码如下: 

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;


int main()
{
//我们创建各个点的Point
	Point2f point1 = Point2f(10,10);
	Point2f point2 = Point2f(50, 10);
	Point2f point3 = Point2f(10,100);
	Point2f point4 = Point2f(50, 100);
	Point2f point5 = Point2f(20, 50);
	vector<Point2f> points;       // 将5个点存储为vector<Point2f>类型
	points.push_back(point1);
	points.push_back(point2);
	points.push_back(point3);
	points.push_back(point4);
	points.push_back(point5);
	
	//在黑色背景上画出这些点
	Mat dst_img = Mat(Size(120,120),CV_8UC3);
	for (auto i = 0; i < 5; ++i)
	{
		circle(dst_img, points[i],5,Scalar(0,0,255),-1,8,0);
	}
	
	// 计算点集的最小外包旋转矩形
	RotatedRect rRect = minAreaRect(points);
	//打印最小外包旋转矩形的信息
	cout << "最小外包旋转矩形的中心坐标:" << rRect.center<< endl;
	cout << "最小外包旋转矩形的尺寸:" << rRect.size << endl;
	cout << "最小外包旋转矩形的旋转角度:" << rRect.angle << endl;
	imshow("绘制出点集",dst_img);
	waitKey(0);
	return 0;
}


运行程序可以看到:



1.2 旋转矩形的四个顶点

        在上一节我们说到,旋转矩形是通过中心点坐标、尺寸和旋转角度三个方面来定义的,当然通过这三个属性值就可以计算出旋转矩形的四个顶点,这样虽然简单,但是写起来比较复杂。OpenCV 3.X提供了函数:

void boxPoints(RotatedRect box, OutputArray Points)

便于计算旋转矩形的4个顶点,这样就可以使用函数line画出四个顶点的连线,从而画出旋转矩形。演示代码如下:

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;


int main()
{
//我们创建各个点的Point
	Point2f point1 = Point2f(200,200);
	Point2f point2 = Point2f(280, 150);
	Point2f point3 = Point2f(300,100);
	Point2f point4 = Point2f(260, 300);
	vector<Point2f> points;
	points.push_back(point1);
	points.push_back(point2);
	points.push_back(point3);
	points.push_back(point4);
	
	//在黑色背景上画出这些点
	Mat dst_img = Mat(Size(400,400),CV_8UC3);
	for (auto i = 0; i <points.size(); ++i)
	{
		circle(dst_img, points[i],5,Scalar(0,0,0),-1,8,0);
	}
	
	// 计算点集的最小外包旋转矩形
	RotatedRect rRect = minAreaRect(points);
	Mat vertices;     

	// 求得旋转矩形的四个顶点坐标,然后在黑色画板上用line函数画出四条边,得到该矩形
	boxPoints(rRect, vertices);       //计算出的4个顶点存在一个4行2列的Mat对象中,每一行代表一个顶点坐标
	cout << "四个顶点坐标如下:" << endl;
	cout << vertices << endl;  // 打印四个顶点
	for (auto i = 0; i < vertices.rows; ++i)
	{
		Point p1 = Point(vertices.at<float>(i,0), vertices.at<float>(i, 1));
		Point p2 = Point(vertices.at<float>((i+1)%4, 0), vertices.at<float>((i + 1) % 4, 1));
		line(dst_img,p1,p2,Scalar(0,0,255),3);
	}

	imshow("绘制出点集以及对应的最小旋转矩形", dst_img);
	waitKey(0);
	return 0;
}


运行程序如下:



1.3、最小外包直立矩形

        OpenCV提供了函数:

Rect boundingRect(InputArray points)

    在VS2015中的提示为:

   

     来实现点集的最小外包直立矩形。在OpenCV 2.X中,该函数的C++API输入点集只有两种形式:vector<Point2f>、

vector<Point>或者N行1列的双通道Mat类型且数据只能是CV_32S或者CV_32F,不再适用于N*2的单通道Mat类型。而在OpenCV种,为了和函数minAreaRect、minEnclosingCircle统一,该函数也适用于三种点集形式,在OpenCV 3.X版本中对该函数的C++API做了改变,输入点集也可以是一个N*2的单通道Mat对象,代码如下:

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;


int main()
{
//我们创建各个点的Point
	Point2f point1 = Point2f(200,200);
	Point2f point2 = Point2f(280, 150);
	Point2f point3 = Point2f(300,100);
	Point2f point4 = Point2f(260, 300);
	vector<Point2f> points;
	points.push_back(point1);
	points.push_back(point2);
	points.push_back(point3);
	points.push_back(point4);
	
	//在黑色背景上画出这些点
	Mat dst_img = Mat(Size(400,400),CV_8UC3);
	for (auto i = 0; i <points.size(); ++i)
	{
		circle(dst_img, points[i],5,Scalar(0,0,0),-1,8,0);
	}
	
	// 计算点集的最小外包直立矩形
	Rect rRect = boundingRect(points);
	
	cout << "最小外包直立矩形的顶点坐标如下:" << endl;
	cout << rRect << endl;  
	rectangle(dst_img,Point(rRect.x, rRect.y), Point(rRect.x+ rRect.width, rRect.y + rRect.height),Scalar(0,0,255),2,8,0);

	imshow("绘制出点集以及对应的最小旋转矩形", dst_img);
	waitKey(0);
	return 0;
}


运行程序,如下所示:



二、轮廓的周长和面积

      如何计算点集所围区域的周长和面积呢?OpenCV对这两方面的度量都给出了相应的计算函数,其中函数:

double arcLength(InputArray curve,bool closed)

      用来计算点集所围区域的周长,参数curve代表输入点集,对于该函数的C++API,假设点集中有n个坐标点,curve一般有三种形式:vector<Point>、N*2的单通道Mat(一行代表一个坐标点)、N*1的双通道Mat;参数closed是指点集是否首尾相接。

    函数:

double contourArea(InputArray contour, bool oriented=false)

用来计算点集所围区域的面积,参数contour和函数arcLength的参数curve类似,代表一个点集。

    下面介绍contourArea和arcLength的C++API的使用方式:

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;


int main()
{
//我们创建各个点的Point
	Point2f point1 = Point2f(0,0);
	Point2f point2 = Point2f(50, 30);
	Point2f point3 = Point2f(100,0);
	Point2f point4 = Point2f(100, 100);
	vector<Point2f> points;
	points.push_back(point1);
	points.push_back(point2);
	points.push_back(point3);
	points.push_back(point4);
	
	//在黑色背景上画出这些点
	Mat dst_img = Mat(Size(200,200),CV_8UC3);
	for (auto i = 0; i <points.size(); ++i)
	{
		circle(dst_img, points[i],5,Scalar(0,0,0),-1,8,0);
	}
	
	// 计算点集所围区域的周长和面积
	double length1 = arcLength(points,false);     // 首尾不相连
	double length2 = arcLength(points, true);    //   首尾相连
	double area = contourArea(points);
	//打印周长和面积
	cout << "首尾不相连的周长:" << length1 << endl;
	cout << "首尾相连的周长:" << length2 << endl;
	cout << "面积:" << area << endl;
	imshow("绘制出点集", dst_img);
	waitKey(0);
	
	return 0;
}


运行程序如下:


猜你喜欢

转载自blog.csdn.net/weixin_41695564/article/details/80100207
今日推荐