SAR-DeepSeg is an end-to-end pipeline for semantic segmentation of Synthetic Aperture Radar (SAR) images. It employs deep learning models to process and segment SAR imagery, facilitating accurate interpretation for applications such as land cover segmentation, environmental monitoring, and disaster response, enhancing geospatial analysis.
The pipeline offers a robust solution for handling image data of arbitrary sizes by employing a systematic approach of patching images into fixed-size patches. This not only enables efficient data augmentation but also facilitates training models on very high-resolution data that would otherwise exceed GPU memory limitations.
The process of training begins with the train.py script, where paths are created and essential utilities from utils.py are invoked. The data loading and preparation steps are handled in dataset.py, where the dataset is generated, split, and train, valid and test CSV files are saved. The image patching process, including saving co-ordinate of patches to train, valid, and json files is executed, followed by data augmentation and transformation through the Augment class. The dataset is then prepared using MyDataset class, which handles data fetching and transformation.
Metrics calculation, focal loss computation, and model initialization are handled in separate modules respectively metrics.py, loss.py, model.py, ensuring modularity and ease of management. Callback selection, including learning rate scheduling and validation visualization, is orchestrated through SelectCallbacks class.
For evaluation, two distinct scenario are provided in test.py. In the first scenario (evaluation = False), the evaluation dataset is prepared similarly to the training dataset, and predictions are made and displayed for analysis. In the second scenario (evaluation = True), additional steps for generating evaluation CSVs named eval_csv_gen is called
Throughout the pipeline, emphasis is placed on modularity, allowing for easy integration of new components or modifications. Understanding the intricacies of this pipeline is paramount for leveraging its capabilities effectively. For a deeper dive into its workings, referencing the FAPNET paper is recommended, providing insights into the architecture and rationale behind its design.
To ensure proper organization of input features, adhere to the following guidelines:
-
Separate Files for Different Input Channels: Each channel of input feature should be stored in separate files.
-
Unique Filename for Each Channel: Ensure that filenames are unique for all channels, except for the last portion of the filename.
-
Example Naming Convention:
- For instance, if the name of the VV channel is
rice_01_vv.tif, then the corresponding VH and DEM files should be named as follows:- VH:
rice_01_vh.tif - DEM:
rice_01_nasadem.tif
- VH:
- The mask filename should retain the first unique portion, such as
rice_01.tifin the example provided.
- For instance, if the name of the VV channel is
-
Key Consideration:
- The first part of the filename for all channels should be unique, while the last part should vary to denote different channels. But the mask name should be the first unique portion of the name.
To ensure proper organization of input features, adhere to the following guidelines:
-
Separate Files for Different Input Channels: Each channel of input feature should be stored in separate files.
-
Unique Filename for Each Channel: Ensure that filenames are unique for all channels, except for the last portion of the filename.
-
Example Naming Convention:
- For instance, if the name of the VV channel is
rice_01_vv.tif, then the corresponding VH and DEM files should be named as follows:- VH:
rice_01_vh.tif - DEM:
rice_01_nasadem.tif
- VH:
- The mask filename should retain the first unique portion, such as
rice_01.tifin the example provided.
- For instance, if the name of the VV channel is
-
Key Consideration:
- The first part of the filename for all channels should be unique, while the last part should vary to denote different channels. But the mask name should be the first unique portion of the name.
Our current pipeline supports semantic segmentation for both binary and multi-class tasks. To configure the pipeline for your desired number of classes, simply adjust the num_classes variable in the config.py file. No modifications are required in the model.py file. This straightforward configuration process allows for seamless adaptation to different classification requirements without the need for extensive code changes.
First clone the github repo in your local or server machine by following:
git clone https://github.com/manik-500/SAR-DeepSeg.git
Change the working directory to project root directory. Use Conda/Pip to create a new environment and install dependency from requirement.txt file. The following command will install the packages according to the configuration file requirement.txt.
pip install -r requirements.txt
Keep the above mention dataset in the data folder that give you following structure.
data
file_01_chip0_vv.tif
file_01_chip0_vh.tif
file_01_chip0_nasadem.tif
file_01_chip0.tif
This experiment utilizes the dataset as it is. The image size must follow
python train.py --root_dir YOUR_ROOT_DIR \
--model_name unet \
--epochs 10 \
--batch_size 3 \
--experiment cfr
python train.py --root_dir /deepseg/ \
--model_name unet \
--epochs 10 \
--batch_size 3 \
--experiment cfr
We balance the dataset biasness towards non-water class in this experiment.
python train.py --root_dir YOUR_ROOT_DIR \
--model_name unet \
--epochs 10 \
--batch_size 3 \
--experiment cfr_cb \
python train.py --root_dir /deepseg/ \
--model_name unet \
--epochs 10 \
--batch_size 3 \
--experiment cfr_cb
In this experiment we take all the patch images for each chip. Data preprocessing can handle any image shape and convert it to a specific patch size.
python train.py --root_dir YOUR_ROOT_DIR \
--model_name unet \
--epochs 10 \
--batch_size 3 \
--experiment phr \
python train.py --root_dir /deepseg/ \
--model_name unet \
--epochs 10 \
--batch_size 3 \
--experiment phr
In this experiment we take a threshold value (19%) of water class and remove the patch images for each chip that are less than threshold value.
python train.py --root_dir YOUR_ROOT_DIR \
--model_name unet \
--epochs 10 \
--batch_size 3 \
--experiment phr_cb
python train.py --root_dir /deepseg/ \
--model_name unet \
--epochs 10 \
--batch_size 3 \
--experiment phr_cb
- Patchify Half Resolution with Class Balance Weight (PHR-CBW):
To address data imbalance problems, one can utilize the following method:
If encountering a scenario where there are only two classes, with the first class representing 60% of the dataset and the second class comprising 40%, the imbalance can be rectified by setting weights= True and specifying balance_weights = [4,6] in the config.py file.
However, in cases where there are three classes, and one of them is considered a boundary that should be disregarded, the weight of the corresponding class must be set to 0. For instance, in the command line, this would be denoted as balance_weights = [4,6,0] in the config.py file.
python train.py --root_dir YOUR_ROOT_DIR \
--model_name unet \
--epochs 10 \
--batch_size 3 \
--experiment phr_cb
python train.py --root_dir /deepseg/ \
--model_name unet \
--epochs 10 \
--batch_size 3 \
--experiment phr_cb \
In this project, the behavior of the model training process is determined by certain variables in the config.py file. Here's how they influence the training approach:
- If
transfer_lrvariable is set toTrueand aload_model_nameis provided inconfig.py, the model will undergo transfer learning.
- If
transfer_lris set toFalsebut aload_model_nameis provided inconfig.py, the model will undergo fine-tuning.
- If
transfer_lris set toFalseand noload_model_nameis provided inconfig.py, the model will be trained from scratch.
Run following model for evaluating train model on test dataset.
python test.py \
--dataset_dir YOUR_ROOT_DIR/data/ \
--model_name unet \
--load_model_name MODEL_CHECKPOINT_NAME \
--experiment cfr
python test.py \
--dataset_dir /deepseg/data/ \
--model_name unet \
--load_model_name my_model.hdf5 \
--experiment cfr
python test.py \
--dataset_dir YOUR_ROOT_DIR/data/ \
--model_name unet \
--load_model_name my_model.hdf5 \
--experiment phr
python test.py \
--dataset_dir /deepseg/data/ \
--model_name unet \
--load_model_name my_model.hdf5 \
--experiment phr
If you have the images without mask, we need to do data pre-preprocessing before passing to the model checkpoint. In that case, run the following command to evaluate the model without any mask.
- You can check the prediction of test images inside the
logs > prediction > YOUR_MODELNAME > eval > experiment.
python project/test.py \
--dataset_dir YOUR_IMAGE_DIR/ \
--model_name fapnet \
--load_model_name MODEL_CHECKPOINT_NAME \
--experiment road_seg \
--gpu YOUR_GPU_NUMBER \
--evaluation True \
python project/test.py \
--dataset_dir /deepseg/data/ \
--model_name fapnet \
--load_model_name my_model.hdf5 \
--experiment road_seg \
--gpu 0 \
--evaluation True \
To ensure compatibility with our pipeline, we need to modify the rename_files function defined in the visualization.ipynb notebook. This function currently identifies file names starting with 'VV', 'VH', or 'DEM' and adjusts them to fit our required format for generating CSV files. However, if the dataset names are formatted differently but still start with 'VV', 'VH', or 'DEM', we must update the rename_files function accordingly.
To achieve this, we can enhance the rename_files function to recognize various dataset name formats and adapt them to our specified format. This may involve adding conditional statements within the function to handle different naming conventions.
By refining the rename_files function in this manner, we can ensure that SAR files are renamed appropriately to seamlessly integrate with our pipeline, facilitating the generation of CSV files for training, validation, and testing datasets.
During validation, plots can be generated by configuring the val_plot_epoch variable within the config.py module. This variable determines the frequency of validation during training. For instance, setting val_plot_epoch to 3 means validation will be conducted after every 3 epochs. The resulting validation plots will be saved in the following folder structure:
root/logs/prediction/model_name/validation/experiment
In this structure, root refers to the root directory of the project, model_name is the name of the model being trained, and experiment denotes the experimental configurations defined within the config.py module.
To plot during testing, follow these steps:
-
Execute the command in the terminal:
python test.py --evaluation FalseThis command generates predictions on the test dataset without evaluation.
-
After executing the command, the plots will be stored in the following directory:
root_dir/logs/prediction/model_name/test/experimentEnsure to replace
root_dir,model_name, andexperimentwith the appropriate directory names based on your project setup.
To plot during evaluation, you need to follow these steps:
-
Execute Evaluation Script: Initiate evaluation on the
test.pyscript by running the following command in the terminal:test.py --evaluation True -
Generate Predictions: Upon execution, predictions will be generated using the evaluation dataset.
-
View Resulting Plots: The resulting plots will be automatically saved in the designated folder structure:
root_dir/logs/prediction/model_name/eval/experimentHere, replace
root_dirwith the root directory of your project,model_namewith the name of your model, andexperimentwith the specific experiment or evaluation run.
To select different channel types, follow these steps:
-
Open
config.py: Navigate to theconfig.pyfile in your project directory. -
Locate
channel_typevariable: Inside theconfig.pyfile, find the variable named "channel_type". -
Update channel names: Modify the
channel_typevariable to include the desired channel names as strings within a list. For example:channel_type = ["vv", "vh", "nasadem", "new_channel"]
You can include any number of channel names within this list as strings.
-
Update label normalization: If you're adding a new channel type to the
channel_typelist, ensure to update thelabel_normvariable. Thelabel_normvariable contains mean and standard deviation values for each channel type. Add the mean and standard deviation values for the new channel type in thelabel_normdictionary. For example:label_norm = { 'vv': ["_vv.tif", -12.204613877277495, 4.0422273221629785], 'vh': ["_vh.tif", -18.96970667660666, 4.367189151420688], 'nasadem': ["_nasadem.tif", 812.64422736, 425.41858324129265], 'new_channel': ["_new_channel.tif", new_channel_mean, new_channel_std] # Add mean and std for the new channel type }
Ensure to replace
new_channel_meanandnew_channel_stdwith the actual mean and standard deviation values for the new channel. -
Save changes: After updating the
config.pyfile, save the changes.
By following these steps, you can select different channel types by modifying the channel_type variable and ensuring proper label normalization in the label_norm variable.
To visualize the dataset, we must execute the display_all function as defined in the visualization.ipynb notebook. This function necessitates the CSV file that corresponds to the dataset we intend to visualize, along with a name parameter to establish a folder where the figure's visualization will be stored. For instance, calling
display_all(data=train_df, name="train")
accomplishes this task.
If we run train.py then the the following functions will be executed in the mentioned flow. If we run train.py then the the following functions will be executed in the mentioned flow.
create_paths(test=False)- [utils.py]get_train_val_dataloader()data_csv_gen()data_split()save_csv()
patch_images()save_patch_idx()class_percentage_check()
write_json()
Augment()- Classcall()read_img()
MyDataset()- Class__getitem__()read_img()transform_data()
get_random_data()read_img()transform_data()
get_metrics()- [metrics.py]MyMeanIOU()- Class
focal_loss()- [loss.py]get_model_transfer_lr(model, num_classes)- [model.py]get_model()- [model.py]models- call all model functions
SelectCallbacks()- Class - [utils.py]__init__()lr_scheduler()on_epoch_end()val_show_predictions()read_img()transform_data()display()
get_callbacks()
If we run test.py with evaluation = False, then the the following functions will be executed in the mentioned flow.
If we run test.py with evaluation = False, then the the following functions will be executed in the mentioned flow.
create_paths()get_test_dataloader()data_csv_gen()data_split()save_csv()
patch_images()save_patch_idx()class_percentage_check()
write_json()
MyDataset()- Class__getitem__()read_img()transform_data()
get_random_data()read_img()transform_data()
test_eval_show_predictions()*read_img()transform_data()display()
get_metrics()
If we run test.py with evaluation = True, then the the following functions will be executed in the mentioned flow.
If we run test.py with evaluation = True, then the the following functions will be executed in the mentioned flow.
create_paths()get_test_dataloader()eval_csv_gen()*save_csv()
patch_images()save_patch_idx()class_percentage_check()
write_json()
MyDataset()- Class__getitem__()read_img()transform_data()
get_random_data()read_img()transform_data()
test_eval_show_predictions()*read_img()transform_data()display_label()*display()
get_metrics()