Passed
Pull Request — master (#584)
by Fernando
01:16
created

CopyAffine.apply_transform()   A

Complexity

Conditions 3

Size

Total Lines 8
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 8
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 3
nop 2
1
import copy
2
from ....data.subject import Subject
3
from ... import SpatialTransform
4
5
6
class CopyAffine(SpatialTransform):
7
    """Copy the spatial metadata from a reference image in the subject.
8
9
    Small unexpected differences in spatial metadata across different images
10
    of a subject can arise due to rounding errors while converting formats.
11
12
    If the ``shape`` and ``orientation`` of the images are the same and their
13
    ``affine`` attributes are different but very similar, this transform can be
14
    used to avoid errors during safety checks in other transforms and samplers.
15
16
    Args:
17
        target: Name of the image within the subject whose affine matrix will
18
            be used.
19
20
    Example:
21
        >>> import torch
22
        >>> import torchio as tio
23
        >>> import numpy as np
24
        >>> np.random.seed(0)
25
        >>> affine = np.diag((*(np.random.rand(3) + 0.5), 1))
26
        >>> t1 = tio.ScalarImage(tensor=torch.rand(1, 100, 100, 100), affine=affine)
27
        >>> # Let's simulate a loss of precision
28
        >>> # (caused for example by NIfTI storing spatial metadata in single precision)
29
        >>> bad_affine = affine.astype(np.float16)
30
        >>> t2 = tio.ScalarImage(tensor=torch.rand(1, 100, 100, 100), affine=bad_affine)
31
        >>> subject = tio.Subject(t1=t1, t2=t2)
32
        >>> resample = tio.Resample(0.5)
33
        >>> resample(subject).shape  # error as images are in different spaces
34
        Traceback (most recent call last):
35
        File "<stdin>", line 1, in <module>
36
        File "/Users/fernando/git/torchio/torchio/data/subject.py", line 101, in shape
37
            self.check_consistent_attribute('shape')
38
        File "/Users/fernando/git/torchio/torchio/data/subject.py", line 229, in check_consistent_attribute
39
            raise RuntimeError(message)
40
        RuntimeError: More than one shape found in subject images:
41
        {'t1': (1, 210, 244, 221), 't2': (1, 210, 243, 221)}
42
        >>> transform = tio.CopyAffine('t1')
43
        >>> fixed = transform(subject)
44
        >>> resample(fixed).shape
45
        (1, 210, 244, 221)
46
47
48
    .. warning:: This transform should be used with caution. Modifying the
49
        spatial metadata of an image manually can lead to incorrect processing
50
        of the position of anatomical structures. For example, a machine
51
        learning algorithm might incorrectly predict that a lesion on the right
52
        lung is on the left lung.
53
54
    .. note:: For more information, see some related discussions on GitHub:
55
56
    * https://github.com/fepegar/torchio/issues/354
57
    * https://github.com/fepegar/torchio/discussions/489
58
    * https://github.com/fepegar/torchio/pull/584
59
    * https://github.com/fepegar/torchio/issues/430
60
    * https://github.com/fepegar/torchio/issues/382
61
    * https://github.com/fepegar/torchio/pull/592
62
    """  # noqa: E501
63
    def __init__(self, target: str, **kwargs):
64
        super().__init__(**kwargs)
65
        if not isinstance(target, str):
66
            message = (
67
                f'The target must be a string, but "{type(target)}" was found'
68
            )
69
            raise ValueError(message)
70
        self.target = target
71
72
    def apply_transform(self, subject: Subject) -> Subject:
73
        if self.target not in subject:
74
            message = f'Target image "{self.target}" not found in subject'
75
            raise RuntimeError(message)
76
        affine = subject[self.target].affine
77
        for image in self.get_images(subject):
78
            image.affine = copy.deepcopy(affine)
79
        return subject
80