Skip to content

Introduce coloring to HDmapping - #476

Open
michalpelka wants to merge 13 commits into
mainfrom
mp/port_coloring
Open

Introduce coloring to HDmapping#476
michalpelka wants to merge 13 commits into
mainfrom
mp/port_coloring

Conversation

@michalpelka

@michalpelka michalpelka commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

This bundle introduces coloring to HDMapping.
It adds three apps:

  • camera_lidar_trajectory viewer
  • camera_lidar_calibration
  • intrinsics calibration
Screenshot 2026-08-04 at 22 06 00

I also done some deduplication, added first unit tests (yay!) Hope it won't break other things.

Michal and others added 13 commits July 30, 2026 22:54
We compile automatic jacoians (24 k chars headers) without debug symbols.
pair_wise_iterative_closest_point.cpp stayed in CORE_BASE_SOURCES while
pose_graph_loop_closure.cpp (which calls PairWiseICP::compute) moved to
CORE_MATH_SOURCES, splitting a symbol and its only definition across two
static libs with a link-order dependency in the wrong direction. This broke
linking for any executable that pulls in PoseGraphLoopClosure without
referencing PairWiseICP directly, e.g. multi_view_tls_registration_step_2.

pair_wise_iterative_closest_point.cpp also includes an auto-generated
Jacobian header, so it belongs in core_math anyway.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DiH2pr8ruiHu6k7Y2wSXS2
pair_wise_iterative_closest_point.cpp (moved into core_math in the previous
commit) calls get_rgd_index_3d(), which lived in hash_utils.cpp under
CORE_BASE_SOURCES -- reintroducing the same cross-archive circular
dependency, just in the opposite direction (core_math needing a symbol
from core/core_no_gui instead of the other way around). Nothing else in
CORE_BASE_SOURCES/CORE_GUI_SOURCES calls into core_math, so moving
hash_utils.cpp there too makes the dependency one-directional again
(core/core_no_gui -> core_math, never the reverse).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DiH2pr8ruiHu6k7Y2wSXS2
…lors

Ports the three raylib/ImGui tools from the sibling mandeye-colors repo as
new apps (camera_lidar_calibration, camera_lidar_trajectory_viewer,
camera_lidar_intrinsics_calib), backed by a new calib_core static library
for their shared non-GUI logic (camera projection math, LAS/LAZ loading,
trajectory CSV parsing, CLI args). Reuses HDMapping's existing raylib/
imgui_raylib/rlimgui/Eigen/LASzip/OpenCV/json wiring instead of vendoring
mandeye-colors' own duplicate copies of those dependencies.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DiH2pr8ruiHu6k7Y2wSXS2
All three text fields that take a file or directory path (image, point
cloud, intrinsics, calibration, session/CAMERA_0 dirs, export/ROS/COLMAP
output paths) now have a "Browse..." button backed by portable-file-dialogs,
matching the mandeye::fd wrapper core already uses elsewhere in HDMapping.

Implemented as calib_core's own calib::fd (rather than reusing
core/include/Core/pfd_wrapper.hpp directly) since that wrapper only builds
into the GUI-enabled `core` target, and linking `core` here would pull in
core_math/session/SLAM code these apps otherwise don't depend on --
portable-file-dialogs itself is a single vendored header with no dependency
on `core`, so wrapping it directly in calib_core is cheap.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DiH2pr8ruiHu6k7Y2wSXS2
… apps

Re-ports camera_lidar_trajectory_viewer's TrajectoryViewer.cpp from
mandeye-colors' mp/roi branch (the initial import was mistakenly based on
master, before the ROI work landed there): adds a Roi struct (calib_core's
Camera.h), SLERP pose interpolation instead of nearest-neighbor lookups,
distortion-aware point projection, a "Geometry" coloring strategy (closest
camera by depth) alongside the existing temporal one, ROI-filtered
colorization with a Camera-ID/In-ROI render mode and an ROI overlay on the
image preview, and colored/uncolored point-count stats.

Also fixes three real bugs surfaced by manually running the apps:

- Fixed-size windows (1400x900 etc.) could be taller than the screen once
  the OS menu bar + title bar are accounted for, silently pushing the top
  of the control panel off-screen. Added fitWindowToScreen() (monitor-aware
  resize/reposition) and SetWindowMinSize() to all three apps.
- fitWindowToScreen() itself was buggy: GetMonitorWidth/Height return the
  monitor's native pixel resolution while GetScreenWidth/Height and
  SetWindowSize/SetWindowPosition operate in logical points, a 2x mismatch
  on Retina displays that pushed the window mostly off-screen. Fixed by
  dividing by GetWindowScaleDPI(). Also added a PollInputEvents() call so
  raylib's cached mouse/window geometry is refreshed before rlImGuiSetup()
  reads it.
- Several ImGui widgets with trailing labels (InputInt, Combo, InputDouble)
  were wrapped in PushItemWidth(-1), which gives the widget box the entire
  row and clips the label off the right edge of the panel. Scoped a
  narrower width around each affected widget.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DiH2pr8ruiHu6k7Y2wSXS2
calib::fd (calib_core/src/FileDialog.cpp) was a byte-for-byte duplicate of
core's mandeye::fd (core/src/pfd_wrapper.cpp), kept separate only because
pfd_wrapper.cpp was compiled straight into the monolithic `core` target,
which drags in core_math/PROJ/spdlog/vqf/Fusion/plycpp/WGS84toCartesian/
imgui/ImGuizmo/freeglut -- none of which file dialogs need.

