Detecting Specific Models and Their Parameters in 3D Point Clouds#

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 sample_consensus && cd sample_consensus
    
  2. Create the file oneapi_sample_consensus.cpp:

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

    /*
     * Software License Agreement (BSD License)
     *
     *  Point Cloud Library (PCL) - www.pointclouds.org
     *  Copyright (c) 2010-2012, Willow Garage, Inc.
     *  Copyright (c) 2014-, Open Perception, Inc.
     *
     *  All rights reserved.
     *
     *  Redistribution and use in source and binary forms, with or without
     *  modification, are permitted provided that the following conditions
     *  are met:
     *
     *   * Redistributions of source code must retain the above copyright
     *     notice, this list of conditions and the following disclaimer.
     *   * Redistributions in binary form must reproduce the above
     *     copyright notice, this list of conditions and the following
     *     disclaimer in the documentation and/or other materials provided
     *     with the distribution.
     *   * Neither the name of the copyright holder(s) nor the names of its
     *     contributors may be used to endorse or promote products derived
     *     from this software without specific prior written permission.
     *
     *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
     *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
     *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
     *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     *  POSSIBILITY OF SUCH DAMAGE.
     *
     */
    
    #include <pcl/oneapi/sample_consensus/sac_model_plane.h>
    #include <pcl/oneapi/sample_consensus/ransac.h>
    #include <pcl/io/pcd_io.h>
    #include <pcl/point_types.h>
    #include <pcl/point_cloud.h>
    
    
    int main (int argc, char** argv)
    {
      // Read Point Cloud
      pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_ptr( new pcl::PointCloud<pcl::PointXYZ>() );
      pcl::PointCloud<pcl::PointXYZ>::Ptr convex_ptr;
      int result = pcl::io::loadPCDFile(argv[1], *cloud_ptr);
      if (result != 0)
      {
        pcl::console::print_info ("Load pcd file failed.\n");
        return result;
      }
    
      // Prepare Device Point Cloud Memory
      pcl::oneapi::SampleConsensusModel::PointCloud_xyz cloud_device_xyz;
      cloud_device_xyz.upload(cloud_ptr->points);
      pcl::oneapi::SampleConsensusModel::PointCloud & cloud_device = (pcl::oneapi::SampleConsensusModel::PointCloud &)cloud_device_xyz;
    
      // Algorithm tests
      typename pcl::oneapi::SampleConsensusModelPlane::Ptr sac_model (new pcl::oneapi::SampleConsensusModelPlane (cloud_device));
      pcl::oneapi::RandomSampleConsensus sac (sac_model);
      sac.setMaxIterations (10000);
      sac.setDistanceThreshold (0.03);
      result = sac.computeModel ();
    
      // Best model
      pcl::oneapi::SampleConsensusModelPlane::Indices sample;
      sac.getModel (sample);
    
      // Coefficient
      pcl::oneapi::SampleConsensusModelPlane::Coefficients coeffs;
      sac.getModelCoefficients (coeffs);
    
      // Inliers
      pcl::Indices pcl_inliers;
      int inliers_size = sac.getInliersSize ();
      pcl_inliers.resize(inliers_size);
    
      pcl::oneapi::SampleConsensusModelPlane::IndicesPtr inliers = sac.getInliers ();
      inliers->download(pcl_inliers.data(), 0, inliers_size);
    
      // Refined coefficient
      pcl::oneapi::SampleConsensusModelPlane::Coefficients coeff_refined;
      sac_model->optimizeModelCoefficients (*cloud_ptr, pcl_inliers, coeffs, coeff_refined);
    
      // print log
      std::cout << "input cloud size: " << cloud_ptr->points.size() << std::endl;
      std::cout << "inliers size    : " << inliers_size << std::endl;
      std::cout << "  plane model coefficient: " << coeffs[0] << ", " << coeffs[1] << ", " << coeffs[2] << ", " << coeffs[3] << std::endl;
      std::cout << "  Optimized coefficient  : " << coeff_refined[0] << ", " << coeff_refined[1] << ", " << coeff_refined[2] << ", " << coeff_refined[3] << std::endl;
    }
    
  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_sample_consensus)
    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(${target})
    
    find_package(PCL 1.12 REQUIRED)
    find_package(PCL-ONEAPI 1.12 REQUIRED)
    
    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})
    
    add_executable (${target} oneapi_sample_consensus.cpp)
    target_link_libraries (${target} sycl pcl_oneapi_sample_consensus ${PCL_LIBRARIES})
    
  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/sample_consensus/
    mkdir build && cd build
    cmake ../
    make -j
    
  8. Download the test data from GitHub*:

    wget https://raw.githubusercontent.com/PointCloudLibrary/data/5c26bdd0591ba150b91858b5c9fe5e91cb39ae86/segmentation/mOSD/test/test59.pcd
    # if the binary 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_sample_consensus ./test59.pcd
    

