Training data API: /train/tile + /train/mask (rollout step 1) #182

Closed
ned wants to merge 1 commit from issue-13-train-data-api into main
Member

First slice of the training-data API designed in nemish PR #14 (doc/nidus-sync-training-data-api.md), per issue #13. Implements rollout step 1 — tile + mask — which unblocks the Stadia-on-SkySat geometry sanity check.

GET /train/tile/{item_id}/{asset_type}/{z}/{x}/{y}.png — raw 16-bit per-band training tiles, worker Bearer auth:

  • format=multipart (default): one 16-bit grayscale PNG per band (B,G,R,NIR = 0,1,2,3), lossless, band order preserved
  • format=png: single-band 16-bit grayscale PNG
  • format=tiff: 4-band uint16 GeoTIFF with EPSG:3857 georeferencing (hand-rolled uncompressed chunky writer; verified with rasterio: 4 x uint16, crs EPSG:3857)

GET /train/mask/{item_id}/{z}/{x}/{y}.png — pool mask for the same tile:

  • Rasterizes feature_pool.geometry (3857) intersecting tile bounds to binary 8-bit PNG (0/255)
  • source=stadia (default) vs source=confirmed (only pools with committed review_task — the ~1k human-confirmed eval set)
  • dilation_px=N for the geometry sanity check

Both reuse authenticateWorkerToken (same as /vision/task) and the existing Gray16 tile pyramid — no new deps.

Verified: go build + go vet clean; 11 new tests pass (platform + api): point-in-ring, world-to-pixel, mask rasterization/dilation, TIFF structure + pixel round-trip, multipart round-trip, band parsing.

Next per rollout order: POST /train/manifest + GET /train/stats (unblocks the 3-way ablation), then /train/corpus + /train/udm.

First slice of the training-data API designed in nemish PR #14 (doc/nidus-sync-training-data-api.md), per issue #13. Implements rollout step 1 — tile + mask — which unblocks the Stadia-on-SkySat geometry sanity check. **GET /train/tile/{item_id}/{asset_type}/{z}/{x}/{y}.png** — raw 16-bit per-band training tiles, worker Bearer auth: - `format=multipart` (default): one 16-bit grayscale PNG per band (B,G,R,NIR = 0,1,2,3), lossless, band order preserved - `format=png`: single-band 16-bit grayscale PNG - `format=tiff`: 4-band uint16 GeoTIFF with EPSG:3857 georeferencing (hand-rolled uncompressed chunky writer; verified with rasterio: 4 x uint16, crs EPSG:3857) **GET /train/mask/{item_id}/{z}/{x}/{y}.png** — pool mask for the same tile: - Rasterizes feature_pool.geometry (3857) intersecting tile bounds to binary 8-bit PNG (0/255) - `source=stadia` (default) vs `source=confirmed` (only pools with committed review_task — the ~1k human-confirmed eval set) - `dilation_px=N` for the geometry sanity check Both reuse authenticateWorkerToken (same as /vision/task) and the existing Gray16 tile pyramid — no new deps. Verified: go build + go vet clean; 11 new tests pass (platform + api): point-in-ring, world-to-pixel, mask rasterization/dilation, TIFF structure + pixel round-trip, multipart round-trip, band parsing. Next per rollout order: POST /train/manifest + GET /train/stats (unblocks the 3-way ablation), then /train/corpus + /train/udm.
Add /train/tile and /train/mask endpoints for multi-spectral training data
All checks were successful
/ golint (push) Successful in 21s
/ pnpm-build (push) Successful in 1m14s
2c60650b6a
Issue #13 (nemish) needs nidus-sync to serve lossless training imagery and
pool masks to GPU hosts. The vision-worker auth and XYZ tile idioms already
exist; these endpoints expose the raw data the trainer consumes without
round-tripping through the 8-bit visualization path.

/train/tile serves raw 16-bit per-band Gray16 pyramid tiles (the same
pyramid the planet tile server uses) as multipart PNG (one part per band,
lossless), single-band PNG, or a hand-rolled 4-band uint16 GeoTIFF with
EPSG:3857 georeferencing. The TIFF writer emits a classic little-endian
uncompressed chunky layout with ModelPixelScale/ModelTiepoint and a
GeoKeyDirectory declaring 3857; verified readable via rasterio (4 x uint16,
crs EPSG:3857). Bands default to 0,1,2,3 (B,G,R,NIR) and are reordered on
the trainer side via band_indices.

/train/mask rasterizes feature_pool.geometry (3857) intersecting the tile
bounds into a binary 8-bit PNG (0/255) with optional source=confirmed
(restricts to pools whose review_task resolution is committed — the ~1k
human-confirmed eval set) and dilation_px. Rasterization is a scanline ray
cast per pixel row over each polygon's pixel-space bbox; no new deps (go-geom
already in tree).

Rollout order per doc/nidus-sync-training-data-api.md: tile+mask first to
unblock the Stadia-on-SkySat geometry sanity check; manifest+stats next.

Tests cover point-in-ring, world-to-pixel mapping, mask rasterization and
dilation, TIFF structure + pixel round-trip, multipart round-trip, and band
parsing. go build/vet clean; platform + api tests pass.
eliribble force-pushed issue-13-train-data-api from 2c60650b6a
All checks were successful
/ golint (push) Successful in 21s
/ pnpm-build (push) Successful in 1m14s
to 2fc7d6ede7
All checks were successful
/ golint (push) Successful in 21s
/ pnpm-build (push) Successful in 1m15s
2026-08-29 22:18:09 +00:00
Compare
Owner

superceded by later work.

superceded by later work.
eliribble closed this pull request 2026-09-04 23:49:26 +00:00
All checks were successful
/ golint (push) Successful in 21s
/ pnpm-build (push) Successful in 1m15s

Pull request closed

Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Gleipnir/nidus-sync!182
No description provided.