pcod-common v1.0.0
Shared preprocessing and postprocessing for point-cloud object detection
Loading...
Searching...
No Matches
pcod-common


Shared Preprocessing and Postprocessing Library for Point Cloud Object Detection

This repository provides shared C++ and Python components for point cloud object detection training, model export, and ROS 2 inference. Using the same geometry, decoding, non-maximum suppression (NMS), and model-manifest implementations keeps the training and inference pipelines consistent.

The library includes PBOD decoding, rotated NMS, point filtering, CUDA kernels for pillarization and rotated NMS, and a model-manifest schema shared by the C++ and Python APIs.

🚀 Quick Start💻 Development📝 Documentation

Important
This repository is part of OpenADS, the Open Automated Driving Systems project. OpenADS and its modules have been initiated and are currently being maintained by the Institute for Automotive Engineering (ika) at RWTH Aachen University.

🚀 Quick Start

Requirements

  • CMake 3.16 or newer
  • A C++17 compiler
  • yaml-cpp
  • Python 3.12 or newer for the Python package
  • Optional: a CUDA toolkit compatible with the installed PyTorch build and a CUDA-capable GPU for the CUDA extensions

C++ Installation

Clone, build, and install the C++ library:

git clone https://github.com/openads-project/pcod-common.git
cmake -S pcod-common -B build/pcod-common -DPCOD_COMMON_BUILD_TESTS=OFF
cmake --build build/pcod-common
cmake --install build/pcod-common --prefix /path/to/prefix

After installation, link your CMake target to the package:

find_package(pcod_common CONFIG REQUIRED)
target_link_libraries(my_target PRIVATE pcod_common::pcod_common)

When using a custom installation prefix, add it to CMAKE_PREFIX_PATH when configuring the consuming project, for example with -DCMAKE_PREFIX_PATH=/path/to/prefix.

Alternatively, add this repository directly to your CMake project:

add_subdirectory(pcod-common)
target_link_libraries(my_target PRIVATE pcod_common)

Python Installation

Install the Python package from the repository root:

pip install .

Python Usage Example

from pcod_common.preprocessing.pillars import PillarPreprocessor, PillarPreprocessorConfig
config = PillarPreprocessorConfig(
x_min=-50.0,
x_max=50.0,
y_min=-50.0,
y_max=50.0,
z_min=-2.0,
z_max=3.0,
voxel_x=0.2,
voxel_y=0.2,
point_feature_dim=1,
)
preprocessor = PillarPreprocessor(config)

💻 Development

Repository Layout

  • include/pcod_common/: public C++ headers
  • src/: C++ implementations
  • csrc/: CUDA/C++ kernels for PyTorch extensions
  • python/pcod_common/: Python package sources
  • schemas/: JSON schema for the model manifest
  • tests/: C++ tests
  • python/tests/: Python tests

C++ Tests

On Debian or Ubuntu, install the required build dependencies and run the test suite:

apt-get update && apt-get install -y cmake g++ pkg-config libyaml-cpp-dev python3 python3-yaml
cmake -S . -B build -DPCOD_COMMON_BUILD_TESTS=ON
cmake --build build
ctest --test-dir build

Some C++ tests compare the Python and C++ contracts and require python3 to be available on PATH.

Python Tests

Install the package in editable mode with its development dependencies and run the test suite:

pip install -e ".[dev]"
pytest

python/tests/test_postprocess.py requires PyTorch and TorchVision. Tests whose optional dependencies or CUDA extensions are unavailable are skipped; the manifest tests still run.

Build Python Distributions

Build and validate the wheel and source distribution from the repository root:

