Skip to content

[feat] Epipolar-guided L2R tracking for multicamera mode - #71

Open
slepichev wants to merge 7 commits into
mainfrom
slepichev/improve_lr_sof_tracking
Open

[feat] Epipolar-guided L2R tracking for multicamera mode#71
slepichev wants to merge 7 commits into
mainfrom
slepichev/improve_lr_sof_tracking

Conversation

@slepichev

@slepichev slepichev commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator
Replace the projection-based initial guess for left-to-right (L2R) LK
tracking with per-pixel epipolar curves. Both MultiSOFCPU and MultiSOFGPU
now seed LK from precomputed candidate positions sampled along each
feature's epipolar curve, tried in first-successful-wins order.

Mechanics:
- EpipolarCurves precomputes a corner-grid of per-pixel candidate lists (log-uniform depth sampling, dedup at >= 1 top-level pixel spacing). Candidates() bilinearly interpolates from the four surrounding corners.
- Depth range flows from Odometry::Config::{min_depth,max_depth}. Any negative value auto-detects from the pair baseline: ~7 cm baseline -> [0.1 m, 20 m], KITTI-scale (~0.5 m) -> [7 m, 1000 m].
- Multi-launch scan stops advancing the candidate index once kL2REarlyExitFraction of observations are tracked; identical semantics on CPU and GPU.
- Search radius capped at CrossCamSearchRadius(top_l) to prevent LK from drifting onto decoy features (previously scaled with baseline * focal and could reach hundreds of pixels).
- GPU batching sentinels ({0, 0} offset for exhausted / already-won points) are discarded host-side; only real candidate outcomes can win.

Hygiene:
- Per-pair scratch (uvL, cands, winners) lives on the pair struct so allocations persist across frames.
- AutoDetectDepthRange throws std::invalid_argument on non-positive or non-monotonic ranges after auto-detection.
- .at() on pair lookups fails loudly on unknown ids instead of silently inserting a default-constructed entry.

Public API:
- Odometry::Config::{min_depth,max_depth} (default -1.f = auto).
- Python binding, YAML loader (python/utils.py) and debug_dump.cpp updated.
- TROUBLESHOOTING.md Step 8 covers when and how to override.

Summary by CodeRabbit

  • New Features
    • Improved multicamera left-to-right tracking by generating epipolar-curve candidate points and scanning them on both CPU and GPU.
    • Added configurable multicamera depth sampling bounds (min_depth/max_depth) with auto-detection when set negative.
    • Exposed min_depth/max_depth in the Python Odometry.Config.
  • Documentation
    • Added troubleshooting guidance: “Tune multicamera L2R depth range”.
  • Tests
    • Added a left-to-right multicamera SOF regression test covering both CPU and GPU.
  • Chores
    • Extended configuration diagnostics output to include min_depth/max_depth.

    Replace the projection-based initial guess for left-to-right (L2R) LK
    tracking with per-pixel epipolar curves. Both MultiSOFCPU and MultiSOFGPU
    now seed LK from precomputed candidate positions sampled along each
    feature's epipolar curve, tried in first-successful-wins order.

    Mechanics:
    - EpipolarCurves precomputes a corner-grid of per-pixel candidate lists
      (log-uniform depth sampling, dedup at >= 1 top-level pixel spacing).
      Candidates() bilinearly interpolates from the four surrounding corners.
    - Depth range flows from Odometry::Config::{min_depth,max_depth}. Any
      negative value auto-detects from the pair baseline: ~7 cm baseline
      -> [0.1 m, 20 m], KITTI-scale (~0.5 m) -> [7 m, 1000 m].
    - Multi-launch scan stops advancing the candidate index once
      kL2REarlyExitFraction of observations are tracked; identical semantics
      on CPU and GPU.
    - Search radius capped at CrossCamSearchRadius(top_l) to prevent LK from
      drifting onto decoy features (previously scaled with baseline * focal
      and could reach hundreds of pixels).
    - GPU batching sentinels ({0, 0} offset for exhausted / already-won
      points) are discarded host-side; only real candidate outcomes can win.

    Hygiene:
    - Per-pair scratch (uvL, cands, winners) lives on the pair struct so
      allocations persist across frames.
    - AutoDetectDepthRange throws std::invalid_argument on non-positive or
      non-monotonic ranges after auto-detection.
    - .at() on pair lookups fails loudly on unknown ids instead of silently
      inserting a default-constructed entry.

    Public API:
    - Odometry::Config::{min_depth,max_depth} (default -1.f = auto).
    - Python binding, YAML loader (python/utils.py) and debug_dump.cpp
      updated.
    - TROUBLESHOOTING.md Step 8 covers when and how to override.
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Multicamera L2R tracking now accepts configurable depth bounds, generates cached epipolar candidates, and uses candidate-driven matching on CPU and GPU. Configuration is exposed through C++, Python, JSON dumps, and troubleshooting documentation, with new stereo regression tests.

