AI3D-382 · detection passes 2 and 3 on the no-scan-angle-filter data

6 Sep 2026 · segments 018 / 060 / 066 / 085 / 086 · base = all knobs off, r1 = pass 2, r2 = pass 3

  1. Pass 2 (r1) added canopy extension driven by multi-return statistics, a returns stripper on the sign and rail paths, and a hedge-gate returns veto.
  2. Pass 3 (r2) kept canopy extension unchanged and added hard-blob unpaint + a sparse-sign guard, retuned the stripper, and removed it from the rail path.
  3. Recall: canopy recall above 10 m 0.019 → 0.879, segment-wide foliage-cell recall 0.58 → 0.92 — the whole win of the round, unchanged bit-for-bit in pass 3.
  4. Precision: hard-structure contamination −79 % (195 676 → 41 187 pts), both known 018 ghost signs gone, +2 real signs, delineator damage from pass 2 repaired.
  5. Net: canopy extension, hard-blob unpaint and the sparse-sign guard ship; the hedge-gate veto and the rail-path stripper are thrown away; the sign-path stripper stays off until one 060 false pole is gone.

0. How to read the renders

Every render is one spot in the raw new-data cloud, columns left to right: true RGB | number_of_returns (grey = 1, orange = 2, red = ≥ 3) | BASE paint | r1 paint | r2 paint. Top row is the oblique view, bottom row the same points top-down. Segments not run in a pass have no such column (066 has no r1; the r1-era renders stop at the r1 column).

unmaskedtreemedium_vegetationsign-ish (sign / pole / delineator)guardrailother

Click any image for full resolution; ← / → (or swipe) step through the images of that card only; Esc closes.

1. What changed, per pass

passfeatureknobverdictreason (one line)
2 (r1)canopy extensionenabled, min_multi 0.20 / min_triple 0.02keep+0.86 canopy recall above 10 m with hard contamination going down, not up.
2 (r1)hedge-gate returns veto0.55 / 0.15revertFires on 0 of 8 gate-evaluated parents and no segment emits medium_vegetation at all.
2 (r1)returns stripper, sign pathtau 0.30, r 0.75retune+2 real signs but −1 real delineator and +1 false pole, with 64/847 clusters changing verdict on 018.
2 (r1)returns stripper, rail half-post pathtau 0.30, r 0.75revertEmitted half-post set identical to base on all 4 segments — the pass is inert, so it was removed from railpost.py.
3 (r2)hard-blob unpaintmodel defaultskeep−79 % hard contamination, 10/10 unpainted blobs are panels, billboards or carriageway (max mrf 0.045).
3 (r2)sparse-sign guardsign_post.min_points_per_m = 100keepDrops both known ghosts (45 / 51 pts per m) and nothing else; real signs sit at 1014–2951 pts per m.
3 (r2)returns stripper, sign path (retuned)+ height gate 1.5 m, unstripped geometry for neighbour rulesretuneFixed the delineator, the churn (62 → 17) and the scrub gantry FP, but the 060 false pole is byte-identical to pass 2.
3 (r2)canopy extensionunchangedkeepRegression check only: per-segment recall, summary blocks and the 085 GT matrix are bit-identical to pass 2.

2. Improved

Canopy extension fills the crowns pass 2, kept in pass 3

Base paints only the lower part of every crown on the un-angle-filtered data; the extension grows the tree mask into cells whose multi-return statistics look like foliage.

BEFORE = 3rd panel · 085 woodland edge, 25–30 m · base paints only the lower ~15 m, r1 fills the whole crown
BEFORE = 3rd panel · 085 woodland edge, 25–30 m · base paints only the lower ~15 m, r1 fills the whole crown
BEFORE = 3rd panel · 060 embankment canopy, 19 m · look at the grey cap on top of the base paint closing in r1
BEFORE = 3rd panel · 060 embankment canopy, 19 m · look at the grey cap on top of the base paint closing in r1

canopy recall > 10 m 0.019 → 0.879 · 4–10 m 0.717 → 0.937 · foliage-cell recall 0.582 → 0.924 · +4.82 M tree-painted points · hard contamination −540 pts (down, not up) · runtime +1.7 %.

On 085 the extension adds 6.77 M points of which 0 % exist within 5 cm of any human-GT point — it paints exactly what the old angle-filtered cloud never had, so it cannot have damaged the GT.

+2 real signs recovered from foliage pass 2, survives pass 3

The returns stripper removes foliage-like points from sign candidates, which lets two 018 signs that base never saw pass the plate test.

BEFORE = 3rd panel · 018 rectangular sign panel ~1 m on a post behind the guardrail · base paints nothing, r1 paints plate + post (magenta)
BEFORE = 3rd panel · 018 rectangular sign panel ~1 m on a post behind the guardrail · base paints nothing, r1 paints plate + post (magenta)
BEFORE = 3rd panel · 018 circular sign plate on a slim 2.8 m post · base misses it entirely, r1 magenta is exactly plate + post
BEFORE = 3rd panel · 018 circular sign plate on a slim 2.8 m post · base misses it entirely, r1 magenta is exactly plate + post

