Skip to content

feat(core): drop PCL + Boost; release travel-seg 1.1.0 with cibuildwheel (Linux + macOS + Windows) - #29

Merged
LimHyungTae merged 4 commits into
mainfrom
feat/drop-pcl
May 10, 2026
Merged

LimHyungTae merged 4 commits into
mainfrom
feat/drop-pcl

Conversation

@LimHyungTae

Copy link
Copy Markdown
Contributor

Plan A from the wheel-matrix discussion: rather than fight per-platform PCL installs (vcpkg PCL alone makes a Windows wheel build ~30 min), strip PCL + Boost out of the algorithm core. The core only borrowed a tiny slice of PCL — type wrappers, plus a single algorithm call to `pcl::computeMeanAndCovarianceMatrix`. Replacing those is contained.

Why

Goal: `pip install travel-seg` on macOS arm64 / Linux x86_64 / Windows x86_64 all picks up a prebuilt wheel for CPython 3.8–3.13. Patchwork++ already does this; it's possible because that project only depends on Eigen. After this PR, travel-seg is in the same bucket.

What changed in the core

  • New `travel/types.hpp` with the slice of PCL we used to borrow:
    • `travel::PCLHeader`, `travel::PointCloud` (vector-with-header container with the PCL-shaped API surface we actually call)
    • `travel::PointXYZ`, `travel::PointXYZI` (POD)
    • Global `PointXYZILID` retained, but the `PCL_ADD_POINT4D` macro replaced with the equivalent anonymous union, and the `POINT_CLOUD_REGISTER_POINT_STRUCT` registry call dropped.
    • `travel::computeMeanAndCovarianceMatrix` ports PCL's accumulator pattern bit-for-bit: same Eigen 1×9 row-major storage, same single-pass formulation, same float order, same `coeffRef` indices.
  • `tgs.hpp` / `aos.hpp` / `save_labels.hpp` / `kitti_loader.hpp` now refer to `travel::PointCloud<...>` and `travel::computeMeanAndCovarianceMatrix`.
  • `aos.hpp` swapped `boost::optional` for C++17 `std::optional`.
  • `kitti_loader.hpp` swapped `boost::filesystem` + `boost::format` for `` + `snprintf`.
  • `cpp/travel/CMakeLists.txt` only does `find_package(Eigen3)` now. PCL, Boost, MPI/VTK workarounds gone. Project version 1.1.0.

Why this is safe under the "never break the pipeline" rule

The fix preserves byte-for-byte output. `computeMeanAndCovarianceMatrix` was the one real PCL algorithm call; the port keeps the exact float-arithmetic order PCL used.

```
$ ./build/tests/regression_kitti kitti00_000000.bin /tmp/run.bin cpp/tests/data/kitti00_000000_gold.bin
REGRESSION PASS: bit-identical to cpp/tests/data/kitti00_000000_gold.bin (374008 bytes)
```

Verified bit-identical on:

  • macOS arm64 + Apple Silicon Homebrew clang + Eigen 3.4.0
  • Ubuntu 24.04 + GCC 13.3 + libeigen3-dev (linux/arm64 docker)

Python: `pytest python/tests/` -> 7/7 passed on macOS arm64.

ROS wrapper

`ros/` keeps PCL — it still needs `pcl::fromROSMsg` / `pcl::toROSMsg` for ROS message conversion. The boundary in `ros/src/travel_node.cpp` now converts `pcl::PointCloudpcl::PointXYZ` → `travel::PointCloud` on input, and copies the algorithm output back out into `pcl::PointXYZI` / `pcl::PointXYZL` clouds for the publish path. PCL stays a ros wrapper dependency, not a core dependency.

Wheel matrix

`.github/workflows/release.yml` rewritten to a cibuildwheel matrix:

  • `ubuntu-22.04` runner → manylinux_2_17 x86_64 wheels (cibuildwheel runs the build inside the manylinux container).
  • `macos-14` runner → arm64 + x86_64 wheels (cross-compile via `CIBW_ARCHS_MACOS`).
  • `windows-2022` runner → win_amd64 wheel.
  • `CIBW_BEFORE_ALL_LINUX="yum install -y eigen3-devel"`, macOS `brew install eigen`, Windows `vcpkg install eigen3:x64-windows` with `CMAKE_PREFIX_PATH` forwarded.
  • `CIBW_SKIP="pp* -musllinux_"` — CPython + manylinux only.
  • Trusted-Publisher PyPI upload only when run from a git tag (`v*`).

Test plan

  • CI green across all 6 jobs (KITTI bit-compare in particular).
  • Release workflow run on this PR's HEAD: produces sdist + Linux/macOS/Windows wheels under `wheelhouse/` (no upload, just artefact build) and twine-checks them.
  • After merge: `git tag v1.1.0 && git push origin v1.1.0` → release workflow builds wheels and uploads them to PyPI via the existing pending publisher.
  • Fresh-venv install on a Windows machine: `pip install travel-seg==1.1.0` lands a prebuilt `win_amd64` wheel (no source build).