Changes

Multicamera L2R tracking

Layer / File(s) Summary
Depth-range configuration contract
libs/cuvslam/cuvslam2.h, libs/cuvslam/cuvslam2.cpp, libs/sof/sof_config.h, python/cuvslam2.cpp, libs/cuvslam/debug_dump.cpp, TROUBLESHOOTING.md
Adds min_depth and max_depth, propagates them into SOF settings, exposes them in Python, records them in configuration output, and documents auto-detection.
Epipolar curve generation and caching
libs/sof/epipolar_curves.*, libs/sof/internal/sof_multicamera_base.h, libs/sof/sof_multicamera_base.cpp, libs/sof/CMakeLists.txt
Generates depth-sampled epipolar curves, validates depth ranges, interpolates candidates, and caches curves per camera pair.
CPU candidate tracking flow
libs/sof/internal/sof_multicamera_cpu.h, libs/sof/sof_multicamera_cpu.cpp
Retains per-pair scratch buffers, scans candidates in order, records first successful matches, and exits after the configured tracking fraction.
GPU candidate tracking flow
libs/sof/internal/sof_multicamera_gpu.h, libs/sof/sof_multicamera_gpu.cpp
Uses repeated GPU launches over epipolar candidates, tracks winners, publishes results, and manages per-pair launch state.
L2R regression coverage
libs/sof/test/*, test_data/sof/lr_test/stereo.edex
Adds CPU and CUDA-gated GPU tests using stereo calibration data and a minimum tracked-point threshold.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested reviewers: hrabeti-nvidia, vikuznetsov-nvidia

Sequence Diagram(s)

sequenceDiagram
  participant MultiSOFCPU
  participant MultiSOFBase
  participant EpipolarCurves
  participant IFeatureTracker
  MultiSOFCPU->>MultiSOFBase: GetOrBuildEpipolarCurves(camera pair, pyramid level)
  MultiSOFBase->>EpipolarCurves: Candidates(left observation)
  EpipolarCurves-->>MultiSOFCPU: Right-image candidate positions
  MultiSOFCPU->>IFeatureTracker: Track candidate UV pair
  IFeatureTracker-->>MultiSOFCPU: Tracking result
  MultiSOFCPU-->>MultiSOFCPU: Publish first successful winner
Loading
sequenceDiagram
  participant MultiSOFGPU
  participant MultiSOFBase
  participant EpipolarCurves
  participant track_points
  MultiSOFGPU->>MultiSOFBase: GetOrBuildEpipolarCurves(camera pair, pyramid level)
  MultiSOFBase->>EpipolarCurves: Candidates(left observation)
  EpipolarCurves-->>MultiSOFGPU: Candidate offsets
  MultiSOFGPU->>track_points: Launch candidate index
  track_points-->>MultiSOFGPU: Track statuses and information
  MultiSOFGPU-->>MultiSOFGPU: Store winners and publish tracks
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly captures the main multicamera epipolar-guided L2R tracking change.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch slepichev/improve_lr_sof_tracking

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@libs/sof/epipolar_curves.cpp`:
- Line 29: Replace the fixed kNumDepthSamples value used by
GetOrBuildEpipolarCurves with an adaptive, bounded sample count based on each
epipolar curve’s projected pixel extent, reducing projections for short curves
while preserving sufficient sampling for long curves. Update the sampling logic
across the curve-building path, including the code spanning the referenced
range, so it no longer performs 10,000 projections per corner.
- Around line 42-57: Update AutoDetectDepthRange to trigger
baseline-interpolated defaults only when min_depth or max_depth is strictly
negative, preserving zero as an explicit value subject to validation. Extend its
final validation to reject non-finite bounds, including positive infinity, while
retaining the required 0 < min_depth < max_depth relationship and
std::invalid_argument behavior.

In `@libs/sof/epipolar_curves.h`:
- Around line 43-45: Update the EpipolarCurves constructor and its cache-builder
path to accept Pose for the right_from_left SE(3) transform, following the
cuvslam2.h API type. Convert Pose internally only at points requiring Eigen
operations, and update all related declarations, definitions, and call sites
consistently.

In `@libs/sof/sof_multicamera_gpu.cpp`:
- Line 73: Update the tracker construction flow around CreateGPUTracker so a
nullptr result is handled immediately and cannot reach
pair.tracker->track_points(...). Replace reliance on the assert with a
release-safe fail-fast check while preserving the existing valid-tracker path.
- Around line 113-165: Refactor the candidate loop around tracks_data and the
winner-update logic to compact only active point indices into a dense
per-iteration sub-batch. Launch copy and tracking operations with active_count,
then map returned results back to their original points through an index vector,
preserving candidate ordering, winner handling, early exit, and tracked_count
behavior without processing sentinel entries.
- Around line 90-105: Guard PrimaryToSecondaryGPUTracker::tracks_data before the
loops that index it with primary_obs.size(). Ensure the buffer has capacity for
n elements, preferably resizing it to n, or clamp n to its fixed capacity and
consistently use the clamped count in iteration and GetTrackingResults paths;
prevent any unchecked tracks_data[i] access when the observation count exceeds
1000.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: e7f8f189-c228-473d-b0ee-03ecdbda1762

📥 Commits

Reviewing files that changed from the base of the PR and between 57f42cc and de8f3de.

📒 Files selected for processing (15)
  • TROUBLESHOOTING.md
  • libs/cuvslam/cuvslam2.cpp
  • libs/cuvslam/cuvslam2.h
  • libs/cuvslam/debug_dump.cpp
  • libs/sof/CMakeLists.txt
  • libs/sof/epipolar_curves.cpp
  • libs/sof/epipolar_curves.h
  • libs/sof/internal/sof_multicamera_base.h
  • libs/sof/internal/sof_multicamera_cpu.h
  • libs/sof/internal/sof_multicamera_gpu.h
  • libs/sof/sof_config.h
  • libs/sof/sof_multicamera_base.cpp
  • libs/sof/sof_multicamera_cpu.cpp
  • libs/sof/sof_multicamera_gpu.cpp
  • python/cuvslam2.cpp

Comment thread libs/sof/epipolar_curves.cpp Outdated
Comment thread libs/sof/epipolar_curves.cpp Outdated
Comment thread libs/sof/epipolar_curves.h
Comment thread libs/sof/sof_multicamera_gpu.cpp
Comment thread libs/sof/sof_multicamera_gpu.cpp
Comment thread libs/sof/sof_multicamera_gpu.cpp
@slepichev slepichev changed the title Epipolar-guided L2R tracking for multicamera mode [feat] Epipolar-guided L2R tracking for multicamera mode Jul 28, 2026
@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown

Test Results

Status Platform Language Total Passed Failed Errors Skipped
Orin C++ 16 16 0 0 0
Orin Python 71 70 0 0 1
Thor C++ 16 16 0 0 0
Thor Python 71 70 0 0 1
x86_64 C++ 16 16 0 0 0
x86_64 Python 71 70 0 0 1

cuVSLAM Evaluation KPIs

Config Dataset ATE,% ARE,º/m Kabsch, Losts, diff ATE,% diff ARE,º/m diff Kabsch, diff Losts, FPS,Hz
x86_64-cuda12.6.3-ubuntu24.04 KITTI-STEREO_ODOM 0.82 0.0023 2.7857 0 NA NA NA NA 289.7
x86_64-cuda12.6.3-ubuntu24.04 KITTI-STEREO_SLAM 0.7298 0.002 1.9147 0 NA NA NA NA 164.5

Artifacts

The epipolar-guided left-to-right tracker added in de8f3de shipped
without unit coverage on either the CPU or the GPU path.

sof_l2r_test drives MultiSOF through its public trackNextFrame on a
single frame and asserts the number of published secondary-camera
observations. One test per implementation; the GPU case compiles only
under USE_CUDA.

The frame comes from the new test_data/sof/lr_test dataset (640x400,
7.5 cm baseline, polynomial distortion). Images are committed next to
stereo.edex, so the test is self-contained and needs no dataset mount.
The 7.5 cm baseline also exercises the small-stereo depth-range
auto-detect anchor.

Under USE_RERUN the two frames are logged side by side with one line
per match, through the shared RerunVisualizer. Off by default like
every other unit test; run with RERUN=1 to view.

libs/sof/test gains a pnp link (TrackPerFrameSettings pulls in
PNPSettings::InertialSettings) and, under USE_RERUN, visualizer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@libs/sof/test/sof_l2r_test.cpp`:
- Line 90: In TrackL2R, replace the non-fatal expectation for edex_rig.start()
with failure reporting that immediately returns 0 when startup fails; likewise,
handle getFrame() failure by reporting it and returning 0 before accessing frame
data. Apply these changes at libs/sof/test/sof_l2r_test.cpp lines 90-90 and
114-114.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 5f4136f7-edbf-4044-918d-14b5b739fb01

📥 Commits

Reviewing files that changed from the base of the PR and between de8f3de and 7302323.

⛔ Files ignored due to path filters (2)
  • test_data/sof/lr_test/left/000000.png is excluded by !**/*.png
  • test_data/sof/lr_test/right/000000.png is excluded by !**/*.png
📒 Files selected for processing (3)
  • libs/sof/test/CMakeLists.txt
  • libs/sof/test/sof_l2r_test.cpp
  • test_data/sof/lr_test/stereo.edex

Comment thread libs/sof/test/sof_l2r_test.cpp Outdated
slepichev and others added 2 commits July 29, 2026 03:11
EpipolarCurves sampled 10000 log-uniform depths per pixel corner and
then threw nearly all of them away: the dedup pass keeps a sample only
if it lands at least one top-level pixel from the last kept one, which
leaves a handful of candidates at KITTI-scale baselines and a few dozen
at short ones. Over 99% of the projections were computed and discarded.

50 samples cover the same log-uniform range densely enough to reach
every candidate the dedup would have kept, so the curves come out
effectively unchanged while the per-pair build cost drops by ~200x.

Verified: no metric degradation on KITTI, and SOFL2R tracks the same
number of points on test_data/sof/lr_test for both CPU and GPU.

Also replaces the repeated type names on three adjacent casts with
const auto.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
TrackL2R used non-fatal expectations for CameraRigEdex::start and
getFrame, then kept going. EXPECT_* cannot abort a function that
returns a value, so a missing or unreadable test_data/sof/lr_test
carried on into getCamerasNum() == 0, and fig.primary_cameras().front()
dereferenced an empty vector. getFrame failing left metas empty and
metas[0] read out of bounds. Either way a dataset problem surfaced as a
crash instead of a test failure.

Report the ErrorCode with ADD_FAILURE and return 0 immediately, so the
test fails with the reason instead of taking the process down.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
libs/sof/epipolar_curves.cpp (1)

63-74: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Bounds-check before converting to size_t. !(x >= 0.f) still lets +inf and oversized inputs reach static_cast<size_t>, which is undefined behavior. Reject non-finite values and coordinates at or beyond the last interpolable cell before the cast.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/sof/epipolar_curves.cpp` around lines 63 - 74, Update the bounds
validation in the epipolar-curve interpolation path before the v0/u0
conversions: reject non-finite v_f or u_f values and reject coordinates at or
beyond the last interpolable cell using the available curve dimensions. Ensure
these checks occur before static_cast<size_t>, while preserving interpolation
for valid coordinates whose v1/u1 corners are in range.
♻️ Duplicate comments (1)
libs/sof/epipolar_curves.cpp (1)

45-57: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject zero and non-finite depth bounds.

The public contract in libs/sof/epipolar_curves.h specifies negative values for auto-detection, but <= 0.f silently converts an explicit zero. max_depth = +inf also passes validation and later produces non-finite sampling values. Use < 0.f for auto-detection and require both bounds to be finite.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/sof/epipolar_curves.cpp` around lines 45 - 57, Update
AutoDetectDepthRange so only negative min_depth or max_depth values trigger
auto-detection, preserving explicit zero values for validation. Extend the final
validation to require both bounds are finite while retaining the 0 < min_depth <
max_depth constraint.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@libs/sof/epipolar_curves.cpp`:
- Around line 63-74: Update the bounds validation in the epipolar-curve
interpolation path before the v0/u0 conversions: reject non-finite v_f or u_f
values and reject coordinates at or beyond the last interpolable cell using the
available curve dimensions. Ensure these checks occur before
static_cast<size_t>, while preserving interpolation for valid coordinates whose
v1/u1 corners are in range.

---

Duplicate comments:
In `@libs/sof/epipolar_curves.cpp`:
- Around line 45-57: Update AutoDetectDepthRange so only negative min_depth or
max_depth values trigger auto-detection, preserving explicit zero values for
validation. Extend the final validation to require both bounds are finite while
retaining the 0 < min_depth < max_depth constraint.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 51667ab1-5a4e-4fa1-833b-3fd2043f2a6c

📥 Commits

Reviewing files that changed from the base of the PR and between 7302323 and 40b8647.

📒 Files selected for processing (1)
  • libs/sof/epipolar_curves.cpp

Two gaps in AutoDetectDepthRange:

min_depth / max_depth of exactly 0 triggered auto-detection. The public
API and the header both document a *negative* value as the auto trigger,
so a 0 is an explicit input and almost certainly a mistake — silently
replacing it with a baseline-interpolated default hid that. Only
negative values auto-detect now; 0 falls through to validation and
throws.

+inf passed validation. min_depth > 0 and min_depth < max_depth both
hold for max_depth = +inf, so log_max was infinite, log_step was
infinite, and log_d went NaN on the first decrement — every depth sample
after that projected to NaN and the curves came out silently empty.
Validation now requires both bounds to be finite. NaN was already
rejected by the !(x > 0) / !(x < y) form and still is.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@libs/sof/epipolar_curves.cpp`:
- Around line 49-55: Update the min_depth and max_depth auto-detection guards in
the surrounding epipolar-curve setup so only finite negative values are replaced
with computed defaults. Ensure ±infinity and other non-finite bounds remain
unchanged and reach the existing validation condition, while preserving the
current interpolation for valid finite negative inputs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 1659f099-65fd-4874-a998-28dc2789b170

📥 Commits

Reviewing files that changed from the base of the PR and between 7f1ed54 and 880fee6.

📒 Files selected for processing (1)
  • libs/sof/epipolar_curves.cpp

Comment thread libs/sof/epipolar_curves.cpp Outdated
slepichev and others added 2 commits July 29, 2026 03:29
AutoDetectDepthRange silently replaced inputs it should have rejected:

- 0 triggered auto-detection. The header and public API both document a
  negative value as the auto trigger, so 0 is an explicit input and
  almost certainly a mistake; substituting a baseline default hid it.
- -inf triggered auto-detection, while NaN did not. Non-finite inputs
  now behave uniformly: none are replaced, all reach validation.
- +inf passed validation, since min_depth > 0 and min_depth < +inf both
  hold. log_max was then infinite, log_step infinite, and log_d went NaN
  on the first decrement, so every depth sample projected to NaN and the
  curves came out silently empty.

Only finite negative values auto-detect now, and validation requires
both bounds to be finite on top of 0 < min_depth < max_depth.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CreateGPUTracker handles only TrackerType::LK and LKHorizontal; KLT and
KLTHorizontal fall to the default branch and return nullptr. That null
was stored and only checked by assert(pair.tracker != nullptr) in
LaunchTrackingPrimaryToSecondary, which is compiled out under NDEBUG.
Release builds configured with a KLT lr_tracker therefore dereferenced
null inside track_points instead of reporting the bad setting.

Throw std::invalid_argument from the MultiSOFGPU constructor when the
tracker cannot be created, so the failure names the cause and the
existing assert becomes a genuine invariant rather than the only check.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.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