Handoff: Tree Recall on the No-Angle-Filter Dataset
Summary
Continue tree-recall work in the verticalsigns detector. The previous round (v6 hedge gate)
is shipped (spot recall 0.920, 2/26 FP on truth_v4). This round exploits a new,
denser dataset generated without the scan-angle filter — trees are much more
visible (1.62× points in seg 021) and a new
number_of_returns field arrives per point.
Goal unchanged: improve tree recall until perfect, until more data is needed, or until human
input is required. Miro answers no more truth questions — iterate autonomously.
The new dataset
/mnt/d/a123-data/Abschnitt_1_no_angle_filter_returns/ (184 GB, treat as read-only
source — stage what you need, budget disk accordingly).
- No scan-angle filter: segment 021 has 1.62× the points of the old staged data (42.6M vs 26.3M). Previously-missing trunks/crowns may now be present.
- New per-point key:
number_of_returns(uint8) alongside the old keys (pointsfloat64,scan_angleint8,intensity/red/green/blueuint16). Multi-return points are a strong vegetation signal — foliage multiplies returns, hard surfaces don't. - Layout:
branch_000/lane_points/segment_NNN/*_run3_points.npz(131 segments) andbranch_001/…(11 segments). Note the extrabranch_NNN/level vs the old staged root/home/ai/seg3d_stage_260807b/A1B0/lane_points/segment_NNN/. run3_geoshift.jsonis byte-identical to A1B0's (x 694541.44…, y 5386372.37…, z 467.41…) — truth-spot coordinates transfer directly, no reprojection needed.
Current state (already done)
- Worktree
/home/ai/dev/wt-vs-recall, branchfeat/AI3D-373-tree-recall, PR #7 → master (Bitbucket), HEADad41427, all pushed, 1597 tests green. - v6 gate: size-based hedge disjunction — legacy (area ≥ 250 m², relief ≤ 1.5) OR huge
(area ≥ 1250 m², relief ≤ 2.2) OR ext (area ≥ 450 m² ∧ len ≥ 44 m ∧ relief ≤ 1.68),
all under shared conjuncts + h_top ≤ 3.3. Implemented as
BigBandBranchesintree_instances.py; knobstile_hedge_gate_*+hedge_band_as_medium_vegetation. Repo defaults all None; live values in/home/ai/veg373_work/recall/phase3/cfg/v6.jsonandv6mv.json. - Eval on truth_v4 (113 spots, 87 tree / 26 not): v6 recall 0.920, FP 2/26. Baselines: v0 0.460/1FP, v4 0.920/5FP, v5 0.897/1FP.
- Corpus (104 prod-paired segments): prod 14.0M tree pts → v6 23.9M (1.71×, zero losses vs prod).
- Demo videos delivered on N: (
260716_videos/260831_tree_recall_v6/). - Full context in memory files
tree-recall-diagnosis.md,miro-hedge-definition.md,ai3d-373-vegetation-split.mdunder/home/ai/.claude/projects/-home-ai-dev-3dai-iolabs-pointcloud-verticalsigns/memory/.
Where the remaining recall lives (ranked)
- Rerun on the new data, re-score. cheapest first
The known-unfixable FNs died on RF point mass (~90-pt trunks: 066 c15 tiles s10/s15, 083 c3 s1). 1.6× density may fix them with zero code change. Run this experiment first. number_of_returnsas a feature. Candidate RF feature or pre-filter (multi-return fraction per cluster/tile). Pursue only if lead 1 leaves FNs on the table; changing the RF feature set means retraining/threshold work — check with Miro first.- 096 c6 sub-parent hedge strip — below parent granularity; likely still needs splitter work; reassess on denser data.
How to run
- Detector: from
wt-vs-recall,uv runthe detect entry point with a cfg fromphase3/cfg/(copyphase3/run_v6.shas a template). The detector expects the A1B0-style layout (lane_points/segment_NNN/), so stage per-branch or symlinkbranch_000/lane_points. - Eval:
phase3/eval/scripts scoretree_clusters.csvagainstphase3/phase0/truth_v4.json(spot-based; coordinates valid on new data — same geoshift). Expected-gate checker:phase3/gate3/check_v6_expected.py. - Truth caveat on new data: point counts per spot change; recall by spot-hit stays valid, weighted-by-points comparisons across datasets do not.
Rules violations caused real damage before
- Soft labels: 021 c0, 068 c1, 121 c5, 108 c34, 066 c8, 094 c1, 066 c15 — Miro: "it can be either… tree trunks are often missing". Either verdict counts as correct; ambiguity default = tree. Tune thresholds only against hard-labeled objects.
- One
--outdir per detector process. Corridor post-passes glob and rewrite everysegment_*/verticalsigns.jsonin--out; concurrent sharing is a data race (bit us twice:out/v6_corpusmasks untrusted,out/v5_corpusnon-tree CSVs rewritten). ≤4 concurrent processes, each with its own--out. - RAM ≤ 32 GB/process: stream per-file, float32 after applying the float64 geoshift.
- Runtime budget < 20 min/segment (051 and 015 already exceed it — pre-existing).
- Scratch under
/home/ai/veg373_work/(/tmpis tmpfs, wiped on WSL reboot). - Keep
candidates.min_height_mat its current value;CSV_FIELDSis append-only. - Scope: vegetation + vertical signs in this repo only (no guardrails, no seg3d colour knobs, no sign path).
- Worktrees
/home/ai/dev/3dai.iolabs.pointcloud.verticalsigns(main checkout),wt-seg3d-fuse-prod-260827, and others belong to other sessions — work only inwt-vs-recall(or a fresh worktree). Baregit stash/popforbidden (shared stack); usegit stash push -u -m "<tag>"+applyby SHA if unavoidable. - Commits: 1-line,
AI3D-373: …, no co-author lines. Push to the existing branch/PR. Ruff only viauv tool run ruff.
Open items you inherit
- verify
out/v6_corpus/*/verticalsigns.jsonpossibly race-contaminated — verify or regenerate before any seg3d-fuse consumes them (tree_clusters.csv+ eval numbers unaffected). - Proposed A1 production config (v6 gate + medveg emission) delivered to Miro; adoption is his call.
- PR #7 description stale (no Bitbucket API creds on this machine; pushes still update commits).
- Future notes, not to iterate now: sub-shared gate
max_reliefinversion footgun (documented, low), 032 c0 stems 1.609 knife-edge (unlabeled).
Definition of done for this round
A rerun + rescore on the new dataset with a v6-vs-new comparison table (spot-based recall +
FP count), and each remaining FN either fixed, reclassified as soft,
or documented with the specific evidence for why it needs data/annotation the
detector doesn't have. Report deltas to Miro extremely concisely.