seg3d — tree precedence & vehicle reclaim: results of the 2026-09-07 pass

2026-09-07 · for Miro · worktree t3code-6c51b0ba, commits 50b7b7d + 61701dd (ticket AI3D-379) · run root /home/ai/seg3d_out/260907/

Summary

Change 1 (tree in PAINT_FIRST_CLASSES) is correct but almost a no-op on this data: it moves ≤46 k points per segment (tree → asphalt / lines), zero on 060, and grows no guardrail/wall anywhere. The mega-instance rows over the road were still unclassified at mask time, exactly as the handoff predicted, so only the vehicle-sweep reclaim (change 2) reaches them.

Change 2 as specified (reclaim anywhere inside the corridor polygon, variant c1c2) fixes every car-noise point the 085 ground truth still saw as tree (571 → 0) but risk confirmed strips verge canopy: the corridor polygon runs past the guardrail, and 1,654 human-tree + 2,793 human-low-veg points on 085 became vehicle. Net on GT it is negative.

Refinement shipped as the default (vehicle_reclaim_rule=asphalt_column, variant c3): reclaim only greenery whose XY cell holds painted asphalt, on the vegetation stage's occupancy grid. On 085 GT it keeps the car-noise fix (571 → 0 tree) and cuts the real-tree loss from 1,654 to 1,278 points; the remainder is canopy overhanging the hard shoulder, which a planar rule cannot tell from a truck — a height-above-DTM gate is the next lever. The corridor rule stays selectable. Per Miro's instruction the reclaim covers tree, low_vegetation and medium_vegetation; the r9 masks carry no detector low/medium instances, so that part is idle on these segments. The per-point vegetation stage's own rows are exempt (AI3D-373 median hedges).

What changed in the code

Results per segment (full resolution and decimated)

Variants: base = main a584a8a; c1 = change 1 only (--set vehicle_reclaim_vegetation=false); c1c2 = change 1 + reclaim, corridor rule; c3 = change 1 + reclaim, asphalt_column rule (shipped default). Inputs: 260905/260904 stages, verticalsigns r9 masks (018/060/066/085) and val_r9 (093), 1 cm voxel. Direction check: in every variant only tree goes down; asphalt, lines, guardrail and vehicle go up (tiny ground/low-veg wobbles in the decimated counts are voxel-representative re-picks).

Segment 018

Reclaimed greenery → vehicle: c1c2 (corridor) 2,870 · c3 (asphalt_column) 1,001 points. Tree instances: 41 → 41 (none vanish; they shrink).

Full-resolution deltas vs base
classbaseΔ c1Δ c1c2Δ c3
tree5,097,956-1,242-4,112-2,243
vehicle342,304+0+2,870+1,001
asphalt6,129,721+1,242+1,242+1,242
solid_line1,137,764+0+0+0
guardrail1,364,970+0+0+0
wall134+0+0+0
ground16,625,712+0+0+0
low_vegetation3,143,268+0+0+0
medium_vegetation1,155,591+0+0+0
Decimated (1 cm) deltas vs base
classbaseΔ c1Δ c1c2Δ c3
tree4,937,177-1,214-4,015-2,189
vehicle254,737+0+2,800+974
asphalt3,667,309+1,214+1,215+1,215
solid_line637,686+0+0+0
guardrail1,043,712+0+0+0
wall134+0+0+0
ground10,075,245+0+0+0
low_vegetation2,911,933+0+0+0
medium_vegetation1,048,851+0+0+0

Segment 060

Reclaimed greenery → vehicle: c1c2 (corridor) 0 · c3 (asphalt_column) 0 points. Tree instances: 52 → 52 (none vanish; they shrink).

Full-resolution deltas vs base
classbaseΔ c1Δ c1c2Δ c3
tree7,163,411+0+0+0
vehicle410,837+0+0+0
asphalt16,481,924+0+0+0
solid_line877,212+0+0+0
guardrail3,040,409+0+0+0
ground1,366,595+0+0+0
low_vegetation411,959+0+0+0
medium_vegetation668,206+0+0+0
Decimated (1 cm) deltas vs base
classbaseΔ c1Δ c1c2Δ c3
tree7,094,478+0+0+0
vehicle321,808+0+0+0
asphalt9,115,452+0+0+0
solid_line457,728+0+0+0
guardrail2,518,981+0+0+0
ground1,151,167+0+0+0
low_vegetation384,024+0+0+0
medium_vegetation661,828+0+0+0

Segment 066

Reclaimed greenery → vehicle: c1c2 (corridor) 60,614 · c3 (asphalt_column) 43,334 points. Tree instances: 61 → 61 (none vanish; they shrink).

Full-resolution deltas vs base
classbaseΔ c1Δ c1c2Δ c3
tree12,794,916-16,516-77,130-59,850
vehicle695,114+0+60,614+43,334
asphalt15,415,798+16,510+16,510+16,510
solid_line914,455+6+6+6
guardrail3,156,030+0+0+0
ground3,449,574+0+0+0
low_vegetation540,936+0+0+0
medium_vegetation933,989+0+0+0
Decimated (1 cm) deltas vs base
classbaseΔ c1Δ c1c2Δ c3
tree12,708,084-16,277-75,486-58,568
vehicle518,205+0+59,201+42,284
asphalt8,672,826+16,271+16,276+16,276
solid_line518,792+6+6+6
guardrail2,506,335+0+0+0
ground2,480,665+0+2+2
low_vegetation480,252+0+1+0
medium_vegetation914,754+0+0+0

