AI3D-382 · detection pass 5: robust per-segment TCS ground and the stripper pole guard

6 Sep 2026 · segments 018 / 060 / 066 / 085 · roots: r3pr (round-4 code, per-record TCS) | r3 (naive per-segment, the pass-4 failure) | r4pr (round-5 code, per-record TCS) | r4 (round-5 code, robust per-segment TCS) · code wt-vs-382 @ 01cef83 · previous rounds: passes 2–3 · pass 4 · background: what the DEM (Digital Elevation Model) bias was

  1. Pass 5 shipped three things: (A) robust per-segment TCS — supported min-Z seeding plus a clamp to the per-record surface (effective_z = max(seg_z, rec_z − 0.05)); (B) a stripper pole-promotion guard; (C) the review minors.
  2. A fixes every catastrophic pass-4 regression. The 018 sign is back at ml 0.6653, the truth-crown tile hole is gone, the hedge belt is whole, z_ground p5 at 066 truth spots goes −4.17 → −0.15 m with zero clusters below −0.5 m anywhere, detection churn collapses 27/35 → 4/4 and paint churn 2.3 % → 0.2 %.
  3. But 2 of 7 canaries still miss. Hard contamination is 38 792 vs the 29 769 bar (+30 %) and all of the excess is one billboard cell on 060, whose instance hard share slipped 0.5815 → 0.5075 under the 0.6 cap; the 066 streak misses by 226 pts (+0.18 %). Per-segment TCS also still loses 085’s only delineator, on a 3 mm z_ground shift.
  4. And the pass-4 GT recall “gain” evaporated — it was ground leakage. 085 GT recall keeps 0.0009 of r3’s +0.1318; M1/M1b/M2 all move in the third decimal. Per-segment now buys ≈ nothing for +9–22 % runtime, so it ships OFF. The clamp means the union can only raise the ground, i.e. per-segment has been reduced to “per-record with holes filled”.
  5. B is surgical, keep it. The 060 billboard-post pole_other FP is gone, both recovered 018 signs are kept (including the sf 0.2091 one), clusters.csv churn is 1 row in the whole corpus.
  6. Review found and fixed two majors — a uint16 histogram wrap that could lift a seed, and a cache sidecar that ignored the tablecloth profile — and flagged that 34 % of real cells still fall back to plain min-Z (too sparse, or the floor sits outside the 1 m window) and that the histogram grid is sized by the bounding box (650 MB on 018, not 16 MB).

0. How to read the renders

Every render is one spot in the raw no-scan-angle-filter cloud, 6 columns left to right: true RGB | number_of_returns (grey = 1, orange = 2, red = ≥ 3) | r3pr (round-4 code, per-record) | r3 (naive per-segment = the pass-4 failure) | r4pr (round-5 code, per-record) | r4 (round-5 code, robust per-segment). The two BEFORE columns come first; top row oblique, bottom row the same points top-down; each column is labelled in the image itself.

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

featureknobverdictreason (one line)
robust per-segment TCS (supported min-Z seeding + clamp to the per-record surface)tcs_ground.per_segmentkeep code, ship offEvery pass-4 catastrophe is fixed, but 2 of 7 canaries still miss and the metrics now move in the third decimal for +9–22 % runtime.
ground clamp max(seg_z, rec_z − 0.05)always on inside per-segment modeit is the whole fixThe naive union is still as broken as in r3 (066 segment-only kept fraction 0.383); the clamp pulls final back to within 0.0143 of per-record.
stripper pole-promotion guardreturns_stripper.reject_promoted_poles = truekeep1 of 318 stripped clusters affected corpus-wide — the target FP; det_diff REMOVED 1 / ADDED 0 / MOVED 0.
hard-blob share cap 0.6unchanged (no survivor test yet)fragile060 cid93 sits at 0.5815; a few-cm DEM change drops it to 0.5075 and one cell out of the blob → +13 800 points stay painted on the billboard.
histogram seeding internalsreview fixes (C)2 majors fixeduint16 counter wrap could lift a seed above truth; the cache sidecar ignored the tablecloth profile, so a profile change did not invalidate the cache.
seed support coverage1 m window, cumulative supportopen34 % of real cells fall back to plain min-Z (too sparse, or the floor lies outside the 1 m window) — those cells get no robustness at all.
histogram memorygrid sized by bounding boxopen650 MB on 018 where a populated-cells-only (sparse) grid would be ~16 MB.
canopy extension (tree-path only, ae988ba)0.20 / 0.02unexercisedByte-identical r3pr → r4pr on all four segments — a no-op on this corpus, so untested rather than validated.

2. Fixed — what the clamp bought

