Attention

You are viewing an older version of the documentation. The latest version is 2.2.

Fast Triangulation of Unordered Point Clouds (OpenMP Version)

This tutorial explains how to run a greedy surface triangulation algorithm (OpenMP version) on a PointCloud with normals, to obtain a triangle mesh based on projections of the local neighborhoods. The OpenMP version will break the point cloud into multiple segments and call the original version of the greedy surface triangulation based on the segment count. By running the segments independently and concurrently, there will be a disjoint result between each segment. To overcome the disjoint issue, additional overlap segments will be created to go through greedy surface triangulation and combine with other processed segments.

Note

The output of OpenMP version may differ from serial version of greedy projection triangulation.

Background: Algorithm and Parameters

Refer to original greedy_triangulation for more detail original greedy triangulation parameters. All greedy triangulation parameters are supported except getPointStates(), getPartIDs(), getSFN() and getFFN().

Additional parameters are defined to provide control segments and overlap.

  • setNumberofThreads(unsigned) controls how many segments are created from the input point cloud. Each segment size is determined using the formula (maximum x - minimum x) / number of threads, and all segment sizes are equal. Each segment will be assigned to a CPU thread/core. It is recommended to obtain the number of threads/cores using omp_get_max_threads().

  • setBlockOverlapPercentage(double) controls how wide to create the overlap region among two segments. The overlap region is this parameter percentage multiply the size of segment. If there is still gap between segments, can overcome by increase the overlap percentage.

  • setRemoveDuplicateMesh(bool) controls whether to clean up the duplicate point cloud and polygons after combining all the segments output from GP3.

Note

This tutorial can be run both inside and outside a Docker* image. We assume that the pcl-oneapi-tutorial Deb package has been installed, and the user has copied the tutorial directory from /opt/intel/pcl/oneapi/tutorials/ to a user-writable directory.

  1. Prepare the environment:

    cd <path-to-oneapi-tutorials>/greedy_projection
    
    Copy to clipboard
  2. greedy_projectoin.cpp should be in the directory with following content:

     1#include <pcl/point_types.h>
     2#include <pcl/io/pcd_io.h>
     3#include <pcl/io/vtk_io.h>
     4#include <pcl/search/kdtree.h> // for KdTree
     5#include <pcl/features/normal_3d.h>
     6#include <pcl/oneapi/surface_omp/gp3.h>
     7
     8int
     9main ()
    10{
    11  // Load input file into a PointCloud<T> with an appropriate type
    12  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
    13  pcl::PCLPointCloud2 cloud_blob;
    14  pcl::io::loadPCDFile ("bun0.pcd", cloud_blob);
    15  pcl::fromPCLPointCloud2 (cloud_blob, *cloud);
    16  //* the data should be available in cloud
    17
    18  // Normal estimation*
    19  pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> n;
    20  pcl::PointCloud<pcl::Normal>::Ptr normals (new pcl::PointCloud<pcl::Normal>);
    21  pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ>);
    22  tree->setInputCloud (cloud);
    23  n.setInputCloud (cloud);
    24  n.setSearchMethod (tree);
    25  n.setKSearch (20);
    26  n.compute (*normals);
    27  //* normals should not contain the point normals + surface curvatures
    28
    29  // Concatenate the XYZ and normal fields*
    30  pcl::PointCloud<pcl::PointNormal>::Ptr cloud_with_normals (new pcl::PointCloud<pcl::PointNormal>);
    31  pcl::concatenateFields (*cloud, *normals, *cloud_with_normals);
    32  //* cloud_with_normals = cloud + normals
    33
    34  // Create search tree*
    35  pcl::search::KdTree<pcl::PointNormal>::Ptr tree2 (new pcl::search::KdTree<pcl::PointNormal>);
    36  tree2->setInputCloud (cloud_with_normals);
    37
    38  // Initialize objects
    39  pcl::GreedyProjectionTriangulationOMP<pcl::PointNormal> gp3;
    40  pcl::PolygonMesh triangles;
    41
    42  // Set the maximum distance between connected points (maximum edge length)
    43  gp3.setSearchRadius (0.025);
    44
    45  // Set typical values for the parameters
    46  gp3.setMu (2.5);
    47  gp3.setMaximumNearestNeighbors (100);
    48  gp3.setMaximumSurfaceAngle(M_PI/4); // 45 degrees
    49  gp3.setMinimumAngle(M_PI/18); // 10 degrees
    50  gp3.setMaximumAngle(2*M_PI/3); // 120 degrees
    51  gp3.setNormalConsistency(false);
    52  gp3.setNumberOfThreads(3);
    53  gp3.setBlockOverlapPercentage(0.04);
    54  gp3.setRemoveDuplicateMesh(true);
    55
    56  // Get result
    57  gp3.setInputCloud (cloud_with_normals);
    58  gp3.setSearchMethod (tree2);
    59  gp3.reconstruct (triangles);
    60
    61  pcl::io::saveVTKFile("mesh.vtk", triangles);
    62
    63  // Finish
    64  return (0);
    65}
    
    Copy to clipboard
  3. Source the Intel® oneAPI Base Toolkit environment:

    source /opt/intel/oneapi/setvars.sh
    
    Copy to clipboard
  4. (Optional) Setup proxy setting to download test data:

    export http_proxy="http://<http_proxy>:port"
    export https_proxy="http://<https_proxy>:port"
    
    Copy to clipboard
  5. Build the code:

    mkdir build && cd build
    cmake ../
    make -j
    
    Copy to clipboard
  6. Run the binary:

    ./greedy_projection
    
    Copy to clipboard
  7. Output:

    The program will save the output as “mesh.vtk”. View the VTK file by:

    pcl_viewer mesh.vtk
    
    Copy to clipboard