Split pfd_wrapper.cpp out into its own minimal `core_pfd` static lib
(portable-file-dialogs + std only). `core` links it publicly so all
existing apps keep working unchanged via #include <Core/pfd_wrapper.hpp>.
The three camera_lidar_* apps now link core_pfd directly and use
mandeye::fd instead of their own copy; calib_core drops FileDialog
entirely, since file dialogs are a GUI concern rather than calibration
logic.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DiH2pr8ruiHu6k7Y2wSXS2
Window titles now follow the project-wide "<Name> " + HDMAPPING_VERSION_STRING
convention used by every other app (hd_mapper, manual_color, trajectory
viewers, etc.) instead of bare, unversioned strings -- these three apps had
neither the version suffix nor the <HDMapping/Version.hpp> include.

Also document the three tools in README.md, which didn't mention them at
all despite covering every other app in the suite.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DiH2pr8ruiHu6k7Y2wSXS2
…_lidar_* and step2

UX/code harmonization across camera_lidar_calibration, camera_lidar_trajectory_viewer,
camera_lidar_intrinsics_calib, and multi_view_tls_registration_step_2:

- Top menu bars (File/View/Help) replacing ad-hoc side-panel Browse buttons,
  matching the convention used by the rest of HDMapping's apps.
- Keyboard shortcuts for File actions and view toggles, following each file's
  existing shortcut-handling idiom (ImGui::IsKeyPressed+io.AddKeyEvent for the
  calibration app, raylib IsKeyPressed for the trajectory viewer). Chords that
  collided in meaning with step2's existing bindings were re-lettered
  (Ctrl+L->Ctrl+Shift+C, Ctrl+E->Ctrl+S, bare F->bare V); Ctrl+O and bare C/P
  were kept aligned since they already matched step2's intent.
- New raylib_widgets/ static lib (raylib+imgui_raylib only, no Eigen/core
  coupling) holding code that was duplicated or near-duplicated across these
  apps: the compass/ruler 3D overlay, the DPI-aware fitWindowToScreen window
  positioning (previously byte-identical in 3 apps; step2's own inline copy
  lacked the DPI-scale correction, now fixed), and a simplified
  ShortcutEntry/ShowShortcutsTable shortcuts-help table. The trajectory viewer
  gets an in-app shortcuts reference (Help menu) for the first time.
- Merging step2's two-list shortcut-table indirection (rl_utils.cpp's generic
  scaffold + multi_view_tls_registration_gui.cpp's per-app overrides) into one
  list surfaced two pre-existing bugs, now fixed: a missing "Ctrl+J" entry was
  silently shifting every shortcut description after "J" by one row, and
  "Right click + drag" had a stray "n" instead of its real "camera pan" text.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DiH2pr8ruiHu6k7Y2wSXS2
…amera

raylib_widgets::OrbitCamera (shared with camera_lidar_calibration) gains an
eased target transition, ground-plane ray picking (Ctrl+Right-click or
middle-click), a shared "Center of rotation" dialog, and a 3D crosshair
marker at the current center -- porting multi_view_tls_registration's
center-of-rotation UX without replacing OrbitCamera's Camera3D-based
rendering pipeline. camera_lidar_trajectory_viewer's local Orbit struct is
replaced by the shared OrbitCamera, and its State struct is renamed to
AppState.

Also finishes/fixes the in-progress raylib_widgets refactor found along the
way: builds OrbitCamera.cpp into the library (was listed on disk but never
compiled), removes camera_lidar_calibration's now-duplicate OrbitCamera
definitions (would have been a link error once OrbitCamera.cpp actually
built), and reverts an unfinished move of multi_view_tls_registration's
rl_utils.h/.cpp into raylib_widgets -- it stays local to that app per its own
"deliberately not shared" comment, and the move had left
TrajectoryViewer.cpp with a duplicate app_state definition (compile error)
plus missing Core/Eigen include paths.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ests

getInterpolatedPose was byte-for-byte duplicated between
lidar_odometry_utils.h/.cpp (compiled into lidar_odometry_step_1,
drag_folder_with_mandeye_data_and_drop_here-precision_forestry,
mandeye_compare_trajectories, and multi_view_tls_registration_step_2) and
camera_lidar_trajectory_viewer's TrajectoryViewer.cpp. Moved it to
shared/include/HDMapping/PoseInterpolation.h -- header-only, Eigen/std-only,
in the global namespace like the original, so it needs no new include dir or
library link anywhere (shared/include is already on every target's include
path via the root CMakeLists.txt).

Also adds this project's first unit test infrastructure: doctest (vendored
as its own 3rdparty/doctest, MIT) since neither GoogleTest nor Catch2 is
available offline in this repo (the only existing GTest reference,
3rdparty/manif/test/gtest, fetches it live from GitHub), gated behind a new
opt-in `-DBUILD_TESTING=ON` option so it doesn't affect the default build.

shared/tests/test_pose_interpolation.cpp exercises this "very important
function" and caught a real, pre-existing bug in the process: the
translation lerp adds its delta onto the *later* sample instead of the
earlier one (`it_next.translation + diff * res` instead of
`it_lower.translation + diff * res`), so it extrapolates past the later
pose rather than interpolating between the two -- present in every call
site since this function was first written. The corresponding test case is
left deliberately failing (asserts the mathematically correct result) as a
visible marker; fixing the formula itself is intentionally deferred to a
separate change. `ctest`/running hdmapping_shared_tests today reports 6
passed, 1 failed -- that one failure is expected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Michal Pelka <michal@Mac.lan>
Signed-off-by: Michal Pelka <michalpelka@gmail.com>
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