Handoff: AI3D-382 tree recall and number_of_returns foliage cue on the no-scan-angle-filter dataset

2026-09-06 · for the implementation agent (verticalsigns, branch t3code/improve-tree-foliage-recall) · generated by Claude Fable 5.1 from the research session

Summary

Continuation target. Rebuild tree recall and the foliage-vs-hard-object split of the verticalsigns pipeline on the new dataset /mnt/d/a123-data/Abschnitt_1_no_angle_filter_returns/ (battlebox), using the empirical facts below. Nothing in the research is implemented yet; an implementation agent is already working on this branch.

The one fact that reorders everything: the old NPZ filter abs(scan_angle) < 80 was a height ceiling, not a side filter. scan_angle is nadir-referenced (0 = down at the road, |80–90| ≈ horizontal, >90 = looking up). Trees in the old data were cut at 3–8 m and had 7–25× fewer points; the same truth spots read 18–26 m on the new data. Every height-based rule and the vegetation RF were tuned on decapitated crowns. Measure first, then retune; do not port thresholds.

Second fact: return_number is not lost. The source Riegl LAS (on this workstation, not battlebox) carries it and it is internally consistent; only the Step 3 NPZ export dropped it. Re-exporting it is the cheapest change with the largest analytical payoff.

Priority legend: P0 do first, everything else depends on it · P1 expected main wins · P2 second tier, cheap, may surprise · P3 only if time · impl code change · exp experiment / measurement.

Priorities (ordered)

P0 Foundations — do these before touching any threshold

#ItemTypeWhat / why
P0-1Export return_number in Step 3implSource LAS is LAS 1.2 / PDRF 3 (Riegl rdbconvert 1.11.1): return bits (0–2 of the flag byte) are valid, RN ≤ NoR on every point checked. Add a key next to NUMBER_OF_RETURNS_KEY in segmentationtrajectory/.../segment_mapper.py (_record_field_or_zeros). Unlocks pulse-level counting (RN == 1 = one pulse), Li 2018 R_pc, first-return ratio, Hess-style transmissivity; removes the ×1.78 pulse-weighting ambiguity of the NoR-only fraction.
P0-2Re-measure before re-tuning (report B0)expRun v6 unchanged on the new data, rescore phase0/truth_v4.json by spot-hit, and dump per truth parent: h_max_robust, tile relief, upper_half_surplus, crown_isotropy, tile heights, point counts, NoR shares. Expect the hedge gate (h_top ≤ 3.3) to stop firing on most former "bands" (tops now 15–25 m) and the ~90-point trunks to have 5–20× the points. This dump is the baseline for every P1/P2 decision; without it, threshold changes are guesses.
P0-3Regenerate the feature corpus on unthinned cluster pointsimplThe NoR fraction is pulse-weighted (each echo of an n-echo pulse carries n); whole-pulse filters keep the weighting, per-point thinning or voxel subsampling breaks it. Compute echo features on unthinned points, or on pulses via RN == 1 once P0-1 lands. Note the _row_to_vector KeyError trap on old CSVs — regenerate rather than patch.

P1 Expected main wins

