点云曲面重建

1. 维诺图

  维诺图是对平面内n个离散点而言的,它是由一组由连接两邻点直线的垂直平分线组成的连续多边形组成。N个在平面上有区别的点,将平面划分为N个区域。
            点云曲面重建_第1张图片

2. 维诺图的特点:

  • 每个V多边形内有一个生长元;
  • 每个V多边形内点到该生成元距离短于其它生成元的距离;
  • 多边形边界上的点到生成此边界的生成元距离相等;
  • 邻接图形的Voronoi多边形边界以原邻接界限作为子集。

3. 维诺图的生成

3.1 Delaunay三角网

  建立维诺图的关键是的德劳内三角网的生成。
点云曲面重建_第2张图片

Delaunay 三角剖分的两大优点
  1. 最大化最小角,“最接近于规则化”的三角网;
  2. 唯一性(任意四点不能共圆)。

  空圆特性其实就是对于两个共边的三角形,任意一个三角形的外接圆中都不能包含有另一个三角形的顶点,这种形式的剖分产生的最小角最大。
点云曲面重建_第3张图片点云曲面重建_第4张图片

这两个特性避免了狭长三角形的产生,也使得Delaunay三角剖分应用广泛。

3.2 三角形生长法生成维诺图

点云曲面重建_第5张图片

3. 贪婪投影三角化算法:

  1. 先将点云通过法线投影到某一二维坐标平面内
  2. 然后对投影得到的点云做平面内的三角化,从而得到各点之间的拓扑关系。在平面三角化的过程中用到了基于Delaunay的空间区域增长算法。
  3. 最后根据投影点云的连接关系确定各原始三维点之间的拓扑连接,所得三角网格即为重建得到的曲面模型。

该算法的优点是可以处理来自一个或多个扫描仪扫描得到并且有多个连接处的点云。但该算法有一定的局限性,它更适用于采样点云来自于表面连续光滑的曲面且点云密度变化比较均匀的情况。

