Configuring Input Data for Point Cloud Library Feature Estimation
The Point Cloud Library (PCL) provides a unified interface for extracting geometric descriptors through its pcl::Feature base class. Proper configuration of input data sources is critical for controlling where features are calculated and where neighborhood searches are performed. This configuration relies on three primary setter methods:
setInputCloud(): Defines the primary point cloud. Feature computation is initiated for points contained within this dataset. This parameter is mandatory.setIndices(): Specifies a subset of point indices from the input cloud. When applied, the algorithm restricts feature computation exclusively to the points listed in the index vector.setSearchSurface(): Assigns an alternative point cloud to serve as the spatial reference for nearest-neighbor queries. The search radius or K-nearest neighbor are evaluated against this surface rather than the input cloud itself.
By combining these three methods, the framework supports four distinct operational modes for feature extraction. The following sections detail each configuration using surface normal estimation as the reference implementation.
1. Standard Single-Cloud Estimation
Configuration: setIndices() disabled, setSearchSurface() disabled.
This is the default workflow. The algorithm computes features for every point in the designated input cloud, using the same cloud for spatial queries. The neighborhood search iterates sequentially across the entire dataset.
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/features/normal_3d.h>
#include <pcl/search/kdtree.h>
void compute_global_normals(const pcl::PointCloud<pcl::PointXYZ>::ConstPtr& source_data) {
// Initialize the normal estimator
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normal_extractor;
normal_extractor.setInputCloud(source_data);
// Attach a KD-Tree for spatial indexing
auto spatial_index = std::make_shared<pcl::search::KdTree<pcl::PointXYZ>>();
normal_extractor.setSearchMethod(spatial_index);
normal_extractor.setRadiusSearch(0.03);
// Container for the resulting normals
auto output_normals = std::make_shared<pcl::PointCloud<pcl::Normal>>();
normal_extractor.compute(*output_normals);
}
2. Index-Restricted Computation
Configuration: setIndices() enabled, setSearchSurface() disabled.
This mode limits feature calculation to a predefined list of point indices. The spatial search still occurs within the primary input cloud, but results are only generated for the specified subset. Points outside the index vector are ignored during the computation phase.
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/features/normal_3d.h>
#include <pcl/search/kdtree.h>
#include <vector>
#include <cmath>
void compute_subset_normals(const pcl::PointCloud<pcl::PointXYZ>::ConstPtr& source_data) {
const std::size_t total_points = source_data->size();
const std::size_t subset_count = static_cast<std::size_t>(std::floor(total_points * 0.1));
std::vector<int> selected_indices(subset_count);
for (std::size_t i = 0; i < subset_count; ++i) {
selected_indices[i] = static_cast<int>(i);
}
auto index_container = std::make_shared<std::vector<int>>(selected_indices);
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normal_extractor;
normal_extractor.setInputCloud(source_data);
normal_extractor.setIndices(index_container);
auto spatial_index = std::make_shared<pcl::search::KdTree<pcl::PointXYZ>>();
normal_extractor.setSearchMethod(spatial_index);
normal_extractor.setRadiusSearch(0.03);
auto output_normals = std::make_shared<pcl::PointCloud<pcl::Normal>>();
normal_extractor.compute(*output_normals);
}
3. Cross-Cloud Surface Search
Configuration: setIndices() disabled, setSearchSurface() enabled.
Here, features are computed for every point in the input cloud, but the nearest-neighbor lookup is performed against a separate, often denser, reference cloud. This is particularly useful when computing normals for a sparse set of keypoints or a downsampled representation while leveraging the geometric detail of the original dense point cloud.
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/features/normal_3d.h>
#include <pcl/search/kdtree.h>
void compute_normals_with_reference(const pcl::PointCloud<pcl::PointXYZ>::ConstPtr& sparse_input,
const pcl::PointCloud<pcl::PointXYZ>::ConstPtr& dense_reference) {
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normal_extractor;
normal_extractor.setInputCloud(sparse_input);
normal_extractor.setSearchSurface(dense_reference);
auto spatial_index = std::make_shared<pcl::search::KdTree<pcl::PointXYZ>>();
normal_extractor.setSearchMethod(spatial_index);
normal_extractor.setRadiusSearch(0.03);
auto output_normals = std::make_shared<pcl::PointCloud<pcl::Normal>>();
normal_extractor.compute(*output_normals);
}
4. Indexed Subsets with External Search Surface
Configuration: setIndices() enabled, setSearchSurface() enabled.
This configuration combines index filtering with an external search reference. The algorithm calculates features only for the points specified in the index vector, while conducting neighborhood queries on a completely separate reference dataset. This is the most restrictive mode, offering precise control over both the evaluation points and the spatial context.
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/features/normal_3d.h>
#include <pcl/search/kdtree.h>
#include <vector>
#include <cmath>
void compute_indexed_normals_with_surface(const pcl::PointCloud<pcl::PointXYZ>::ConstPtr& target_cloud,
const pcl::PointCloud<pcl::PointXYZ>::ConstPtr& search_surface) {
const std::size_t total_points = target_cloud->size();
const std::size_t limit = static_cast<std::size_t>(std::floor(total_points * 0.1));
std::vector<int> query_indices(limit);
for (std::size_t i = 0; i < limit; ++i) {
query_indices[i] = static_cast<int>(i);
}
auto idx_ptr = std::make_shared<std::vector<int>>(query_indices);
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normal_extractor;
normal_extractor.setInputCloud(target_cloud);
normal_extractor.setIndices(idx_ptr);
normal_extractor.setSearchSurface(search_surface);
auto spatial_index = std::make_shared<pcl::search::KdTree<pcl::PointXYZ>>();
normal_extractor.setSearchMethod(spatial_index);
normal_extractor.setRadiusSearch(0.03);
auto output_normals = std::make_shared<pcl::PointCloud<pcl::Normal>>();
normal_extractor.compute(*output_normals);
}