…0 with cibuildwheel matrix (Linux + macOS + Windows)

Plan A from the wheel-matrix discussion: rather than fighting per-platform
PCL installs (vcpkg PCL on Windows alone is a 30-min wheel build), strip
PCL + Boost out of the algorithm core. The core only ever borrowed a tiny
slice of PCL — type wrappers, plus a single algorithm call to
`pcl::computeMeanAndCovarianceMatrix`. Replacing those is a contained
change.

What's in cpp/travel/core now:

* New `travel/types.hpp` ships self-contained replacements:
  - `travel::PCLHeader` (POD), `travel::PointCloud<T>` (vector-with-header
    container with the PCL-shaped API surface we actually call: size,
    empty, clear, reserve, resize, push_back, emplace_back, operator[],
    at, begin/end, operator+= and `using Ptr = std::shared_ptr<...>`).
  - `travel::PointXYZ`, `travel::PointXYZI` (POD).
  - Global `PointXYZILID` retained but with the `PCL_ADD_POINT4D` macro
    replaced by an equivalent anonymous union, and the
    `POINT_CLOUD_REGISTER_POINT_STRUCT` macro dropped (we never used the
    PCL runtime field registry, only the field layout).
  - `travel::computeMeanAndCovarianceMatrix` ports PCL's accumulator
    pattern bit-for-bit: same Eigen 1x9 row-major storage, same single-
    pass formulation, same float order, same coeffRef indices.

* `tgs.hpp` / `aos.hpp` / `save_labels.hpp` / `kitti_loader.hpp` now
  refer to `travel::PointCloud<...>` and `travel::computeMeanAndCovarianceMatrix`
  instead of the `pcl::` versions.

* `aos.hpp` switched its two `boost::optional<int>` cluster-size members
  to C++17 `std::optional<int>`, dropping the `<boost/optional.hpp>`
  include.

* `kitti_loader.hpp` switched from `boost::filesystem` + `boost::format`
  to `<filesystem>` + `snprintf`. Same behaviour, no boost dependency.

* `cpp/travel/CMakeLists.txt` now only does `find_package(Eigen3)`. PCL,
  Boost, and the MPI workaround for VTK 9.1 are gone. Project version
  bumped to 1.1.0.

* `cpp/travel/cmake/travelConfig.cmake` likewise only emits
  `find_dependency(Eigen3)`.

* `cpp/examples/CMakeLists.txt` and `cpp/tests/CMakeLists.txt` drop
  PCL + MPI find_package; both link only `travel::travel_core`.

* `cpp/examples/src/run_travel_kitti.cpp` swapped `pcl::io::savePCDFileBinary`
  output for plain Nx4/Nx5 float32 .bin writers — KITTI velodyne shape,
  no PCL dependency.

* `cpp/tests/regression_kitti.cpp` updated to use `travel::PointCloud<>`.

* `.github/workflows/ci.yml`'s cpp-examples job now checks for `.bin`
  outputs instead of `.pcd`.

What stays the same:

* The ROS 2 wrapper (ros/) keeps PCL — it still needs `pcl::fromROSMsg`
  / `pcl::toROSMsg` for ROS message conversion. The boundary in
  `ros/src/travel_node.cpp` now converts pcl::PointCloud<pcl::PointXYZ>
  -> travel::PointCloud<PointXYZILID> on input, and the publish path
  copies travel::PointCloud<PointXYZILID> back out into
  pcl::PointXYZI / pcl::PointXYZL clouds for `pcl::toROSMsg`. PCL stays
  as a *ros wrapper* dependency, not a core dependency.

* The Python package (python/) is unchanged in behaviour. The pybind
  module just refers to `travel::PointCloud<PointXYZILID>` instead of
  `pcl::PointCloud<PointXYZILID>`.

travel-seg version bumps to 1.1.0 across pyproject.toml,
travel_seg/__init__.py, and the pybind `__version__` attribute.

`.github/workflows/release.yml` is rewritten to a cibuildwheel matrix:
  * sdist on ubuntu-22.04
  * wheels on ubuntu-22.04 (manylinux_2_17 x86_64), macos-14
    (arm64 + x86_64), windows-2022 (win_amd64)
  * CIBW_BEFORE_ALL_LINUX="yum install -y eigen3-devel"
  * CIBW_BEFORE_ALL_MACOS="brew install eigen"
  * CIBW_BEFORE_ALL_WINDOWS="vcpkg install eigen3:x64-windows" with
    CMAKE_PREFIX_PATH set so cmake finds it.
  * CIBW_SKIP="pp* *-musllinux_*" — CPython only, manylinux only.
  * Trusted-Publisher PyPI upload only when run from a git tag (v*).

Verification (CLAUDE.md "never break the pipeline" rule):

