OilTrace
U-Net oil-slick detection on Sentinel-1 radar, Smart India Hackathon 2026

Measured
| Metric | Value | Window | Source |
|---|---|---|---|
| Dice, vs 0.06 for the classical dark-spot threshold, 6× better | 0.35 | validation, traps included | ML-service/runs/E6_focal_iter/history.json |
| Object precision, up from 0.28 after the focal-loss fine-tune; raw was 0.045 | 0.47 | validation | ML-service/docs/judge_card.html |
| Object recall, slicks of 10 ha or more, max-IoU matching | 67% | validation | ML-service/docs/judge_card.html |
| Centroid error, about 8 px at 10 m resolution | 85 m | validation | ML-service/docs/judge_card.html |
| Inference, full Sentinel-1 scene on CPU | 10 s | ML-service/service/app.py, measured on the demo scene |
Winner, internal round, Smart India Hackathon 2026 (problem SIH26143); national round pending
Problem
Problem statement SIH26143 asks for oil-spill detection and vessel attribution from satellite data. The detection half sounds easy: oil looks dark on radar. The trouble is that the sea is full of dark things that are not oil: calm wind, algae, rain cells, ship wakes. A detector that flags every dark patch is useless to an investigator, so the exam that matters is rejecting the look-alikes.
Pipeline
Training the detector
About 2,500 real Sentinel-1 scenes, of which 685 are deliberate trap scenes with no oil. Scenes were sharded into 16,926 training and 4,335 validation patches of 512 pixels, with an inventory and error log per shard. Seven experiments are recorded in runs/, each with a manifest (config, git hash, device, shards) and a per-epoch history. The loss moved from Dice to focal after the early runs kept collapsing to “predict nothing” on the imbalanced patches; focal loss concentrates the gradient on exactly the pixels the model gets wrong. A scene classifier gates whole scenes before segmentation. The test set was sealed and never used for a decision.
Before and after
Service and dashboard
The detector runs behind FastAPI: POST /detect takes a GeoTIFF, tiles it, stitches the probability map, applies the threshold and minimum-area filters, and returns GeoJSON; GET / reports the checkpoint, epoch and full validation metrics so any result can be traced to a model version. It was deployed on Modal for the hackathon and runs locally now. The investigation dashboard, React and Leaflet, walks the six stages with map-synced particle clouds and a timeline driven only by backend timestamps. The interface never says “culprit”; it says “probable source region”, because that is what the evidence supports.
What I would do next
Publish the sealed test-set numbers once the national round decides whether they stay sealed. Add a small per-scene report so a judge can see which traps were rejected and why. Wire the detector back onto Modal with a warm-up so the demo does not cold start.
Learned: Put the look-alikes in the validation set or the number is a lie.