The lost sign is back, and the crown hole is gone canaries 1 and 3

Both pass-4 blockers were the same mechanism: a segment DEM metres too low turned road and low ground into candidates and re-cut everything above them. With the clamp, both spots are point-for-point identical to per-record again.

BEFORE = 3rd/4th panel (r3pr, r3) · canary 1 · 018 (−3212.87, −2195.74), a real post with plate at the road edge in front of dense foliage · magenta in r3pr, absent in r3 (pass 4’s blocker), back in r4pr and r4 at ml 0.6653, n 2 496 — identical to r3pr to the point
BEFORE = 3rd/4th panel (r3pr, r3) &middot; canary 1 &middot; 018 (&minus;3212.87, &minus;2195.74), a real post with plate at the road edge in front of dense foliage &middot; magenta in r3pr, <b>absent in r3</b> (pass 4&rsquo;s blocker), <b>back in r4pr and r4</b> at ml <b>0.6653</b>, n 2 496 &mdash; identical to r3pr to the point
BEFORE = 3rd/4th panel (r3pr, r3) · canary 3 · 018 truth spot lowrej_018_c0_s2, canopy top 7.2 m · the r3 column has a clean rectangular tile-shaped hole bitten out of the crown; r4pr and r4 paint the crown whole again
BEFORE = 3rd/4th panel (r3pr, r3) &middot; canary 3 &middot; 018 truth spot <code>lowrej_018_c0_s2</code>, canopy top 7.2 m &middot; the r3 column has a clean rectangular tile-shaped <b>hole</b> bitten out of the crown; r4pr and r4 paint the crown whole again

018 sign (−3212.87, −2195.74) present at ml 0.6653, n 2 496 in r3pr, r4pr and r4 · truth crowns 12/12 recalled with 0 FP in every root · M1b mid 018 back to 0.78461 (r3 had dug a −0.049 hole).

The hedge belt is whole and the road streak is back on baseline z_ground tail eliminated

z_ground, clusters matched at 0.5 m, truth-spot view: the bias flips from negative (ground pushed below truth) to slightly positive (per-record holes filled upward) — exactly the intended behaviour.

BEFORE = 3rd/4th panel (r3pr, r3) · 018 roadside hedge/scrub belt, instance n 140 264 · r3 paints only the right-hand third; r4 restores the whole belt, indistinguishable from r3pr/r4pr
BEFORE = 3rd/4th panel (r3pr, r3) &middot; 018 roadside hedge/scrub belt, instance n 140 264 &middot; r3 paints only the right-hand third; <b>r4 restores the whole belt</b>, indistinguishable from r3pr/r4pr
BEFORE = 3rd/4th panel (r3pr, r3) · canary 7 · 066, 68 m of carriageway with lane markings and a belt each side · r3’s lane-marking line is visibly brighter and thicker (140 087 pts); r4 is back on r3pr’s line (126 987 vs 126 761, +0.18 %) — misses the bar by 226 points
BEFORE = 3rd/4th panel (r3pr, r3) &middot; canary 7 &middot; 066, 68 m of carriageway with lane markings and a belt each side &middot; r3&rsquo;s lane-marking line is visibly brighter and thicker (140 087 pts); r4 is back on r3pr&rsquo;s line (126 987 vs 126 761, <b>+0.18 %</b>) &mdash; misses the bar by 226 points

066 z_ground p5 at truth spots −4.169 → −0.151 m, clusters below −1 m 26 → 0, and zero clusters below −0.5 m at any truth spot on any segment (018 −0.003, 060 −0.008, 085 −0.012) · kept fractions track per-record within 0.0143 on every real record.

The pole guard removes exactly one detection keep

The rule rejects any pole_other that the returns stripper promoted (stripped_fraction > 0). Across the five segments where the stripper has ever run, 318 clusters have sf > 0 and exactly one of them is a pole_other — the target FP. Signs are structurally immune (a sign stays a sign however hard it is stripped); no delineator is ever stripped.

BEFORE = 3rd/4th panel (r3pr, r3) · 060 (293.58, −112.32) · a big deciduous tree with a billboard to the right and a thin stem inside the crown · the magenta pole_other present in r3pr/r3 is gone in r4pr and r4 (stripped_pole_unconfirmed); its 1 191 points turn green, and own-points mrf 0.199 says woody stem, so tree is the better label
BEFORE = 3rd/4th panel (r3pr, r3) &middot; 060 (293.58, &minus;112.32) &middot; a big deciduous tree with a billboard to the right and a thin stem inside the crown &middot; the magenta <code>pole_other</code> present in r3pr/r3 is <b>gone in r4pr and r4</b> (<code>stripped_pole_unconfirmed</code>); its 1 191 points turn green, and own-points mrf 0.199 says woody stem, so tree is the better label

det_diff r3pr → r4pr over all four segments: REMOVED 1 / ADDED 0 / MOVED 0 · both 018 signs kept bit-identically, including the sf 0.2091 one · clusters.csv churn 018 0 / 060 1 / 066 0 / 085 0 · tree paint bit-identical on 018/066/085; on 060 it gains exactly the pole’s 1 191 points — the guard relabels, it does not delete.

Everything else is noise-level churn 0.2 %

r4pr → r4 moves ~93 k removed / ~78 k added points out of 44.6 M painted (r3 moved 2.3 %). All four changed-detection pairs and the three biggest added blobs were rendered; these are the representative ones.

BEFORE = 3rd/4th panel (r3pr, r3) · 018 tall 14 m wood, r4-only tree n 5 005 (018’s biggest added blob, +2 963 pts) · r4 slightly better: marginally fuller paint upper-left, real multi-return foliage
BEFORE = 3rd/4th panel (r3pr, r3) &middot; 018 tall 14 m wood, r4-only tree n 5 005 (018&rsquo;s biggest added blob, +2 963 pts) &middot; <b>r4 slightly better</b>: marginally fuller paint upper-left, real multi-return foliage
BEFORE = 3rd/4th panel (r3pr, r3) · 066 tree belt beside the carriageway, +3 476 pts · r4 reaches a little further toward the road edge than r4pr; both keep the same hard-edged unpainted wedge that only naive r3 filled
BEFORE = 3rd/4th panel (r3pr, r3) &middot; 066 tree belt beside the carriageway, +3 476 pts &middot; r4 reaches a little further toward the road edge than r4pr; both keep the same hard-edged unpainted wedge that only naive r3 filled
BEFORE = 3rd/4th panel (r3pr, r3) · 085 tall 32 m forest, +4 027 pts (085’s biggest added blob) · all four paint panels near-identical; the square unpainted tile gaps are pre-existing in r3pr too
BEFORE = 3rd/4th panel (r3pr, r3) &middot; 085 tall 32 m forest, +4 027 pts (085&rsquo;s biggest added blob) &middot; all four paint panels near-identical; the square unpainted tile gaps are pre-existing in r3pr too

Detection diff r4 vs r4pr: 018 2/2, 060 0/0, 066 0/0, 085 2/2 — 4 REMOVED / 4 ADDED out of 235 (r3: 27/35) · largest MOVED 0.50 m · M6 truth spots 1.000 / 0 in every root.

3. Not solved

The 060 billboard is the entire canary-2 miss +9 023 pts

r4’s hard-cell list is identical to r3pr’s 018 cells point for point, plus one new cell and minus two: + 060 (296,−67) 10 838 pts (the panel), − 060 (293,−112) 1 213 (the pole guard), − 018 (−3306,−2188) 602. Net +9 023 = the whole miss. Cause: cid93’s hard-blob share drops 0.5815 → 0.5075 and 9 → 8 cells, so the unpaint pass leaves ~13 800 more points on the panel — a share cap with no survivor test, exposed by per-segment TCS rather than caused by it.

BEFORE = 3rd/4th panel (r3pr, r3) · canary 2 · 060 billboard panel + frame beside a real conifer, cid93 · r3pr and r4pr leave the panel grey; r3 and r4 both speckle it green — a 10 838-pt hard cell at (296, −67), the whole canary-2 miss
BEFORE = 3rd/4th panel (r3pr, r3) &middot; canary 2 &middot; 060 billboard panel + frame beside a real conifer, cid93 &middot; r3pr and r4pr leave the panel grey; <b>r3 and r4 both speckle it green</b> &mdash; a 10 838-pt hard cell at (296, &minus;67), the whole canary-2 miss

M3 hard contamination 28 556 → 38 792 pts (9 → 10 cells) against a 29 769 bar · unpainted points on the instance 109 697 → 95 906.

Two real items still lost to millimetres of DEM brittleness

Same failure class as pass 4’s lost sign, one size down: a sign-path detection whose verticality straddles a threshold, and a wood belt at the segment edge where the union surface is built from the fewest records.

BEFORE = 3rd/4th panel (r3pr, r3) · 085 (2272.93, 1248.11), the segment’s only delineator (rescue_stage, n 344) · present in base/r3pr/r3/r4pr, gone in r4: verticality 0.9007 → 0.8848 on a 3 mm z_ground shift — same brittleness class as pass 4’s lost sign, smaller item
BEFORE = 3rd/4th panel (r3pr, r3) &middot; 085 (2272.93, 1248.11), the segment&rsquo;s <b>only</b> delineator (<code>rescue_stage</code>, n 344) &middot; present in base/r3pr/r3/r4pr, <b>gone in r4</b>: verticality 0.9007 &rarr; 0.8848 on a <b>3 mm</b> z_ground shift &mdash; same brittleness class as pass 4&rsquo;s lost sign, smaller item
BEFORE = 3rd/4th panel (r3pr, r3) · 018 (−3355.9, −2177.8), dense wood at the west segment edge · r3pr/r4pr paint the belt fully, r4 is thinner and greyer on the left (−19 523 pts in the top 5 m cell) — edge cells have the fewest overlapping records, so the union surface is weakest exactly there
BEFORE = 3rd/4th panel (r3pr, r3) &middot; 018 (&minus;3355.9, &minus;2177.8), dense wood at the <b>west segment edge</b> &middot; r3pr/r4pr paint the belt fully, <b>r4 is thinner and greyer on the left</b> (&minus;19 523 pts in the top 5 m cell) &mdash; edge cells have the fewest overlapping records, so the union surface is weakest exactly there

085 delineator (2272.93, 1248.11) lost on a 3 mm z_ground shift — 085’s only delineator · 018 west-edge wood (−3355.9, −2177.8) thinned by 19 523 pts in its top 5 m cell · both are now canaries for round 6.

4. Results per segment

segmentcanopy recall > 10 m
r3pr / r3 / r4pr / r4
M3 hard cells / points
r3pr / r3 / r4pr / r4
detections
r3pr / r3 / r4pr / r4
runtime s
r4pr / r4
TCS kept fraction (r4)
per-record / segment / final
0180.10735 / 0.09703 / 0.10735 / 0.119679 / 28 556 · 14 / 55 144 · 9 / 28 556 · 9 / 27 95475 / 75 / 75 / 75 (2 out / 2 in)307 / 363 (+18 %)0.7845 / 0.7559 / 0.7813
0600.87665 / 0.88773 / 0.87665 / 0.876661 / 1 213 · 3 / 12 998 · 0 / 0 · 1 / 10 83881 / 85 / 80 / 80 (0 out / 0 in)342 / 373 (+9 %)0.6758 / 0.6342 / 0.6706
066n/a (no truth spots)n/a — road-streak proxy 126 761 · 140 087 · 126 761 · 126 98771 / 72 / 71 / 71 (0 out / 0 in)433 / 528 (+22 %)0.5988 / 0.3828 / 0.5905
085n/a (human-GT segment)n/a9 / 12 / 9 / 9 (2 out / 2 in)433 / 518 (+20 %)0.5907 / 0.5264 / 0.5888
pooledM1 hi 0.83264 / 0.8425 / 0.83264 / 0.83336
M1b mid 0.898 / 0.88803 / 0.898 / 0.89949
10 / 29 769 · 17 / 68 142 · 9 / 28 556 · 10 / 38 792236 / 244 / 235 / 2351 515 / 1 782

M3 is defined on 018+060 only (the segments with truth spots); the 29 769-point r3pr figure is the canary bar. 060’s r4pr 0 / 0 is a metric artefact of the pole guard, not a cleanup: the released 1 191 points raise that cell’s mrf over 0.05, so it stops counting as hard. Runtimes: r4pr and r4 both ran 4-up on a cold tcs_cache_A1NR_r4, so they are comparable to each other but not to r3pr/r3 (warm cache; those were 172 / 247 / 343 / 332 s and 294 / 331 / 450 / 435 s). Worst case 8.8 min, inside the 20 min/segment budget. Per-segment is structurally more expensive: it builds the union surface and a per-record surface for every record, because the clamp needs both. “TCS kept fraction” is the share of input points classified ground, weighted by record: segment is the naive union (still as broken as in pass 4 — 0.383 on 066), final is after the clamp.

085 human GT (1 872 942 annotated points, 100.000 % matched @ 5 cm)

metricbaser3prr3r4prr4
tree precision vs GT tree(5)0.48930.49240.46970.49240.4910
tree recall0.75470.75470.88650.75470.7556
veg-tolerant precision0.74130.74830.71200.74830.7479
hard fp (not class 5/74/75)54 52052 53874 22952 53852 726

r4 keeps 0.7 % of the recall gain r3 showed (+0.0009 of +0.1318) while giving back essentially all of r3’s precision loss — the pass-4 diagnosis confirmed: that recall came from a DEM metres too low promoting road and low ground into candidates. r4pr is bit-identical to r3pr here, which also confirms the round-5 code is GT-neutral in per-record mode.

5. Next steps — pass 6