Step 1: classify moving objects as LAS class 7#448
Closed
piorkoo wants to merge 3 commits into
Closed
Conversation
Points whose indoor RGD bucket was raycast-through (number_of_hits >= threshold, same signal that excludes them from LIO optimization) are now exported in scan_lio_*.laz with LAS classification 7; static points stay class 0. - export_laz.h: writePoint sets point classification; exportLaz takes an optional per-point classification vector (defaults keep existing callers unchanged). - lidar_odometry.cpp: save_result queries params.buckets_indoor per output point and builds the classification vector before exportLaz. - LidarOdometryParams: classify_moving_objects (default on) + moving_object_hits_threshold (default 20), persisted via TOML [moving_objects] section and exposed in the GUI menu. Scope is Step 1 only; preserving the class through Step 2 / insightoBake to the final LAS export is handled on a separate branch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The untwine + libpdal-e57 conda env is created locally for COPC/E57 conversion and must not be tracked; without this, git add -A pulls in thousands of env files. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The first version queried params.buckets_indoor in save_result(), but the RGD grids are transient: process_worker_step_update_rgd_after() clears buckets_indoor/outdoor on every sliding-window shift (~5 m), so at save time only the last window survived — points were marked class 7 only at the very end of the trajectory. It also ignored the outdoor grid and used first-pose-relative coords for the bucket key (world coords were needed). Now compute_step_2() accumulates the keys of every bucket that reached the moving-object hit threshold into params.moving_buckets_indoor/outdoor, snapshotting before each grid clear (segment peak) and once after the loop (last window). save_result() classifies each output point by looking up its world-coordinate bucket key (indoor OR outdoor) in those sets, mirroring the points_to_vector output filter so the per-point vector stays aligned. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Author
|
Closing: the classification reuses the LIO occlusion filter (number_of_hits >= 20), which is a robustness filter for optimization, not a moving-object detector. On real data it marks ~33-68% of points as class 7 (rising along the trajectory) and holds large per-window bucket sets in RAM. Withdrawing to rework the criterion. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Step 1 (LIO) already detects moving/dynamic objects at the RGD-bucket level: in
update_rgd()a ray is cast from each point to the viewport andnumber_of_hitsis incremented for the buckets it passes through. Buckets withnumber_of_hits >= 20are treated as "see-through" space and their points are excluded from the optimization Hessian (lidar_odometry_utils_optimizers.cpp) and hidden by "Show without filtered buckets" in the GUI.Until now this signal was never persisted per point — every output point in
scan_lio_*.lazwas written with LAS classification0. This PR exports points lying in moving buckets with LAS classification 7, so the resulting.lazfiles contain both class0(static) and class7(dynamic).Changes
core/include/Core/export_laz.h—LazWriter::writePoint()now setspoint_->classification;exportLaz()takes an optional per-point classification vector. Both use default arguments, so existing callers (e.g.save_all_to_las) are unchanged.apps/lidar_odometry_step_1/lidar_odometry.cpp— insave_result(), afterpoints_to_vector()produces the global cloud, each point's indoor RGD bucket is looked up (get_rgd_index_3dwithin_out_params_indoorresolution);number_of_hits >= threshold-> class 7, else 0. The grids (params.buckets_indoor) are still alive at save time.LidarOdometryParams— newclassify_moving_objects(default on) andmoving_object_hits_threshold(default 20), persisted via a TOML[moving_objects]section and exposed in the GUI menu.Scope / follow-up
This PR covers Step 1 only (the
scan_lio_*.lazoutput). Carrying the classification through Step 2 / insightoBake to the final LAS export requires readingclassificationinPointCloud::load_pcand writing it insave_all_to_las; that is intentionally left for a separate branch.Known limitation
number_of_hitsis only accumulated for chunks withpoints_global.size() < 100000(existing behaviour inupdate_rgd), so on very large scans the class-7 marking may be sparse or absent. Out of scope here.🤖 Generated with Claude Code