* cpp/tests/regression_kitti against cpp/tests/data/kitti00_000000_gold.bin
  -> REGRESSION PASS, bit-identical (374008 bytes) on:
    - macOS arm64 + Apple Silicon Homebrew clang + Eigen 3.4.0
    - Ubuntu 24.04 + GCC 13.3 + libeigen3-dev (linux/arm64 docker)
  i.e. the algorithm output on KITTI sequence 00 frame 0 with seed=42
  is byte-for-byte the same as the gold dump captured pre-PCL-removal.

* python/tests/ -> 7/7 pass on macOS arm64 (full editable rebuild).
Initial cibuildwheel run on PR #29 surfaced one issue per platform:

* Linux: manylinux2014 (CentOS 7) yum repos do not have eigen3-devel.
  CIBW_BEFORE_ALL_LINUX="yum install -y eigen3-devel" failed with
  "No package eigen3-devel available".

* macOS x86_64: cp38 default deployment target is 10.9; std::thread /
  std::shared_ptr require 10.13+ in libc++. The build failed with
  "'operator delete' is unavailable: introduced in macOS 10.12".

* Windows: aos.hpp used the bare type `uint` (POSIX-only typedef from
  sys/types.h). MSVC errored at the AOSNode/Point member declarations
  with "syntax error: '='".

Fixes:

* aos.hpp: replace every bare `uint` with `std::uint32_t` (5 sites:
  AOSNode::start/end, Point::idx, two locals in labelPointcloud). Add
  <cstdint> include. Header reordering of the include block while
  there.

* cpp/travel/CMakeLists.txt: Eigen find_package now falls back to
  FetchContent-fetching Eigen 3.4.0 from upstream GitLab when the
  system doesn't have it. KITTI regression bit-identical against the
  gold dump verified with the FetchContent path on macOS arm64
  (`-DCMAKE_DISABLE_FIND_PACKAGE_Eigen3=ON`). This removes the need
  for per-platform yum/brew/vcpkg setup in cibuildwheel — every wheel
  build just runs `cmake -S cpp/travel ...` and the FetchContent path
  fires when needed.

* .github/workflows/release.yml:
  - Drop CIBW_BEFORE_ALL_LINUX / _MACOS / _WINDOWS now that Eigen is
    handled via FetchContent. Wheels build straight from the source
    tree; no system package install on the cibuildwheel container.
  - Add CIBW_ENVIRONMENT_MACOS="MACOSX_DEPLOYMENT_TARGET=10.13" so
    libc++ std::thread / std::shared_ptr resolve on the cp38
    x86_64 cross-build.
  - Skip 32-bit Linux (manylinux_i686) and Windows (win32) to keep
    the matrix tight; users on those niches can still build from
    sdist. CIBW_SKIP grew to "pp* *-manylinux_i686 *-musllinux_*
    *-win32".
Second wheel-matrix run got Linux + macOS to green and revealed the next
Windows issue: tgs.hpp and aos.hpp use bare M_PI in 9 places, but MSVC's
<cmath> does not define M_PI by default — it is a POSIX / GNU
extension. The Windows wheel build failed with 9 instances of
"error C2065: 'M_PI': undeclared identifier".

Define M_PI once in travel/types.hpp (which every other core header
transitively includes) under an `#ifndef` guard. POSIX / GNU compilers
that already define it via <cmath> are unaffected. The numeric value is
the long double form used by glibc, truncated to double when assigned.
Third wheel-matrix run got Linux + macOS green again, Windows still
failing — this time on `ssize_t` redefinition. MSVC does not provide a
top-level `ssize_t`; it gets defined transitively by some Python /
pybind11 headers, and our binding then redeclared it. Cascading parse
errors followed.

Use the qualified `py::ssize_t` (pybind11's portable alias) at all
three usage sites in numpyToCloud. POSIX / Linux toolchains where bare
`ssize_t` was already in scope keep working — `py::ssize_t` is just a
typedef there too.
@LimHyungTae
LimHyungTae merged commit fcdd2d7 into main May 10, 2026
11 checks passed
LimHyungTae added a commit that referenced this pull request May 10, 2026
…badges

* Title: "TRAVEL for RA-L'22 w/ IROS Option" -> "TRAVEL: Traversable
  Ground and Above-Ground Object Segmentation". The award subtitle
  ("Best Paper Award winner from RA-L 2022") stays as <h3>.

* New badge rows, modeled on KISS-Matcher:
  - Row 1 (identity): C++, Python, ROS2-Humble|Jazzy, Ubuntu, macOS,
    Windows. The Windows badge reflects v1.1.0 prebuilt-wheel support
    (cibuildwheel matrix landed in #29).
  - Row 2 (existing media/paper): unchanged YouTube×2, RA-L, arXiv.
  - Row 3 (status): GitHub Actions CI workflow badge + PyPI version
    badge for travel-seg.

This branch was successfully deployed

1 active deployment
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant