Intel® oneAPI Base Toolkit's Iterative Closest Point (ICP)#

ICP is an algorithm employed to minimize the difference between two clouds of points. The standard, not joint nor generalized, ICP has been optimized using the Intel® oneAPI Base Toolkit.

See registration_api for details.

This tutorial shows how to use these optimizations inside a Docker* image. For the same functionality outside of Docker* images, see PCL Optimizations Outside of Docker* Images.

  1. Prepare the environment:

    cd <edge_insights_for_amr_path>/Edge_Insights_for_Autonomous_Mobile_Robots_<version>/AMR_containers
    ./run_interactive_docker.sh eiforamr-full-flavour-sdk:2023.1 root -c full_flavor
    mkdir one_api_registration && cd one_api_registration
    
  2. Create the file oneapi_icp_example.cpp:

    vim oneapi_icp_example.cpp
    
  3. Place the following inside the file:

    #include <pcl/oneapi/registration/icp.h>
    #include <pcl/console/parse.h>
    #include <pcl/point_types.h>
    #include <pcl/point_cloud.h>
    #include <pcl/point_representation.h>
    #include <pcl/io/pcd_io.h>
    
    
    using namespace pcl;
    using namespace pcl::io;
    using namespace pcl::console;
    
    /* ---[ */
    int
    main (int argc, char** argv)
    {
      // Parse the command line arguments for .pcd files
      std::vector<int> p_file_indices;
      p_file_indices = parse_file_extension_argument (argc, argv, ".pcd");
      if (p_file_indices.size () != 2)
      {
        print_error ("Need one input source PCD file and one input target PCD file to continue.\n");
        print_error ("Example: %s source.pcd target.pcd\n", argv[0]);
        return (-1);
      }
    
      // Load the files
      print_info ("Loading %s as source and %s as target...\n", argv[p_file_indices[0]], argv[p_file_indices[1]]);
      PointCloud<PointXYZ>::Ptr src, tgt;
      src.reset (new PointCloud<PointXYZ>);
      tgt.reset (new PointCloud<PointXYZ>);
      if (loadPCDFile (argv[p_file_indices[0]], *src) == -1 || loadPCDFile (argv[p_file_indices[1]], *tgt) == -1)
      {
        print_error ("Error reading the input files!\n");
        return (-1);
      }
    
      PointCloud<PointXYZ> output;
      // Compute the best transformtion
      pcl::oneapi::IterativeClosestPoint<PointXYZ, PointXYZ> reg;
      reg.setMaximumIterations(20);
      reg.setTransformationEpsilon(1e-12);
      reg.setMaxCorrespondenceDistance(2);
    
      reg.setInputSource(src);
      reg.setInputTarget(tgt);
    
      // Register
      reg.align(output); //point cloud output of alignment i.e source cloud after transformation is applied.
    
      Eigen::Matrix4f transform = reg.getFinalTransformation();
    
      std::cerr << "Transform Matrix:" << std::endl;
      std::cerr << transform << std::endl;
      // Write transformed data to disk
      savePCDFileBinary ("source_transformed.pcd", output);
    }
    /* ]--- */
    
  4. Create a CMakeLists.txt file:

    vim CMakeLists.txt
    
  5. Place the following inside the file:

    cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
    set(target oneapi_icp_example)
    set(CMAKE_CXX_COMPILER dpcpp)
    set(CMAKE_CXX_STANDARD 17)
    set(CMAKE_CXX_FLAGS "-Wall -Wpedantic -Wno-unknown-pragmas -Wno-pass-failed -Wno-unneeded-internal-declaration -Wno-unused-function -Wno-gnu-anonymous-struct -Wno-nested-anon-types -Wno-extra-semi -Wno-unused-local-typedef -fsycl -fsycl-unnamed-lambda -ferror-limit=1")
    project(registration)
    
    find_package(PCL 1.12 REQUIRED)
    find_package(PCL-ONEAPI 1.12 REQUIRED)
    
    add_executable (${target} oneapi_icp_example.cpp)
    
    include_directories(${PCL_INCLUDE_DIRS} ${PCL-ONEAPI_INCLUDE_DIRS})
    link_directories(${PCL_LIBRARY_DIRS} ${PCL-ONEAPI_LIBRARY_DIRS})
    add_definitions(${PCL_DEFINITIONS} ${PCL-ONEAPI_DEFINITIONS})
    
    target_link_libraries (${target} sycl pcl_oneapi_registration pcl_oneapi_search pcl_oneapi_kdtree pcl_io)
    
  6. Source the Intel® oneAPI Base Toolkit environment:

    export PATH=/home/eiforamr/workspace/lib/pcl/share/pcl-1.12:/home/eiforamr/workspace/lib/pcl/share/pcl-oneapi-1.12:$PATH
    source /opt/intel/oneapi/setvars.sh
    
  7. Build the code:

    cd /home/eiforamr/workspace/one_api_registration/
    mkdir build && cd build
    cmake ../
    make -j
    
  8. Download the test data from GitHub*:

    wget https://raw.githubusercontent.com/NVIDIA-AI-IOT/cuPCL/main/cuOctree/test_P.pcd
    wget https://raw.githubusercontent.com/NVIDIA-AI-IOT/cuPCL/main/cuOctree/test_Q.pcd
    # if the binaries is not downloaded try setting the proxies first and try again:
    export http_proxy="http://<http_proxy>:port"
    export https_proxy="http://<https_proxy>:port"
    
  9. Run the binary:

    ./oneapi_icp_example test_P.pcd test_Q.pcd
    

Expected results example:

Transform Matrix:
0.998899   0.0107221   0.0457259   0.0790768
-0.00950837    0.999602  -0.0266773   0.0252976
-0.0459936    0.026213    0.998599   0.0677631
      0           0           0           1

Code Explanation#

Define two input point Clouds (src, tgt), declare the output point cloud, and load the test data from GitHub*.

  PointCloud<PointXYZ>::Ptr src, tgt;
  src.reset (new PointCloud<PointXYZ>);
  tgt.reset (new PointCloud<PointXYZ>);
  if (loadPCDFile (argv[p_file_indices[0]], *src) == -1 || loadPCDFile (argv[p_file_indices[1]], *tgt) == -1)
  {
    print_error ("Error reading the input files!\n");
    return (-1);
  }

  PointCloud<PointXYZ> output;

Declare the Intel® oneAPI Base Toolkit's ICP, and set the input configuration parameters.

  pcl::oneapi::IterativeClosestPoint<PointXYZ, PointXYZ> reg;
  reg.setMaximumIterations(20);
  reg.setTransformationEpsilon(1e-12);
  reg.setMaxCorrespondenceDistance(2);

Set the two input point clouds for the ICP module, and call the method to align the two point clouds. The align method populates the output point cloud, passed as a parameter, with the src point cloud transformed using the computed transformation matrix.

  reg.setInputSource(src);
  reg.setInputTarget(tgt);

  // Register
  reg.align(output); //point cloud output of alignment i.e source cloud after transformation is applied.

Get the computed matrix transformation, print it, and save the transformed point cloud.

  Eigen::Matrix4f transform = reg.getFinalTransformation();

  std::cerr << "Transform Matrix:" << std::endl;
  std::cerr << transform << std::endl;
  // Write transformed data to disk
  savePCDFileBinary ("source_transformed.pcd", output);