Segment 085

Reclaimed greenery → vehicle: c1c2 (corridor) 158,285 · c3 (asphalt_column) 97,174 points. Tree instances: 4 → 4 (none vanish; they shrink).

Full-resolution deltas vs base
classbaseΔ c1Δ c1c2Δ c3
tree18,635,552-46,209-204,494-143,383
vehicle610,599+0+158,285+97,174
asphalt18,489,172+45,953+45,953+45,953
solid_line1,021,259+256+256+256
guardrail2,139,245+0+0+0
wall60,817+0+0+0
ground3,711,772+0+0+0
low_vegetation747,416+0+0+0
medium_vegetation687,386+0+0+0
Decimated (1 cm) deltas vs base
classbaseΔ c1Δ c1c2Δ c3
tree18,467,888-44,500-194,744-138,055
vehicle455,354+0+150,175+93,509
asphalt9,896,196+44,275+44,319+44,319
solid_line545,294+225+240+225
guardrail1,583,885+0+3+0
wall60,361+0+0+0
ground2,929,504+0+3+2
low_vegetation688,186+0+4+0
medium_vegetation684,897+0+0+0

Segment 093

Reclaimed greenery → vehicle: c1c2 (corridor) 9,411 · c3 (asphalt_column) 8,899 points. Tree instances: 33 → 33 (none vanish; they shrink).

Full-resolution deltas vs base
classbaseΔ c1Δ c1c2Δ c3
tree8,759,943-4,049-13,460-12,948
vehicle887,459+0+9,411+8,899
asphalt17,144,173+4,038+4,038+4,038
solid_line992,778+11+11+11
guardrail2,192,200+0+0+0
ground4,113,604+0+0+0
low_vegetation2,155,829+0+0+0
medium_vegetation1,411,714+0+0+0
Decimated (1 cm) deltas vs base
classbaseΔ c1Δ c1c2Δ c3
tree8,604,954-3,838-12,724-12,264
vehicle650,008+0+8,880+8,420
asphalt9,336,257+3,827+3,832+3,832
solid_line521,395+11+11+11
guardrail1,665,813+0+0+0
ground3,327,591+0+1+1
low_vegetation2,071,688+0+0+0
medium_vegetation1,394,901+0+0+0

085 against the human ground truth (1.87 M annotated points, 100 % matched within 5 cm)

caveat The GT was annotated on the OLD 085 cloud (scan-angle filter at 80°, coarser decimation, 1.87 M points). This pass fused the NEW all-returns / no-angle-filter data. The GT points exist verbatim in the new cloud (100 % matched at 0 mm), so every GT point is scored, but the GT sees only 44.7 % of the new cloud: 56.5 % of the new points lie beyond 80°, and of what we call tree 96 % is beyond 80° (GT covers 4.5 % of our tree points, 86 % of the ≤80° ones). Within ≤80° coverage is 98.6 %, and the reclaim acts there (74 % of our vehicle points are GT-covered). Read the table as a verdict on the road/shoulder zone, not on the added canopy; the tree precision/recall figures describe the old-data slice only.

varianttree precisiontree recalltpfpfn
base (main a584a8a)0.5430.755156,216131,43650,753
c1: tree rank −1 only0.5450.751155,514130,03851,455
c1c2: + reclaim, corridor rule0.5510.743153,858125,55853,111
c3: + reclaim, asphalt_column rule (default)0.5470.746154,296127,77252,673

Where the points moved (human class → our class; counts for base / c1 / c1c2 / c3):

humanoursbase / c1 / c1c2 / c3
human tree (5)ours tree (5)156,216 / 155,514 / 153,858 / 154,296
human tree (5)ours vehicle (73)62 / 62 / 1,716 / 1,278
human tree (5)ours asphalt (11)80 / 782 / 784 / 784
human car noise (76)ours tree (5)571 / 570 / 0 / 0
human car noise (76)ours vehicle (73)15,597 / 15,597 / 16,167 / 16,167
human guardrail (66)ours tree (5)6,276 / 6,195 / 5,521 / 6,070
human guardrail (66)ours vehicle (73)13,589 / 13,588 / 14,262 / 13,713
human low veg (74)ours tree (5)18,428 / 17,291 / 14,491 / 15,873
human low veg (74)ours vehicle (73)9,380 / 9,380 / 12,173 / 10,793
human ground/neutral (71)ours tree (5)19,897 / 19,724 / 19,345 / 19,615
human ground/neutral (71)ours vehicle (73)5,943 / 5,943 / 6,320 / 6,051