三维点云曲面重建
void QTPCL::actionGreedPT()
{
	pcl::PolygonMesh mesh;
	pcl::io::loadPolygonFile("rabbit.pcd", mesh);

	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud2(new pcl::PointCloud<pcl::PointXYZ>);
	pcl::fromPCLPointCloud2(mesh.cloud, *cloud2);
	// 估计法向量  
	pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> n;
	pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);
	pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
	tree->setInputCloud(cloud2);
	n.setInputCloud(cloud2);
	n.setSearchMethod(tree);
	n.setKSearch(20);
	n.compute(*normals); //计算法线,结果存储在normals中  
	//* normals 不能同时包含点的法向量和表面的曲率  

	//将点云和法线放到一起  
	pcl::PointCloud<pcl::PointNormal>::Ptr cloud_with_normals(new pcl::PointCloud<pcl::PointNormal>);
	pcl::concatenateFields(*cloud2, *normals, *cloud_with_normals);
	//* cloud_with_normals = cloud + normals  

	//创建搜索树  
	pcl::search::KdTree<pcl::PointNormal>::Ptr tree2(new pcl::search::KdTree<pcl::PointNormal>);
	tree2->setInputCloud(cloud_with_normals);

	//初始化GreedyProjectionTriangulation对象,并设置参数  
	pcl::GreedyProjectionTriangulation<pcl::PointNormal> gp3;
	//创建多变形网格,用于存储结果  
	pcl::PolygonMesh triangles;

	//设置GreedyProjectionTriangulation对象的参数  
	//第一个参数影响很大  
	gp3.setSearchRadius(200.0f);           //设置连接点之间的最大距离(最大边长)用于确定k近邻的球半径【默认值 0】  
	gp3.setMu(2.5f);                       //设置最近邻距离的乘子,以得到每个点的最终搜索半径【默认值 0】  
	gp3.setMaximumNearestNeighbors(100);   //设置搜索的最近邻点的最大数量  
	gp3.setMaximumSurfaceAngle(M_PI / 4);  // 45 degrees(pi)最大平面角  
	gp3.setMinimumAngle(M_PI / 18);        // 10 degrees 每个三角的最小角度  
	gp3.setMaximumAngle(2 * M_PI / 3);     // 120 degrees 每个三角的最大角度  
	gp3.setNormalConsistency(false);       //如果法向量一致,设置为true  

	//设置搜索方法和输入点云  
	gp3.setInputCloud(cloud_with_normals);
	gp3.setSearchMethod(tree2);

	//执行重构,结果保存在triangles中  
	gp3.reconstruct(triangles);

	//保存网格图  
	//pcl::io::saveOBJFile("result.obj", triangles);

	// Additional vertex information  
	//std::vector parts = gp3.getPartIDs();  
	//std::vector states = gp3.getPointStates();  

	// 显示结果图  
	viewer->addPolygonMesh(triangles, "my");              //设置显示的网格
	//设置网格模型显示模式
	//viewer->setRepresentationToSurfaceForAllActors();   //网格模型以面片形式显示
	//viewer->setRepresentationToPointsForAllActors();    //网格模型以点形式显示
	viewer->setRepresentationToWireframeForAllActors();   //网格模型以线框图模式显示

	ui.qvtkWidget->update();
}`


点云曲面重建_第6张图片
点云曲面重建_第7张图片
点云曲面重建_第8张图片
点云曲面重建_第9张图片

4. 移动立方体算法:(分治思想)体素下采样

   移动立方体算法的基本思想是找出所有与等值面相交的体素,在分别找出每个体素与等值面相交的交面,这些交面连在一起就是所求的等值面。

  1. 首先想象,用一个大的长方体包住目标物体。(目标必须完全位于长方体内。)在将整个长方体分成A X B X C个一模一样的小长方体。
    点云曲面重建_第10张图片
  2. 判断小立方体的8个顶点分别是否在目标物体的内部。如果某个顶点在物体内部,那么就给这个顶点标上一个0,如果顶点在物体外部,则给他标上1。判断出8个顶点的“0”和“1”之后,通过排列组合,就有2^8=256种情况,每一种情况可以在小立方体内生成一些等值面,或者理解成生成0个或多个位于立方体内部的三角形。一共有256种情况能生成的三角形。这256种情况被概括成以下15种情况。
    点云曲面重建_第11张图片
    上图基本说说明了,什么情况就可以在哪生成三角形。其中三角形的每个顶点都是在小立方体的棱上的。于是每个小立方体都这么干,所有小立方体生成的三角形拼在一起就是目标物体的表面了。

  在确定了立方体的三角剖分模式后,就要计算三角面片顶点位置。当三维离散数据场的密度较高时,也就是体素很小的时候。可以假定函数沿体素边界呈线性变化。根据这一假设可以用线性插值计算等值面与体素边界的交点。

//移动立方体算法
void QTPCL::actionMarchingCurb()
{
	定义点云
	pcl::PointCloud<PointXYZ>::Ptr cloud1(new PointCloud<PointXYZ>);
	//载入点云数据
	pcl::io::loadPCDFile("rabbit.pcd", *cloud1);

	// 估计法向量
	pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> n;
	pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);
	pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
	tree->setInputCloud(cloud1);
	n.setInputCloud(cloud1);
	n.setSearchMethod(tree);
	n.setKSearch(20);
	n.compute(*normals); //计算法线,结果存储在normals中
	//* normals 不能同时包含点的法向量和表面的曲率

	//将点云和法线放到一起
	pcl::PointCloud<pcl::PointNormal>::Ptr cloud_with_normals(new pcl::PointCloud<pcl::PointNormal>);
	pcl::concatenateFields(*cloud1, *normals, *cloud_with_normals);
	//* cloud_with_normals = cloud + normals

	//创建搜索树
	pcl::search::KdTree<pcl::PointNormal>::Ptr tree2(new pcl::search::KdTree<pcl::PointNormal>);
	tree2->setInputCloud(cloud_with_normals);

	//初始化MarchingCubes对象,并设置参数
	pcl::MarchingCubes<pcl::PointNormal> *mc;
	mc = new pcl::MarchingCubesHoppe<pcl::PointNormal>();
	/*
  if (hoppe_or_rbf == 0)
	mc = new pcl::MarchingCubesHoppe<pcl::PointNormal> ();
  else
  {
	mc = new pcl::MarchingCubesRBF<pcl::PointNormal> ();
	(reinterpret_cast<pcl::MarchingCubesRBF<pcl::PointNormal>*> (mc))->setOffSurfaceDisplacement (off_surface_displacement);
  }
	*/

	//创建多变形网格,用于存储结果
	pcl::PolygonMesh mesh;

	//设置MarchingCubes对象的参数
	mc->setIsoLevel(0.0f);
	mc->setGridResolution(50, 50, 50);
	mc->setPercentageExtendGrid(0.0f);

	//设置搜索方法
	mc->setInputCloud(cloud_with_normals);

	//执行重构,结果保存在mesh中
	mc->reconstruct(mesh);

	//保存网格图
	//pcl::io::savePLYFile("result.ply", mesh);


	// 显示结果图
	//viewer->setBackgroundColor(0, 0, 0); //设置背景
	viewer->addPolygonMesh(mesh, "my"); //设置显示的网格
	ui.qvtkWidget->update();
}

点云曲面重建_第12张图片

你可能感兴趣的:(点云曲面重建)