#ItemTypeWhat / why
P1-1Echo features into the vegetation RF (A1–A3)implAdd to TREE_FEATURE_NAMES (fractions only, keeps the scale-invariance contract): mr_fraction = share of cluster points with NoR ≥ 2 (≥ 0.3 m AGL); mr3_fraction = share with NoR ≥ 3; mean_nor; per-height-slab mrf for 0.3–1, 1–2, 2–4, 4–8, >8 m plus mr_profile_min/mr_profile_range. Measured on this scanner (6 m truth spots): trees median 0.43 (crowns ≈ 0.55), Miro's confirmed hedge walls 0.09–0.28, clean sign plates 0.11–0.22, delineators 0.15, guardrail half-posts 0.30 (rail-backed hedges inflate it). Foliage interiors are NoR 3–4, foliage edges and hard linear objects NoR 2 — keep NoR = 2 and NoR ≥ 3 shares separate. Never a hard gate: edges, mesh fences and wet surfaces multi-echo; echo-only classifiers confuse 27 % of urban furniture and 39 % of shrubs with trees (Hess 2026).
P1-2Retrain the vegetation RF, don't recalibrate (B1)impl expTrain on full-angle clusters with P1-1 features; segment-grouped CV; report strict and lenient (soft-label) metrics. Add sampling-geometry augmentation: random scan-line dropout and an angle-window crop that reproduces the old |angle| < 80 geometry (verified: scan-line dropout alone recovers +3–5 mIoU cross-sensor and +4 vegetation IoU; a plain 2× density change costs ~2 OA points with column neighbourhoods). Use fixed-radius spherical neighbourhoods for neighbourhood features and expose neighbour count as a reliability feature (B6).
P1-3Stem-seeded splitting of long DBSCAN chains (B2)implTrunks now exist in the data. In the 1–4 m slice: two-pass DBSCAN + RANSAC circle/line → trunk candidates; trunk confidence from existing eigen features + diameter; assign crown points by min-cut / nearest compatible stem with an allometric crown-width cap (re-fit CD–H locally; Fan 2021's CD = 0.446·H^0.854 is Indo-Malayan). Re-run the RF per split tree so crown_isotropy is per crown, not per belt. Trunks are single-return: low mrf in the 0.3–2 m slab is the trunk cue, high mrf above it the crown cue. Literature expectation on a Riegl-class scanner with a full vertical window: ~100 % roadside stem recall (139/139, Hyyppä 2023); the 67–78 % figures come from a 2007 phase-based Faro at 15 Hz.
P1-4Hedge gate re-fit on complete canopies (B3)impl expKeep the gate but re-fit every threshold on the P0-2 distributions and add two conjuncts: mr_fraction of the band (hedges ≈ 0.3 vs tree bands ≈ 0.55) and "no stem seed from P1-3". Count stems_per_10m from the detector, not from cluster shape. Bands with a stem seed or a tree-like mrf profile are never gated. Literature hedge/tree line is a top-height band on complete canopies: 1.3–3 m managed hedge / 3–15 m mature hedge or tree canopy (THaW), 6 m (England model) — our 3.3 m is the THaW band, but it was fitted on truncated crowns.

P2 Second tier — cheap, and the kind of thing that surprises

#ItemTypeWhat / why
P2-1Per-point contamination stripper (A4/A5)impl expNeighbourhood multi-return rate (r = 0.5–1 m) per point → cluster median / P10 / share > 0.3. Before sign/guardrail features, drop points with rate > τ (start τ = 0.4) from those candidates; before crown features, drop rate < 0.1 points above 2 m from vegetation candidates (keeps trunks). Fixes the "hedge behind rail / foliage over plate" contamination both ways. Half-post p90 mrf ≈ 0.8 at r = 1 m shows how fast a hard-object neighbourhood is polluted by verge vegetation — this may matter more for guardrail/sign precision than anything in P1.
P2-2Per-object NoR rates on cluster masksexpAll spot-level numbers above are 6 m circles, not object masks (AUC tree vs hedge only 0.73 at spot level; on segments 085 + 060 the cue separated tree from hedge perfectly). Recompute mrf / mr3 / mean_nor on v6 cluster masks for: clean sign plates, W-beam rails, half-posts, mesh fences, noise walls, confirmed hedges, trees. This is the table the literature does not have for any scanner and it decides where the RF will actually cut. Script to extend: docs/research/deep-research-20260905-mr_stats.py (copy of battlebox:/home/ai/veg373_work/recall/ret_stats/mr_stats.py).
P2-3Thin trunks by linkage, not point mass (B4)implReplace the point-mass floor with: keep a low-mass vertical core if a multi-return-rich crown (mrf ≥ 0.4, ≥ 2 m AGL) sits within 1.5 m above it (Matikainen 2019's rule needs no mass). Also try a vertical-continuity count (number of scan lines intersecting the core) — thin-target point mass is a profiles × points-per-profile property (Cahalane 2015), and the count is more stable than a mass floor. Probably moot after P0-2 shows the trunks are back; verify on the miss list.
P2-4Intensity features after the angle changeexpNear-horizontal and upward looks are now present. Raw intensity is incidence-angle dependent above ~15°; an incidence correction moved an ALS land-cover RF from 44 → 74 % OA (amplitude). Re-check hi_intensity_fraction and sign retro-intensity gates on the new distributions; consider Riegl relative reflectance if the RDB export can provide it. Do not filter by angle.
P2-5Airborne noise at |angle| > 110expLook at the sky/dust population at high angles (isolated low-intensity points) and zbin_count_cv; if present, drop by isolation + intensity floor before clustering (Matikainen used an intensity floor). Never by angle.
P2-6Edge-direction histogram (A6)implHistogram of local principal-direction angles (eigenvectors already computed) → shares of horizontal / vertical / oblique; signs, rails and fences peak at 0°/90°. Cheap; effectiveness unverified.
P2-7Re-ask the soft labels only after P0-2exp021 c0, 068 c1, 121 c5, 108 c34, 066 c8, 094 c1, 066 c15 were judged on decapitated crowns ("trunks are often missing"). They may become hard labels on complete canopies. Ask Miro once, with the new renders, after the v6 rerun.

P3 Only if time

Findings the priorities rest on

Empirical (this scanner, this dataset)
  • Multi-return share by raw scan angle (20 segments, points ≥ 0.5 m AGL): 0.00 for |angle| < 50, 0.03–0.06 at 60–70, 0.25–0.39 at 70–90, 0.39–0.46 for |angle| ≥ 90. The old filter kept the single-return world.
  • Segment 085: everything ≥ 2 m above ground is at |angle| ≥ 80; points 8–15 m up sit at |angle| ≈ 100–125.
  • NoR mix of the handoff sample NPZ (vegetated lane segment): 73 / 24 / 2 / 0.3 %. First 2 M records of the raw LAS Record011 (road-dominated): 91.8 / 6.6 / 1.4 / 0.17 %. Per-segment NoR mix is itself a coarse vegetation indicator.
  • Source LAS: /mnt/data/dev/3dai.lanefinder/data/00_external/251017_Color_Abschnitt_1_long/25-9166A1Record011.las → symlink to /mnt/d/dev/3dai.lanefinder/data/00_external/251017_Color_Abschnitt_1/ (Record011…019). LAS 1.2, PDRF 3, 40-byte records, 49.4 M points, rdbconvert1.11.1-50+dev+d295d19. Scan Angle Rank int8 −130..+125 → Riegl scanner-frame angle written into the byte; the nadir reading is empirical, sign/offset unverified.
Verified literature (adversarial 3-vote panels; report §4 and §7)
  • MLS thresholds for NoR-only features exist but are far below this scanner's foliage: Matikainen 2019 (Riegl VUX-1HA, leaf-off) tree if > 5 % multi-return points and top ≥ 2 m; Liu 2023 (Velodyne HDL-32E) neighbourhood rate > 0.3 in r = 1 m. Use the shape of the rules, not the numbers.
  • Hard objects do multi-echo (Li 2018, 3-0: road-furniture edges; Ouster docs: chain-link fences). Riegl-class echo separation ≈ 0.5 m along the beam, so a plate/pole/rail splits only against a far background; foliage splits routinely and reaches NoR 3–4 because it is deep.
  • Vertical FOV is a height ceiling (Hyyppä 2023, 2-1): −25..+15° scanner → usable density to ~5 m, stems to 3.3 m vs 6.5 m, crowns unmodellable. Same authors: VUX-1HA on a tilted 360° line found 139/139 roadside stems (3-0).
  • Sampling-geometry shift is recovered by retraining with augmentation (Kim 2023 CVPR, 3-0), not recalibration; density halving costs ~2 OA points for an RF (Mohamed 2021, 3-0).
  • Hedge vs tree row: national ALS workflows decide by top-height band and area only (THaW 3-0; England model 2-1), never by crown shape or stems. Where stems are invisible, the honest output is "band, ambiguous".
  • Geometry alone on a Riegl VMX gets sign F1 0.90 / fence 0.86 / vegetation 0.97 with height histograms and OBB features (Voelsen 2021) — the baseline the echo features must beat.
Refuted — do not build on these
  • "Solid surfaces give one return" as a law; any fixed literature threshold (0.05, 0.3, 0.078, 0.761, "10–50 % band") as a gate.
  • "Scan angles > 90 imply LAS point format 6+" — our file is PDRF 3 with int8 values beyond ±90.
  • "Density, not algorithm, explains stem misses" (Li 2018 raised the same site 69.7 → 80 % by algorithm alone).
  • Small footprint ⇒ multi-returns must be edges; pulse width ⇒ echo splitting; fences/walls "multi-echo like vegetation" (all 0-3 as inferences; the quoted facts are verbatim).

Current state and key artifacts

Verification status of this handoff

Experiment protocol (suggested order)

  1. Baseline dump (P0-2): v6 unchanged on the new data → per-parent table (heights, counts, NoR shares, gate decisions) + truth_v4 recall/FP. Keep this table; every later change is diffed against it.
  2. Mask statistics (P2-2): mrf / mr3 / mean_nor / slab profile per v6 cluster class. Publish as a table; this replaces the spot-level numbers in the report.
  3. Feature ablation (P1-1/P1-2): RF with and without echo features, with and without augmentation; segment-grouped CV; strict + lenient metrics. Watch precision on sign / rail classes as much as tree recall.
  4. Stripper sweep (P2-1): τ ∈ {0.3, 0.4, 0.5} on sign/rail candidates; report plate/rail feature stability and FP change.
  5. Stem splitter (P1-3): on chains > 15–20 m; per-tree RF re-score; count stems per 10 m for the gate.
  6. Hedge gate re-fit (P1-4): only after 1–5; compare gate on/off and with/without the mrf and stem conjuncts on Miro's confirmed hedges (002 c0, 018 c0, 060 c5, 060 c12) and the soft-label set.
  7. Backward compatibility check: run the retrained model on old A1B0 (angle-filtered) NPZs to see whether the augmentation kept it usable there.

Report every step with the same table shape (per-segment recall, FP count, and the specific truth spots gained/lost) so second-tier surprises are visible, not averaged away.

Risks and open questions

Suggested skills / tools