signs 10 → 12 corpus-wide (018: 5 → 7) · both survive pass 3 at ml 0.537 / 0.481 · a third 018 sign gets a cleaner plate, ml 0.566 → 0.672.

Both sparse-sign ghosts removed pass 3

Two long-known 018 detections sit on flat asphalt with nothing above it; the point-density guard drops them as sparse_sign.

BEFORE = 3rd/4th panel · 018 ghost sign, 82 pts on flat asphalt · nothing is there; r2 (last panel) emits no magenta
BEFORE = 3rd/4th panel · 018 ghost sign, 82 pts on flat asphalt · nothing is there; r2 (last panel) emits no magenta
BEFORE = 3rd/4th panel · 018 ghost sign, 91 pts on asphalt + verge · no vertical object at all; dropped as sparse_sign in r2
BEFORE = 3rd/4th panel · 018 ghost sign, 91 pts on asphalt + verge · no vertical object at all; dropped as sparse_sign in r2

GHOST signs 2 → 0 · the two ghosts are 45.4 and 50.6 pts per m, every real sign in the 8-segment corpus is 1014–2951 — the 100 pts/m threshold sits in a 20x empty gap · nothing else in the corpus is removed.

Hard-blob unpaint strips tree paint off structure pass 3

Tree instances are scanned for connected blobs of single-return, hard-looking cells; those blobs lose the paint while the detection record itself is untouched.

BEFORE = 3rd/4th panel · 060 billboard panel + frame next to a real tree · whole panel green in base/r1, grey in r2, tree keeps its paint
BEFORE = 3rd/4th panel · 060 billboard panel + frame next to a real tree · whole panel green in base/r1, grey in r2, tree keeps its paint
066 has no r1 run: panels are RGB | returns | BASE | r2 · 38 x 47 m of carriageway + lane markings · the green streaks over the asphalt are gone in r2, both tree belts keep paint
066 has no r1 run: panels are RGB | returns | BASE | r2 · 38 x 47 m of carriageway + lane markings · the green streaks over the asphalt are gone in r2, both tree belts keep paint
BEFORE = 3rd/4th panel · 085 bright linear feature lying on the carriageway · painted tree in base/r1, removed in r2, roadside foliage untouched
BEFORE = 3rd/4th panel · 085 bright linear feature lying on the carriageway · painted tree in base/r1, removed in r2, roadside foliage untouched
BEFORE = 3rd/4th panel · 018 flat sign board on a T-post behind the rail · the green cross-hatched panel becomes grey in r2
BEFORE = 3rd/4th panel · 018 flat sign board on a T-post behind the rail · the green cross-hatched panel becomes grey in r2

hard contamination 44 → 23 cells, 195 676 → 41 187 points (−79 %), 0.0219 → 0.0041 of all tree paint · all 10 unpainted blobs are billboards, sign panels, carriageway or lane markings, max mrf 0.045 (trees are 0.42–0.55) · costs 6–23 s per segment.

On the 085 human GT: tree precision 0.4893 → 0.4924 (veg-tolerant 0.7413 → 0.7483) with recall unchanged to the point; of the removed points the GT can see, 100 % are the annotator's own class 76 and none are tree / low_veg / medium_veg.

Delineator loss from pass 2 repaired pass 3

In pass 2 a delineator vanished although its own cluster was never stripped — it flipped to repetitive_row because its neighbours shrank; pass 3 makes neighbourhood rules read unstripped geometry.

BEFORE = 3rd panel (base) · 018 two small roadside marker posts · base paints both, r1 (4th) paints nothing, r2 (5th) paints both again
BEFORE = 3rd panel (base) · 018 two small roadside marker posts · base paints both, r1 (4th) paints nothing, r2 (5th) paints both again
BEFORE = 3rd panel · same marker pair from the other centre · r1 collapsed it 1337 → 131 pts, r2 is back to base
BEFORE = 3rd panel · same marker pair from the other centre · r1 collapsed it 1337 → 131 pts, r2 is back to base

delineator (−3204.94, −2252.00) back · its neighbour 131 → 1337 pts (= base) · 018 cluster-verdict churn 62 → 17 · stripped clusters 144 → 28 on 018, 43 → 8 on 085 · no delineator_reflective_top cluster is stripped anywhere any more.

3. Did not work / thrown away

