U-Net road-network extraction from SpaceNet-3 Khartoum satellite imagery: mask -> skeleton -> routable GeoJSON graph, with an OSM coverage diff. FastAPI + Vue 3 demo.
# Khartoum Road Graph Extractor
Extracts a routable road network from satellite imagery: a U-Net segments the roads,
the mask is skeletonised and traced into a graph, and the result comes back as GeoJSON
in WGS-84 — then gets compared against what OpenStreetMap already has for the same tile.
## What it does
**Model** — U-Net with an ImageNet-pretrained ResNet-34 encoder, trained on
SpaceNet-3 AOI-5 Khartoum PS-RGB imagery with road-centreline labels buffered into masks.
**Inference** — sliding-window over the tile (512 px windows, 128 px overlap) →
hysteresis threshold + morphological cleanup → `skeletonize` → junction/endpoint
tracing → `networkx` graph → GeoJSON.
**OSM diff** — an OSM reference layer (Overpass, clipped to the tile) is overlaid on the
extracted graph, with a diff layer showing what the model found that OSM does not have.
Several demo tiles sit in areas OSM barely covers, which is where extracting the network
from imagery actually earns its keep.
**App** — Vue 3 + Tailwind single-page frontend (Vite) over a FastAPI backend. The demo
tiles all come from the spatially-blocked test split — the model never saw them in
training — and their results are precomputed so the page loads instantly. Tiles can also
be stitched into multi-tile clusters.
## Layout
train.py training loop
models.py U-Net / encoder setup
dataset.py SpaceNet tiles -> tensors
create_labels.py centreline GeoJSON -> raster masks
app/inference.py sliding-window prediction
app/graph_refine.py mask -> skeleton -> networkx graph -> GeoJSON
app/osm_ref.py Overpass fetch, clipped to tile
app/osm_diff.py extracted-vs-OSM comparison
app/mosaic.py multi-tile stitching
app/precompute.py caches demo tile results
app/main.py FastAPI app
app/frontend/ Vue 3 + Vite SPA
## Run locally
Backend:
```bash
pip install -r app/requirements.txt # plus torch/torchvision …