|
1 | | -# Sentinel-2 Change Detection |
| 1 | +# Sentinel-2 Clay Change Detection |
2 | 2 |
|
| 3 | +This workflow compares two Sentinel-2 time periods with the |
| 4 | +[Clay](https://github.com/Clay-foundation/model) foundation model and writes a patch-level change map to Zarr. |
| 5 | + |
| 6 | +## Design |
| 7 | + |
| 8 | +`DetectChanges` accepts native ODC `Geometry`, `CRS`, and `Resolution` values plus two Tilebox `TimeInterval` values. |
| 9 | +It queries credentials-free Sentinel-2 L2A COGs from AWS Earth Search and divides the exact output `GeoBox` into |
| 10 | +256 × 256 pixel model tiles. |
| 11 | + |
| 12 | +For every tile and period, `ComputeClayTile` reads only the required COG windows, masks clouds with the scene |
| 13 | +classification layer, creates a 25th-percentile composite in memory, and runs Clay. `ComputeChangeTile` then computes |
| 14 | +the cosine distance between corresponding embeddings. No full products, time-series cubes, pickled grids, or |
| 15 | +intermediate mosaics are written. |
| 16 | + |
| 17 | +The Zarr group is written to `change-detection/<job-id>` in the configured `s2-clay` bucket and contains `before` and |
| 18 | +`after` Clay embeddings and the resulting `change` distance array. |
| 19 | + |
| 20 | +## Run the workflow |
| 21 | + |
| 22 | +1. Copy `.env.example` to `.env` and configure the Tilebox and Open Telekom Cloud credentials. |
| 23 | +2. Validate and publish the release: |
| 24 | + |
| 25 | +```bash |
| 26 | +tilebox workflow build-release --debug --json |
| 27 | +tilebox workflow publish-release --json |
| 28 | +tilebox workflow deploy-release --latest --cluster <cluster-slug> --json |
| 29 | +tilebox runner start --cluster <cluster-slug> |
| 30 | +``` |
| 31 | + |
| 32 | +The Clay v1.5 checkpoint is downloaded and cached by `huggingface_hub` on each runner when first needed. |
| 33 | + |
| 34 | +## Submit a job |
| 35 | + |
| 36 | +```python |
| 37 | +from datetime import UTC, datetime |
| 38 | + |
| 39 | +from odc.geo import CRS, Geometry, Resolution |
| 40 | +from shapely import box |
| 41 | +from tilebox.datasets.query import TimeInterval |
| 42 | +from tilebox.workflows import Client |
| 43 | + |
| 44 | +from s2_clay import DetectChanges |
| 45 | + |
| 46 | +client = Client() |
| 47 | +client.jobs().submit( |
| 48 | + "vienna-change-detection", |
| 49 | + DetectChanges( |
| 50 | + area=Geometry(box(16.2, 48.1, 16.5, 48.3), "EPSG:4326"), |
| 51 | + before=TimeInterval(datetime(2024, 6, 1, tzinfo=UTC), datetime(2024, 7, 1, tzinfo=UTC)), |
| 52 | + after=TimeInterval(datetime(2025, 6, 1, tzinfo=UTC), datetime(2025, 7, 1, tzinfo=UTC)), |
| 53 | + output_crs=CRS("EPSG:32633"), |
| 54 | + resolution=Resolution(x=10, y=-10), |
| 55 | + max_cloud_cover=20, |
| 56 | + ), |
| 57 | +) |
| 58 | +``` |
0 commit comments