Skip to content

Faster empirical Camera–LiDAR uncertainty

Empirical uncertainty repeatedly refits the same calibration on seeded temporal block samples. --workers runs independent refits in separate CPU processes:

calibrex camera-lidar empirical-uncertainty correspondence.json problem.json \
  --result-dir outputs/refits --output outputs/uncertainty.yaml \
  --block-length 3 --resample-count 20 --workers 4 --stability-only

The default remains one process. The Python API accepts workers=4 through run_empirical_se3_uncertainty. Scripts calling that API with multiple workers must protect their entry point with if __name__ == "__main__": because workers use Python's spawn start method on every platform. The CLI handles this already. Each process receives the input snapshot once. Concurrency is capped at the number of refits, and at most twice that concurrency is queued. Choose a count appropriate for available CPU and memory; each process holds its own input and solver state. Small workloads can be faster with the default serial execution.

Workers inherit the caller's BLAS thread settings. To prevent nested BLAS oversubscription, the comparison sets OPENBLAS_NUM_THREADS, MKL_NUM_THREADS and OMP_NUM_THREADS to 1 before starting Python. The library leaves those environment settings to the caller.

Preserved calibration behavior

The same iteration index determines each refit's RNG seed, block split and correspondence bootstrap regardless of completion order. Results are collected in their original order. Every refit retains its full trace, selected and candidate poses, holdout, acceptance and failure status. Failed refits retain their artifacts; worker exceptions propagate without a hidden serial retry. The parent captures source provenance once and binds every saved child output to the final uncertainty artifact with SHA-256.

The probabilistic pose evaluator also calculates radial-distortion powers with NumPy's float64 float_power loop, removing per-element Python dispatch. This retains the platform pow operation used by Python float exponentiation. Extreme finite radii use the original scalar powers to preserve overflow exceptions. Projection formulas, reductions, confidence gates, evaluation budgets, covariance, interval construction and coverage controls are unchanged. The scalar evaluator and small-input fallback remain available. Tests compare power bytes across wide, strided, read-only and nonfinite inputs, and complete pose evaluations across camera models and uncertainty ablations.

This is CPU acceleration using existing dependencies. The public runtime schemas and their versions are unchanged; the worker option is recorded in command provenance.

Complete-process comparison

Three alternating pairs use the existing execution control from nine public KITTI raw scans in 2011_09_30_drive_0018_sync. It retains 1,024 points per scan, or 9,216 points in total. The camera correspondences are generated from declared pinhole, MEI and double-sphere projections with fixed noise. This control measures execution and numerical parity. It does not establish real-image calibration accuracy or empirical coverage. --stability-only ignores the injected reference, and all six policies remain inconclusive.

Both variants perform twenty seeded refits over three-frame blocks, with the same 400-evaluation limit for each refit. Timings include fresh Python startup, imports, input validation, process initialization, all solver evaluations and diagnostics, twenty YAML outputs, final uncertainty saving and pool shutdown. Source checks and output auditing run outside that timing. No timing warmup or failed worker is removed. The host was unreserved.

Complete empirical uncertainty CLI Median seconds
Previous implementation, one process 29.935852
Current implementation, four processes 12.643885
Observed ratio 2.367615x
Repeat First variant Previous seconds Current seconds
0 previous 28.047527 14.412418
1 current 29.935852 11.293290
2 previous 31.793272 12.643885

Every run retains exactly 6,224 trace entries across twenty refits. All 120 complete refit results and six final uncertainty results match exactly. Only timestamps, source commit metadata, declared output paths, the worker strategy flag and already verified child-output hashes are normalized. No numeric result, diagnostic, interval or solver status is normalized.

The measured baseline is dd29d3cc554786a333bf9da5a09e012d5cf1ba45 and the candidate is ed83117aab745514c21b5d2e4dd049a9a79020e7. The environment uses Python 3.12.10, NumPy 2.3.4, SciPy 1.17.1, Pydantic 2.12.5 and PyYAML 6.0.3. The ratio combines array powers and process refits; it does not isolate either optimization or establish a portable speed guarantee. Separate exploratory single-pair probes are not pooled with these six fresh-process measurements.

The indexed evidence archive retains every result, timing, input identity, source capsule and parent/child import receipt. The initial Git compatibility failure occurred before any timed worker. A later summary error occurred after all six successful timed workers; the archive retains that error and the corrected postprocessing without reruns or changes to the measurements.

Reproduce and audit

Obtain the nine original scans identified by the archived fixture protocol under KITTI's upstream terms. The builder uses scans present in the supplied directory, so use those nine input identities for this workload. The original builder is retained as fixture-builder.py.txt; the current tool keeps the same fixture construction. Rebuilt input hashes can differ with output paths, creation timestamps and source provenance.

python -m tools.benchmark_probabilistic_refinement \
  --build-kitti-fixture path/to/2011_09_30_drive_0018_sync \
  --points-per-frame 1024 --output-dir outputs/projected-control
python -m tools.benchmark_empirical_refits \
  --baseline-commit dd29d3cc554786a333bf9da5a09e012d5cf1ba45 \
  --fixture-dir outputs/projected-control --workers 4 --repetitions 3 \
  --output-dir outputs/empirical-refit-comparison
python -m tools.benchmark_empirical_refits \
  --audit docs/assets/empirical_refit_workers

Use fresh output directories and the recorded candidate runtime to reproduce the comparison. The frozen launcher in the archive records the original run; the current tool also handles an omitted optional reference during summary generation. Auditing the published archive needs only ordinary CPU dependencies, without the raw scans, generated inputs or optional accelerator packages.