Skip to content

Commit

Permalink
Merge pull request #19 from hi-paris/develop
Browse files Browse the repository at this point in the history
Deploy last changes (denoiser files refactoring)
  • Loading branch information
brash6 authored Mar 26, 2024
2 parents f42a021 + 0f97c3e commit 89cbb91
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 182 deletions.
73 changes: 34 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,48 +29,34 @@ pip install deepdespeckling

To despeckle SAR images using MERLIN, images need to be in `.cos` or `.npy` format.

In order to get the right model, the `model_name` has to be specified in the `get_denoiser()` and the `get_model_weights_path()` functions calls.
In order to get the right model, the `model_name` has to be specified when building a `MerlinDenoiser`.

This `model_name` can either be :
- `"spotlight"` for SAR images retrieved with spotlight mode
- `"stripmap"` for SAR images retrieved with stripmap mode

During the preprocessing steps of the noisy image for MERLIN, the real and the imaginary parts are <strong>"symetrised"</strong> (to match the theoretical assumptions of MERLIN). To skip this step, you can set the `symetrise` parameter to `False`


### Despeckle one image with MERLIN

```python
from deepdespeckling.utils.load_cosar import cos2mat
from deepdespeckling.utils.constants import PATCH_SIZE, STRIDE_SIZE
from deepdespeckling.despeckling import get_model_weights_path, get_denoiser
from deepdespeckling.merlin.merlin_denoiser import MerlinDenoiser

# Path to one image (cos or npy file), can also be a folder of several images
# Path to one image (cos or npy file)
image_path="path/to/cosar/image"
# Model name, can be "spotlight" or "stripmap"
model_name = "spotlight"
symetrise = True

image = cos2mat(image_path).astype(np.float32)
# Get the right model
denoiser = get_denoiser(model_name=model_name)
model_weights_path = get_model_weights_path(model_name=model_name)

denoised_image = denoiser.denoise_image(
image, model_weights_path, patch_size=PATCH_SIZE, stride_size=STRIDE_SIZE)
```

#### Symetrise parameter

During the preprocessing steps of the noisy image for MERLIN, the real and the imaginary parts are <strong>"symetrised"</strong> (to match the theoretical assumptions of MERLIN).
However, we added a parameter to make this step optional (if one wants to implement their own symetrisation for example).
To skip this step, just call :

```python
denoised_image = denoiser.denoise_image(
image, model_weights_path, patch_size=PATCH_SIZE, stride_size=STRIDE_SIZE,
symetrise=False)
denoiser = MerlinDenoiser(model_name=model_name, symetrise=symetrise)
denoised_image = denoiser.denoise_image(image, patch_size=PATCH_SIZE, stride_size=STRIDE_SIZE)
```

This parameter can also be added to the despeckling functions presented in the next section

### Despeckle a set of images using MERLIN

For each of this method, you can choose between 3 different functions to despeckle a set of SAR images contained in a folder :
Expand All @@ -84,12 +70,12 @@ For each of this method, you can choose between 3 different functions to despeck
```python
from deepdespeckling.despeckling import despeckle

# Path to one image (cos or npy file), can also be a folder of several images
# Path to a folder of several images (cos or npy files)
image_path="path/to/cosar/image"
# Folder where results are stored
destination_directory="path/where/to/save/results"

denoised_image = despeckle(image_path, destination_directory, model_name="spotlight")
despeckle(image_path, destination_directory, model_name="spotlight", symetrise=True)
```
Noisy image | Denoised image
:----------------------:|:-------------------------:
Expand All @@ -100,13 +86,14 @@ Noisy image | Denoised image
```python
from deepdespeckling.despeckling import despeckle_from_coordinates

# Path to one image (cos or npy file), can also be a folder of several images
# Path to a folder of several images image (cos or npy files)
image_path="path/to/cosar/image"
# Folder where results are stored
destination_directory="path/where/to/save/results"
# Coordinates of the subparts of the images to be despeckled
coordinates_dictionnary = {'x_start':2600,'y_start':1000,'x_end':3000,'y_end':1200}

denoised_image = despeckle_from_coordinates(image_path, coordinates_dict, destination_directory, model_name="spotlight")
despeckle_from_coordinates(image_path, coordinates_dict, destination_directory, model_name="spotlight", symetrise=True)
```