Two knobs deleted, one still blocked no ship

  • Hedge-gate returns veto (0.55 / 0.15) — 0 of 8 gate-evaluated parents vetoed, 0 hedge verdicts overruled, and medium_vegetation is 0 corpus-wide in base and r1, so there is nothing for it to overrule; reverted to null, no render exists because no band changed verdict.
  • Rail half-post stripper — the emitted half-post set is identical in base and r1 on all four segments (7/7, 25/25, 4/4, 5/5); at tau 0.30 the crest band loses ~100 % of its points and the min_remaining_fraction guard restores it whole, so the pass is inert; the call site is gone from railpost.py.
  • Pass-2 stripper damage — one real delineator lost and one false pole_other created, both shown below; 64/847 clusters changed verdict on 018, 6 of them after losing a single point.
  • Residual in pass 3 — the 060 pole_other/slender_tall_no_plate is byte-identical to pass 2 (n 1191, h 9.715, ml 0.167, sf 0.066): there is a real post there, but the record is typed "no plate" and stretched to 9.7 m by conifer stem, and base rejected it as low_vertical_continuity — 84 stripped points flip the verdict.
PASS-2 DAMAGE · 018 roadside guide post ~0.9 m in grass · base delineator paint (magenta) disappears in r1 although the cluster itself was never stripped
PASS-2 DAMAGE · 018 roadside guide post ~0.9 m in grass · base delineator paint (magenta) disappears in r1 although the cluster itself was never stripped
PASS-2 DAMAGE · 060 foot of a conifer next to a billboard · r1 invents a pole_other 9.7 m tall out of stripped stem residue
PASS-2 DAMAGE · 060 foot of a conifer next to a billboard · r1 invents a pole_other 9.7 m tall out of stripped stem residue
RESIDUAL IN PASS 3 · same spot, 5 panels · the pole_other record is byte-identical in r1 and r2; note r2 does unpaint the sign plate (green → grey) but the record stays "no plate"
RESIDUAL IN PASS 3 · same spot, 5 panels · the pole_other record is byte-identical in r1 and r2; note r2 does unpaint the sign plate (green → grey) but the record stays "no plate"

The sign-path stripper stays disabled until this flip is gone; cheapest lever is to require a relative drop (sf ≥ 0.10) before a cluster's features are recomputed, which makes sf 0.066 a no-op.

4. Results per segment

segmentcanopy recall >10 m
base / r1 / r2
foliage-cell recall
base / r1 / r2
hard cells
base → r2
hard points
base → r2
signs
base / r2
delineators
base / r2
runtime s
base / r1 / r2
0180.011 / 0.107 / 0.1070.585 / 0.922 / 0.92225 → 1760 150 → 35 4465 / 5 (−2 ghosts, +2 real)29 / 29 (r1: 28)134.8 / 135.6 / 236.5
0600.009 / 0.877 / 0.8770.648 / 0.900 / 0.90019 → 6135 526 → 5 7411 / 127 / 27159.6 / 172.9 / 270.2
066n/a (no truth spots)n/an/a435 494 pts unpainted0 / 07 / 7238.8 / n/a / 373.7
085n/a (human GT segment)n/an/a55 783 pts unpainted0 / 05 / 5223.5 / 229.5 / 355.9
0860.020 / 0.886 / n/a0.558 / 0.933 / n/a34 → n/a49 422 → n/a4 / n/a5 / n/a260.7 / 256.0 / n/a

"n/a" = segment not run in that pass (086 only in base/r1, 066 only in base/r2) or metric not defined there (066/085 carry no truth spots, so point-level canopy recall is not measured; 085 is scored against the human GT instead). Tree detection counts never move: 43 / 53 / 64 / 5 / 8 in every root. Rail half-posts identical everywhere (7 / 25 / 7 / 4). Runtime caveat: the four pass-3 runs were launched concurrently and the base/pass-2 runs sequentially, so the +56–75 % is mostly a measurement artefact — the new stage itself costs 6–23 s. Worst segment 6.2 min, well inside the 20 min budget.

5. Still open / next steps

Two 100 %-false tree instances the guard protects pass 4 fixes

The max_instance_fraction = 0.6 guard skips the unpaint when an instance would lose more than 60 % of its points — which is exactly what happens on two instances that are 100 % structure, so they keep their green paint in base, r1 and r2 and now top the residual contamination list.

BEFORE = 3rd/4th panel · 018 curved row of hoops/arches + bar standing on the carriageway, mrf 0.032 · still fully green in r2: 67 % hard, so the unpaint was skipped
BEFORE = 3rd/4th panel · 018 curved row of hoops/arches + bar standing on the carriageway, mrf 0.032 · still fully green in r2: 67 % hard, so the unpaint was skipped
BEFORE = 3rd/4th panel · 060 bare linear streak on empty asphalt, mrf 0.018 · still green in r2: 65 % hard, unpaint skipped
BEFORE = 3rd/4th panel · 060 bare linear streak on empty asphalt, mrf 0.018 · still green in r2: 65 % hard, unpaint skipped

018 (−3234.54, −2221.36) 67 % hard, 060 (255.64, −107.32) 65 % hard · pass 4 rejects such a detection instead of keeping the paint.