Reading: c1c2 (corridor) buys 571 correct car-noise points and loses 1,654 real tree points; c3 (asphalt_column) keeps the 571 and loses 1,278 — the residual sits on the shoulder where asphalt cells exist under real canopy. Tree precision 0.543 → 0.547, recall 0.755 → 0.746 (base → c3). Human "low veg" and "ground" points inside the corridor that the detector had painted tree are wrong either way (tree or vehicle); they are not a loss but not a gain.

Images

Click a thumbnail for the lightbox; ←/→ step within one segment. Top-down rasters are priority-coloured (a tree point wins the pixel over vehicle and asphalt), so on-road changes are best read from the magenta reclaim overlays and the change-map crops.

Segment 066 — the mega-instance segment

066 BEFORE · base top-down (render_las.py, priority-coloured)
066 BEFORE · base top-down (render_las.py, priority-coloured)
066 AFTER · c3 top-down
066 AFTER · c3 top-down
066 · reclaim under the CORRIDOR rule (c1c2): magenta = c1c2 vehicle points on pixels base called tree
066 · reclaim under the CORRIDOR rule (c1c2): magenta = c1c2 vehicle points on pixels base called tree
066 · reclaim under the ASPHALT_COLUMN rule (c3): magenta = c3 vehicle points on pixels base called tree
066 · reclaim under the ASPHALT_COLUMN rule (c3): magenta = c3 vehicle points on pixels base called tree
066 · base | c3 | changed pixels (base→c3), crop around the changes
066 · base | c3 | changed pixels (base→c3), crop around the changes

missing: diff/066_base_vs_c1_crop.png

Segment 085 — ground-truth segment

085 BEFORE · base top-down (render_las.py, priority-coloured)
085 BEFORE · base top-down (render_las.py, priority-coloured)
085 AFTER · c3 top-down
085 AFTER · c3 top-down
085 · reclaim under the CORRIDOR rule (c1c2): magenta = c1c2 vehicle points on pixels base called tree
085 · reclaim under the CORRIDOR rule (c1c2): magenta = c1c2 vehicle points on pixels base called tree
085 · reclaim under the ASPHALT_COLUMN rule (c3): magenta = c3 vehicle points on pixels base called tree
085 · reclaim under the ASPHALT_COLUMN rule (c3): magenta = c3 vehicle points on pixels base called tree
085 · base | c3 | changed pixels (base→c3), crop around the changes
085 · base | c3 | changed pixels (base→c3), crop around the changes
085 · base | c1 | changed pixels (change 1 alone)
085 · base | c1 | changed pixels (change 1 alone)

Segment 093 — road panel under the gantry

093 BEFORE · base top-down (render_las.py, priority-coloured)
093 BEFORE · base top-down (render_las.py, priority-coloured)
093 AFTER · c3 top-down
093 AFTER · c3 top-down
093 · reclaim under the CORRIDOR rule (c1c2): magenta = c1c2 vehicle points on pixels base called tree
093 · reclaim under the CORRIDOR rule (c1c2): magenta = c1c2 vehicle points on pixels base called tree
093 · reclaim under the ASPHALT_COLUMN rule (c3): magenta = c3 vehicle points on pixels base called tree
093 · reclaim under the ASPHALT_COLUMN rule (c3): magenta = c3 vehicle points on pixels base called tree
093 · base | c3 | changed pixels (base→c3), crop around the changes
093 · base | c3 | changed pixels (base→c3), crop around the changes
093 · base | c1 | changed pixels (change 1 alone)
093 · base | c1 | changed pixels (change 1 alone)

Segment 018 — tree-heavy regression segment

018 BEFORE · base top-down (render_las.py, priority-coloured)
018 BEFORE · base top-down (render_las.py, priority-coloured)
018 AFTER · c3 top-down
018 AFTER · c3 top-down
018 · reclaim under the CORRIDOR rule (c1c2): magenta = c1c2 vehicle points on pixels base called tree
018 · reclaim under the CORRIDOR rule (c1c2): magenta = c1c2 vehicle points on pixels base called tree
018 · reclaim under the ASPHALT_COLUMN rule (c3): magenta = c3 vehicle points on pixels base called tree
018 · reclaim under the ASPHALT_COLUMN rule (c3): magenta = c3 vehicle points on pixels base called tree
018 · base | c3 | changed pixels (base→c3), crop around the changes
018 · base | c3 | changed pixels (base→c3), crop around the changes
018 · base | c1 | changed pixels (change 1 alone)
018 · base | c1 | changed pixels (change 1 alone)

Not improved / caveats

Next

  1. PR from t3code/6c51b0ba under AI3D-379. No push done.
  2. Decide the default: asphalt_column (shipped) vs corridor — the 085 GT table above is the number to decide on.
  3. Optional third lever if the 1.3 k canopy points matter: gate the reclaim on height above the vegetation stage's DTM (vehicles < ~4.5 m, crowns above), since the pipeline carries no other height-above-surface value.
  4. Detector side (wt-vs-382): the mega-instances themselves (tree_detection.hedge_split_enabled, tree_instance.enabled) and the 003 mast/gantry rows.
  5. Delete the throwaway baseline worktree /home/ai/wt-seg3d-base-260907 and, if the run root is not needed, /home/ai/seg3d_out/260907/fuse_* (≈15 GB).
+