Initial public release
GeoIntel release gates / Compile, test, contracts and builds (push) Successful in 1m49s
GeoIntel release gates / Python and npm vulnerability policy (push) Successful in 21s
GeoIntel release gates / Production AI image, SBOM and container scan (push) Successful in 5m39s
GeoIntel release gates / Deploy exact gated revision to Unraid (push) Failing after 58m43s
GeoIntel release gates / Compile, test, contracts and builds (push) Successful in 1m49s
GeoIntel release gates / Python and npm vulnerability policy (push) Successful in 21s
GeoIntel release gates / Production AI image, SBOM and container scan (push) Successful in 5m39s
GeoIntel release gates / Deploy exact gated revision to Unraid (push) Failing after 58m43s
This commit is contained in:
@@ -0,0 +1,793 @@
|
||||
# AI Pipelines
|
||||
|
||||
The cross-task PyTorch scope, capability matrix and national promotion waves are
|
||||
defined in `docs/PYTORCH_MODEL_PROGRAM.md`. PyTorch is used only for trainable
|
||||
imagery tasks; authoritative GIS measurements remain source-derived. The
|
||||
single-class tile exporter accepts explicit `--class-name`,
|
||||
`--reference-source` and `--reference-layer` bindings and persists them in its
|
||||
evidence summary. Production Tower training uses `TRAIN_DEVICE=cuda:0` with
|
||||
`TRAIN_REQUIRE_CUDA=true`, which fails closed without CUDA and records the
|
||||
PyTorch/CUDA runtime in `training_summary.json`.
|
||||
|
||||
## 1. Object Detection Pipeline
|
||||
|
||||
```text
|
||||
Raster dataset
|
||||
↓
|
||||
Clip to analysis area
|
||||
↓
|
||||
Tile raster
|
||||
↓
|
||||
Normalize tiles
|
||||
↓
|
||||
Run YOLO/PyTorch inference
|
||||
↓
|
||||
Filter by confidence
|
||||
↓
|
||||
Convert pixel boxes to geospatial polygons
|
||||
↓
|
||||
Merge overlapping detections
|
||||
↓
|
||||
Store in PostGIS
|
||||
↓
|
||||
Expose as GeoJSON layer
|
||||
↓
|
||||
Run QA/QC if reference data exists
|
||||
```
|
||||
|
||||
### Sprint 8 foundation status
|
||||
|
||||
Sprint 8 implements the detection persistence and execution boundary only:
|
||||
|
||||
- `detections` are first-class PostGIS records linked to project, dataset, job and analysis run.
|
||||
- `analysis_runs` remain separate from jobs and store model metadata, parameters, result summaries and lifecycle status.
|
||||
- `yolo-placeholder` reports `not_configured`; no YOLO/PyTorch model is downloaded or executed.
|
||||
- `manual-fixture-detector` is test/demo-only and persists detections only when `fixture_mode=true` and fixture detections are explicitly supplied.
|
||||
- Normal application behavior must not create fake detections.
|
||||
|
||||
### Sprint 8B configured YOLO status
|
||||
|
||||
Sprint 8B adds an import-safe real YOLO adapter path:
|
||||
|
||||
- `ultralytics` and `torch` are optional backend extras, not default runtime dependencies.
|
||||
- `yolo-configured` reports `not_configured` until `YOLO_ENABLED=true`, `YOLO_MODEL_PATH` points to an existing local model file and optional AI dependencies are installed.
|
||||
- GeoIntel never downloads model weights automatically.
|
||||
- Real YOLO inference uses an existing raster tile manifest generated by the raster tile operation.
|
||||
- YOLO raster tiles are normalized to RGB for inference when the tile artifact is not already a 3-band RGB image; the persisted georeferencing still comes from the tile manifest.
|
||||
- YOLO pixel boxes are converted to EPSG:4326 detection polygons from tile transform or tile bounds metadata.
|
||||
- YOLO class labels are normalized to lowercase for persisted detection records and filtering, while the original model label remains available in detection provenance.
|
||||
- Detection runs remain synchronous behind the existing job and analysis-run persistence boundary for Sprint 8B.
|
||||
|
||||
The guided Detection Lab action does not introduce another inference pipeline. It creates a tile manifest through the existing raster service, validates that manifest and the selected local asset through YOLO preflight, then invokes the same configured detection service. Persisted `Detection` geometry remains the authoritative map output; QA continues to compare those rows against persisted reference `vector_features` and stores `QualityCheck`/`Metric` records.
|
||||
|
||||
### Sprint 13 YOLO operational preflight
|
||||
|
||||
Sprint 13 adds a local preflight command for configured YOLO operation:
|
||||
|
||||
```bash
|
||||
python scripts/yolo_preflight.py --model-path /absolute/path/to/model.pt --tile-manifest-path /absolute/path/to/manifest.json
|
||||
```
|
||||
|
||||
For machines without optional AI dependencies, path and manifest checks can be exercised without pretending inference is available:
|
||||
|
||||
```bash
|
||||
python scripts/yolo_preflight.py --model-path /absolute/path/to/model.pt --tile-manifest-path /absolute/path/to/manifest.json --assume-dependencies --json
|
||||
```
|
||||
|
||||
The preflight checks:
|
||||
|
||||
- `YOLO_ENABLED` / explicit enabled state;
|
||||
- optional dependency availability unless `--assume-dependencies` is used;
|
||||
- local model file existence;
|
||||
- tile manifest JSON validity;
|
||||
- tile count against `YOLO_MAX_TILES`;
|
||||
- referenced tile file existence.
|
||||
|
||||
JSON output also reports runtime diagnostics: whether dependencies were assumed,
|
||||
the configured model directory, `YOLO_CONFIG_DIR`, installed `torch` and
|
||||
`ultralytics` versions, and CUDA availability when dependency checks pass.
|
||||
|
||||
The preflight does not load the model, does not import Ultralytics unless dependency discovery requires package metadata, does not run inference and never downloads model weights.
|
||||
|
||||
Sprint 25 adds an explicit local model compatibility smoke:
|
||||
|
||||
```bash
|
||||
python scripts/yolo_preflight.py --model-path /absolute/path/to/model.pt --tile-manifest-path /absolute/path/to/manifest.json --check-model-load --json
|
||||
```
|
||||
|
||||
`--check-model-load` requires real optional AI dependencies and an existing local
|
||||
model file. It loads that local file through the configured adapter to verify
|
||||
Ultralytics/PyTorch compatibility, but it still does not run tile prediction and
|
||||
does not download weights. It cannot be combined with `--assume-dependencies`
|
||||
because that would turn the smoke into a false positive.
|
||||
|
||||
Docker AI dependencies remain opt-in. Set `GEOINTEL_INSTALL_AI=true`
|
||||
at build time to install the backend `.[gis,ai]` extra into the container. Leave
|
||||
it unset or `false` for the default GIS-only image. Runtime model files should be
|
||||
mounted into the container, for example `/app/models/local-model.pt`, and enabled
|
||||
with `YOLO_ENABLED=true` plus `YOLO_MODEL_PATH=/app/models/local-model.pt`.
|
||||
GeoIntel never downloads weights automatically.
|
||||
|
||||
Environment variables:
|
||||
|
||||
- `GEOINTEL_INSTALL_AI`
|
||||
- `YOLO_ENABLED`
|
||||
- `YOLO_MODELS_DIR`
|
||||
- `YOLO_MODEL_PATH`
|
||||
- `YOLO_MODEL_ID`
|
||||
- `YOLO_MODEL_DISPLAY_NAME`
|
||||
- `YOLO_MODEL_VERSION`
|
||||
- `YOLO_DEVICE`
|
||||
- `YOLO_REQUIRE_CUDA` (set to `true` on the production server; inference then
|
||||
fails closed when CUDA is unavailable or `YOLO_DEVICE` selects CPU)
|
||||
- `YOLO_MODEL_CLASSES` (the active promoted detector is `building` only)
|
||||
- `YOLO_ENFORCE_VALIDATION_SCOPE` (keep `true` in production)
|
||||
- `YOLO_VALIDATION_SCOPE_MANIFEST_PATH` and
|
||||
`YOLO_VALIDATION_SCOPE_MANIFEST_SHA256` (production accepts inference only
|
||||
when the exact active model bytes match the manifest and the complete
|
||||
persisted Dataset AOI is covered by its valid EPSG:4326 geometry)
|
||||
- `YOLO_VALIDATED_AREA_NAMES` is deprecated display metadata and never grants
|
||||
inference access
|
||||
- `YOLO_IMAGE_SIZE`
|
||||
- `YOLO_MAX_TILES`
|
||||
- `YOLO_MAX_DETECTIONS`
|
||||
- `YOLO_DUPLICATE_IOU_THRESHOLD`
|
||||
- `YOLO_BATCH_SIZE`
|
||||
|
||||
`YOLO_MAX_DETECTIONS` is forwarded to Ultralytics as `max_det` for each
|
||||
prediction call. GeoIntel defaults it to `1000` because building-rich AOIs can
|
||||
contain far more than the Ultralytics default of 300 candidate boxes; keeping
|
||||
the upstream default would cap recall before QA/QC begins. Operators may lower
|
||||
the value for small rasters or raise it for dense urban tiles after reviewing
|
||||
runtime and false-positive behavior.
|
||||
|
||||
Create a new immutable scope artifact whenever either the model bytes or the
|
||||
governed validation boundary changes:
|
||||
|
||||
```bash
|
||||
python /app/scripts/build_model_validation_scope_manifest.py \
|
||||
--model /app/models/active-building.pt \
|
||||
--model-id yolo-configured \
|
||||
--scope-geojson /app/storage/operator-data/geographic-scopes/kempen-transport-region/kempen_transport_region_boundary_YYYY-MM-DD.geojson \
|
||||
--scope-key kempen-transport-region \
|
||||
--authority "Digitaal Vlaanderen VRBG/Refgem" \
|
||||
--snapshot-date YYYY-MM-DD \
|
||||
--output /app/storage/operator-data/model-validation-scopes/active-building-model.json
|
||||
```
|
||||
|
||||
The command refuses to overwrite an existing manifest and prints the checksum
|
||||
for `YOLO_VALIDATION_SCOPE_MANIFEST_SHA256`. Area names are intentionally not
|
||||
part of this decision: they are mutable presentation text, not accuracy or
|
||||
authorization evidence.
|
||||
|
||||
After YOLO boxes are georeferenced, configured-YOLO runs apply a GeoIntel
|
||||
cross-tile duplicate suppression pass before persistence. Candidates are grouped
|
||||
by canonical class and sorted by confidence; lower-confidence same-class
|
||||
candidates with EPSG:4326 geometry IoU greater than or equal to
|
||||
`YOLO_DUPLICATE_IOU_THRESHOLD` are suppressed. The default is `0.5`; set it to
|
||||
`0` to disable this post-processing for operator debugging. Run summaries record
|
||||
raw, persisted and suppressed detection counts so calibration evidence remains
|
||||
auditable.
|
||||
|
||||
### Persisted false-positive visual review
|
||||
|
||||
Detection QA labels a candidate as a false-positive only relative to the
|
||||
selected persisted reference dataset and matching tolerance. That finding is
|
||||
not automatically a model error: the reference can be incomplete or stale, and
|
||||
alignment can be wrong. GeoIntel therefore exposes persisted detection
|
||||
confidence/model/tile/bbox provenance in the existing QA evidence GeoJSON and
|
||||
provides a read-only contact-sheet workflow.
|
||||
|
||||
The operator must explicitly select one of:
|
||||
|
||||
- `confirmed_model_false_positive`;
|
||||
- `reference_gap_or_change`;
|
||||
- `qa_alignment_mismatch`;
|
||||
- `uncertain`;
|
||||
- `unreviewed`.
|
||||
|
||||
Only records explicitly marked `confirmed_model_false_positive` are emitted by
|
||||
the validator as possible hard-negative review input. The workflow does not
|
||||
train a model, mutate QA persistence, fetch data or infer review decisions.
|
||||
|
||||
### Persisted false-negative visual review
|
||||
|
||||
False negatives require the same manual distinction. A missed reference can be
|
||||
a true model miss, a stale reference, an obscured object, a box-to-footprint
|
||||
matching failure or an object outside the raster actually presented to the
|
||||
model. The read-only false-negative renderer resolves the persisted tile
|
||||
manifest from the fixed-threshold run and overlays:
|
||||
|
||||
- red: the missed GRB/reference footprint;
|
||||
- blue: nearby persisted candidate detections;
|
||||
- green: nearby matched reference footprints.
|
||||
|
||||
The decision contract is `confirmed_model_false_negative`,
|
||||
`reference_gap_or_change`, `qa_alignment_mismatch`,
|
||||
`imagery_obscured_or_uncertain` or `unreviewed`. References outside every
|
||||
persisted inference tile are written to a separate exclusion GeoJSON and are
|
||||
not treated as reviewable model misses. This renderer does not alter persisted
|
||||
QA metrics; coverage-adjusted values remain audit diagnostics until the QA
|
||||
service evaluation population is deliberately hardened.
|
||||
|
||||
`validate_detection_false_negative_review_decisions.py` provides the same
|
||||
fail-closed validation as the false-positive workflow. It requires an exact
|
||||
one-to-one set of reviewed reference ids and emits only explicit
|
||||
`confirmed_model_false_negative` geometries. `--require-complete` rejects any
|
||||
remaining `unreviewed` row. The July 2026 96-card review is recorded in
|
||||
the controlled model-review evidence outside Git; it yielded no novel,
|
||||
leakage-free labels and therefore did not trigger model training.
|
||||
|
||||
### Local model asset catalog
|
||||
|
||||
GeoIntel can list local runtime model files mounted into the backend model
|
||||
directory through `GET /api/v1/detection/model-assets`. The catalog is
|
||||
filesystem-backed and read-only: it reports existing `.pt`, `.onnx` and
|
||||
`.engine` files, size, checksum and whether the file matches `YOLO_MODEL_PATH`.
|
||||
|
||||
Detection runs still use `model_id="yolo-configured"` for the configured YOLO
|
||||
execution path. A selected `model_asset_id` can be supplied to use one specific
|
||||
cataloged file for that run. The backend resolves the ID to a local path and
|
||||
persists the selected asset metadata in Job/AnalysisRun parameters. GeoIntel
|
||||
does not download weights or accept arbitrary model paths from the browser.
|
||||
|
||||
Operational runtime validation can be run against Docker/Tower with:
|
||||
|
||||
```bash
|
||||
bash scripts/verify_model_asset_detection_workflow.sh http://192.0.2.10:1202
|
||||
```
|
||||
|
||||
The smoke seeds the explicit offline demo raster, creates a tile manifest,
|
||||
selects a local model asset, checks read-only preflight, runs the existing
|
||||
configured-YOLO detection endpoint and verifies persisted AnalysisRun,
|
||||
Detection list and Detection GeoJSON outputs. It intentionally does not inject
|
||||
detector fixtures or download weights. A zero detection count is acceptable on
|
||||
the synthetic demo raster; production usefulness still requires validation on
|
||||
real georeferenced orthophotos and reference vectors.
|
||||
|
||||
### Map-driven building analysis
|
||||
|
||||
The primary map can hand an explicit EPSG:4326 rectangle to the bounded
|
||||
orthophoto acquisition endpoint. Its canonical raster Dataset then uses the
|
||||
unchanged configured-YOLO pipeline: 512 px tiles with 64 px overlap, preflight,
|
||||
local inference, Job + AnalysisRun + Detection persistence and persisted
|
||||
GeoJSON. When a ready GRB buildings reference Dataset exists, the same action
|
||||
launches existing detection QA and persists QualityCheck and Metric rows.
|
||||
|
||||
This flow does not download a model, bypass the model registry, write directly
|
||||
to Detection/vector tables or present AI boxes as official building truth.
|
||||
The 1 m request sampling is an operational model profile; provenance retains
|
||||
the official orthophoto source and latest-mosaic limitation.
|
||||
|
||||
### Real-data detection and QA validation
|
||||
|
||||
The real operational validation path uses operator-provided files rather than
|
||||
demo fixtures:
|
||||
|
||||
```bash
|
||||
REAL_RASTER_PATH=/mnt/user/appdata/geointel/data/orthophoto.tif \
|
||||
REAL_REFERENCE_VECTOR_PATH=/mnt/user/appdata/geointel/data/reference-buildings.geojson \
|
||||
bash scripts/verify_real_data_detection_qa_workflow.sh http://192.0.2.10:1202
|
||||
```
|
||||
|
||||
The script verifies the full persisted chain:
|
||||
|
||||
- source raster upload with CRS and bounds metadata;
|
||||
- reference building vector upload as `dataset_role=reference`;
|
||||
- raster inspect and tile manifest generation;
|
||||
- local model asset selection and read-only YOLO preflight;
|
||||
- configured-YOLO detection run through Job, AnalysisRun and Detection rows;
|
||||
- detection GeoJSON generated from persisted geometry;
|
||||
- detection QA against persisted reference `vector_features` with persisted
|
||||
`QualityCheck` and `Metric` rows;
|
||||
- detection run GeoJSON export.
|
||||
|
||||
It refuses to run without a real GeoTIFF-style raster and GeoJSON/JSON reference
|
||||
vector. It does not seed demo data, use `fixture_mode`, fetch live providers or
|
||||
download model weights. A zero detection count is valid as runtime evidence only
|
||||
when the selected model genuinely returns no usable detections after canonical
|
||||
class filtering; it does not prove the model is useful for the target imagery.
|
||||
|
||||
Documented operator samples can be prepared inside the all-in-one runtime
|
||||
container:
|
||||
|
||||
```bash
|
||||
docker exec -it geointel python3 /app/scripts/prepare_operator_real_data_samples.py
|
||||
```
|
||||
|
||||
The helper fetches explicit Digitaal Vlaanderen orthophoto/GRB GBG sample pairs
|
||||
for the documented AOIs only and writes `operator_samples_manifest.json`. The
|
||||
default corpus includes dense reference AOIs for Geel, Mol, Turnhout, Herentals,
|
||||
Balen, Retie and Westerlo plus explicitly marked background candidates for
|
||||
Postel-bos, Lommel-heide, Kasterlee-bos, Dessel-heide, Ravels-bos,
|
||||
Meerhout-bos, Geel-Bel, Arendonk-heide and Herenthout-bos. Background
|
||||
candidates may persist empty GRB FeatureCollections for negative-tile training;
|
||||
normal reference AOIs still fail on empty GRB responses. Dense GRB references
|
||||
are fetched through OGC API `rel=next` pagination links instead of trusting only
|
||||
the first 1000-feature page. Generated reference GeoJSON records
|
||||
`reference_pages_fetched`, `reference_truncated`, `reference_page_limit`,
|
||||
`reference_max_features` and `source_urls` for auditability. The application
|
||||
itself still does not perform live provider fetching.
|
||||
|
||||
### Mol operational validation pack
|
||||
|
||||
The operator registry includes a Mol-first validation pack: Mol center,
|
||||
Achterbos residential, Gompel mixed settlement, Donk canal/industrial and
|
||||
Postel rural village. The four new zones are marked as validation holdouts so
|
||||
future training exports cannot silently consume the operational benchmark.
|
||||
Postel-bos is evaluated separately as a background control.
|
||||
|
||||
`run_mol_operational_validation.sh` composes the existing positive multi-sample
|
||||
quality matrix and background detection matrix. Positive runs use persisted GRB
|
||||
`vector_features` and create real `QualityCheck`/`Metric` rows; background runs
|
||||
only report persisted detection pressure and never synthesize QA metrics. AOI
|
||||
bounds from the operator manifest are persisted as EPSG:4326 `Area` records so
|
||||
every generated project opens as a complete map context.
|
||||
In the all-in-one runtime combined JSON/Markdown evidence defaults to
|
||||
`/app/storage/operator-evidence/mol-operational-validation`, which is part of
|
||||
the persistent storage mount rather than the replaceable container layer.
|
||||
|
||||
For confidence-threshold calibration, use the sweep wrapper:
|
||||
|
||||
```bash
|
||||
REAL_RASTER_PATH=/mnt/user/appdata/geointel/storage/operator-data/geel_orthophoto_wms_512.tif \
|
||||
REAL_REFERENCE_VECTOR_PATH=/mnt/user/appdata/geointel/storage/operator-data/geel_grb_gbg_buildings.geojson \
|
||||
CALIBRATION_THRESHOLDS="0.50 0.35 0.25 0.15" \
|
||||
bash scripts/run_detection_calibration_sweep.sh http://192.0.2.10:1202
|
||||
```
|
||||
|
||||
The sweep runs the real-data workflow once per threshold and then reads the
|
||||
persisted project quality-check list to build `calibration_summary.json`.
|
||||
Results are honest QA/QC evidence from persisted detections and persisted
|
||||
reference `vector_features`; no demo detections, live provider fetches or model
|
||||
downloads are introduced by the calibration tool.
|
||||
Summaries include raw detection candidate count, persisted detection count and
|
||||
suppressed duplicate count so operators can distinguish model output volume from
|
||||
GeoIntel post-processing.
|
||||
|
||||
For model/tile/threshold selection, use the quality matrix wrapper:
|
||||
|
||||
```bash
|
||||
REAL_RASTER_PATH=/mnt/user/appdata/geointel/storage/operator-data/geel_orthophoto_wms_512.tif \
|
||||
REAL_REFERENCE_VECTOR_PATH=/mnt/user/appdata/geointel/storage/operator-data/geel_grb_gbg_buildings.geojson \
|
||||
QUALITY_MODEL_ASSET_IDS="yolov8n-building-segmentation-pt yolov8n-pt" \
|
||||
QUALITY_TILE_SIZES="512 640" \
|
||||
QUALITY_TILE_OVERLAPS="64" \
|
||||
QUALITY_THRESHOLDS="0.50 0.15" \
|
||||
bash scripts/run_detection_quality_matrix.sh http://192.0.2.10:1202
|
||||
```
|
||||
|
||||
The matrix repeats the same persisted real-data workflow for every combination
|
||||
and writes `quality_matrix_summary.json` with detection count, QA score,
|
||||
precision, recall, F1, mean IoU and false-positive/false-negative counts. The
|
||||
rankings `best_by_score`, `best_by_recall` and `best_by_precision` are operator
|
||||
decision aids only; GeoIntel still does not download models, seed fixture
|
||||
detections or treat AI detections as ground truth without QA/QC. The same
|
||||
candidate should also pass the background false-positive matrix before it is
|
||||
considered as a default:
|
||||
|
||||
```bash
|
||||
OPERATOR_SAMPLE_MANIFEST_PATH=storage/operator-data/operator_samples_manifest.json \
|
||||
OPERATOR_BACKGROUND_SAMPLE_SLUGS="postel_bos lommel_heide kasterlee_bos" \
|
||||
QUALITY_MODEL_ASSET_IDS="geointel-building-yolov8s-aoi1024bg512r3e50-pt" \
|
||||
QUALITY_TILE_SIZES="512" \
|
||||
QUALITY_TILE_OVERLAPS="64" \
|
||||
QUALITY_THRESHOLDS="0.35 0.15" \
|
||||
BACKGROUND_SPLIT_OUTPUT_DIR=artifacts/detection-hard-negatives/aoi1024bg512r3e50-split \
|
||||
bash scripts/run_background_corpus_split_matrix.sh http://192.0.2.10:1202
|
||||
```
|
||||
|
||||
The split runner writes `background_corpus_split_summary.json` and Markdown
|
||||
handoff output with a strict `pure_empty_negative` gate and a separate
|
||||
review-only `sparse_building_context` block.
|
||||
|
||||
Use that split summary directly in the model promotion report:
|
||||
|
||||
```bash
|
||||
python scripts/build_detection_model_promotion_report.py \
|
||||
--positive-portfolio artifacts/detection-quality-matrix/multi-sample/aoi1024bg512r3e50-positive/multi_sample_quality_summary.json \
|
||||
--background-split-summary artifacts/detection-hard-negatives/aoi1024bg512r3e50-split/background_corpus_split_summary.json \
|
||||
--output-dir artifacts/detection-model-promotion/aoi1024bg512r3e50-split-aware \
|
||||
--min-positive-samples 7 \
|
||||
--min-background-samples 2 \
|
||||
--min-mean-f1 0.25 \
|
||||
--max-background-detections-per-sample 0
|
||||
```
|
||||
|
||||
The promotion report follows the split contract: `pure_empty_negative` is the
|
||||
only strict background gate for default promotion, while
|
||||
`sparse_building_context` remains review-only evidence in the report. This keeps
|
||||
contextual buildings from being treated as empty-background false positives.
|
||||
|
||||
For the Tower/runtime pass, run the split matrix and promotion report together:
|
||||
|
||||
```bash
|
||||
PROMOTION_POSITIVE_PORTFOLIO_PATH=artifacts/detection-quality-matrix/multi-sample/aoi1024bg512r3e50-positive/multi_sample_quality_summary.json \
|
||||
OPERATOR_SAMPLE_MANIFEST_PATH=storage/operator-data/operator_samples_manifest.json \
|
||||
QUALITY_MODEL_ASSET_IDS="geointel-building-yolov8s-aoi1024bg512r3e50-pt" \
|
||||
QUALITY_TILE_SIZES="512" \
|
||||
QUALITY_TILE_OVERLAPS="64" \
|
||||
QUALITY_THRESHOLDS="0.35 0.15" \
|
||||
BACKGROUND_SPLIT_OUTPUT_DIR=artifacts/detection-hard-negatives/aoi1024bg512r3e50-split \
|
||||
PROMOTION_OUTPUT_DIR=artifacts/detection-model-promotion/aoi1024bg512r3e50-split-aware \
|
||||
bash scripts/run_split_background_promotion_workflow.sh http://192.0.2.10:1202
|
||||
```
|
||||
|
||||
The wrapper keeps the same safety boundary: existing dataset upload, configured
|
||||
YOLO detection and report tooling only. It does not change model configuration
|
||||
or bypass the persisted QA/QC evidence requirement.
|
||||
|
||||
For a quick post-redeploy check before the long matrix starts, use
|
||||
`--preflight-only` with the same positive portfolio and operator manifest. This
|
||||
checks local paths, required background categories and the runtime API envelope
|
||||
without running inference:
|
||||
|
||||
Legacy operator manifests that do not yet contain explicit `background_category`
|
||||
remain supported: the preflight derives `pure_empty_negative` from
|
||||
`reference_feature_count == 0` and `sparse_building_context` from background
|
||||
samples with persisted reference features, matching the matrix runner.
|
||||
|
||||
```bash
|
||||
PROMOTION_POSITIVE_PORTFOLIO_PATH=artifacts/detection-quality-matrix/multi-sample/aoi1024bg512r3e50-positive/multi_sample_quality_summary.json \
|
||||
OPERATOR_SAMPLE_MANIFEST_PATH=storage/operator-data/operator_samples_manifest.json \
|
||||
bash scripts/run_split_background_promotion_workflow.sh --preflight-only http://192.0.2.10:1202
|
||||
```
|
||||
|
||||
The underlying single-category matrix remains available:
|
||||
|
||||
```bash
|
||||
OPERATOR_SAMPLE_MANIFEST_PATH=storage/operator-data/operator_samples_manifest.json \
|
||||
OPERATOR_BACKGROUND_CATEGORIES="pure_empty_negative" \
|
||||
OPERATOR_BACKGROUND_SAMPLE_SLUGS="postel_bos lommel_heide kasterlee_bos" \
|
||||
QUALITY_MODEL_ASSET_IDS="geointel-building-yolov8n-expanded160e50-pt geointel-building-yolov8n-tile30-pt yolov8s-building-segmentation-pt" \
|
||||
QUALITY_TILE_SIZES="640" \
|
||||
QUALITY_TILE_OVERLAPS="64" \
|
||||
QUALITY_THRESHOLDS="0.25 0.15 0.05" \
|
||||
bash scripts/run_operator_hard_negative_detection_matrix.sh http://192.0.2.10:1202
|
||||
```
|
||||
|
||||
The hard-negative matrix uploads only background rasters and counts detections
|
||||
as false-positive pressure. It does not run QA/QC or invent reference metrics
|
||||
for empty/sparse background AOIs. Operator manifests classify background
|
||||
samples as `pure_empty_negative` when GRB returns zero reference buildings and
|
||||
`sparse_building_context` when contextual buildings are present. Use
|
||||
`OPERATOR_BACKGROUND_CATEGORIES="pure_empty_negative"` for default-promotion
|
||||
hard-negative gates, then run `sparse_building_context` as a separate review
|
||||
matrix. The first expanded local model improved dense AOI F1, but Kasterlee-bos
|
||||
false positives block default promotion.
|
||||
|
||||
The focused small-building local model asset,
|
||||
`geointel-building-yolov8s-smallbld-minpx3-img640-ft30-pt`, is the current
|
||||
recommended Detection Lab operator profile. Use tile size `512`, overlap `64`
|
||||
and confidence threshold `0.15`. Its original promotion evidence at match IoU
|
||||
`0.25` across seven positive AOIs measured mean precision `0.5898`, recall
|
||||
`0.5770` and F1 `0.5825`; minimum per-AOI F1 was `0.5528`. The strict
|
||||
three-sample pure-empty
|
||||
background gate produced zero detections. Compared with the previous balanced
|
||||
profile, the same persisted reference populations contain 1,571 fewer false
|
||||
negatives, including 745 fewer misses in the 25-100 m2 bucket and 181 fewer
|
||||
below 25 m2. This recall gain increases the false-positive review load, so the
|
||||
previous `geointel-building-yolov8s-aoi1024expandedminpx4vis035e50-pt` profile
|
||||
remains available as a higher-precision legacy `0.15` choice. The older
|
||||
`geointel-building-yolov8s-aoi1024bg512r3e50-pt` remains the conservative
|
||||
`0.35` profile. Sparse-context detections remain review-only evidence, not a
|
||||
default-promotion blocker. Every production-like run still requires persisted
|
||||
QA/QC against suitable reference data.
|
||||
|
||||
The map-driven building workflow uses canonical footprint IoU `0.25`, matching
|
||||
the promotion evidence above. A July 2026 Mol-only holdout audit compared
|
||||
confidence `0.10` and `0.15` over Achterbos, Gompel, Donk and Postel. Confidence
|
||||
`0.15` produced the better F1 in all four positive zones; both thresholds
|
||||
produced zero detections in the pure-empty Postel forest control. The active
|
||||
confidence therefore remains `0.15`. This result does not claim production
|
||||
perfection and does not justify another model-training run by itself.
|
||||
|
||||
A coverage-aligned July 2026 rerun supersedes the older displayed profile
|
||||
averages above without changing the active model or threshold. On the exact
|
||||
current pipeline, the seven independent Mol/Kempen zones measured mean
|
||||
precision `0.6141`, recall `0.6062` and F1 `0.6069`; the minimum zone F1 was
|
||||
`0.4749` in Mol Postel. The active model again produced zero detections in
|
||||
Postel-bos, Lommel-heide and Arendonk-heide. These are the values shown in the
|
||||
Detection Lab operator profile.
|
||||
|
||||
The reviewed-accuracy experiment added six training-only AOIs from Arendonk,
|
||||
Dessel, Meerhout, Laakdal, Nijlen and Hulshout. The paged GRB export contained
|
||||
9,964 complete reference features. Its audited `512`-tile corpus retained 252
|
||||
tiles and 79,192 labels with no invalid or missing labels. The inactive
|
||||
`geointel-building-yolov8s-reviewedexp6-minpx3-img640-ft20-pt` challenger
|
||||
improved mean seven-zone F1 to `0.6248`, but produced two detections in the
|
||||
explicitly empty Postel-bos control. The formal fail-closed promotion report
|
||||
therefore retained the current active model. Positive-score gains never
|
||||
override a failed pure-empty background gate.
|
||||
|
||||
False-positive and false-negative evidence from persisted detection QA can be
|
||||
classified through `detection_reviews`. The queue derives from quality-check
|
||||
evidence ids and resolves persisted Detection and reference VectorFeature rows.
|
||||
`qa_alignment_mismatch`, `reference_gap_or_change`, uncertain imagery and
|
||||
unreviewed items must never be exported as hard-negative or missed-positive
|
||||
training labels. Canonical QA metrics remain unchanged after review.
|
||||
|
||||
The persisted seven-AOI evidence for this profile contains 5,568 false
|
||||
positives among 13,613 candidate detections. The read-only audit command in
|
||||
`scripts/README.md` reports the largest review volumes in Turnhout, Herentals
|
||||
and Geel, a median false-positive geometry area of about 184.5 m2, and 25.8%
|
||||
tiny/small geometry below 100 m2. Current evidence does not include
|
||||
per-detection confidence, so model-review reports must retain confidence
|
||||
coverage as zero rather than treating threshold `0.15` as an observed score.
|
||||
Combined false-positive GeoJSON is evidence for operator review only; a feature
|
||||
must be visually confirmed before it is used as a hard-negative label.
|
||||
|
||||
To update a Tower/Unraid `.env` from a promoted report, use the guarded
|
||||
activation helper. It validates the exact report candidate key, verifies that
|
||||
the candidate has `promotion_status=promote_candidate`, resolves the local model
|
||||
asset under the mounted models directory, and writes environment updates only
|
||||
when `--apply` is supplied:
|
||||
|
||||
```bash
|
||||
python scripts/activate_promoted_yolo_candidate.py \
|
||||
--promotion-report storage/operator-data/model-review/small-building-candidate/promotion/detection_model_promotion_report.json \
|
||||
--candidate-key 'geointel-building-yolov8s-smallbld-minpx3-img640-ft30-pt|512|64|0.15' \
|
||||
--models-dir /mnt/user/appdata/geointel/models \
|
||||
--env-file /mnt/user/appdata/geointel/.env \
|
||||
--json
|
||||
```
|
||||
|
||||
Re-run with `--apply` only after reviewing the emitted env updates. The helper
|
||||
does not download weights, load a model or run inference. Restart or rebuild the
|
||||
runtime after applying because `YOLO_MODEL_PATH` is read from environment
|
||||
configuration.
|
||||
|
||||
To compare the same model/tile/threshold grid across all prepared operator
|
||||
samples, use:
|
||||
|
||||
```bash
|
||||
OPERATOR_SAMPLE_MANIFEST_PATH=storage/operator-data/operator_samples_manifest.json \
|
||||
QUALITY_MODEL_ASSET_IDS="yolov8n-building-segmentation-pt yolov8n-pt" \
|
||||
QUALITY_TILE_SIZES="512 640" \
|
||||
QUALITY_TILE_OVERLAPS="64" \
|
||||
QUALITY_THRESHOLDS="0.50 0.15" \
|
||||
bash scripts/run_multi_sample_detection_quality_matrix.sh http://192.0.2.10:1202
|
||||
```
|
||||
|
||||
The multi-sample summary exposes `best_overall_by_score`,
|
||||
`best_overall_by_recall`, `best_overall_by_precision` and `best_by_sample` so
|
||||
model-quality decisions are based on repeated persisted QA/QC evidence rather
|
||||
than one AOI.
|
||||
|
||||
When repeated public model benchmarks remain too weak, the operator can convert
|
||||
the prepared real-data samples into a local YOLO training dataset:
|
||||
|
||||
```bash
|
||||
docker exec -it geointel python3 /app/scripts/export_operator_yolo_dataset.py \
|
||||
--manifest-path /app/storage/operator-data/operator_samples_manifest.json \
|
||||
--output-dir /app/storage/operator-data/yolo-building-dataset \
|
||||
--val-samples turnhout \
|
||||
--force
|
||||
```
|
||||
|
||||
The exporter creates a standard YOLO detection layout with `dataset.yaml`,
|
||||
`images/train`, `labels/train`, `images/val` and `labels/val`. It converts GRB
|
||||
building reference geometries to pixel-space bounding boxes for the matching
|
||||
orthophoto sample and records `yolo_dataset_summary.json`.
|
||||
|
||||
A minimal local training smoke can then be run explicitly in an AI-enabled
|
||||
runtime:
|
||||
|
||||
```bash
|
||||
docker exec \
|
||||
-e OPERATOR_YOLO_DATASET_DIR=/app/storage/operator-data/yolo-building-dataset \
|
||||
-e YOLO_BASE_MODEL_PATH=/app/models/yolov8n.pt \
|
||||
-e TRAIN_MODEL_OUTPUT_PATH=/app/models/geointel-building-detector.pt \
|
||||
-e TRAIN_EPOCHS=8 \
|
||||
-e TRAIN_IMGSZ=512 \
|
||||
-e TRAIN_BATCH=2 \
|
||||
-e TRAIN_WORKERS=0 \
|
||||
-e TRAIN_DEVICE=cpu \
|
||||
-e PYTHON_BIN=python3 \
|
||||
geointel bash /app/scripts/train_operator_yolo_detector.sh
|
||||
```
|
||||
|
||||
This remains operator tooling only. GeoIntel does not expose Training Studio in
|
||||
V1, does not generate labels from predictions and does not treat the trained
|
||||
artifact as useful until it passes the same real-data Detection + QA matrix.
|
||||
|
||||
If the whole-image dataset underfits or produces unusable detections, export
|
||||
overlapping tile-level samples:
|
||||
|
||||
```bash
|
||||
docker exec -it geointel python3 /app/scripts/export_operator_yolo_tile_dataset.py \
|
||||
--manifest-path /app/storage/operator-data/operator_samples_manifest.json \
|
||||
--output-dir /app/storage/operator-data/yolo-building-tile-dataset \
|
||||
--tile-size 192 \
|
||||
--stride 96 \
|
||||
--negative-keep-ratio 0.5 \
|
||||
--val-samples turnhout \
|
||||
--force
|
||||
```
|
||||
|
||||
The tile exporter clips reference building boxes into tile-local YOLO labels
|
||||
and records the positive/negative tile counts. This gives the training smoke
|
||||
more image samples while preserving the same explicit operator-data and QA/QC
|
||||
validation boundary.
|
||||
|
||||
Focused small-building experiments use Beerse, Rijkevorsel, Hoogstraten and
|
||||
Vorselaar as training AOIs, with Vosselaar and Grobbendonk retained as
|
||||
independent validation AOIs. The exporter accepts an explicit `--samples`
|
||||
subset and records `source_manifest_sample_count`, `selected_sample_slugs` and
|
||||
`excluded_sample_slugs` in its summary. Manifest-backed validation samples
|
||||
cannot silently enter training.
|
||||
|
||||
For visual error inspection, export the persisted QA evidence from a calibration
|
||||
summary:
|
||||
|
||||
```bash
|
||||
CALIBRATION_SUMMARY_PATH=/mnt/user/appdata/geointel/artifacts/detection-calibration/20260707T002103Z/calibration_summary.json \
|
||||
bash scripts/export_detection_calibration_evidence.sh http://192.0.2.10:1202
|
||||
```
|
||||
|
||||
The evidence bundle calls
|
||||
`/api/v1/projects/{project_id}/quality-checks/{quality_check_id}/evidence/geojson`
|
||||
for the persisted quality checks and writes combined GeoJSON plus an HTML/SVG
|
||||
review artifact. This is an inspection aid only; it does not rerun inference or
|
||||
alter stored detections.
|
||||
|
||||
### Sprint 8C detection visualization and QA status
|
||||
|
||||
Sprint 8C makes persisted detections reviewable:
|
||||
|
||||
- Detection runs can be listed and selected.
|
||||
- Persisted detections can be listed and filtered by run, dataset, class and minimum confidence.
|
||||
- Persisted detection geometries can be returned as GeoJSON FeatureCollections for MapLibre display.
|
||||
- Detection QA compares candidate detection geometries against persisted reference `vector_features`.
|
||||
- QA results reuse `quality_checks` and `metrics`; no parallel QA persistence system is introduced.
|
||||
- Configured-YOLO QA derives its evaluation extent from the persisted tile
|
||||
manifest. Tile bounds are transformed from their explicit source CRS to
|
||||
EPSG:4326 and unioned. The union is first applied as a GiST-backed PostGIS
|
||||
spatial predicate, then used to clip the bounded candidate/reference
|
||||
populations before canonical footprint-IoU matching. Complete source counts
|
||||
remain in QA evidence, but regional geometries outside inference coverage are
|
||||
not materialized in application memory and do not count as false negatives.
|
||||
- Canonical one-to-one IoU matching uses an in-memory spatial index only to
|
||||
discard geometries whose envelopes cannot intersect. It does not change the
|
||||
configured IoU threshold, greedy match ownership or persisted metrics.
|
||||
- A separate reference-envelope IoU pass is persisted as
|
||||
`box_to_footprint_diagnostics`. It quantifies possible matching artifacts from
|
||||
comparing rectangular detections with irregular building footprints, but is
|
||||
diagnostic only and never changes canonical QA metrics.
|
||||
- Segmentation remains out of scope for Sprint 8C.
|
||||
|
||||
## 2. Tile Metadata
|
||||
|
||||
Elke tile moet opslaan:
|
||||
|
||||
- tile path
|
||||
- parent raster id
|
||||
- pixel window
|
||||
- geospatial bounds
|
||||
- transform
|
||||
- CRS, and the manifest must also carry source CRS metadata
|
||||
- tile size
|
||||
- overlap
|
||||
|
||||
Zonder tile metadata kunnen modeloutputs niet correct teruggeprojecteerd worden.
|
||||
|
||||
## 3. Detection Output Contract
|
||||
|
||||
Elke detectie bevat:
|
||||
|
||||
- class_name
|
||||
- confidence
|
||||
- bbox pixel coords
|
||||
- source tile
|
||||
- geospatial polygon
|
||||
- model id/version
|
||||
- analysis run id
|
||||
|
||||
## 4. Segmentation Pipeline
|
||||
|
||||
```text
|
||||
Raster dataset
|
||||
↓
|
||||
Clip/tile
|
||||
↓
|
||||
Run segmentation model
|
||||
↓
|
||||
Generate mask
|
||||
↓
|
||||
Georeference mask
|
||||
↓
|
||||
Polygonize mask
|
||||
↓
|
||||
Simplify/clean geometries
|
||||
↓
|
||||
Store polygons + mask path
|
||||
↓
|
||||
Expose as map layer
|
||||
```
|
||||
|
||||
### Sprint 9 segmentation foundation status
|
||||
|
||||
Sprint 9 implements the segmentation persistence and review boundary only:
|
||||
|
||||
- `segmentations` are first-class PostGIS records linked to project, dataset, job and analysis run.
|
||||
- PostGIS MultiPolygon geometry in EPSG:4326 is authoritative for map display, QA and GeoJSON output.
|
||||
- Mask paths are persisted as artifact/provenance references, not authoritative feature state.
|
||||
- `segmentation-placeholder`, `yolo-seg-configured` and `sam-configured` report `not_configured`.
|
||||
- `fixture-segmenter` is test/demo-only and persists segmentations only when `fixture_mode=true` and fixture segmentations are explicitly supplied.
|
||||
- Segmentation QA compares persisted segmentation geometries against persisted reference `vector_features`.
|
||||
- QA results reuse `quality_checks` and `metrics`; no parallel QA system is introduced.
|
||||
- GeoIntel does not install SAM, run YOLO-seg, download model weights or fake production segmentations in Sprint 9.
|
||||
|
||||
## 5. Change Detection Pipeline
|
||||
|
||||
Fase 1: vector/detection based.
|
||||
|
||||
```text
|
||||
Run A detections
|
||||
+
|
||||
Run B detections
|
||||
↓
|
||||
Spatial matching
|
||||
↓
|
||||
added / removed / changed
|
||||
↓
|
||||
Change polygons
|
||||
↓
|
||||
Metrics
|
||||
```
|
||||
|
||||
Fase 2: raster index based.
|
||||
|
||||
```text
|
||||
Raster A index
|
||||
+
|
||||
Raster B index
|
||||
↓
|
||||
Difference raster
|
||||
↓
|
||||
Threshold
|
||||
↓
|
||||
Polygonize changed zones
|
||||
```
|
||||
|
||||
Fase 3: segmentation based.
|
||||
|
||||
```text
|
||||
Mask A
|
||||
+
|
||||
Mask B
|
||||
↓
|
||||
Class difference
|
||||
↓
|
||||
Change polygons
|
||||
```
|
||||
|
||||
## 6. Model Strategy
|
||||
|
||||
V1:
|
||||
|
||||
- gebruik een bestaande YOLO-integratie met configureerbaar modelpad
|
||||
- demo-model mag lokaal worden geplaatst in `models/`
|
||||
- code moet ook zonder model kunnen starten, maar detection job moet dan duidelijke fout geven
|
||||
|
||||
V2:
|
||||
|
||||
- SAM/YOLO segmentation
|
||||
|
||||
V3:
|
||||
|
||||
- annotation export
|
||||
- finetuning
|
||||
|
||||
## 7. Reproduceerbaarheid
|
||||
|
||||
Elke analysis run moet bewaren:
|
||||
|
||||
- model id
|
||||
- model version
|
||||
- parameters
|
||||
- confidence threshold
|
||||
- tile size
|
||||
- overlap
|
||||
- input dataset id
|
||||
- code path/version indien mogelijk
|
||||
Reference in New Issue
Block a user