Noisy image | Denoised image
Expand All @@ -118,13 +105,13 @@ Noisy image | Denoised image
```python
from deepdespeckling.merlin.inference.despeckling import despeckle_from_crop

# Path to one image (cos or npy file), can also be a folder of several images
# Path to a folder of several images image (cos or npy files)
image_path="path/to/cosar/image"
# Folder where results are stored
destination_directory="path/where/to/save/results"
fixed = True "(it will crop a 256*256 image from the position of your click)" or False "(you will draw free-handly the area of your interest)"

denoised_image = despeckle_from_crop(image_path, destination_directory, model_name="spotlight", fixed=False)
despeckle_from_crop(image_path, destination_directory, model_name="spotlight", fixed=False, symetrise=True)
```

* The cropping tool: Just select an area and press "q" when you are satisfied with the crop !
Expand All @@ -143,29 +130,22 @@ Noisy cropped image | Denoised cropped image

To despeckle SAR images using SAR2SAR, images need to be in `.tiff` or `.npy` format.

In order to get the right model, the `model_name` has to be specified in the `get_denoiser()` and the `get_model_weights_path()` functions calls.

The `model_name` has therefore to be set to `"sar2sar"`. Then, <strong>the despeckling functions (`despeckle, despeckle_from_coordinates, despeckle_from_crop`) work the same as with MERLIN.</strong>


### Despeckle one image with SAR2SAR

```python
from deepdespeckling.utils.load_cosar import cos2mat
from deepdespeckling.utils.constants import PATCH_SIZE, STRIDE_SIZE
from deepdespeckling.despeckling import get_model_weights_path, get_denoiser
from deepdespeckling.sar2sar.sar2sar_denoiser import Sar2SarDenoiser

# Path to one image (tiff or npy file), can also be a folder of several images
# Path to one image (tiff or npy file)
image_path="path/to/cosar/image"
# Model name, can be "spotlight" or "stripmap"
model_name = "sar2sar"

# Works exactly the same as with MERLIN
image = cos2mat(image_path).astype(np.float32)
# Get the right model
denoiser = get_denoiser(model_name=model_name)
model_weights_path = get_model_weights_path(model_name=model_name)

# Denoise the image with SAR2SAR
denoiser = Sar2SarDenoiser()
denoised_image = denoiser.denoise_image(
image, model_weights_path, patch_size=PATCH_SIZE, stride_size=STRIDE_SIZE)
```
Expand All @@ -178,6 +158,21 @@ Noisy image | Denoised image
![](img/entire/sar2sar_noisy.png) | ![](img/entire/sar2sar_denoised.png)


### Despeckle a set of images using SAR2SAR

The despeckling functions (`despeckle, despeckle_from_coordinates, despeckle_from_crop`) work the same as with MERLIN. To use SAR2SAR, the `model_name` parameter has to be set to `"sar2sar"`

For example, to despeckle a set of fullsize images:
```python
from deepdespeckling.despeckling import despeckle

# Path to a folder of several images (tiff or npy files)
image_path="path/to/cosar/image"
# Folder where results are stored
destination_directory="path/where/to/save/results"

despeckle(image_path, destination_directory, model_name="sar2sar")
```

## Authors

Expand Down
89 changes: 3 additions & 86 deletions deepdespeckling/denoiser.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import logging
import torch
import numpy as np
from pathlib import Path
from glob import glob

from deepdespeckling.model import Model
from deepdespeckling.utils.utils import load_sar_image


class Denoiser():
class Denoiser:
"""Class to share parameters beyond denoising functions
"""

Expand All @@ -31,23 +26,6 @@ def get_device(self) -> str:

return device

def load_model(self, weights_path: str, patch_size: int) -> Model:
"""Load model with given weights
Args:
weights_path (str): path to weights
patch_size (int): patch size
Returns:
model (Model): model loaded with stored weights
"""
model = Model(torch.device(self.device),
height=patch_size, width=patch_size)
model.load_state_dict(torch.load(
weights_path, map_location=torch.device("cpu")))

return model

