diff --git a/ai2_docs/landsat_vessels/model_summary.md b/ai2_docs/landsat_vessels/model_summary.md index c81a37ce..9092b4f1 100644 --- a/ai2_docs/landsat_vessels/model_summary.md +++ b/ai2_docs/landsat_vessels/model_summary.md @@ -30,8 +30,8 @@ Note: The evaluation metrics are reported for the two-stage model (detector + cl --- ## Model Configurations -- **Detector**: `rslearn_projects/data/landsat_vessels/config.yaml` -- **Classifier**: `rslearn_projects/landsat/recheck_landsat_labels/phase123_config.yaml` +- **Detector**: `rslearn_projects/data/landsat_vessels/config_detector.yaml` +- **Classifier**: `rslearn_projects/data/landsat_vessels/config_classifier.yaml` - **Filters**: marine infrastructure `rslearn_projects/rslp/utils/filter.py` --- diff --git a/ai2_docs/landsat_vessels/train_eval.md b/ai2_docs/landsat_vessels/train_eval.md index 0c3b8da6..31291c8a 100644 --- a/ai2_docs/landsat_vessels/train_eval.md +++ b/ai2_docs/landsat_vessels/train_eval.md @@ -12,17 +12,17 @@ This detects vessels in Landsat imagery using two models: The object detector can be trained like this: - python -m rslp.rslearn_main model fit --config data/landsat_vessels/config.yaml + python -m rslp.rslearn_main model fit --config data/landsat_vessels/config_detector.yaml The dataset was originally labeled in siv and has been converted to rslearn dataset using the code in `landsat/existing_dataset_to_utm/`. The classifier can be trained like this: - python -m rslp.rslearn_main model fit --config landsat/recheck_landsat_labels/phase123_config.yaml + python -m rslp.rslearn_main model fit --config data/landsat_vessels/config_classifier.yaml The data collection process for the classifier is described in -`landsat/recheck_landsat_labels/README.md`. +`one_off_projects/landsat/recheck_landsat_labels/README.md`. --- diff --git a/data/landsat_vessels/config_classifier.yaml b/data/landsat_vessels/config_classifier.yaml new file mode 100644 index 00000000..1288b444 --- /dev/null +++ b/data/landsat_vessels/config_classifier.yaml @@ -0,0 +1,105 @@ +model: + class_path: rslearn.train.lightning_module.RslearnLightningModule + init_args: + model: + class_path: rslearn.models.multitask.MultiTaskModel + init_args: + encoder: + - class_path: rslearn.models.swin.Swin + init_args: + input_channels: 7 + output_layers: [1, 3, 5, 7] + pretrained: true + decoders: + class: + - class_path: rslearn.models.pooling_decoder.PoolingDecoder + init_args: + in_channels: 1024 + out_channels: 2 + - class_path: rslearn.train.tasks.classification.ClassificationHead + lr: 0.0001 + plateau_factor: 0.1 + plateau_patience: 10 + plateau_min_lr: 0 + plateau_cooldown: 0 + restore_config: + restore_path: gcs://rslearn-eai/datasets/landsat_vessel_detection/artifacts/2024-03-13-landsat-vessels/vessel02_satlas_freeze_crop512_nosatlas_b8first2/best.pth + remap_prefixes: + - ["backbone.backbone.", "encoder.0.model."] +data: + class_path: rslearn.train.data_module.RslearnDataModule + init_args: + path: gcs://rslearn-eai/datasets/landsat_vessel_detection/classifier/dataset_20240905/ + inputs: + image: + data_type: "raster" + layers: ["landsat"] + bands: ["B8", "B2", "B3", "B4", "B5", "B6", "B7"] + passthrough: true + label: + data_type: "vector" + layers: ["label"] + is_target: true + task: + class_path: rslearn.train.tasks.multi_task.MultiTask + init_args: + tasks: + class: + class_path: rslearn.train.tasks.classification.ClassificationTask + init_args: + property_name: "label" + classes: ["correct", "incorrect"] + allow_invalid: true + skip_unknown_categories: true + prob_property: "prob" + positive_class: "correct" + positive_class_threshold: 0.85 + input_mapping: + class: + label: "targets" + batch_size: 16 + num_workers: 16 + default_config: + transforms: + - class_path: rslearn.train.transforms.normalize.Normalize + init_args: + mean: 0 + std: 255 + - class_path: rslearn.train.transforms.pad.Pad + init_args: + mode: "center" + size: 32 + - class_path: rslearn.train.transforms.flip.Flip + groups: ["selected_copy", "phase2a_completed", "phase3a_selected"] + train_config: + tags: + split: train + sampler: + class_path: rslearn.train.dataset.WeightedRandomSamplerFactory + init_args: + option_key: "weight" + num_samples: 1000 + val_config: + groups: ["phase2a_completed"] + tags: + split: val + test_config: + groups: ["phase2a_completed"] + tags: + split: val + predict_config: + groups: ["classify_predict"] + skip_targets: true +trainer: + max_epochs: 64 + callbacks: + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + logging_interval: "epoch" + - class_path: rslearn.train.prediction_writer.RslearnWriter + init_args: + path: gcs://rslearn-eai/datasets/landsat_vessel_detection/classifier/dataset_20240905/ + output_layer: output + selector: ["class"] +rslp_project: rslearn-landsat-recheck +rslp_experiment: phase123_20240919_01_copy diff --git a/data/landsat_vessels/config.yaml b/data/landsat_vessels/config_detector.yaml similarity index 100% rename from data/landsat_vessels/config.yaml rename to data/landsat_vessels/config_detector.yaml diff --git a/docs/images/landsat_vessels/prediction.png b/docs/images/landsat_vessels/prediction.png new file mode 100644 index 00000000..6a66d831 Binary files /dev/null and b/docs/images/landsat_vessels/prediction.png differ diff --git a/docs/landsat_vessels.md b/docs/landsat_vessels.md new file mode 100644 index 00000000..ca800087 --- /dev/null +++ b/docs/landsat_vessels.md @@ -0,0 +1,91 @@ +Landsat Vessel Detection +--------------------------- + +The Landsat vessel detection model detects ships in Landsat 8/9 scenes. We use Level-1 data since they are released with a lower latency, and latency is +important for [Skylight](https://www.skylight.global/) (which is the primary use of +this model within Ai2). + +The model includes of a detector and a classifier: the detector detects ship-like objects, and the classifier refines these detections by pruning ones that it is confident are not ships. The detector is trained on a dataset consisting of 7,954 Landsat patches (ranging from 384x384 to 768x768) with 18,509 ship labels. The classifier is trained on a dataset consisting of 1,733 annotated detections, with each detection represented as a 64x64 patch centered at the position of a detected ship. See our paper for more details about the model and dataset. + +
+ Image showing a Landsat image with predicted positions of ships from the model overlayed. +
+ + +Inference +--------- + +First, download the detector and classifier checkpoints to the `RSLP_PREFIX` directory. + + cd rslearn_projects + mkdir -p project_data/projects/landsat_vessels/data_20240924_model_20240924_imagenet_patch512_flip_03/checkpoints/ + wget https://storage.googleapis.com/ai2-rslearn-projects-data/landsat_vessels/detector/best.ckpt -O project_data/projects/landsat_vessels/data_20240924_model_20240924_imagenet_patch512_flip_03/checkpoints/last.ckpt + + mkdir -p project_data/projects/rslearn-landsat-recheck/phase123_20240919_01_copy/checkpoints/ + wget https://storage.googleapis.com/ai2-rslearn-projects-data/landsat_vessels/classifer/best.ckpt -O project_data/projects/rslearn-landsat-recheck/phase123_20240919_01_copy/checkpoints/last.ckpt + +The easiest way to apply the model is using the prediction pipeline in `rslp/landsat_vessels/predict_pipeline.py`. You can download the Landsat scene files, e.g. from USGS EarthExplorer or AWS, and then create a configuration file for the prediction pipeline, here is an example: + +```json +{ + "image_files": { + "B2": "/home/data/LC08_L1TP_125059_20240727_20240801_02_T1_B2.TIF", + "B3": "/home/data/LC08_L1TP_125059_20240727_20240801_02_T1_B3.TIF", + "B4": "/home/data/LC08_L1TP_125059_20240727_20240801_02_T1_B4.TIF", + "B5": "/home/data/LC08_L1TP_125059_20240727_20240801_02_T1_B5.TIF", + "B6": "/home/data/LC08_L1TP_125059_20240727_20240801_02_T1_B6.TIF", + "B7": "/home/data/LC08_L1TP_125059_20240727_20240801_02_T1_B7.TIF", + "B8": "/home/data/LC08_L1TP_125059_20240727_20240801_02_T1_B8.TIF", + }, + "scratch_path": "/home/data/scratch/", + "json_path": "/home/data/vessels.json", + "crop_path": "/home/data/crops/" +} +``` + +This specifies the arguments to +`rslp.landsat_vessels.predict_pipeline.predict_pipeline` via `jsonargparse`. + +Now we can run the pipeline: + + python -m rslp.main landsat_vessels predict --config /path/to/config.json + +Here, `scratch_path` saves the rslearn dataset, `crop_path` saves the cropped RGB images centered around the detected ships, and `json_path` saves the JSON output of the detected ships, all of which are optional, depending on whether the user wants to save the intermediate results or not. + +The prediction pipeline also accepts a Landsat scene ID and automatically downloads the scene images from [AWS](https://aws.amazon.com/marketplace/pp/prodview-ivr4jeq6flk7u#resources). You will need to set up your AWS account for accessing Landsat data. Use the command below to run the pipeline with scene ID: + + python -m rslp.main landsat_vessels predict --scene_id LC08_L1TP_125059_20240727_20240801_02_T1 + + +Training +-------- + +First, download the training dataset for detector: + + cd rslearn_projects + mkdir -p project_data/datasets/landsat_vessels/ + wget https://storage.googleapis.com/ai2-rslearn-projects-data/landsat_vessels/landsat_vessels_detector.tar -0 project_data/datasets/landsat_vessels_detector.tar + tar xvf project_data/datasets/landsat_vessels_detector.tar --directory project_data/datasets/landsat_vessels/ + +It is an rslearn dataset consisting of window folders like `windows/labels_utm/41984_2354176_f7c057a567ee40b694d0a77ea59ef81a_6359/`. Inside each window folder: + +- `layers/landsat/` contains different Landsat bands used by the model. +- `layers/label/data.geojson` contains the positions of ships. These are offset from + the bounds of the window which are in `metadata.json`, so subtract the window's + bounds to get pixel coordinates relative to the image. + +Use the command below to train the detector. Note that Weights & Biases is needed. You can +disable W&B with `--no_log true` but then it may be difficult to track the metrics. + + python -m rslp.rslearn_main model fit --config data/landsat_vessels/config_detector.yaml --data.init_args.path project_data/datasets/landsat_vessels/dataset_20240924/ + +Second, download the training dataset for classifier: + + wget https://storage.googleapis.com/ai2-rslearn-projects-data/landsat_vessels/landsat_vessels_classifier.tar -0 project_data/datasets/landsat_vessels_classifier.tar + tar xvf project_data/dataset/landsat_vessels_classifier.tar --directory project_data/datasets/landsat_vessels/ + +Use the command below to train the classifier. + + python -m rslp.rslearn_main model fit --config data/landsat_vessels/config_classifier.yaml --data.init_args.path project_data/datasets/landsat_vessels/dataset_20240905/ diff --git a/landsat/confirm_same_performance_as_integration/README.md b/one_off_projects/landsat/confirm_same_performance_as_integration/README.md similarity index 100% rename from landsat/confirm_same_performance_as_integration/README.md rename to one_off_projects/landsat/confirm_same_performance_as_integration/README.md diff --git a/landsat/confirm_same_performance_as_integration/rslearn_to_multisat.py b/one_off_projects/landsat/confirm_same_performance_as_integration/rslearn_to_multisat.py similarity index 100% rename from landsat/confirm_same_performance_as_integration/rslearn_to_multisat.py rename to one_off_projects/landsat/confirm_same_performance_as_integration/rslearn_to_multisat.py diff --git a/landsat/existing_dataset_to_utm/add_label_layer.py b/one_off_projects/landsat/existing_dataset_to_utm/add_label_layer.py similarity index 100% rename from landsat/existing_dataset_to_utm/add_label_layer.py rename to one_off_projects/landsat/existing_dataset_to_utm/add_label_layer.py diff --git a/landsat/existing_dataset_to_utm/config.json b/one_off_projects/landsat/existing_dataset_to_utm/config.json similarity index 100% rename from landsat/existing_dataset_to_utm/config.json rename to one_off_projects/landsat/existing_dataset_to_utm/config.json diff --git a/landsat/existing_dataset_to_utm/prepare_windows.py b/one_off_projects/landsat/existing_dataset_to_utm/prepare_windows.py similarity index 100% rename from landsat/existing_dataset_to_utm/prepare_windows.py rename to one_off_projects/landsat/existing_dataset_to_utm/prepare_windows.py diff --git a/landsat/existing_dataset_to_utm/reformat_multisat.py b/one_off_projects/landsat/existing_dataset_to_utm/reformat_multisat.py similarity index 100% rename from landsat/existing_dataset_to_utm/reformat_multisat.py rename to one_off_projects/landsat/existing_dataset_to_utm/reformat_multisat.py diff --git a/landsat/quick_vis_script.py b/one_off_projects/landsat/quick_vis_script.py similarity index 100% rename from landsat/quick_vis_script.py rename to one_off_projects/landsat/quick_vis_script.py diff --git a/landsat/random_landsat/convert_to_multisat_dataset.py b/one_off_projects/landsat/random_landsat/convert_to_multisat_dataset.py similarity index 100% rename from landsat/random_landsat/convert_to_multisat_dataset.py rename to one_off_projects/landsat/random_landsat/convert_to_multisat_dataset.py diff --git a/landsat/random_landsat/convert_windows_to_utm.py b/one_off_projects/landsat/random_landsat/convert_windows_to_utm.py similarity index 100% rename from landsat/random_landsat/convert_windows_to_utm.py rename to one_off_projects/landsat/random_landsat/convert_windows_to_utm.py diff --git a/landsat/random_landsat/random_landsat_windows.py b/one_off_projects/landsat/random_landsat/random_landsat_windows.py similarity index 100% rename from landsat/random_landsat/random_landsat_windows.py rename to one_off_projects/landsat/random_landsat/random_landsat_windows.py diff --git a/landsat/recheck_landsat_labels/README.md b/one_off_projects/landsat/recheck_landsat_labels/README.md similarity index 100% rename from landsat/recheck_landsat_labels/README.md rename to one_off_projects/landsat/recheck_landsat_labels/README.md diff --git a/landsat/recheck_landsat_labels/phase123_config.yaml b/one_off_projects/landsat/recheck_landsat_labels/phase123_config.yaml similarity index 100% rename from landsat/recheck_landsat_labels/phase123_config.yaml rename to one_off_projects/landsat/recheck_landsat_labels/phase123_config.yaml diff --git a/landsat/recheck_landsat_labels/phase123_config_hparams.yaml b/one_off_projects/landsat/recheck_landsat_labels/phase123_config_hparams.yaml similarity index 100% rename from landsat/recheck_landsat_labels/phase123_config_hparams.yaml rename to one_off_projects/landsat/recheck_landsat_labels/phase123_config_hparams.yaml diff --git a/landsat/recheck_landsat_labels/phase12_config.yaml b/one_off_projects/landsat/recheck_landsat_labels/phase12_config.yaml similarity index 100% rename from landsat/recheck_landsat_labels/phase12_config.yaml rename to one_off_projects/landsat/recheck_landsat_labels/phase12_config.yaml diff --git a/landsat/recheck_landsat_labels/phase1_assign_split.py b/one_off_projects/landsat/recheck_landsat_labels/phase1_assign_split.py similarity index 100% rename from landsat/recheck_landsat_labels/phase1_assign_split.py rename to one_off_projects/landsat/recheck_landsat_labels/phase1_assign_split.py diff --git a/landsat/recheck_landsat_labels/phase1_config.json b/one_off_projects/landsat/recheck_landsat_labels/phase1_config.json similarity index 100% rename from landsat/recheck_landsat_labels/phase1_config.json rename to one_off_projects/landsat/recheck_landsat_labels/phase1_config.json diff --git a/landsat/recheck_landsat_labels/phase1_config.yaml b/one_off_projects/landsat/recheck_landsat_labels/phase1_config.yaml similarity index 100% rename from landsat/recheck_landsat_labels/phase1_config.yaml rename to one_off_projects/landsat/recheck_landsat_labels/phase1_config.yaml diff --git a/landsat/recheck_landsat_labels/phase1_get_1000.py b/one_off_projects/landsat/recheck_landsat_labels/phase1_get_1000.py similarity index 100% rename from landsat/recheck_landsat_labels/phase1_get_1000.py rename to one_off_projects/landsat/recheck_landsat_labels/phase1_get_1000.py diff --git a/landsat/recheck_landsat_labels/phase1_index.html b/one_off_projects/landsat/recheck_landsat_labels/phase1_index.html similarity index 100% rename from landsat/recheck_landsat_labels/phase1_index.html rename to one_off_projects/landsat/recheck_landsat_labels/phase1_index.html diff --git a/landsat/recheck_landsat_labels/phase1_server.py b/one_off_projects/landsat/recheck_landsat_labels/phase1_server.py similarity index 100% rename from landsat/recheck_landsat_labels/phase1_server.py rename to one_off_projects/landsat/recheck_landsat_labels/phase1_server.py diff --git a/landsat/recheck_landsat_labels/phase2_config.json b/one_off_projects/landsat/recheck_landsat_labels/phase2_config.json similarity index 100% rename from landsat/recheck_landsat_labels/phase2_config.json rename to one_off_projects/landsat/recheck_landsat_labels/phase2_config.json diff --git a/landsat/recheck_landsat_labels/phase2_config.yaml b/one_off_projects/landsat/recheck_landsat_labels/phase2_config.yaml similarity index 100% rename from landsat/recheck_landsat_labels/phase2_config.yaml rename to one_off_projects/landsat/recheck_landsat_labels/phase2_config.yaml diff --git a/landsat/recheck_landsat_labels/phase2_get_3000.py b/one_off_projects/landsat/recheck_landsat_labels/phase2_get_3000.py similarity index 100% rename from landsat/recheck_landsat_labels/phase2_get_3000.py rename to one_off_projects/landsat/recheck_landsat_labels/phase2_get_3000.py diff --git a/landsat/recheck_landsat_labels/phase2_index.html b/one_off_projects/landsat/recheck_landsat_labels/phase2_index.html similarity index 100% rename from landsat/recheck_landsat_labels/phase2_index.html rename to one_off_projects/landsat/recheck_landsat_labels/phase2_index.html diff --git a/landsat/recheck_landsat_labels/phase2_make_overview_windows.py b/one_off_projects/landsat/recheck_landsat_labels/phase2_make_overview_windows.py similarity index 100% rename from landsat/recheck_landsat_labels/phase2_make_overview_windows.py rename to one_off_projects/landsat/recheck_landsat_labels/phase2_make_overview_windows.py diff --git a/landsat/recheck_landsat_labels/phase2_overview_rgb.py b/one_off_projects/landsat/recheck_landsat_labels/phase2_overview_rgb.py similarity index 100% rename from landsat/recheck_landsat_labels/phase2_overview_rgb.py rename to one_off_projects/landsat/recheck_landsat_labels/phase2_overview_rgb.py diff --git a/landsat/recheck_landsat_labels/phase2_pansharpen.py b/one_off_projects/landsat/recheck_landsat_labels/phase2_pansharpen.py similarity index 100% rename from landsat/recheck_landsat_labels/phase2_pansharpen.py rename to one_off_projects/landsat/recheck_landsat_labels/phase2_pansharpen.py diff --git a/landsat/recheck_landsat_labels/phase2_server.py b/one_off_projects/landsat/recheck_landsat_labels/phase2_server.py similarity index 100% rename from landsat/recheck_landsat_labels/phase2_server.py rename to one_off_projects/landsat/recheck_landsat_labels/phase2_server.py diff --git a/landsat/recheck_landsat_labels/phase3_get_750.py b/one_off_projects/landsat/recheck_landsat_labels/phase3_get_750.py similarity index 100% rename from landsat/recheck_landsat_labels/phase3_get_750.py rename to one_off_projects/landsat/recheck_landsat_labels/phase3_get_750.py diff --git a/landsat/recheck_landsat_labels/phase3a_selected.csv b/one_off_projects/landsat/recheck_landsat_labels/phase3a_selected.csv similarity index 100% rename from landsat/recheck_landsat_labels/phase3a_selected.csv rename to one_off_projects/landsat/recheck_landsat_labels/phase3a_selected.csv diff --git a/rslp/landsat_vessels/config.py b/rslp/landsat_vessels/config.py index 96a5b613..510eb82a 100644 --- a/rslp/landsat_vessels/config.py +++ b/rslp/landsat_vessels/config.py @@ -6,13 +6,9 @@ LANDSAT_LAYER_NAME = "landsat" LANDSAT_RESOLUTION = 15 -# Detector config +# Data config LOCAL_FILES_DATASET_CONFIG = "data/landsat_vessels/predict_dataset_config.json" AWS_DATASET_CONFIG = "data/landsat_vessels/predict_dataset_config_aws.json" -DETECT_MODEL_CONFIG = "data/landsat_vessels/config.yaml" -DETECT_MODEL_EVAL_CONFIG = ( - "data/landsat_vessels/config_eval.yaml" # config for evaluation -) # Extract Landsat bands from local config file with open(LOCAL_FILES_DATASET_CONFIG) as f: @@ -21,8 +17,9 @@ band["bands"][0] for band in json_data["layers"][LANDSAT_LAYER_NAME]["band_sets"] ] -# Classifier config -CLASSIFY_MODEL_CONFIG = "landsat/recheck_landsat_labels/phase123_config.yaml" +# Model config +DETECT_MODEL_CONFIG = "data/landsat_vessels/config_detector.yaml" +CLASSIFY_MODEL_CONFIG = "data/landsat_vessels/config_classifier.yaml" CLASSIFY_WINDOW_SIZE = 64 # Filter config