Skip to content

[WIP] Sentinel-1 vessel detection pipeline #146

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/workflows/sentinel1_vessel.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: Sentinel1-Vessel-Detection

on:
workflow_dispatch: # Manual trigger
push:
tags:
- "sentinel1_vessels_v*" # Trigger only when a version tag (e.g., sentinel1_vessels_v0.0.1) is pushed

jobs:
build-and-push:
uses: ./.github/workflows/publish_project_docker_image.yaml
with:
rslp_project: "sentinel1_vessels"
image_name: "sentinel1-vessel-detection"
38 changes: 38 additions & 0 deletions data/sentinel1_vessels/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
These are the config files for Sentinel-1 vessel detection.

The training data has separate groups for ascending orbit direction versus descending
orbit direction. This is because the model only performs well with historical images,
and the historical images are aligned with the current image when they share the same
orbit direction (otherwise, terrain correction is needed, which is expensive).


config.json
-----------

This is the configuration for the training data.

Note that the orbit_direction attribute of the sentinel1_historical layer needs to be
set differently for the ascending groups vs the descending groups. See
`one_off_projects/convert_satlas_webmercator_to_rslearn/sentinel1_vessel/README.md` for
details.

This is also used for prediction when scene ID is provided and data is fetched from AWS
bucket. In this case the prediction pipeline populates the items.json so the orbit
direction in the config is unmodified (but has no effect).


config_predict_local_files.json
-------------------------------

This is for prediction when the user provides the vv/vh files directly. The user must
ensure the historical images are the same orbit direction as the target image.


Dataset Versions
----------------

