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;
}


运行程序如下:


<think>好的,我现在需要帮助用户解决关于使用RGBD相机测量规则物体并实现最优装箱的问题。用户希望得到Python代码实现的完整流程,包括实时检测和后处理,以及布局算法。我需要先理清整个流程,分步骤来思考。 首先,RGBD相机能提供RGB图像和深度信息,这对于检测物体的几何形状和尺寸非常重要。规则物体如立方体、长方体、球体等,可能需要不同的检测方法。实时检测可能需要使用深度学习模型,比如YOLO或Mask R-CNN,来识别物体并分割出它们的区域。同时,深度信息可以用来计算实际尺寸。 然后是测量部分。对于立方体或长方体,可能需要检测边缘和角点,利用深度数据计算长宽高。球体的话,可能需要检测球面并通过点云数据计算半径和中心位置。这部分可能需要用到Open3D或者PCL库来处理点云。 后处理方面,可能需要处理静态图像,进行更精确的测量,比如使用边缘检测算法(Canny)或霍夫变换来识别几何形状,再结合深度图计算尺寸。这时候需要将2D图像坐标转换为3D点云坐标,通过相机内参和坐标变换来实现。 接下来是最优装箱问题,这属于三维装箱问题,属于NP难问题,通常需要启发式算法或遗传算法等来寻找较优解。可能需要将物体按体积排序,使用贪心算法,或者寻找现有的装箱算法库来整合。 现在,我需要考虑代码的结构。实时检测部分可能需要用到OpenCV和Pyrealsense(如果使用Intel RealSense相机),或者其他的RGBD相机SDK。然后,使用深度学习框架如PyTorch或TensorFlow加载预训练模型进行物体检测和分割。测量部分需要结合点云处理,可能需要Open3D库。装箱算法可能需要自己实现或调用现有库。 可能遇到的难点包括:点云处理的精度、实时性能的优化、不同形状物体的准确测量,以及装箱算法的效率和效果。需要分步骤解决每个部分,并确保各模块之间的数据流正确。 还需要考虑用户的实际应用场景,比如工业装箱、物流等,不同的场景可能需要不同的优化策略。例如,实时检测可能需要高帧率,而后处理可能更注重精度。 最后,代码示例需要模块化,分步骤展示各个部分,并提供必要的注释,方便用户理解和调整参数。同时,要提醒用户可能需要根据实际硬件和环境调整参数,比如相机标定、光照条件等。</think>以下是一个基于Python的RGBD相机规则几何体测量与最优装箱解决方案的完整流程框架,包含实时检测和离线处理两种模式: ### 一、系统架构设计 ```mermaid graph TD A[RGBD数据获取] --> B{处理模式选择} B --> C[实时检测模式] B --> D[图像后处理模式] C --> E[目标检测与分割] D --> E E --> F[三维尺寸测量] F --> G[装箱优化算法] ``` ### 二、核心代码实现 #### 1. RGBD数据采集(以Intel RealSense为例) ```python import pyrealsense2 as rs pipeline = rs.pipeline() config = rs.config() config.enable_stream(rs.stream.color, 640, 480, rs.format.bgr8, 30) config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30) profile = pipeline.start(config) depth_scale = profile.get_device().first_depth_sensor().get_depth_scale() ``` #### 2. 实时检测与测量(基于Open3D和OpenCV) ```python def measure_object(depth_frame, bbox): # 转换为点云数据 pc = rs.pointcloud() points = pc.calculate(depth_frame) vertices = np.asanyarray(points.get_vertices()) # 提取目标区域点云 obj_points = vertices[bbox[1]:bbox[3], bbox[0]:bbox[2]] # 几何特征分析 pcd = o3d.geometry.PointCloud() pcd.points = o3d.utility.Vector3dVector(obj_points) aabb = pcd.get_axis_aligned_bounding_box() return { 'width': aabb.get_max_bound()[0] - aabb.get_min_bound()[0], 'height': aabb.get_max_bound()[1] - aabb.get_min_bound()[1], 'depth': aabb.get_max_bound()[2] - aabb.get_min_bound()[2] } ``` #### 3. 后处理测量算法(平面拟合优化) ```python def refine_measurement(pcd): plane_model, inliers = pcd.segment_plane( distance_threshold=0.01, ransac_n=3, num_iterations=1000 ) [a, b, c, d] = plane_model return np.sqrt(a**2 + b**2 + c**2) # 返回平面法向量长度用于验证 ``` #### 4. 三维装箱算法示例(基于贪心策略) ```python def bin_packing(items, container_size): sorted_items = sorted(items, key=lambda x: x['volume'], reverse=True) positions = [] for item in sorted_items: placed = False for rotation in get_valid_rotations(item): for pos in candidate_positions(container_size): if validate_placement(pos, rotation, positions): positions.append({ 'position': pos, 'rotation': rotation }) placed = True break if placed: break return positions ``` ### 三、关键技术实现 1. **深度-彩色对齐**:使用`rs.align`实现深度与彩色图像对齐,建立像素级对应关系 2. **点云配准**:采用ICP算法优化多视角点云融合,提高测量精度[^2] 3. **几何识别**: - 立方体:通过平面检测和正交约束识别 - 球体:使用RANSAC球面拟合算法 - 圆柱体:基于最小二乘法圆柱拟合 ### 四、性能优化建议 1. **并行计算**:使用多线程分离数据采集和处理流程 ```python from concurrent.futures import ThreadPoolExecutor with ThreadPoolExecutor(max_workers=3) as executor: future_cam = executor.submit(capture_frame) future_proc = executor.submit(process_frame) ``` 2. **GPU加速**:启用OpenCV的CUDA支持加速图像处理 3. **量化评估**:使用Hausdorff距离评估点云匹配精度[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值