fepegar / torchio

Medical imaging toolkit for deep learning
https://torchio.org
Apache License 2.0
2.08k stars 240 forks source link

Implement missing restore feature for RandomGhosting #1211

Closed fepegar closed 1 week ago

fepegar commented 2 months ago

Fixes #1196.

Description

As reported by @sravan953, the restore argument in RandomGhosting is unused. This PR addresses this issue.

Checklist

codecov[bot] commented 2 months ago

Codecov Report

Attention: Patch coverage is 80.48780% with 8 lines in your changes missing coverage. Please review.

Project coverage is 84.64%. Comparing base (7b7a732) to head (9b8f119). Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...ansforms/augmentation/intensity/random_ghosting.py 80.48% 8 Missing :warning:
Additional details and impacted files ```diff @@ Coverage Diff @@ ## main #1211 +/- ## ========================================== - Coverage 84.77% 84.64% -0.13% ========================================== Files 92 92 Lines 6023 6026 +3 ========================================== - Hits 5106 5101 -5 - Misses 917 925 +8 ```

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

fepegar commented 2 months ago

@romainVala @sravan953 I've implemented the feature as expected, but I'm not sure it's very useful. What do you think? Here's an example:

from itertools import product

import numpy as np
import torchvision.transforms as T
from PIL import Image
from torchvision.utils import make_grid

import torchio as tio

t1 = tio.datasets.FPG().t1
t1 = tio.ToCanonical()(t1)

restores = None, 0.1, 0.25, 0.5, 0.75
intensities = 0.25, 0.5, 0.75, 1

images = {}
t1_slice = t1.data[0, :, :, t1.shape[2] // 2].float()
min_data, max_data = t1_slice.data.min(), t1_slice.data.max()
for intensity, restore in product(intensities, restores):
    print(f'intensity={intensity}, restore={restore}')
    if restore is None:
        restore_ = None
    else:
        restore_ = restore, restore
    transform = tio.Ghosting(
        num_ghosts=2,
        intensity=intensity,
        restore=restore,
        axis=1,
    )
    transformed = transform(t1)
    t1_slice = transformed.data[0, :, :, transformed.shape[2] // 2].float()
    t1_slice = (t1_slice - min_data) / (max_data - min_data) * 255
    # t1_slice = (t1_slice - t1_slice.min()) / (t1_slice.max() - t1_slice.min()) * 255
    array = t1_slice.clamp(0, 255).numpy().astype('uint8')
    array = np.rot90(array)
    image = Image.fromarray(array)
    images[intensity, restore] = image

images_list = list(images.values())
images_list = [T.ToTensor()(image) for image in images_list]
grid = make_grid(images_list, nrow=len(restores))
T.ToPILImage()(grid)

image

romainVala commented 2 months ago

Hi thanks Fernando for a quick fix

(and nice plot !)

It is not clear to me how "physical" in this implementation of MRI ghosting effect (In EPI this is the phase difference from one line to an other that cause the ghosting)

So difficult to say if (from a physical point of view) one should keep this parameter

But from a visual point of view, this restore parameter make sense (it remove the low frequency part of the ghost, ) and so it produce more variability of the artefact

So I think it is good to keep !

many thanks

fepegar commented 1 week ago

@allcontributors please add @sravan953 for bug

allcontributors[bot] commented 1 week ago

@fepegar

I've put up a pull request to add @sravan953! :tada: