Canopy extension fills the crowns pass 2, kept in pass 3
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 %.
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
| pass | feature | knob | verdict | reason (one line) |
|---|---|---|---|---|
| 2 (r1) | canopy extension | enabled, min_multi 0.20 / min_triple 0.02 | keep | +0.86 canopy recall above 10 m with hard contamination going down, not up. |
| 2 (r1) | hedge-gate returns veto | 0.55 / 0.15 | revert | Fires on 0 of 8 gate-evaluated parents and no segment emits medium_vegetation at all. |
| 2 (r1) | returns stripper, sign path | tau 0.30, r 0.75 | retune | +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 path | tau 0.30, r 0.75 | revert | Emitted 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 unpaint | model defaults | keep | −79 % hard contamination, 10/10 unpainted blobs are panels, billboards or carriageway (max mrf 0.045). |
| 3 (r2) | sparse-sign guard | sign_post.min_points_per_m = 100 | keep | Drops 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 rules | retune | Fixed 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 extension | unchanged | keep | Regression check only: per-segment recall, summary blocks and the 085 GT matrix are bit-identical to pass 2. |
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 %.
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.
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 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.
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.
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.min_remaining_fraction guard restores it whole, so the pass is inert; the call site is gone from railpost.py.pole_other created, both shown below; 64/847 clusters changed verdict on 018, 6 of them after losing a single point.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.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.
| segment | canopy 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 |
|---|---|---|---|---|---|---|---|
| 018 | 0.011 / 0.107 / 0.107 | 0.585 / 0.922 / 0.922 | 25 → 17 | 60 150 → 35 446 | 5 / 5 | 29 / 29 | 134.8 / 135.6 / 236.5 |
| 060 | 0.009 / 0.877 / 0.877 | 0.648 / 0.900 / 0.900 | 19 → 6 | 135 526 → 5 741 | 1 / 1 | 27 / 27 | 159.6 / 172.9 / 270.2 |
| 066 | n/a (no truth spots) | n/a | n/a | 435 494 pts unpainted | 0 / 0 | 7 / 7 | 238.8 / n/a / 373.7 |
| 085 | n/a (human GT segment) | n/a | n/a | 55 783 pts unpainted | 0 / 0 | 5 / 5 | 223.5 / 229.5 / 355.9 |
| 086 | 0.020 / 0.886 / n/a | 0.558 / 0.933 / n/a | 34 → n/a | 49 422 → n/a | 4 / n/a | 5 / n/a | 260.7 / 256.0 / n/a |
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.
min_points_per_cell.pole_other: pass 3 unpaints its plate (28 376 pts) but never hands the plate to the sign candidate, so the record stays "no plate".number_of_returns only; whether to export return_number too is still a decision for you.