1
|
|
|
import warnings |
2
|
|
|
|
3
|
|
|
import numpy as np |
4
|
|
|
|
5
|
|
|
from ....utils import to_tuple |
6
|
|
|
from ....data.subject import Subject |
7
|
|
|
from ....typing import TypeSpatialShape |
8
|
|
|
from ... import SpatialTransform |
9
|
|
|
from .resample import Resample |
10
|
|
|
from .crop_or_pad import CropOrPad |
11
|
|
|
|
12
|
|
|
|
13
|
|
|
class Resize(SpatialTransform): |
14
|
|
|
"""Resample images so the output shape matches the given target shape. |
15
|
|
|
|
16
|
|
|
The field of view remains the same. |
17
|
|
|
|
18
|
|
|
Args: |
19
|
|
|
target_shape: Tuple :math:`(W, H, D)`. If a single value :math:`N` is |
20
|
|
|
provided, then :math:`W = H = D = N`. |
21
|
|
|
image_interpolation: See :ref:`Interpolation`. |
22
|
|
|
""" |
23
|
|
|
def __init__( |
24
|
|
|
self, |
25
|
|
|
target_shape: TypeSpatialShape, |
26
|
|
|
image_interpolation: str = 'linear', |
27
|
|
|
**kwargs |
28
|
|
|
): |
29
|
|
|
super().__init__(**kwargs) |
30
|
|
|
self.target_shape = np.asarray(to_tuple(target_shape, length=3)) |
31
|
|
|
self.image_interpolation = self.parse_interpolation( |
32
|
|
|
image_interpolation) |
33
|
|
|
self.args_names = ( |
34
|
|
|
'target_shape', |
35
|
|
|
'image_interpolation', |
36
|
|
|
) |
37
|
|
|
|
38
|
|
|
def apply_transform(self, subject: Subject) -> Subject: |
39
|
|
|
shape_in = np.asarray(subject.spatial_shape) |
40
|
|
|
shape_out = self.target_shape |
41
|
|
|
spacing_in = np.asarray(subject.spacing) |
42
|
|
|
spacing_out = shape_in / shape_out * spacing_in |
43
|
|
|
resample = Resample( |
44
|
|
|
spacing_out, |
45
|
|
|
image_interpolation=self.image_interpolation, |
46
|
|
|
) |
47
|
|
|
resampled = resample(subject) |
48
|
|
|
# Sometimes, the output shape is one voxel too large |
49
|
|
|
# Probably because Resample uses np.ceil to compute the shape |
50
|
|
|
if not resampled.spatial_shape == tuple(shape_out): |
51
|
|
|
message = ( |
52
|
|
|
f'Output shape {resampled.spatial_shape}' |
53
|
|
|
f' != target shape {tuple(shape_out)}. Fixing with CropOrPad' |
54
|
|
|
) |
55
|
|
|
warnings.warn(message) |
56
|
|
|
crop_pad = CropOrPad(shape_out) |
57
|
|
|
resampled = crop_pad(resampled) |
58
|
|
|
return resampled |
59
|
|
|
|