Code Explanation

As the example PCD has only XYZ coordinates, load it into a PointCloud<PointXYZ>.

  // Load input file into a PointCloud<T> with an appropriate type
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
  pcl::PCLPointCloud2 cloud_blob;
  pcl::io::loadPCDFile ("bun0.pcd", cloud_blob);
  pcl::fromPCLPointCloud2 (cloud_blob, *cloud);
Copy to clipboard

The method requires normals, so normals are estimated using the standard method from PCL.

  // Normal estimation*
  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);
Copy to clipboard

Since coordinates and normals need to be in the same PointCloud, a PointNormal type point cloud is created.

  // Concatenate the XYZ and normal fields*
  pcl::PointCloud<pcl::PointNormal>::Ptr cloud_with_normals (new pcl::PointCloud<pcl::PointNormal>);
  pcl::concatenateFields (*cloud, *normals, *cloud_with_normals);
Copy to clipboard

The lines below deal with the initialization of the required objects. In the OpenMP version, include <pcl/oneapi/surface_omp/gp3.h> and declare the class by appending OMP to the original class.”

  // Create search tree*
  pcl::search::KdTree<pcl::PointNormal>::Ptr tree2 (new pcl::search::KdTree<pcl::PointNormal>);
  tree2->setInputCloud (cloud_with_normals);

  // Initialize objects
  pcl::GreedyProjectionTriangulationOMP<pcl::PointNormal> gp3;
Copy to clipboard

The lines below set the parameters, as explained above, to configure the algorithm.

  // Set the maximum distance between connected points (maximum edge length)
  gp3.setSearchRadius (0.025);

  // Set typical values for the parameters
  gp3.setMu (2.5);
  gp3.setMaximumNearestNeighbors (100);
  gp3.setMaximumSurfaceAngle(M_PI/4); // 45 degrees
  gp3.setMinimumAngle(M_PI/18); // 10 degrees
  gp3.setMaximumAngle(2*M_PI/3); // 120 degrees
  gp3.setNormalConsistency(false);
  gp3.setNumberOfThreads(3);
  gp3.setBlockOverlapPercentage(0.04);
  gp3.setRemoveDuplicateMesh(true);
Copy to clipboard

The lines below set the input objects and perform the actual triangulation.

  gp3.setInputCloud (cloud_with_normals);
  gp3.setSearchMethod (tree2);
  gp3.reconstruct (triangles);
Copy to clipboard