def initialize_axis_range(self, image_axis_dim: int, patch_size: int, stride_size: int) -> list:
"""Initialize the convolution range for x or y axis
Expand Down Expand Up @@ -82,66 +60,5 @@ def preprocess_denoised_image(self):
def denoise_image(self):
raise NotImplementedError

def build_dynamic_parameters_dict(self, noisy_image: np.array, weights_path: str, patch_size: int, stride_size: int, symetrise: bool = True) -> dict:
"""build a dictionary of parameters for denoise_images function excluding symetrise parameter
if using Sar2SarDenoiser
Args:
noisy_image (numpy array): numpy array containing the noisy image to despeckle
weights_path (str): path to the pth model file
patch_size (int): size of the patch of the convolution
stride_size (int): number of pixels between one convolution to the next
symetrise (bool, optional): _description_. Defaults to True.
Returns:
dict: dictionary of parameters for denoise_images function
"""

dict_params = {
"noisy_image": noisy_image,
"weights_path": weights_path,
"patch_size": patch_size,
"stride_size": stride_size,
"symetrise": symetrise
}

if self.__class__.__name__ == "Sar2SarDenoiser":
del dict_params['symetrise']

return dict_params

def denoise_images(self, images_to_denoise_path: list, weights_path: str, save_dir: str, patch_size: int,
stride_size: int, symetrise: bool = True):
"""Iterate over a directory of coSAR images and store the denoised images in a directory
Args:
images_to_denoise_path (list): a list of paths of npy images to denoise
weights_path (str): path to the pth file containing the weights of the model
save_dir (str): repository to save sar images, real images and noisy images
patch_size (int): size of the patch of the convolution
stride_size (int): number of pixels between one convolution to the next
symetrise (bool) : if using MerlinDenoiser, if True, will symetrise the real and
imaginary parts of the noisy image. Defaults to True
"""

images_to_denoise_paths = glob((images_to_denoise_path + '/*.npy'))

assert len(images_to_denoise_paths) != 0, 'No data!'

logging.info(f"Starting denoising images in {images_to_denoise_paths}")

for idx in range(len(images_to_denoise_paths)):
image_name = Path(images_to_denoise_paths[idx]).name
logging.info(
f"Despeckling {image_name}")

noisy_image_idx = load_sar_image(
images_to_denoise_paths[idx]).astype(np.float32)
dict_params = self.build_dynamic_parameters_dict(
noisy_image_idx, weights_path, patch_size, stride_size, symetrise)
despeckled_images = self.denoise_image(**dict_params)

logging.info(
f"Saving despeckled images in {save_dir}")
self.save_despeckled_images(
despeckled_images, image_name, save_dir)
def denoise_images(self):
raise NotImplementedError
56 changes: 13 additions & 43 deletions deepdespeckling/despeckling.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,46 +10,22 @@
create_empty_folder_in_directory, preprocess_and_store_sar_images)


this_dir, this_filename = os.path.split(__file__)
logging.basicConfig(level=logging.INFO)


def get_model_weights_path(model_name: str) -> str:
"""Get model weights path from model name
Args:
model_name (str): model name, either "spotlight" or "stripmap" to select MERLIN model on the
right cosar image format or "sar2sar" for SAR2SAR model
Returns:
model_weights_path (str): the path of the weights of the specified model
"""
if model_name == "spotlight":
model_weights_path = os.path.join(
this_dir, "merlin/saved_model", "spotlight.pth")
elif model_name == "stripmap":
model_weights_path = os.path.join(
this_dir, "merlin/saved_model", "stripmap.pth")
elif model_name == "sar2sar":
model_weights_path = os.path.join(
this_dir, "sar2sar/saved_model", "sar2sar.pth")
else:
raise ValueError("The model name doesn't refer to an existing model ")

return model_weights_path


def get_denoiser(model_name: str) -> Denoiser:
def get_denoiser(model_name: str, symetrise: bool = True) -> Denoiser:
"""Get the right denoiser object from the model name
Args:
model_name (str): model name to be use for despeckling
symetrise (bool) : if using spotlight or stripmap model, if True, will symetrise the real and
imaginary parts of the noisy image. Defaults to True
Returns:
denoiser (Denoiser): the right denoiser, Sar2SarDenoiser or MerlinDenoiser
"""
if model_name in ["spotlight", "stripmap"]:
denoiser = MerlinDenoiser()
denoiser = MerlinDenoiser(model_name=model_name, symetrise=symetrise)
elif model_name == "sar2sar":
denoiser = Sar2SarDenoiser()
else:
Expand All @@ -76,8 +52,6 @@ def despeckle(sar_images_path: str, destination_directory_path: str, model_name:
logging.info(
f"""Despeckling entire images using {model_name} weights""")

model_weights_path = get_model_weights_path(model_name=model_name)

processed_images_path = create_empty_folder_in_directory(destination_directory_path=destination_directory_path,
folder_name="processed_images")
preprocess_and_store_sar_images(
Expand All @@ -86,9 +60,9 @@ def despeckle(sar_images_path: str, destination_directory_path: str, model_name:
logging.info(
f"Starting inference.. Collecting data from {sar_images_path} and storing test results in {destination_directory_path}")

denoiser = get_denoiser(model_name=model_name)
denoiser.denoise_images(images_to_denoise_path=processed_images_path, weights_path=model_weights_path, save_dir=destination_directory_path,
patch_size=patch_size, stride_size=stride_size, symetrise=symetrise)
denoiser = get_denoiser(model_name=model_name, symetrise=symetrise)
denoiser.denoise_images(images_to_denoise_path=processed_images_path, save_dir=destination_directory_path,
patch_size=patch_size, stride_size=stride_size)


def despeckle_from_coordinates(sar_images_path: str, coordinates_dict: dict, destination_directory_path: str, model_name: str = "spotlight",
Expand All @@ -110,8 +84,6 @@ def despeckle_from_coordinates(sar_images_path: str, coordinates_dict: dict, des
logging.info(
f"""Despeckling images from coordinates using {model_name} weights""")

model_weights_path = get_model_weights_path(model_name=model_name)

processed_images_path = create_empty_folder_in_directory(destination_directory_path=destination_directory_path,
folder_name="processed_images")
preprocess_and_store_sar_images_from_coordinates(sar_images_path=sar_images_path, processed_images_path=processed_images_path,
Expand All @@ -120,9 +92,9 @@ def despeckle_from_coordinates(sar_images_path: str, coordinates_dict: dict, des
logging.info(
f"Starting inference.. Collecting data from {sar_images_path} and storing test results in {destination_directory_path}")

denoiser = get_denoiser(model_name=model_name)
denoiser.denoise_images(images_to_denoise_path=processed_images_path, weights_path=model_weights_path, save_dir=destination_directory_path,
patch_size=patch_size, stride_size=stride_size, symetrise=symetrise)
denoiser = get_denoiser(model_name=model_name, symetrise=symetrise)
denoiser.denoise_images(images_to_denoise_path=processed_images_path, save_dir=destination_directory_path,
patch_size=patch_size, stride_size=stride_size)


def despeckle_from_crop(sar_images_path: str, destination_directory_path: str, model_name: str = "spotlight",
Expand All @@ -144,8 +116,6 @@ def despeckle_from_crop(sar_images_path: str, destination_directory_path: str, m
logging.info(
f"""Cropping and despeckling images using {model_name} weights""")

model_weights_path = get_model_weights_path(model_name=model_name)

processed_images_path = create_empty_folder_in_directory(destination_directory_path=destination_directory_path,
folder_name="processed_images")

Expand All @@ -169,6 +139,6 @@ def despeckle_from_crop(sar_images_path: str, destination_directory_path: str, m
logging.info(
f"Starting inference.. Collecting data from {sar_images_path} and storing results in {destination_directory_path}")

denoiser = get_denoiser(model_name=model_name)
denoiser.denoise_images(images_to_denoise_path=processed_images_path, weights_path=model_weights_path, save_dir=destination_directory_path,
patch_size=patch_size, stride_size=stride_size, symetrise=symetrise)
denoiser = get_denoiser(model_name=model_name, symetrise=symetrise)
denoiser.denoise_images(images_to_denoise_path=processed_images_path, save_dir=destination_directory_path,
patch_size=patch_size, stride_size=stride_size)
Loading

0 comments on commit 89cbb91

Please sign in to comment.