将三维的点通过法向量投影到某一个平面,然后对投影得到的点云做平面内的三角化,从而得到个点的连接关系。
在平面区域三角化的过程中用到了基于Delauney 的空间区域生长算法,这个方法通过选取一个样本三角片作为初始曲面,不断扩张曲面边界,最后形成一张完整的三角网格曲面,最后根据投影的点云连接关系确定各原始三维点之间的拓补连接,所得到的三角网格就是重建得到的曲面模型。
该方法使用于 点云密度均匀,不能在算讲话的同时对曲面进行平滑和孔洞的修复。
1、求点云的法向量
2、将点云和其1计算的法向量放一起
3、使用法向量做曲面重建
4、显示
#if 1 // 曲面重建 - 贪婪三角
using namespace std;
int main()
{
pcl::PointCloud::Ptr cloud(new pcl::PointCloud); //待滤波点云
pcl::PointCloud::Ptr cloud_filtered(new pcl::PointCloud); //滤波后点云
///读入点云数据
cout << "->正在读入点云..." << endl;
pcl::PCDReader reader;
reader.read("1.pcd", *cloud);
cout << "\t\t<读入点云信息>\n" << *cloud << endl;
// 由于贪婪三角是基于点的法向量的,因此我们需要开始的时候就要进行求法向量的操作
pcl::NormalEstimation n;
pcl::PointCloud::Ptr normals(new pcl::PointCloud());
// 我用要使用kd tree 来做法向量
pcl::search::KdTree::Ptr kdtree(new pcl::search::KdTree());
kdtree->setInputCloud(cloud);
n.setInputCloud(cloud);
n.setSearchMethod(kdtree); // 在计算法向量的时候使用kdtree 作为方法
n.setKSearch(20); // k
n.compute(*normals); // 我们计算出了 法向量和曲率
//我们要把点云和法向量都放到一起,因此我们需要对这个点云添加一个维度,这样才能做曲面重建
pcl::PointCloud::Ptr cloud_with_normals(new pcl::PointCloud);
pcl::concatenateFields(*cloud, *normals, *cloud_with_normals); // concat 就是将xyz和normal 组合到一起
// 再次创建一个kd tree 用来做曲面重建
pcl::search::KdTree::Ptr kdtree_rec(new pcl::search::KdTree());
// 这里我们需要处理的是xyz +_normal 的点云和上面的点云的是不一样的
kdtree_rec->setInputCloud(cloud_with_normals);
// ===========================自此我们三角化的条件已经都准备好了,下面我们开始三角化=====================================
pcl::GreedyProjectionTriangulation gpt; // 贪婪三角对象
pcl::PolygonMesh triangles; // 存储最终三件化的网格模型,
// 三维曲面重建 ,设置参数
gpt.setSearchRadius(0.025); // 设置搜索半径,也就是k -临近的球半径
// 设置样本点到最近邻域距离的乘积系数mu 来获每个样本点的最大搜索距离,这样是的算法自适应点云密度的变化 ,简单理解就是一个系数的问题,也就是说当使用半径来找
// 周围的点的时候,半径范围里面没有找到点,那么我们会启用radius * gpt.setMu(2.5); 作为最远距离来找点(一定要看书,网上有的解释是错的这个系数在【2.5,3】
gpt.setMu(2.5);
gpt.setMaximumNearestNeighbors(100); // 设置样本点最多可以设置搜索的领域数目
gpt.setMaximumSurfaceAngle(M_PI / 4); //连接的时候最大的角度,当某点法线相当于采样点的法线偏离角度超过这个最大角度的时候,连接不考虑这个点
gpt.setMinimumAngle(M_PI /18); // 设置三角化后三角形的最小角度
gpt.setMaximumAngle(2*M_PI/3);
// 设置法向量的朝向是不是要一致,true 表示一致,false表示不一样
gpt.setNormalConsistency(false);
// 开始三维曲面重建
gpt.setInputCloud(cloud_with_normals);
gpt.setSearchMethod(kdtree_rec);
gpt.reconstruct(triangles);
// std::cout << triangles;
// Additional vertex information
std::vector parts = gpt.getPartIDs(); //获取ID字段
std::vector states = gpt.getPointStates(); //获取点状态
boost::shared_ptr viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
///*-----视口1-----*/
//int v1(0);
//viewer->createViewPort(0.0, 0.0, 0.5, 1.0, v1); //设置第一个视口在X轴、Y轴的最小值、最大值,取值在0-1之间
//viewer->setBackgroundColor(0, 0, 0, v1); //设置背景颜色,0-1,默认黑色(0,0,0)
//viewer->addText("befor_filtered", 10, 10, "v1_text", v1);
//viewer->addPointCloud(cloud, "befor_filtered_cloud", v1);
///*-----视口2-----*/
//int v2(0);
//viewer->createViewPort(0.5, 0.0, 1.0, 1.0, v2);
//viewer->setBackgroundColor(0.3, 0.3, 0.3, v2);
//viewer->addText("after_filtered", 10, 10, "v2_text", v2);
//viewer->addPointCloud(cloud_filtered, "after_filtered_cloud", v2);
///*-----设置相关属性-----*/
//viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "befor_filtered_cloud", v1);
//viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1, 0, 0, "befor_filtered_cloud", v1);
//viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "after_filtered_cloud", v2);
//viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 0, 1, 0, "after_filtered_cloud", v2);
可视化
viewer->setBackgroundColor (0, 0, 0);
viewer->addPolygonMesh(triangles,"mytriangles");
// viewer->addCoordinateSystem (1.0);
viewer->initCameraParameters ();
// 主循环
while (!viewer->wasStopped ())
{
viewer->spinOnce (100);
boost::this_thread::sleep (boost::posix_time::microseconds (100000));
}
// Finish
return (0);
}
#endif