- 20250521: this is generated from the siv.sqlite3 in sentinel-vessel-detections.
- 20250602: this is generated from `gs://satlas-explorer-data/siv-annotations/sentinel1.tar`.
It corresponds to the first subset of annotations which are included in 20250521. These
annotations may be higher quality but we seem to get better performance from 20250521, so
this dataset should not be used.
65 changes: 65 additions & 0 deletions data/sentinel1_vessels/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"layers": {
"label": {
"type": "vector"
},
"mask": {
"band_sets": [
{
"bands": [
"mask"
],
"dtype": "uint8",
"format": {
"format": "png",
"name": "single_image"
}
}
],
"type": "raster"
},
"output": {
"type": "vector"
},
"sentinel1": {
"alias": "sentinel1",
"band_sets": [
{
"bands": [
"vv",
"vh"
],
"dtype": "float32"
}
],
"data_source": {
"name": "rslearn.data_sources.aws_sentinel1.Sentinel1"
},
"resampling_method": "cubic",
"type": "raster"
},
"sentinel1_historical": {
"alias": "sentinel1",
"band_sets": [
{
"bands": [
"vv",
"vh"
],
"dtype": "float32"
}
],
"data_source": {
"duration": "60d",
"name": "rslearn.data_sources.aws_sentinel1.Sentinel1",
"orbit_direction": "ASCENDING",
"query_config": {
"max_matches": 2
},
"time_offset": "-90d"
},
"resampling_method": "cubic",
"type": "raster"
}
}
}
220 changes: 220 additions & 0 deletions data/sentinel1_vessels/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
model:
class_path: rslearn.train.lightning_module.RslearnLightningModule
init_args:
model:
class_path: rslearn.models.multitask.MultiTaskModel
init_args:
encoder:
- class_path: rslearn.models.simple_time_series.SimpleTimeSeries
init_args:
encoder:
class_path: rslearn.models.swin.Swin
init_args:
pretrained: false
input_channels: 2
output_layers: [1, 3, 5, 7]
image_channels: 2
groups: [[0], [1, 2]]
- class_path: rslearn.models.fpn.Fpn
init_args:
in_channels: [256, 512, 1024, 2048]
out_channels: 256
decoders:
detect:
- class_path: rslearn.models.faster_rcnn.FasterRCNN
init_args:
downsample_factors: [4, 8, 16, 32]
num_channels: 256
num_classes: 2
anchor_sizes: [[32], [64], [128], [256]]
lr: 0.0001
plateau: true
plateau_factor: 0.2
plateau_patience: 2
plateau_min_lr: 1e-6
plateau_cooldown: 10
restore_config:
restore_path: /weka/dfive-default/rslearn-eai/artifacts/satlaspretrain_models/sentinel1_swinb_mi.pth
remap_prefixes:
- ["backbone.backbone.backbone.", "encoder.0.encoder.model."]
data:
class_path: rslearn.train.data_module.RslearnDataModule
init_args:
path: /weka/dfive-default/rslearn-eai/datasets/sentinel1_vessels/dataset_v1/20250521/
inputs:
image_0:
data_type: "raster"
layers: ["sentinel1"]
bands: ["vv", "vh"]
passthrough: true
dtype: FLOAT32
image_1:
data_type: "raster"
layers: ["sentinel1_historical"]
bands: ["vv", "vh"]
passthrough: true
dtype: FLOAT32
image_2:
data_type: "raster"
layers: ["sentinel1_historical.1"]
bands: ["vv", "vh"]
passthrough: true
dtype: FLOAT32
mask:
data_type: "raster"
layers: ["mask"]
bands: ["mask"]
passthrough: true
dtype: INT32
is_target: true
targets:
data_type: "vector"
layers: ["label"]
is_target: true
task:
class_path: rslearn.train.tasks.multi_task.MultiTask
init_args:
tasks:
detect:
class_path: rslearn.train.tasks.detection.DetectionTask
init_args:
property_name: "category"
classes: ["unknown", "vessel"]
box_size: 15
remap_values: [[0, 1], [0, 255]]
image_bands: [1, 1, 1]
exclude_by_center: true
score_threshold: 0.9
enable_map_metric: true
enable_f1_metric: true
f1_metric_thresholds: [[0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.95], [0.1], [0.2], [0.3], [0.4], [0.5], [0.6], [0.7], [0.8], [0.9]]
f1_metric_kwargs:
cmp_mode: "distance"
cmp_threshold: 15
flatten_classes: true
input_mapping:
detect:
targets: "targets"
batch_size: 8
num_workers: 32
default_config:
transforms:
- class_path: rslearn.train.transforms.concatenate.Concatenate
init_args:
selections:
image_0: []
image_1: []
image_2: []
output_selector: image
- class_path: rslearn.train.transforms.normalize.Normalize
init_args:
mean: 0
std: 250
valid_range: [0, 4]
- class_path: rslp.transforms.mask.Mask
train_config:
patch_size: 512
transforms:
- class_path: rslearn.train.transforms.concatenate.Concatenate
init_args:
selections:
image_0: []
image_1: []
image_2: []
output_selector: image
- class_path: rslearn.train.transforms.normalize.Normalize
init_args:
mean: 0
std: 250
valid_range: [0, 4]
- class_path: rslp.transforms.mask.Mask
- class_path: rslearn.train.transforms.flip.Flip
init_args:
image_selectors: ["image"]
box_selectors: ["target/detect"]
groups:
- apr-2022-point-train_ascending
- apr-2022-point-train_descending
- jan-march-may-2022-point-train_ascending
- jan-march-may-2022-point-train_descending
- jun-2020-point-train_ascending
- jun-2020-point-train_descending
- jun-july-aug-2022-point-train_ascending
- jun-july-aug-2022-point-train_descending
- nov-2021-point-train_ascending
- nov-2021-point-train_descending
val_config:
load_all_patches: true
patch_size: 512
groups:
- apr-2022-point-val_ascending
- apr-2022-point-val_descending
- jan-march-may-2022-point-val_ascending
- jan-march-may-2022-point-val_descending
- jun-2020-point-val_ascending
- jun-july-aug-2022-point-val_ascending
- jun-july-aug-2022-point-val_descending
- nov-2021-point-val_ascending
- nov-2021-point-val_descending
test_config:
load_all_patches: true
patch_size: 512
groups:
- apr-2022-point-val_ascending
- apr-2022-point-val_descending
- jan-march-may-2022-point-val_ascending
- jan-march-may-2022-point-val_descending
- jun-2020-point-val_ascending
- jun-july-aug-2022-point-val_ascending
- jun-july-aug-2022-point-val_descending
- nov-2021-point-val_ascending
- nov-2021-point-val_descending
predict_config:
transforms:
- class_path: rslearn.train.transforms.concatenate.Concatenate
init_args:
selections:
image_0: []
image_1: []
image_2: []
output_selector: image
- class_path: rslearn.train.transforms.normalize.Normalize
init_args:
mean: 0
std: 250
valid_range: [0, 4]
groups: ["detector_predict"]
load_all_patches: true
skip_targets: true
patch_size: 512
overlap_ratio: 0.1
trainer:
max_epochs: 500
callbacks:
- class_path: lightning.pytorch.callbacks.LearningRateMonitor
init_args:
logging_interval: "epoch"
- class_path: rslearn.train.prediction_writer.RslearnWriter
init_args:
path: placeholder
output_layer: output
selector: ["detect"]
merger:
class_path: rslp.utils.nms.NMSDistanceMerger
init_args:
grid_size: 64
distance_threshold: 10
property_name: "category" # same as task.property_name
class_agnostic: false
- class_path: lightning.pytorch.callbacks.ModelCheckpoint
init_args:
save_top_k: 1
save_last: true
monitor: val_detect/mAP
mode: max
- class_path: rslearn.train.callbacks.freeze_unfreeze.FreezeUnfreeze
init_args:
module_selector: ["model", "encoder", 0, "encoder", "model"]
unfreeze_at_epoch: 4
rslp_project: sentinel1_vessels
rslp_experiment: data_20250521_model_20250530_satlaspretrain_unfreeze4_13
Loading
Loading