This roadmap tracks the engineering milestones for Structura. It follows Semantic Versioning (pre-1.0, so the API may change between minor versions).
Most of Structura can be built and tested without real excavation data: implementations run on synthetic / sample rasters and are covered in CI. Only the comparative evaluation and the quality verdicts (research hypotheses H_A–H_C) genuinely require a captured trench. That work is isolated to the last two milestones; everything before is buildable today.
The track letters (A–E) cross-reference the sub-studies in the companion research plan, so the two repositories stay aligned.
Package, CLI, configuration, data model, track/sink interfaces (stubs), packaging, docs, license metadata, citation metadata, CI.
The first milestone where the pipeline actually produces georeferenced output — no GPU, no model download, no database required.
-
geo.mask_to_polygonsimplemented (rasteriofeatures.shapes→ Shapely, applying the affine transform; world-unitmin_areafilter). -
ClassicalSegmenterimplemented (Otsu-marker watershed) — deterministic, CI-testable, the reference backend. - New
FileSink(GeoPackage / GeoJSON) — inspect output in QGIS before the PostGIS-vs-API decision is made; the testable default sink. -
structura runproduces real polygons from a sample orthophoto. - Tests on a tiny synthetic GeoTIFF; CI green end-to-end (geo extra installed).
-
SamSegmenterimplemented (SAM automatic mask generation via samgeo). -
CellposeSegmenterimplemented (Cellpose-SAM v4 instance segmentation). - Backend selection via configuration (
STRUCTURA_2D_BACKEND,make_segmenter). - Geometry metric utilities (over-/under-segmentation rate, a/b-axis error;
plus IoU/matching, precision/recall/F1) in
structura.metrics. - Data-dependent: model fine-tuning and the backend verdict belong to the evaluation milestone (v0.9), not here.
- The axis is zero-shot generic vs. domain-finetuned, not "SAM vs. Cellpose".
CellposeSegmenterruns Cellpose-SAM v4 (Pachitariu et al. 2025), which is itself SAM-backboned — the two learned backends share an architecture and differ in what they were trained on. The companion plan's H_A states it that way: a specialist fine-tuned on ≤ 300 hand-labelled stones against zero-shot SAM.
-
dem.reliefderivatives (hillshade, slope, curvature, local relief model, RVT-style multiscale blend) — self-implemented (numpy/scikit-image). -
geo.skeleton_to_polylinesimplemented (junction-clustered trace + spur prune). -
WallTracer(multiscale relief ridges → polyline) with gap bridging;EdgeTracer(slope discontinuities → EDGE), edges kept as separate polylines.
Scope proposed in ADR-0001: hand off files, do not write to PostGIS directly. The downstream API does not exist today, and both candidate sinks are blocked by the same gap — Contexta has no landing zone for geometry that carries no context yet.
- Confirm or reject ADR-0001 (decides the rest of this milestone).
- Agree the GeoPackage handoff contract with Contexta: layer, attribute names, CRS, and the key that makes a re-run idempotent.
- Blocked on Contexta: a landing zone for context-less geometry, plus an import command going through the ORM.
-
DjangoApiSink— implement if and when Contexta grows an API. -
— withdrawn by ADR-0001; the stub stays, it is not a candidate default.PostGISSink
-
temporal.overlay/temporal.intersectover per-day layers. - Per-stratum
ST_Intersection/ difference in PostGIS over the time series. -
captured_onpropagated through intake → features → sink. - Daily-series CLI ergonomics.
- Spatial (per-region) colour calibration of a rectified section image.
- Colour / texture stratum segmentation → section boundary polylines.
The upstream field exists as of 2026-07-26: Effigies writes
odm_semantic/orthophoto_semantic.tif (byte GeoTIFF with colour table, plus
orthophoto_semantic.legend.json), pixel-identical to
odm_orthophoto/odm_orthophoto.tif and odm_dem/dsm.tif — same grid, same
occlusion decisions — with class codes 0 nodata, 1 ground, 2 vegetation,
3 structure.
The mechanism is finished; the content is not. Those classes come from
OpenPointClass's stock vehicles-vegetation-buildings model, trained for aerial
and urban scenes. On a trench, ground/vegetation/structure carries almost no
archaeological information, so consuming the field as-is buys little.
The fine material classes (stone / soil / ceramic / mortar) are Structura's
deliverable, not an upstream input: a trained 2D image model, run per view and
fused onto the mesh by the Effigies multi-view blend (texture_blend.py). That
reverses the direction — 3D→2D today, 2D→3D planned — and is the only route by
which vertical section faces are classified at all; a nadir ortho loses them
by construction. Effigies already ships ONNX Runtime (CPU + CUDA) and the pattern
for versioned, SHA256-pinned weights in $EFFIGIES_MODEL_DIR, which is the
intended delivery path.
- Intake prerequisite:
SEMANTICRasterKindadded, and discovery is now layout-aware (odm_orthophoto/,odm_dem/,odm_semantic/) with the filename fallback testing"semantic"before"ortho". The class raster is tagged and deliberately left unconsumed, which is what keeps it out of the 2D segmenter until the rest of this milestone lands. - Consume the class raster as a prior / input channel for the vector tracks
(e.g. vectorise only where the field says
structure). - Implement the field-vs-object contract: Structura owns vector objects, the upstream engine owns the geometry-space field; the runtime flow is one-way (Effigies → Structura, which never reads back). Paving is not a field class — same material as a single stone, differing only in arrangement, so it is derived in the object layer.
- Train and ship the fine material-class 2D model. This is a research deliverable rather than a plumbing task and may warrant its own milestone.
- Validate the archaeological usefulness of the semantic ortho — Structura's evaluation, not Effigies'.
The blocker is annotation, not capture. A real orthophoto and DEM exist
(paper/input/data/Export/, Tiberias 2023-03-10) and the pipeline has been run
on them end-to-end. What does not exist is ground truth: no reference stone
outlines anywhere in the paper repo, so no metric can be scored. H_A puts a
bound on it — ≤ 300 hand-labelled instances — which makes this a schedulable task
rather than a wait for a season.
- Ground-truth annotation on frozen hold-out scenes (freeze the scene IDs first — plan Task 1.1; looking at model output on a scene before it is designated spends it).
- Reproducible evaluation scripts producing the paper's metrics (AP@IoU; completeness/correctness/quality + clDice/APLS; LoD95%).
- Wire results into
paper/output/data-analysis/.
- One full trench processed end-to-end.
- DB default locked; stable public API.
- Documentation complete; first Zenodo DOI minted.
- Keep CI green (ruff + mypy + pytest).
- Keep
CHANGELOG.md[Unreleased]current. - Update
docs/alongside the code it describes.
Decisions with real alternatives are recorded in docs/adr/;
smaller open questions are tracked in
docs/architecture.md. Currently open:
the sink (proposed in ADR-0001, awaiting
confirmation) and the 2D model default (decided by the v0.9 evaluation).
The intake layout heuristic is resolved — discovery now reads the delivery layout
and distinguishes a semantic class raster from an orthophoto.
This is a living document. Milestone scope and ordering may shift as the research plan and the upstream Effigies engine evolve.