本文讨论了三维重建中的网格类型选择和点云贪婪三角化方法。首先介绍了六种不同类型的网格,包括三角网格、非结构化网格等,并分析了它们的特点和应用场景。接着详细阐述了点云贪婪三角化的工作原理及参数设置。最后简要介绍了用于创建平滑三维表面模型的Poisson重建技术。
网格类型是指用于表示重建后三维模型的网格结构。这些网格类型在形状、大小和连接方式上有所不同,以适应不同的应用和处理需求。
网格类型 | 描述 | 应用 |
---|---|---|
三角网格(Triangular Mesh) | 由三角形组成,每个三角形有三个顶点和三条边。 | 适用于大多数三维表面,尤其是不规则或复杂的形状。 |
四边形网格(Quadrilateral Mesh) | 由四边形组成,每个四边形通常是一个平面。 | 常用于建筑建模和一些工程应用,可以高效进行细分和平滑处理。 |
多边形网格(Polygonal Mesh) | 由多边形构成,边数可以多于四条。 | 适用于表示更复杂的几何结构,如地质建模等专业应用。 |
正则网格(Regular Grid) | 由规则排列的四边形或六边形组成,具有统一的大小和形状。 | 常用于医学影像和某些类型的流体动力学模拟。 |
非结构化网格(Unstructured Mesh) | 不遵循规则排列的三角形或多边形。 | 适用于适应各种复杂的表面和体积形状,常用于复杂的科学和工程计算。 |
体素网格(Voxel Grid) | 由体素(三维像素)组成的规则立方体网格。 | 用于体积建模,如医学成像中的CT扫描重建,适用于3D打印。 |
在点云三维重建的过程中,选择合适的网格类型取决于源数据的特点、所需的模型精度和最终应用目的。
参考资料:【PCL学习:无序点云的快速三角化】
将三维点通过法线投影到某一平面,然后对投影得到的点云作平面内的三角化,从而得到各点的连接关系。在平面区域三角化的过程中用到了基于 Delaunay 的 空间区域增长算法,该方法通过选取一个样本三角片作为初始曲面,不断扩张曲面边界,最后形成一张完整的三角网格曲面。最后根据投影点云的连接关系确定各原始三维点间的拓扑连接,所得三角格网即为重建得到的曲面模型。
在PCL中,类GreedyProjectionTriangulation
实现了将三维点投影到某一局部二维坐标平面的贪婪三角化算法,该算法需要点云平滑,并且密度变化连续。
关键成员函数:
SetMaximumNearestNeighbors(unsigned):
SetMu(double):
SetSearchRadius(double):
SetMinimumAngle(double) 和 SetMaximumAngle(double):
SetMaximumSurfaceAngle(double):
SetNormalConsistency(bool):
false
。下面将以一个实例来感受一下这个用法:
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/kdtree/kdtree_flann.h>
#include <pcl/features/normal_3d.h>
#include <pcl/surface/gp3.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>
int main(int argc, char** argv)
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>); //智能指针加载点云数据
pcl::PCLPointCloud2::Ptr cloud_blob(new pcl::PCLPointCloud2);
pcl::io::loadPCDFile("E:\\PCL1\\pcl1\\bun000_StructuredASCII.pcd", *cloud_blob);
pcl::fromPCLPointCloud2(*cloud_blob, *cloud);
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>); //智能指针存储kd tree
tree->setInputCloud(cloud);
n.setInputCloud(cloud);
n.setSearchMethod(tree);
n.setKSearch(20);
n.compute(*normals); //执行法线估计
pcl::PointCloud<pcl::PointNormal>::Ptr cloud_with_normals(new pcl::PointCloud<pcl::PointNormal>); //将点云与法线进行字段拼接
pcl::concatenateFields(*cloud, *normals, *cloud_with_normals);
pcl::search::KdTree<pcl::PointNormal>::Ptr tree2(new pcl::search::KdTree<pcl::PointNormal>); //创建搜索树
tree2->setInputCloud(cloud_with_normals);
pcl::GreedyProjectionTriangulation<pcl::PointNormal> gp3; //初始化贪婪三角形的对象
pcl::PolygonMesh triangles;
gp3.setSearchRadius(0.1); //设置两点之间的最近距离
gp3.setMu(2.5);
gp3.setMaximumNearestNeighbors(1000); //设置最大近邻点的个数
gp3.setMaximumSurfaceAngle(M_PI / 4); //设置最大搜索角度
gp3.setMinimumAngle(M_PI / 18); //设置最小角度
gp3.setMaximumAngle(2 * M_PI / 3); //设置最大角度
gp3.setNormalConsistency(false); //是否考虑法线
gp3.setInputCloud(cloud_with_normals);
gp3.setSearchMethod(tree2);
gp3.reconstruct(triangles); //利用三角形进行重构
std::vector<int>parts = gp3.getPartIDs(); //贪婪三角形可视化
std::vector<int>states = gp3.getPointStates();
boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
viewer->setBackgroundColor(0, 0, 0);
viewer->addPolygonMesh(triangles, "my");
viewer->addCoordinateSystem(1.0);
viewer->initCameraParameters();
while (!viewer->wasStopped())
{
viewer->spinOnce(100);
boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}
return(0);
}
报错“Not enough neighbors are considered: ffn or sfn out of range! Consider increasing nnn_... Setting R=4276 to be BOUNDARY! Not enough neighbors are considered: source of R=22352 is out of range! Consider increasing nnn_...”的修改意见:
增加最大近邻数:增加gp3.setMaximumNearestNeighbors
的值。
调整搜索半径:增加gp3.setSearchRadius
的值,以覆盖更大的区域,找到更多的邻居点。
Poisson重建是一种用于从点云数据创建三维表面模型的技术。这个名字来源于它使用的一个数学工具,叫做泊松方程。想象一下,你有很多散落的点,这些点表示一个物体的外表面,但它们之间没有直接的连接。Poisson重建的目的就是找到一个平滑的表面,这个表面最好地穿过这些点,从而形成一个连续的三维模型。
这个过程可以比作是解决一个谜题。你有很多线索(点云),而你需要找到一个解决方案(表面)来连接所有的线索。在Poisson重建中,每个点都有一个称为“法线”的小箭头,指示表面在该点处应该朝向哪个方向。这就像每个线索都附带了一个小提示,帮助解决谜题。
使用泊松方程,这种方法可以计算出一个平滑的表面,不仅经过所有的点,而且尽可能地沿着这些小箭头的方向。最终的结果是一个非常平滑、自然的三维模型,这在处理复杂或不完整的点云数据时特别有用。
在PCL中可以很简单的实现:
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/kdtree/kdtree_flann.h>
#include <pcl/features/normal_3d_omp.h>
#include <pcl/features/normal_3d.h>
#include <pcl/surface/gp3.h>
#include <pcl/surface/poisson.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>
#include <fstream>
#include <iostream>
#include <string.h>
#include <string>
int main(int argc, char** argv)
{
std::string input_file = "E:\\PCL1\\pcl1\\bun000_StructuredASCII.pcd";
// Load the point cloud
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
if (pcl::io::loadPCDFile<pcl::PointXYZ>(input_file, *cloud) == -1)
{
PCL_ERROR("Could not read pcd file!\n");
return -1;
}
// Calculate normals
pcl::PointCloud<pcl::PointNormal>::Ptr cloud_with_normals(new pcl::PointCloud<pcl::PointNormal>);
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(cloud);
n.setInputCloud(cloud);
n.setSearchMethod(tree);
n.setKSearch(20);
n.compute(*normals);
// Concatenate point cloud and normals
pcl::concatenateFields(*cloud, *normals, *cloud_with_normals);
// Create KD tree
pcl::search::KdTree<pcl::PointNormal>::Ptr tree2(new pcl::search::KdTree<pcl::PointNormal>);
tree2->setInputCloud(cloud_with_normals);
// Create Poisson object and set parameters
pcl::Poisson<pcl::PointNormal> pn;
pn.setConfidence(false);
pn.setDegree(2);
pn.setDepth(8);
pn.setIsoDivide(8);
pn.setManifold(false);
pn.setOutputPolygons(false);
pn.setSamplesPerNode(3.0);
pn.setScale(1.25);
pn.setSolverDivide(8);
// Set search method and input point cloud
pn.setSearchMethod(tree2);
pn.setInputCloud(cloud_with_normals);
// Create polygon mesh to store result
pcl::PolygonMesh mesh;
// Perform reconstruction
pn.performReconstruction(mesh);
// Display result
boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("3D viewer"));
viewer->setBackgroundColor(0, 0, 0);
viewer->addPolygonMesh(mesh, "my");
// viewer->addCoordinateSystem(50.0);
// viewer->initCameraParameters();
while (!viewer->wasStopped())
{
viewer->spinOnce(100);
boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}
return 0;
}
运行效果: