Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lazy image #1232

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/torchio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -36,6 +39,9 @@
'Image',
'ScalarImage',
'LabelMap',
'LazyImage',
'LazyScalarImage',
'LazyLabelMap',
'Queue',
'Subject',
'datasets',
Expand Down
6 changes: 6 additions & 0 deletions src/torchio/data/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -20,6 +23,9 @@
'Image',
'ScalarImage',
'LabelMap',
'LazyImage',
'LazyScalarImage',
'LazyLabelMap',
'GridSampler',
'GridAggregator',
'PatchSampler',
Expand Down
70 changes: 70 additions & 0 deletions src/torchio/data/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -917,3 +917,73 @@
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)

Check warning on line 924 in src/torchio/data/image.py

View check run for this annotation

Codecov / codecov/patch

src/torchio/data/image.py#L924

Added line #L924 was not covered by tests

def load(self):
if self._is_multipath():
message = f'No multiple paths for LazyImage'
RuntimeError(message)

Check warning on line 929 in src/torchio/data/image.py

View check run for this annotation

Codecov / codecov/patch

src/torchio/data/image.py#L927-L929

Added lines #L927 - L929 were not covered by tests

tensor, affine = self.read_and_check(self.path)
self.set_data(tensor)
self.affine = affine
self._loaded = True

Check warning on line 934 in src/torchio/data/image.py

View check run for this annotation

Codecov / codecov/patch

src/torchio/data/image.py#L931-L934

Added lines #L931 - L934 were not covered by tests

def _parse_tensor(
self,
tensor: Optional[TypeData],
none_ok: bool = True,
) -> Optional[torch.Tensor]:
if tensor is None:
if none_ok:
return None

Check warning on line 943 in src/torchio/data/image.py

View check run for this annotation

Codecov / codecov/patch

src/torchio/data/image.py#L941-L943

Added lines #L941 - L943 were not covered by tests
else:
raise RuntimeError('Input tensor cannot be None')

Check warning on line 945 in src/torchio/data/image.py

View check run for this annotation

Codecov / codecov/patch

src/torchio/data/image.py#L945

Added line #L945 was not covered by tests

ndim = tensor.ndim
if ndim != 4:
raise ValueError(f'Input tensor must be 4D, but it is {ndim}D')

Check warning on line 949 in src/torchio/data/image.py

View check run for this annotation

Codecov / codecov/patch

src/torchio/data/image.py#L947-L949

Added lines #L947 - L949 were not covered by tests

return tensor

Check warning on line 951 in src/torchio/data/image.py

View check run for this annotation

Codecov / codecov/patch

src/torchio/data/image.py#L951

Added line #L951 was not covered by tests

@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

Check warning on line 958 in src/torchio/data/image.py

View check run for this annotation

Codecov / codecov/patch

src/torchio/data/image.py#L958

Added line #L958 was not covered by tests

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(

Check warning on line 964 in src/torchio/data/image.py

View check run for this annotation

Codecov / codecov/patch

src/torchio/data/image.py#L963-L964

Added lines #L963 - L964 were not covered by tests
[
f'shape: {self.shape}',
f'spacing: {self.get_spacing_string()}',
f'orientation: {"".join(self.orientation)}+',
]
)
if self._loaded:

Check warning on line 971 in src/torchio/data/image.py

View check run for this annotation

Codecov / codecov/patch

src/torchio/data/image.py#L971

Added line #L971 was not covered by tests
# instead of adding dtype and memory, just print the data
properties.append(f'dtype: {self.data}')

Check warning on line 973 in src/torchio/data/image.py

View check run for this annotation

Codecov / codecov/patch

src/torchio/data/image.py#L973

Added line #L973 was not covered by tests
else:
properties.append(f'path: "{self.path}"')

Check warning on line 975 in src/torchio/data/image.py

View check run for this annotation

Codecov / codecov/patch

src/torchio/data/image.py#L975

Added line #L975 was not covered by tests

properties = '; '.join(properties)
string = f'{self.__class__.__name__}({properties})'
return string

Check warning on line 979 in src/torchio/data/image.py

View check run for this annotation

Codecov / codecov/patch

src/torchio/data/image.py#L977-L979

Added lines #L977 - L979 were not covered by tests


class LazyScalarImage(LazyImage, ScalarImage):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

Check warning on line 984 in src/torchio/data/image.py

View check run for this annotation

Codecov / codecov/patch

src/torchio/data/image.py#L984

Added line #L984 was not covered by tests


class LazyLabelMap(LazyImage, LabelMap):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

Check warning on line 989 in src/torchio/data/image.py

View check run for this annotation

Codecov / codecov/patch

src/torchio/data/image.py#L989

Added line #L989 was not covered by tests
6 changes: 5 additions & 1 deletion src/torchio/transforms/preprocessing/spatial/crop.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import nibabel as nib
import numpy as np
import torch

from ....data.subject import Subject
from .bounds_transform import BoundsTransform
Expand Down Expand Up @@ -48,7 +49,10 @@
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]))

Check warning on line 55 in src/torchio/transforms/preprocessing/spatial/crop.py

View check run for this annotation

Codecov / codecov/patch

src/torchio/transforms/preprocessing/spatial/crop.py#L55

Added line #L55 was not covered by tests
image.affine = new_affine
return sample

Expand Down
Loading