Passed
Pull Request — main (#656)
by Yunguan
02:46
created

test_local_net_residual3d_block()   A

Complexity

Conditions 1

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 13
rs 10
c 0
b 0
f 0
cc 1
nop 0
1
# coding=utf-8
2
3
"""
4
Tests for deepreg/model/layer
5
"""
6
7
import numpy as np
8
import pytest
9
import tensorflow as tf
10
11
import deepreg.model.layer as layer
12
13
14
@pytest.mark.parametrize("layer_name", ["conv3d", "deconv3d"])
15
@pytest.mark.parametrize("norm_name", ["batch", "layer"])
16
@pytest.mark.parametrize("activation", ["relu", "elu"])
17
def test_norm_block(layer_name: str, norm_name: str, activation: str):
18
    """
19
    Test output shapes and configs.
20
21
    :param layer_name: layer_name for layer definition
22
    :param norm_name: norm_name for layer definition
23
    :param activation: activation for layer definition
24
    """
25
    input_size = (2, 3, 4, 5, 6)  # (batch, *shape, ch)
26
    norm_block = layer.NormBlock(
27
        layer_name=layer_name,
28
        norm_name=norm_name,
29
        activation=activation,
30
        filters=3,
31
        kernel_size=1,
32
        padding="same",
33
    )
34
    inputs = tf.ones(shape=input_size)
35
    outputs = norm_block(inputs)
36
    assert outputs.shape == input_size[:-1] + (3,)
37
38
    config = norm_block.get_config()
39
    assert config == dict(
40
        layer_name=layer_name,
41
        norm_name=norm_name,
42
        activation=activation,
43
        filters=3,
44
        kernel_size=1,
45
        padding="same",
46
        name="norm_block",
47
        trainable=True,
48
        dtype="float32",
49
    )
50
51
52
class TestWarping:
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
53
    @pytest.mark.parametrize(
54
        ("moving_image_size", "fixed_image_size"),
55
        [
56
            ((1, 2, 3), (3, 4, 5)),
57
            ((1, 2, 3), (1, 2, 3)),
58
        ],
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
59
    )
60
    def test_forward(self, moving_image_size, fixed_image_size):
61
        batch_size = 2
62
        image = tf.ones(shape=(batch_size,) + moving_image_size)
63
        ddf = tf.ones(shape=(batch_size,) + fixed_image_size + (3,))
64
        outputs = layer.Warping(fixed_image_size=fixed_image_size)([ddf, image])
65
        assert outputs.shape == (batch_size, *fixed_image_size)
66
67
    def test_get_config(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
68
        warping = layer.Warping(fixed_image_size=(2, 3, 4))
69
        config = warping.get_config()
70
        assert config == dict(
71
            fixed_image_size=(2, 3, 4),
72
            name="warping",
73
            trainable=True,
74
            dtype="float32",
75
        )
76
77
78
@pytest.mark.parametrize("layer_name", ["conv3d", "deconv3d"])
79
@pytest.mark.parametrize("norm_name", ["batch", "layer"])
80
@pytest.mark.parametrize("activation", ["relu", "elu"])
81
@pytest.mark.parametrize("num_layers", [2, 3])
82
def test_res_block(layer_name: str, norm_name: str, activation: str, num_layers: int):
83
    """
84
    Test output shapes and configs.
85
86
    :param layer_name: layer_name for layer definition
87
    :param norm_name: norm_name for layer definition
88
    :param activation: activation for layer definition
89
    :param num_layers: number of blocks in res block
90
    """
91
    ch = 3
92
    input_size = (2, 3, 4, 5, ch)  # (batch, *shape, ch)
93
    res_block = layer.ResidualBlock(
94
        layer_name=layer_name,
95
        num_layers=num_layers,
96
        norm_name=norm_name,
97
        activation=activation,
98
        filters=ch,
99
        kernel_size=3,
100
        padding="same",
101
    )
102
    inputs = tf.ones(shape=input_size)
103
    outputs = res_block(inputs)
104
    assert outputs.shape == input_size[:-1] + (3,)
105
106
    config = res_block.get_config()
107
    assert config == dict(
108
        layer_name=layer_name,
109
        num_layers=num_layers,
110
        norm_name=norm_name,
111
        activation=activation,
112
        filters=ch,
113
        kernel_size=3,
114
        padding="same",
115
        name="res_block",
116
        trainable=True,
117
        dtype="float32",
118
    )
119
120
121
class TestIntDVF:
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
122
    def test_forward(self):
123
        """
124
        Test output shape and config.
125
        """
126
127
        fixed_image_size = (8, 9, 10)
128
        input_shape = (2, *fixed_image_size, 3)
129
130
        int_layer = layer.IntDVF(fixed_image_size=fixed_image_size)
131
132
        inputs = tf.ones(shape=input_shape)
133
        outputs = int_layer(inputs)
134
        assert outputs.shape == input_shape
135
136
        config = int_layer.get_config()
137
        assert config == dict(
138
            fixed_image_size=fixed_image_size,
139
            num_steps=7,
140
            name="int_dvf",
141
            trainable=True,
142
            dtype="float32",
143
        )
144
145
    def test_err(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
146
        with pytest.raises(AssertionError):
147
            layer.IntDVF(fixed_image_size=(2, 3))
148
149
150
def test_local_net_residual3d_block():
151
    """
152
    Test the layer.LocalNetResidual3dBlock class's,
153
    default attributes and call() function.
154
    """
155
156
    # Test __init__()
157
    conv3d_block = layer.LocalNetResidual3dBlock(8)
158
159
    assert conv3d_block._conv3d.kernel_size == (3, 3, 3)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _conv3d was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
160
    assert conv3d_block._conv3d.strides == (1, 1, 1)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _conv3d was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
161
    assert conv3d_block._conv3d.padding == "same"
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _conv3d was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
162
    assert conv3d_block._conv3d.use_bias is False
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _conv3d was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
163
164
165
class TestResizeCPTransform:
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
166
    @pytest.mark.parametrize(
167
        "parameter,cp_spacing", [((8, 8, 8), 8), ((8, 24, 16), (8, 24, 16))]
168
    )
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
169
    def test_attributes(self, parameter, cp_spacing):
170
        model = layer.ResizeCPTransform(cp_spacing)
171
172
        if isinstance(cp_spacing, int):
173
            cp_spacing = [cp_spacing] * 3
174
        assert list(model.cp_spacing) == list(parameter)
175
        assert model.kernel_sigma == [0.44 * cp for cp in cp_spacing]
176
177
    @pytest.mark.parametrize(
178
        "input_size,output_size,cp_spacing",
179
        [
180
            ((1, 8, 8, 8, 3), (12, 8, 12), (8, 16, 8)),
181
            ((1, 8, 8, 8, 3), (12, 12, 12), 8),
182
        ],
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
183
    )
184
    def test_build(self, input_size, output_size, cp_spacing):
185
        model = layer.ResizeCPTransform(cp_spacing)
186
        model.build(input_size)
187
188
        assert [a == b for a, b, in zip(model._output_shape, output_size)]
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _output_shape was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
189
190
    @pytest.mark.parametrize(
191
        "input_size,output_size,cp_spacing",
192
        [
193
            ((1, 68, 68, 68, 3), (1, 12, 8, 12, 3), (8, 16, 8)),
194
            ((1, 68, 68, 68, 3), (1, 12, 12, 12, 3), 8),
195
        ],
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
196
    )
197
    def test_call(self, input_size, output_size, cp_spacing):
198
        model = layer.ResizeCPTransform(cp_spacing)
199
        model.build(input_size)
200
201
        input = tf.random.normal(shape=input_size, dtype=tf.float32)
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in input.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
202
        output = model(input)
203
204
        assert output.shape == output_size
205
206
207
class TestBSplines3DTransform:
208
    """
209
    Test the layer.BSplines3DTransform class,
210
    its default attributes and its call() function.
211
    """
212
213
    @pytest.mark.parametrize(
214
        "input_size,cp",
215
        [((1, 8, 8, 8, 3), 8), ((1, 8, 8, 8, 3), (8, 16, 12))],
216
    )
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
217
    def test_init(self, input_size, cp):
218
        model = layer.BSplines3DTransform(cp, input_size[1:-1])
219
220
        if isinstance(cp, int):
221
            cp = (cp, cp, cp)
222
223
        assert model.cp_spacing == cp
224
225
    @pytest.mark.parametrize(
226
        "input_size,cp",
227
        [((1, 8, 8, 8, 3), (8, 8, 8)), ((1, 8, 8, 8, 3), (8, 16, 12))],
228
    )
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
229
    def generate_filter_coefficients(self, cp_spacing):
230
231
        b = {
232
            0: lambda u: np.float64((1 - u) ** 3 / 6),
233
            1: lambda u: np.float64((3 * (u ** 3) - 6 * (u ** 2) + 4) / 6),
234
            2: lambda u: np.float64((-3 * (u ** 3) + 3 * (u ** 2) + 3 * u + 1) / 6),
235
            3: lambda u: np.float64(u ** 3 / 6),
236
        }
237
238
        filters = np.zeros(
239
            (
240
                4 * cp_spacing[0],
241
                4 * cp_spacing[1],
242
                4 * cp_spacing[2],
243
                3,
244
                3,
245
            ),
246
            dtype=np.float32,
247
        )
248
249
        for u in range(cp_spacing[0]):
0 ignored issues
show
unused-code introduced by
Too many nested blocks (7/5)
Loading history...
250
            for v in range(cp_spacing[1]):
251
                for w in range(cp_spacing[2]):
252
                    for x in range(4):
253
                        for y in range(4):
254
                            for z in range(4):
255
                                for it_dim in range(3):
256
                                    u_norm = 1 - (u + 0.5) / cp_spacing[0]
257
                                    v_norm = 1 - (v + 0.5) / cp_spacing[1]
258
                                    w_norm = 1 - (w + 0.5) / cp_spacing[2]
259
                                    filters[
260
                                        x * cp_spacing[0] + u,
261
                                        y * cp_spacing[1] + v,
262
                                        z * cp_spacing[2] + w,
263
                                        it_dim,
264
                                        it_dim,
265
                                    ] = (
266
                                        b[x](u_norm) * b[y](v_norm) * b[z](w_norm)
267
                                    )
268
        return filters
269
270
    @pytest.mark.parametrize(
271
        "input_size,cp",
272
        [((1, 8, 8, 8, 3), (8, 8, 8)), ((1, 8, 8, 8, 3), (8, 16, 12))],
273
    )
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
274
    def test_build(self, input_size, cp):
275
        model = layer.BSplines3DTransform(cp, input_size[1:-1])
276
277
        model.build(input_size)
278
        assert model.filter.shape == (
279
            4 * cp[0],
280
            4 * cp[1],
281
            4 * cp[2],
282
            3,
283
            3,
284
        )
285
286
    @pytest.mark.parametrize(
287
        "input_size,cp",
288
        [((1, 8, 8, 8, 3), (8, 8, 8)), ((1, 8, 8, 8, 3), (8, 16, 12))],
289
    )
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
290
    def test_coefficients(self, input_size, cp):
291
292
        filters = self.generate_filter_coefficients(cp_spacing=cp)
293
294
        model = layer.BSplines3DTransform(cp, input_size[1:-1])
295
        model.build(input_size)
296
297
        assert np.allclose(filters, model.filter.numpy(), atol=1e-8)
298
299
    @pytest.mark.parametrize(
300
        "input_size,cp",
301
        [((1, 8, 8, 8, 3), (8, 8, 8)), ((1, 8, 8, 8, 3), (8, 16, 12))],
302
    )
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
303
    def test_interpolation(self, input_size, cp):
304
        model = layer.BSplines3DTransform(cp, input_size[1:-1])
305
        model.build(input_size)
306
307
        vol_shape = input_size[1:-1]
308
        num_cp = (
309
            [input_size[0]]
310
            + [int(np.ceil(isize / cpsize) + 3) for isize, cpsize in zip(vol_shape, cp)]
311
            + [input_size[-1]]
312
        )
313
314
        field = tf.random.normal(shape=num_cp, dtype=tf.float32)
315
316
        ddf = model.call(field)
317
        assert ddf.shape == input_size
318
319
320
class TestResize3d:
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
321
    @pytest.mark.parametrize(
322
        ("input_shape", "resize_shape", "output_shape"),
323
        [
324
            ((1, 2, 3), (3, 4, 5), (3, 4, 5)),
325
            ((2, 1, 2, 3), (3, 4, 5), (2, 3, 4, 5)),
326
            ((2, 1, 2, 3, 1), (3, 4, 5), (2, 3, 4, 5, 1)),
327
            ((2, 1, 2, 3, 6), (3, 4, 5), (2, 3, 4, 5, 6)),
328
            ((1, 2, 3), (1, 2, 3), (1, 2, 3)),
329
            ((2, 1, 2, 3), (1, 2, 3), (2, 1, 2, 3)),
330
            ((2, 1, 2, 3, 1), (1, 2, 3), (2, 1, 2, 3, 1)),
331
            ((2, 1, 2, 3, 6), (1, 2, 3), (2, 1, 2, 3, 6)),
332
        ],
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
333
    )
334
    def test_forward(self, input_shape, resize_shape, output_shape):
335
        inputs = tf.ones(shape=input_shape)
336
        outputs = layer.Resize3d(shape=resize_shape)(inputs)
337
        assert outputs.shape == output_shape
338
339
    def test_get_config(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
340
        resize = layer.Resize3d(shape=(2, 3, 4))
341
        config = resize.get_config()
342
        assert config == dict(
343
            shape=(2, 3, 4),
344
            method=tf.image.ResizeMethod.BILINEAR,
345
            name="resize3d",
346
            trainable=True,
347
            dtype="float32",
348
        )
349
350
    def test_shape_err(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
351
        with pytest.raises(AssertionError):
352
            layer.Resize3d(shape=(2, 3))
353
354
    def test_image_shape_err(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
355
        with pytest.raises(ValueError) as err_info:
356
            resize = layer.Resize3d(shape=(2, 3, 4))
357
            resize(tf.ones(1, 1))
358
        assert "Resize3d takes input image of dimension 3 or 4 or 5" in str(
359
            err_info.value
360
        )
361