From 17ddb86f87f64b6a2a835ad4c69de24079646e03 Mon Sep 17 00:00:00 2001 From: valabregue Date: Tue, 5 Nov 2024 16:11:43 +0100 Subject: [PATCH 1/4] class definition of LazyImage (and LazyScalarImage LazyLabelMap ) --- src/torchio/data/image.py | 74 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/src/torchio/data/image.py b/src/torchio/data/image.py index a48b21d67..b4e5510ee 100644 --- a/src/torchio/data/image.py +++ b/src/torchio/data/image.py @@ -920,3 +920,77 @@ def count_labels(self) -> Dict[int, int]: counter = Counter(values_list) counts = {label: counter[label] for label in sorted(counter)} return counts + + +class LazyImage(Image): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def load(self): + + if self._is_multipath(): + message = (f'No multiple paths for LazyImage') + RuntimeError(message) + + tensor, affine = self.read_and_check(self.path) + self.set_data(tensor) + self.affine = affine + self._loaded = True + + def _parse_tensor( + self, + tensor: Optional[TypeData], + none_ok: bool = True, + ) -> Optional[torch.Tensor]: + + if tensor is None: + if none_ok: + return None + else: + raise RuntimeError('Input tensor cannot be None') + + ndim = tensor.ndim + if ndim != 4: + raise ValueError(f'Input tensor must be 4D, but it is {ndim}D') + + return tensor + + @staticmethod + def _parse_tensor_shape(tensor: torch.Tensor) -> TypeData: + # here we do not want to maniulate the whole data as tensor, to avoid loading + # so we skip check here, so we can not repare bad shape ... + # _parse_tensor, is already checking if ndim==4 + return tensor + + def __repr__(self): + # alternative would be to modify the __repr__ function of parent class (image + # in order to avoid the call self.data.type() (which is only defined for tensor) + properties = [] + properties.extend( + [ + f'shape: {self.shape}', + f'spacing: {self.get_spacing_string()}', + f'orientation: {"".join(self.orientation)}+', + ] + ) + if self._loaded: + # instead of adding dtype and memory, just print the data + properties.append(f'dtype: {self.data}') + else: + properties.append(f'path: "{self.path}"') + + properties = '; '.join(properties) + string = f'{self.__class__.__name__}({properties})' + return string + + +class LazyScalarImage(LazyImage, ScalarImage): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + +class LazyLabelMap(LazyImage, LabelMap): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) From 2fe3eb2e6eca77e16e6cd8778d031c6617805d8d Mon Sep 17 00:00:00 2001 From: valabregue Date: Tue, 5 Nov 2024 16:11:51 +0100 Subject: [PATCH 2/4] change init to add new class LazyImage LazyScalarImage LazyLabelMap --- src/torchio/__init__.py | 6 ++++++ src/torchio/data/__init__.py | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/torchio/__init__.py b/src/torchio/__init__.py index de63c18b6..39a4e510e 100644 --- a/src/torchio/__init__.py +++ b/src/torchio/__init__.py @@ -16,6 +16,9 @@ from .data import LabelSampler from .data import Queue from .data import ScalarImage +from .data import LazyImage +from .data import LazyScalarImage +from .data import LazyLabelMap from .data import Subject from .data import SubjectsDataset from .data import SubjectsLoader @@ -36,6 +39,9 @@ 'Image', 'ScalarImage', 'LabelMap', + 'LazyImage', + 'LazyScalarImage', + 'LazyLabelMap', 'Queue', 'Subject', 'datasets', diff --git a/src/torchio/data/__init__.py b/src/torchio/data/__init__.py index d3250c983..a875158dc 100644 --- a/src/torchio/data/__init__.py +++ b/src/torchio/data/__init__.py @@ -2,6 +2,9 @@ from .image import Image from .image import LabelMap from .image import ScalarImage +from .image import LazyImage +from .image import LazyScalarImage +from .image import LazyLabelMap from .inference import GridAggregator from .loader import SubjectsLoader from .queue import Queue @@ -20,6 +23,9 @@ 'Image', 'ScalarImage', 'LabelMap', + 'LazyImage', + 'LazyScalarImage', + 'LazyLabelMap', 'GridSampler', 'GridAggregator', 'PatchSampler', From 354dbf311f2691bc0ca42092da04099cd39d02ee Mon Sep 17 00:00:00 2001 From: valabregue Date: Tue, 5 Nov 2024 16:11:59 +0100 Subject: [PATCH 3/4] when using crop with LazyImage the image data is not a tensor --- src/torchio/transforms/preprocessing/spatial/crop.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/torchio/transforms/preprocessing/spatial/crop.py b/src/torchio/transforms/preprocessing/spatial/crop.py index 2895d266b..cfee47852 100644 --- a/src/torchio/transforms/preprocessing/spatial/crop.py +++ b/src/torchio/transforms/preprocessing/spatial/crop.py @@ -1,5 +1,6 @@ import nibabel as nib import numpy as np +import torch from ....data.subject import Subject from .bounds_transform import BoundsTransform @@ -48,7 +49,10 @@ def apply_transform(self, sample) -> Subject: new_affine[:3, 3] = new_origin i0, j0, k0 = index_ini i1, j1, k1 = index_fin - image.set_data(image.data[:, i0:i1, j0:j1, k0:k1].clone()) + if isinstance(image.data,torch.Tensor): + image.set_data(image.data[:, i0:i1, j0:j1, k0:k1].clone()) + else: + image.set_data(torch.as_tensor(image.data[:, i0:i1, j0:j1, k0:k1])) image.affine = new_affine return sample From f306a9efbc54eb0731dedac7131131d1e4336df1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2024 15:49:42 +0000 Subject: [PATCH 4/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/torchio/data/image.py | 8 ++------ src/torchio/transforms/preprocessing/spatial/crop.py | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/torchio/data/image.py b/src/torchio/data/image.py index b4e5510ee..1d869b361 100644 --- a/src/torchio/data/image.py +++ b/src/torchio/data/image.py @@ -923,14 +923,12 @@ def count_labels(self) -> Dict[int, int]: class LazyImage(Image): - def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def load(self): - if self._is_multipath(): - message = (f'No multiple paths for LazyImage') + message = f'No multiple paths for LazyImage' RuntimeError(message) tensor, affine = self.read_and_check(self.path) @@ -943,7 +941,6 @@ def _parse_tensor( tensor: Optional[TypeData], none_ok: bool = True, ) -> Optional[torch.Tensor]: - if tensor is None: if none_ok: return None @@ -986,11 +983,10 @@ def __repr__(self): class LazyScalarImage(LazyImage, ScalarImage): - def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) -class LazyLabelMap(LazyImage, LabelMap): +class LazyLabelMap(LazyImage, LabelMap): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/src/torchio/transforms/preprocessing/spatial/crop.py b/src/torchio/transforms/preprocessing/spatial/crop.py index cfee47852..3527a5f1f 100644 --- a/src/torchio/transforms/preprocessing/spatial/crop.py +++ b/src/torchio/transforms/preprocessing/spatial/crop.py @@ -49,7 +49,7 @@ def apply_transform(self, sample) -> Subject: new_affine[:3, 3] = new_origin i0, j0, k0 = index_ini i1, j1, k1 = index_fin - if isinstance(image.data,torch.Tensor): + if isinstance(image.data, torch.Tensor): image.set_data(image.data[:, i0:i1, j0:j1, k0:k1].clone()) else: image.set_data(torch.as_tensor(image.data[:, i0:i1, j0:j1, k0:k1]))