Estimating Surface Normals in a PointCloud#
In this tutorial, we will learn how to obtain the surface normals of each point in the cloud.
Note
This tutorial is applicable for execution for both within inside and outside a Docker image. It assumes that the pcl-oneapi-tutorials Deb package is installed, and the user has copied the tutorial directory from /opt/intel/pcl/oneapi/tutorials/ to a user-writable directory.
Prepare the environment:
cd <path-to-oneapi-tutorials>/normal_estimation
oneapi_normal_estimation.cpp
should be in the directory with following content:1// SPDX-License-Identifier: Apache-2.0 2// Copyright (C) 2025 Intel Corporation 3#include <pcl/io/pcd_io.h> 4#include <pcl/oneapi/features/normal_3d.h> 5#include <pcl/oneapi/kdtree/kdtree_flann.h> 6#include <pcl/oneapi/point_cloud.h> 7 8int main (int argc, char** argv) 9{ 10 int k = 10; 11 float radius = 0.01; 12 13 std::cout << "Running on device: " << dpct::get_default_queue().get_device().get_info<sycl::info::device::name>() << "\n"; 14 15 // load point cloud 16 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_ptr( new pcl::PointCloud<pcl::PointXYZ>() ); 17 18 int result = pcl::io::loadPCDFile("bun0.pcd", *cloud_ptr); 19 if (result != 0) 20 { 21 pcl::console::print_info ("Load pcd file failed.\n"); 22 return result; 23 } 24 25 // estimate normals with knn search 26 pcl::oneapi::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne; 27 ne.setSearchMethod (pcl::oneapi::KdTreeFLANN<pcl::PointXYZ>::Ptr (new pcl::oneapi::KdTreeFLANN<pcl::PointXYZ>)); 28 ne.setInputCloud(cloud_ptr); 29 ne.setKSearch(k); 30 31 // save normal estimation to CPU memory point cloud 32 pcl::PointCloud<pcl::Normal>::Ptr normals_knn(new pcl::PointCloud<pcl::Normal>); 33 ne.compute(*normals_knn); 34 35 std::cout << "normals_knn.size (): " << normals_knn->size () << std::endl; 36 37 // estimate normals with radius search 38 ne.setSearchMethod (pcl::oneapi::KdTreeFLANN<pcl::PointXYZ>::Ptr (new pcl::oneapi::KdTreeFLANN<pcl::PointXYZ>)); 39 ne.setInputCloud(cloud_ptr); 40 ne.setRadiusSearch(radius); 41 ne.setKSearch(0); 42 43 // save normal estimation output to device shared memory point cloud 44 pcl::oneapi::PointCloudDev<pcl::Normal>::Ptr normals_radius(new pcl::oneapi::PointCloudDev<pcl::Normal>) ; 45 ne.compute(*normals_radius); 46 47 std::cout << "normals_radius.size (): " << normals_radius->size () << std::endl; 48 49 return 0; 50}
Source the Intel® oneAPI Base Toolkit environment:
source /opt/intel/oneapi/setvars.sh
(Optional) Set up proxy setting to download test data:
export http_proxy="http://<http_proxy>:port" export https_proxy="http://<https_proxy>:port"
Build the code:
mkdir build && cd build cmake ../ make -j
Run the binary:
./oneapi_normal_estimation
Expected results example:
normals_knn.size (): 397 normals_radius.size (): 397
Code Explanation#
The example PCD is initially loaded into a PointCloud<PointXYZ>.
// load point cloud
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_ptr( new pcl::PointCloud<pcl::PointXYZ>() );
int result = pcl::io::loadPCDFile("bun0.pcd", *cloud_ptr);
if (result != 0)
{
pcl::console::print_info ("Load pcd file failed.\n");
return result;
}
This tutorial includes two normal estimation processes: KNN search and Radius search. The initial step involves adjusting the parameters for normal estimation in the KNN search.
// estimate normals with knn search
pcl::oneapi::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
ne.setSearchMethod (pcl::oneapi::KdTreeFLANN<pcl::PointXYZ>::Ptr (new pcl::oneapi::KdTreeFLANN<pcl::PointXYZ>));
ne.setInputCloud(cloud_ptr);
ne.setKSearch(k);
Normal estimation is then executed.
pcl::PointCloud<pcl::Normal>::Ptr normals_knn(new pcl::PointCloud<pcl::Normal>);
ne.compute(*normals_knn);
The parameters for normal estimation are modified for the radius search.
// estimate normals with radius search
ne.setSearchMethod (pcl::oneapi::KdTreeFLANN<pcl::PointXYZ>::Ptr (new pcl::oneapi::KdTreeFLANN<pcl::PointXYZ>));
ne.setInputCloud(cloud_ptr);
ne.setRadiusSearch(radius);
ne.setKSearch(0);
Normal estimation is performed once more.
// save normal estimation output to device shared memory point cloud
pcl::oneapi::PointCloudDev<pcl::Normal>::Ptr normals_radius(new pcl::oneapi::PointCloudDev<pcl::Normal>) ;
ne.compute(*normals_radius);