Skip to content

material-raster: prefer authoritative land-cover masks over satellite color - #100

Merged
transfix merged 2 commits into
mainfrom
feat/material-raster-masks
Sep 27, 2026
Merged

transfix merged 2 commits into
mainfrom
feat/material-raster-masks

Conversation

@transfix

Copy link
Copy Markdown
Contributor

Upgrade grl-snam material-raster (and the shared logic the new C++ cvc::nav version will mirror) to prefer the bundle's authoritative land-cover masks over the satellite color heuristic, per the source-strategy decision.

Why

A full scene bundle already ships the scene pipeline's OSM + ML (unet-efficientnet-b4) land cover as per-cell masks — far more accurate than classifying satellite.png:

  • foliage_mask.png — per-pixel land class (1 tree / 2 grass / 4 shrub → foliage; 3 rock; 5 bare → soil)
  • roads.png / water.png / open_fields.png — alpha masks (roads → open_air, water → water, open_fields → soil)

The color heuristic over-tagged foliage (31%, from muted-green urban tones) and missed the OSM waterways; the masks give the real vegetation (16.5%) and the actual creek.

What

  • segment_from_masks(bundle, rows, cols) — build material_id from the masks when present. Priority (later wins): open_air default → open_fields=soil → foliage classes → water → roads=open_air (you drive on roads, even through vegetation/fields). Downsampled by majority class, flipped to row 0 == world min_y. Returns None if masks absent.
  • from_bundle now prefers the masks and falls back to classify(satellite.png) only when they're missing (the lean DBG bundle the demo/harness ship). Same material.json format either way — the C++ cvc::nav segmenter mirrors this for byte-parity.
  • Refactored the write into a shared _write().

Validated

Austin full bundle (mask-derived): open_air 81% / foliage 16.5% / water 2.2% — visually tracks the OSM land cover incl. the creek. 4 new tests (mask mapping+priority+flip, absent→None, satellite fallback); 8/8, black/ruff clean.

…ver satellite color

A full scene bundle already ships the scene pipeline's OSM + ML (unet-efficientnet) land-cover as
per-cell masks — foliage_mask.png (land class 0..5: tree/grass/shrub -> foliage, rock, bare -> soil),
roads.png / water.png / open_fields.png (alpha masks) — which is far more accurate than a satellite
color heuristic (it has the real OSM waterways + true vegetation, not muted-green false positives).

- segment_from_masks(bundle, rows, cols): build the material_id grid from those masks when present,
  priority (later wins) open_air default -> open_fields=soil -> foliage classes -> water -> roads
  =open_air (you drive on roads, even through vegetation/fields); downsample to the grid by MAJORITY
  class, flip to row 0 == min_y. Returns None if the masks are absent (a lean bundle).
- from_bundle now PREFERS the masks and falls back to classify(satellite.png) only when they are
  missing (e.g. the lean DBG bundle the demo/harness ship). Same material.json format either way; the
  C++ cvc::nav segmenter mirrors this logic for byte-parity.
- Refactor the material.json write into _write() shared by generate() + from_bundle().

Austin (full bundle, mask-derived): open_air 81% / foliage 16.5% / water 2.2% (found the OSM creek the
color classifier missed) vs the satellite heuristic's over-tagged foliage 31%. 4 new tests (mask
mapping+priority+flip, absent->None, satellite fallback). 8/8, black/ruff clean.
…riented)

write_preview flips rows for DISPLAY so the tag map reads north-up like the satellite / a human map;
the material_id DATA is unchanged and stays sim-oriented (row 0 == world min_y), aligned with
occupancy (verified: occupancy_from_model rasterizes row = (y-min_y)*sy, and world building footprints
match the satellite only under a rows-flip). The south-up preview was the only thing that looked
mirrored; the raster itself samples correctly.
@transfix
transfix merged commit 885dd31 into main Sep 27, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant