Skip to content

feat: add ObjectMinPointsFilter transform to drop sparse GT boxes - #135

Open
simonschlaepfer wants to merge 8 commits into
mainfrom
feat/object-min-points-filter
Open

simonschlaepfer wants to merge 8 commits into
mainfrom
feat/object-min-points-filter

Conversation

@simonschlaepfer

Copy link
Copy Markdown
Collaborator

Summary

  • Adds ObjectMinPointsFilter, a transform that drops ground-truth boxes enclosing fewer than min_points LiDAR points, filtering labels in sync. Motivation: boxes with too few points carry little geometric evidence, so training against them mostly injects noise. This is the analog of mmdetection3d's filter_by_min_points ground-truth-database option, exposed as a composable pipeline transform.
  • min_points: int: a box survives iff its interior point count is >= min_points (strictly-less-than is dropped). 0 keeps every box; 1 keeps every box with at least one point.
  • Counting is format-agnostic (incl. 9-DoF); a point in the overlap of several boxes is counted once for each box it lies in (membership, not first-box assignment), so a shared point can't push a box below threshold on a neighbour's account.
  • Only the boxes/labels are filtered — the PointCloud3D and every other sample entry pass through unchanged. Follows RangeFilter3D's dict-based convention: accepts both a single sample dict and an (inputs, targets) pair.
  • Absent points entry → no box has a defined count, so all boxes are kept; a present-but-empty point cloud counts as zero points per box (dropped unless min_points=0).
  • Pairs naturally with ObjectPointsSample (feat: add ObjectPointsSample transform for per-object point thinning #133): after that transform simulates sparse returns by thinning each object to a target keep count, applying this filter with a matching min_points drops the objects sparse simulation pushed below the detectability floor. The two stay orthogonal — thresholds are matched by the user, nothing couples them automatically.
  • Reuses the existing points_in_boxes_3d op (per-box counts are a sum over the [N, M] membership mask), so no new functional kernel was introduced.

Test plan

  • pytest test/test_transforms_object_min_points_filter.py (40 tests: threshold boundary from both sides (min_points 0/1/2/99 over known per-box counts); overlap point counted for each box; box type/format preservation; format-agnostic across all four box formats; points and unrelated entries pass through unchanged; single-dict vs (inputs, targets) structure; absent-points keep-all vs empty-cloud drop-all; missing boxes/labels keys; empty boxes; min_points validation for negative/non-int/bool)
  • ruff check and ruff format --check clean
  • pyrefly check clean (0 errors)

@simonschlaepfer simonschlaepfer changed the title feat: add ObjectMinPointsFilter transform to drop sparse ground-truth boxes feat: add ObjectMinPointsFilter transform to drop sparse GT boxes Jul 7, 2026
@simonschlaepfer
simonschlaepfer force-pushed the feat/object-min-points-filter branch 3 times, most recently from 99d5d02 to b00b94d Compare July 7, 2026 15:44
simonschlaepfer and others added 2 commits July 8, 2026 09:10
- Extract _BoxFilterTransform base so RangeFilter3D and
  ObjectMinPointsFilter share the rebuild-boxes + sync-labels logic in
  one place instead of duplicating it.
- ObjectMinPointsFilter: short-circuit min_points == 0 (skip the
  point-in-box computation entirely) and return a fresh inputs dict in
  the pair path to match RangeFilter3D (no aliasing of the caller's dict).
- Add mixed-dtype and inputs-dict-aliasing regression tests.

Assisted-by: Claude Code (Opus 4.8)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@simonschlaepfer
simonschlaepfer force-pushed the feat/object-min-points-filter branch from b00b94d to bfb343a Compare July 8, 2026 07:14
@simonschlaepfer
simonschlaepfer marked this pull request as ready for review July 8, 2026 07:28
@simonschlaepfer
simonschlaepfer requested a review from yeetypete July 8, 2026 07:28

@yeetypete yeetypete left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some comments. Biggest issue is caused by me not using pytree in RangeFilter3D.

Comment thread src/vision3d/transforms/_box_filter.py Outdated
Comment thread src/vision3d/transforms/_object_min_points_filter.py Outdated
Comment thread src/vision3d/transforms/_box_filter.py Outdated
Comment thread test/test_transforms_object_min_points_filter.py
Comment thread src/vision3d/transforms/_object_min_points_filter.py Outdated

This branch has not been deployed

No deployments
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.

2 participants