You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Flat-ground distance carries a ~11% range-proportional scale error, not just scatter — multi-view self-consistency measures it and DA3 can anchor it #101
Multi-view association in the auto-labeler (sidewalk-auto-labeler#27) fuses detections of the same physical ramp seen from several panos. That makes a new check possible: the same ramp, raycast from cameras at different distances, should land in the same place. It does not.
For every fused site, take each member's signed along-ray residual — how far that view's ground point sits beyond (or short of) the fused position, measured along its own ray — and regress it on that member's range. Demeaned within each site (fixed effects, so per-site offsets cancel), pooled across sites with ≥3 refit members spanning ≥4 m of range:
city
source
sites
members
slope (m per m)
paterson
GSV
4,433
19,588
+0.1159 ± 0.0021
gainesville
GSV
1,774
7,033
+0.1179 ± 0.0038
bend
GSV
9,388
42,240
+0.1082 ± 0.0013
richmond
Mapillary
890
7,040
+0.1275 ± 0.0058
sao_paulo
GSV
1,877
8,156
+0.0572 ± 0.0034
All 50–90σ. The discrepancy is not scatter — it is proportional to range, the signature of a scale error in d = camera_height / tan(depression), at roughly 11% almost everywhere.
At 20 m that is a ~2.3 m radial displacement, and because a street's cameras mostly sit along one line, it does not average out — it pushes detections consistently away from the roadway.
This is the same effect §0 of detection_recall_analysis.md already saw
That doc reports flat-ground geometry and DA3 metric depth agreeing to within 6.5–8.5%, and notes geometry placing four Richmond ramps above the horizon. Those were read as agreement plus rig noise. The multi-view result says the residual has structure: it is a proportional scale error, not a spread. A scale error is a one-constant fix; noise is not.
Two independent methods — an external monocular depth model, and pure self-consistency across views — now point the same way, and neither shares the other's failure modes.
What it is not
The obvious explanation is that the assumed camera height is too high (+0.116 implies ~2.33 m against the 2.5 m this repo assumes and the 2.6 m the labeler's geo.py assumes — worth reconciling in its own right). That hypothesis fails its own test. Re-fusing the labeler's runs over 2.10–2.60 m does lower raw within-site scatter at ~2.33 m, but lowering the assumed height shrinks every range proportionally, so raw metres shrink for free. Normalize scatter by the assumed height and 2.60 m is monotonically the best value tried, on both a GSV and a Mapillary city. So this is not simply the wrong constant.
The decisive experiment, using tooling that already exists here
The multi-view regression proves the disagreement is range-proportional but cannot say which end is right — it is anchored to a weighted mean of the same biased rays. scripts/analysis/depth_extract_da3.py can anchor it:
Regress DA3 metric depth on flat-ground distance for benchmark detections and look at the slope, not the correlation.
Slope ≠ 1 → the geometry is biased. Fixable as a calibration; the distance buckets in detection_recall_analysis.md §1 and precision_by_distance.py are stretched, and the apparent-size relation W/d × (4096/2π) inherits it directly.
Splitting those two is cheap and needs no new data.
Why it is worth doing
Distance-conditioned results. Recall by distance (0.842 / 0.879 / 0.812 / 0.564 across the buckets) is reported against an axis that may be stretched ~11%; bucket edges move.
Position accuracy downstream. Auto-labeler world positions inherit it, and it is the largest single systematic we can currently name — median agreement with GT is ~0.8–1.6 m, so a 2 m bias at 20 m is not a rounding error.
Note the Mapillary/GSV split does not separate the hypotheses: richmond (Mapillary, varied rigs, SfM poses) shows the largest slope while sharing no camera geometry with GSV, which argues against a single rig constant and toward something shared — the model, or the flat-ground assumption itself. São Paulo, the hilliest split, is the outlier at half the slope, which is the opposite of what pure terrain noise would predict and deserves its own look.
Measurement scripts are in the auto-labeler scratchpad (heightbias.py, heightsweep.py); the method above is complete enough to reproduce from sites.jsonl alone.
The measurement
Multi-view association in the auto-labeler (sidewalk-auto-labeler#27) fuses detections of the same physical ramp seen from several panos. That makes a new check possible: the same ramp, raycast from cameras at different distances, should land in the same place. It does not.
For every fused site, take each member's signed along-ray residual — how far that view's ground point sits beyond (or short of) the fused position, measured along its own ray — and regress it on that member's range. Demeaned within each site (fixed effects, so per-site offsets cancel), pooled across sites with ≥3 refit members spanning ≥4 m of range:
All 50–90σ. The discrepancy is not scatter — it is proportional to range, the signature of a scale error in
d = camera_height / tan(depression), at roughly 11% almost everywhere.At 20 m that is a ~2.3 m radial displacement, and because a street's cameras mostly sit along one line, it does not average out — it pushes detections consistently away from the roadway.
This is the same effect §0 of
detection_recall_analysis.mdalready sawThat doc reports flat-ground geometry and DA3 metric depth agreeing to within 6.5–8.5%, and notes geometry placing four Richmond ramps above the horizon. Those were read as agreement plus rig noise. The multi-view result says the residual has structure: it is a proportional scale error, not a spread. A scale error is a one-constant fix; noise is not.
Two independent methods — an external monocular depth model, and pure self-consistency across views — now point the same way, and neither shares the other's failure modes.
What it is not
The obvious explanation is that the assumed camera height is too high (+0.116 implies ~2.33 m against the 2.5 m this repo assumes and the 2.6 m the labeler's
geo.pyassumes — worth reconciling in its own right). That hypothesis fails its own test. Re-fusing the labeler's runs over 2.10–2.60 m does lower raw within-site scatter at ~2.33 m, but lowering the assumed height shrinks every range proportionally, so raw metres shrink for free. Normalize scatter by the assumed height and 2.60 m is monotonically the best value tried, on both a GSV and a Mapillary city. So this is not simply the wrong constant.The decisive experiment, using tooling that already exists here
The multi-view regression proves the disagreement is range-proportional but cannot say which end is right — it is anchored to a weighted mean of the same biased rays.
scripts/analysis/depth_extract_da3.pycan anchor it:Regress DA3 metric depth on flat-ground distance for benchmark detections and look at the slope, not the correlation.
detection_recall_analysis.md§1 andprecision_by_distance.pyare stretched, and the apparent-size relationW/d × (4096/2π)inherits it directly.Splitting those two is cheap and needs no new data.
Why it is worth doing
Note the Mapillary/GSV split does not separate the hypotheses: richmond (Mapillary, varied rigs, SfM poses) shows the largest slope while sharing no camera geometry with GSV, which argues against a single rig constant and toward something shared — the model, or the flat-ground assumption itself. São Paulo, the hilliest split, is the outlier at half the slope, which is the opposite of what pure terrain noise would predict and deserves its own look.
Measurement scripts are in the auto-labeler scratchpad (
heightbias.py,heightsweep.py); the method above is complete enough to reproduce fromsites.jsonlalone.🤖 Generated with Claude Code (claude-opus-5[1m])