python3 -m pip install build twine
python3 -m build
python3 -m twine check dist/*

Published distributions include the model-manifest schema and the C++/CUDA sources required to build the optional PyTorch extensions at runtime. Validating a distribution does not require a GPU. Compiling the extensions requires Ninja and a CUDA toolkit compatible with the installed PyTorch build; running them requires a CUDA-capable GPU.

Development Container

A basic development container configuration is provided in .devcontainer/. In the container, install the system and Python dependencies, including PyTorch, and run:

sudo apt-get update
sudo apt-get install -y pkg-config libyaml-cpp-dev
pip install -e ".[dev]"
cmake -S . -B build -DPCOD_COMMON_BUILD_TESTS=ON
cmake --build build
ctest --test-dir build
pytest

CUDA kernels are built on demand by the PyTorch extension loaders in python/pcod_common/torch_extensions/.

C++ Usage Example

This example demonstrates point filtering and PBOD decoding with the C++ API. It uses four pillars and two classes to keep the control flow easy to follow.

#include <vector>
int main() {
pre_cfg.x_min = -1.0f;
pre_cfg.x_max = 1.0f;
pre_cfg.y_min = -1.0f;
pre_cfg.y_max = 1.0f;
pre_cfg.z_min = -1.0f;
pre_cfg.z_max = 1.0f;
pre_cfg.value_threshold = 10.0f;
// 1) Basic point filtering (range checks + optional masks).
pcod_common::PointPreprocessor preprocessor(pre_cfg);
if (!preprocessor.IsPointValid(0.5f, 0.1f, 0.0f)) {
return 1;
}
// 2) Build a 2x2 pillar grid so the decoder has a center location.
{2, 2}, {{{0.0f, 2.0f}, {0.0f, 2.0f}, {0.0f, 1.0f}}}, 1, 1);
// 3) Dummy model outputs for four pillars and two classes.
// Two pillars will be filtered out by the score threshold below.
const int num_pillars = 4;
const int num_classes = 2;
float focal_logits[num_pillars] = {2.0f, -2.0f, 2.0f, -2.0f};
float class_logits[num_pillars * num_classes] = {
0.1f, 0.9f, 0.1f, 0.9f, 0.1f, 0.9f, 0.1f, 0.9f};
std::vector<float> size_posterior(num_pillars * num_classes * 3, 1.0f);
std::vector<float> reg_logits(num_pillars * num_classes * 7, 0.0f);
view.focal_logits = focal_logits;
view.size_posterior = size_posterior.data();
view.class_logits = class_logits;
view.reg_logits = reg_logits.data();
view.num_pillars = num_pillars;
view.num_classes = num_classes;
view.reg_dim = 7;
// 4) Decode into bounding boxes (class list sets the output metadata).
post_cfg.class_names = {"car", "pedestrian"};
post_cfg.score_thresholds = {0.5f};
auto boxes = pcod_common::DecodePbod(view, grid, post_cfg);
const std::size_t expected_boxes = 2;
return boxes.size() == expected_boxes ? 0 : 1;
}
int main()
Definition main.cpp:7
PillarGrid BuildPillarGrid(const std::array< int, 2 > &pillar_map_size, const std::array< std::array< float, 2 >, 3 > &pillar_map_range, int first_up_stride, int stride)
std::vector< BoundingBox > DecodePbod(const PbodOutputsView &outputs, const PillarGrid &grid, const PbodPostprocessConfig &config)
const float * focal_logits
Shape [num_pillars].
int num_classes
Number of semantic classes.
int reg_dim
Regression values per pillar and class.
int num_pillars
Number of spatial pillars.
const float * class_logits
Shape [num_pillars, num_classes].
const float * size_posterior
Shape [num_pillars, num_classes * 3].
const float * reg_logits
Shape [num_pillars, num_classes * reg_dim].
std::vector< float > score_thresholds
Per-class thresholds, or one shared threshold.
std::vector< std::string > class_names
Class name in model-output order.
float x_max
Maximum accepted X coordinate.
float value_threshold
Divisor for value-threshold normalization.
float y_min
Minimum accepted Y coordinate.
float z_min
Minimum accepted Z coordinate.
float x_min
Minimum accepted X coordinate.
float z_max
Maximum accepted Z coordinate.
float y_max
Maximum accepted Y coordinate.
PointFeatureNormalizationType normalization_type
Feature transform.

Model Manifest

Each exported model bundle contains a model_manifest.yml file with three sections:

  • artifact: bundle metadata and references to files within the bundle
  • frozen_contract: model settings that inference applications cannot override and that must match the exported model
  • runtime_defaults: default inference settings that applications may override

The ROS 2 inference node uses frozen_contract as the authoritative model configuration and initializes overridable ROS parameters, such as preprocessing.point_feature.value_threshold and the NMS thresholds, from runtime_defaults. The schema is defined in schemas/model_manifest.schema.json.

Integration Notes

  • For training and model export, include pcod-common as a Git submodule and add it to the Python environment, for example with pip install -e pcod-common.
  • For ROS 2 inference, include pcod-common as a Git submodule and link against the C++ library.

For a complete ROS 2 integration example, see point_cloud_object_detection, which includes pcod-common as a Git submodule and links against its C++ library.

📝 Documentation

Implementation details are available in the Source Code Documentation.

⚖️ Licensing

The source code in this repository is licensed under Apache-2.0, see [LICENSE](LICENSE).

🙏 Acknowledgements

Development and maintenance of this repository are supported by the following projects. We acknowledge the funding of the respective institutions.

Project Funding Institution Grant Number
AIGGREGATE 🇪🇺 European Union 101202457

Funded by the European Union. Views and opinions expressed are however those of the author(s) only and do not necessarily reflect those of the European Union or the European Climate, Infrastructure and Environment Executive Agency (CINEA). Neither the European Union nor CINEA can be held responsible for them.