Expected results example:

input cloud size: 307200
inliers size    : 77316
plane model coefficient: -0.0789502, -0.816661, -0.571692, 0.546386
Optimized coefficient  : -0.0722213, -0.818286, -0.570256, 0.547587

Code Explanation#

Load the test data from GitHub* into a PointCloud<PointXYZ>.

  // Read Point Cloud
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_ptr( new pcl::PointCloud<pcl::PointXYZ>() );
  pcl::PointCloud<pcl::PointXYZ>::Ptr convex_ptr;
  int result = pcl::io::loadPCDFile(argv[1], *cloud_ptr);
  if (result != 0)
  {
    pcl::console::print_info ("Load pcd file failed.\n");
    return result;
  }

Create GPU input and output device arrays, and load point cloud data into the input device array.

  // Prepare Device Point Cloud Memory
  pcl::oneapi::SampleConsensusModel::PointCloud_xyz cloud_device_xyz;
  cloud_device_xyz.upload(cloud_ptr->points);
  pcl::oneapi::SampleConsensusModel::PointCloud & cloud_device = (pcl::oneapi::SampleConsensusModel::PointCloud &)cloud_device_xyz;

GPU: Start computing the model.

  typename pcl::oneapi::SampleConsensusModelPlane::Ptr sac_model (new pcl::oneapi::SampleConsensusModelPlane (cloud_device));
  pcl::oneapi::RandomSampleConsensus sac (sac_model);
  sac.setMaxIterations (10000);
  sac.setDistanceThreshold (0.03);
  result = sac.computeModel ();

Result (best model):

  pcl::oneapi::SampleConsensusModelPlane::Indices sample;
  sac.getModel (sample);

Result (coefficient model):

  pcl::oneapi::SampleConsensusModelPlane::Coefficients coeffs;
  sac.getModelCoefficients (coeffs);

Result (inliers model):

  pcl::Indices pcl_inliers;
  int inliers_size = sac.getInliersSize ();
  pcl_inliers.resize(inliers_size);

  pcl::oneapi::SampleConsensusModelPlane::IndicesPtr inliers = sac.getInliers ();
  inliers->download(pcl_inliers.data(), 0, inliers_size);

Result (refined coefficient model):

  pcl::oneapi::SampleConsensusModelPlane::Coefficients coeff_refined;
  sac_model->optimizeModelCoefficients (*cloud_ptr, pcl_inliers, coeffs, coeff_refined);

Result (output log):

  std::cout << "input cloud size: " << cloud_ptr->points.size() << std::endl;
  std::cout << "inliers size    : " << inliers_size << std::endl;
  std::cout << "  plane model coefficient: " << coeffs[0] << ", " << coeffs[1] << ", " << coeffs[2] << ", " << coeffs[3] << std::endl;
  std::cout << "  Optimized coefficient  : " << coeff_refined[0] << ", " << coeff_refined[1] << ", " << coeff_refined[2] << ", " << coeff_refined[3] << std::endl;