Passed
Push — master ( 3a869f...6c7a18 )
by Fernando
01:18
created

torchio.transforms.preprocessing.spatial.copy_affine   A

Complexity

Total Complexity 5

Size/Duplication

Total Lines 80
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 5
eloc 20
dl 0
loc 80
rs 10
c 0
b 0
f 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
A CopyAffine.apply_transform() 0 8 3
A CopyAffine.__init__() 0 8 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