test_transforms_tensor.py 14.8 KB
Newer Older
1
2
3
import torch
from torchvision import transforms as T
from torchvision.transforms import functional as F
4

vfdev's avatar
vfdev committed
5
from PIL.Image import NEAREST, BILINEAR, BICUBIC
6
7
8
9
10

import numpy as np

import unittest

11
from common_utils import TransformsTester
12
13


14
class Tester(TransformsTester):
15

16
17
18
    def setUp(self):
        self.device = "cpu"

19
    def _test_functional_op(self, func, fn_kwargs):
20
21
        if fn_kwargs is None:
            fn_kwargs = {}
22
        tensor, pil_img = self._create_data(height=10, width=10, device=self.device)
23
24
25
26
        transformed_tensor = getattr(F, func)(tensor, **fn_kwargs)
        transformed_pil_img = getattr(F, func)(pil_img, **fn_kwargs)
        self.compareTensorToPIL(transformed_tensor, transformed_pil_img)

27
    def _test_class_op(self, method, meth_kwargs=None, test_exact_match=True, **match_kwargs):
28
29
        if meth_kwargs is None:
            meth_kwargs = {}
vfdev's avatar
vfdev committed
30

31
        tensor, pil_img = self._create_data(height=10, width=10, device=self.device)
vfdev's avatar
vfdev committed
32
33
34
35
36
37
38
39
40
        # test for class interface
        f = getattr(T, method)(**meth_kwargs)
        scripted_fn = torch.jit.script(f)

        # set seed to reproduce the same transformation for tensor and PIL image
        torch.manual_seed(12)
        transformed_tensor = f(tensor)
        torch.manual_seed(12)
        transformed_pil_img = f(pil_img)
41
42
43
44
        if test_exact_match:
            self.compareTensorToPIL(transformed_tensor, transformed_pil_img, **match_kwargs)
        else:
            self.approxEqualTensorToPIL(transformed_tensor.float(), transformed_pil_img, **match_kwargs)
45

vfdev's avatar
vfdev committed
46
47
        torch.manual_seed(12)
        transformed_tensor_script = scripted_fn(tensor)
48
        self.assertTrue(transformed_tensor.equal(transformed_tensor_script))
49

50
51
52
    def _test_op(self, func, method, fn_kwargs=None, meth_kwargs=None):
        self._test_functional_op(func, fn_kwargs)
        self._test_class_op(method, meth_kwargs)
53
54

    def test_random_horizontal_flip(self):
55
        self._test_op('hflip', 'RandomHorizontalFlip')
56
57

    def test_random_vertical_flip(self):
58
        self._test_op('vflip', 'RandomVerticalFlip')
59

60
61
62
63
    def test_adjustments(self):
        fns = ['adjust_brightness', 'adjust_contrast', 'adjust_saturation']
        for _ in range(20):
            factor = 3 * torch.rand(1).item()
64
            tensor, _ = self._create_data(device=self.device)
65
66
67
68
69
70
            pil_img = T.ToPILImage()(tensor)

            for func in fns:
                adjusted_tensor = getattr(F, func)(tensor, factor)
                adjusted_pil_img = getattr(F, func)(pil_img, factor)

71
                adjusted_pil_tensor = T.ToTensor()(adjusted_pil_img).to(self.device)
72
73
74
75
76
77
78
79
80
81
82
83
84
85
                scripted_fn = torch.jit.script(getattr(F, func))
                adjusted_tensor_script = scripted_fn(tensor, factor)

                if not tensor.dtype.is_floating_point:
                    adjusted_tensor = adjusted_tensor.to(torch.float) / 255
                    adjusted_tensor_script = adjusted_tensor_script.to(torch.float) / 255

                # F uses uint8 and F_t uses float, so there is a small
                # difference in values caused by (at most 5) truncations.
                max_diff = (adjusted_tensor - adjusted_pil_tensor).abs().max()
                max_diff_scripted = (adjusted_tensor - adjusted_tensor_script).abs().max()
                self.assertLess(max_diff, 5 / 255 + 1e-5)
                self.assertLess(max_diff_scripted, 5 / 255 + 1e-5)

86
87
88
    def test_pad(self):

        # Test functional.pad (PIL and Tensor) with padding as single int
89
        self._test_functional_op(
90
91
92
93
            "pad", fn_kwargs={"padding": 2, "fill": 0, "padding_mode": "constant"}
        )
        # Test functional.pad and transforms.Pad with padding as [int, ]
        fn_kwargs = meth_kwargs = {"padding": [2, ], "fill": 0, "padding_mode": "constant"}
94
        self._test_op(
95
96
97
98
            "pad", "Pad", fn_kwargs=fn_kwargs, meth_kwargs=meth_kwargs
        )
        # Test functional.pad and transforms.Pad with padding as list
        fn_kwargs = meth_kwargs = {"padding": [4, 4], "fill": 0, "padding_mode": "constant"}
99
        self._test_op(
100
101
102
103
            "pad", "Pad", fn_kwargs=fn_kwargs, meth_kwargs=meth_kwargs
        )
        # Test functional.pad and transforms.Pad with padding as tuple
        fn_kwargs = meth_kwargs = {"padding": (2, 2, 2, 2), "fill": 127, "padding_mode": "constant"}
104
        self._test_op(
105
106
107
            "pad", "Pad", fn_kwargs=fn_kwargs, meth_kwargs=meth_kwargs
        )

vfdev's avatar
vfdev committed
108
109
110
111
    def test_crop(self):
        fn_kwargs = {"top": 2, "left": 3, "height": 4, "width": 5}
        # Test transforms.RandomCrop with size and padding as tuple
        meth_kwargs = {"size": (4, 5), "padding": (4, 4), "pad_if_needed": True, }
112
        self._test_op(
vfdev's avatar
vfdev committed
113
114
115
            'crop', 'RandomCrop', fn_kwargs=fn_kwargs, meth_kwargs=meth_kwargs
        )

vfdev's avatar
vfdev committed
116
117
118
119
120
121
122
123
124
125
126
127
128
        sizes = [5, [5, ], [6, 6]]
        padding_configs = [
            {"padding_mode": "constant", "fill": 0},
            {"padding_mode": "constant", "fill": 10},
            {"padding_mode": "constant", "fill": 20},
            {"padding_mode": "edge"},
            {"padding_mode": "reflect"},
        ]

        for size in sizes:
            for padding_config in padding_configs:
                config = dict(padding_config)
                config["size"] = size
129
                self._test_class_op("RandomCrop", config)
vfdev's avatar
vfdev committed
130
131
132
133

    def test_center_crop(self):
        fn_kwargs = {"output_size": (4, 5)}
        meth_kwargs = {"size": (4, 5), }
134
        self._test_op(
vfdev's avatar
vfdev committed
135
136
137
138
            "center_crop", "CenterCrop", fn_kwargs=fn_kwargs, meth_kwargs=meth_kwargs
        )
        fn_kwargs = {"output_size": (5,)}
        meth_kwargs = {"size": (5, )}
139
        self._test_op(
vfdev's avatar
vfdev committed
140
141
            "center_crop", "CenterCrop", fn_kwargs=fn_kwargs, meth_kwargs=meth_kwargs
        )
142
        tensor = torch.randint(0, 255, (3, 10, 10), dtype=torch.uint8, device=self.device)
vfdev's avatar
vfdev committed
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
        # Test torchscript of transforms.CenterCrop with size as int
        f = T.CenterCrop(size=5)
        scripted_fn = torch.jit.script(f)
        scripted_fn(tensor)

        # Test torchscript of transforms.CenterCrop with size as [int, ]
        f = T.CenterCrop(size=[5, ])
        scripted_fn = torch.jit.script(f)
        scripted_fn(tensor)

        # Test torchscript of transforms.CenterCrop with size as tuple
        f = T.CenterCrop(size=(6, 6))
        scripted_fn = torch.jit.script(f)
        scripted_fn(tensor)

158
    def _test_op_list_output(self, func, method, out_length, fn_kwargs=None, meth_kwargs=None):
vfdev's avatar
vfdev committed
159
160
161
162
        if fn_kwargs is None:
            fn_kwargs = {}
        if meth_kwargs is None:
            meth_kwargs = {}
163
        tensor, pil_img = self._create_data(height=20, width=20, device=self.device)
vfdev's avatar
vfdev committed
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
        transformed_t_list = getattr(F, func)(tensor, **fn_kwargs)
        transformed_p_list = getattr(F, func)(pil_img, **fn_kwargs)
        self.assertEqual(len(transformed_t_list), len(transformed_p_list))
        self.assertEqual(len(transformed_t_list), out_length)
        for transformed_tensor, transformed_pil_img in zip(transformed_t_list, transformed_p_list):
            self.compareTensorToPIL(transformed_tensor, transformed_pil_img)

        scripted_fn = torch.jit.script(getattr(F, func))
        transformed_t_list_script = scripted_fn(tensor.detach().clone(), **fn_kwargs)
        self.assertEqual(len(transformed_t_list), len(transformed_t_list_script))
        self.assertEqual(len(transformed_t_list_script), out_length)
        for transformed_tensor, transformed_tensor_script in zip(transformed_t_list, transformed_t_list_script):
            self.assertTrue(transformed_tensor.equal(transformed_tensor_script),
                            msg="{} vs {}".format(transformed_tensor, transformed_tensor_script))

        # test for class interface
        f = getattr(T, method)(**meth_kwargs)
        scripted_fn = torch.jit.script(f)
        output = scripted_fn(tensor)
        self.assertEqual(len(output), len(transformed_t_list_script))

    def test_five_crop(self):
        fn_kwargs = meth_kwargs = {"size": (5,)}
187
        self._test_op_list_output(
vfdev's avatar
vfdev committed
188
189
190
            "five_crop", "FiveCrop", out_length=5, fn_kwargs=fn_kwargs, meth_kwargs=meth_kwargs
        )
        fn_kwargs = meth_kwargs = {"size": [5, ]}
191
        self._test_op_list_output(
vfdev's avatar
vfdev committed
192
193
194
            "five_crop", "FiveCrop", out_length=5, fn_kwargs=fn_kwargs, meth_kwargs=meth_kwargs
        )
        fn_kwargs = meth_kwargs = {"size": (4, 5)}
195
        self._test_op_list_output(
vfdev's avatar
vfdev committed
196
197
198
            "five_crop", "FiveCrop", out_length=5, fn_kwargs=fn_kwargs, meth_kwargs=meth_kwargs
        )
        fn_kwargs = meth_kwargs = {"size": [4, 5]}
199
        self._test_op_list_output(
vfdev's avatar
vfdev committed
200
201
202
203
204
            "five_crop", "FiveCrop", out_length=5, fn_kwargs=fn_kwargs, meth_kwargs=meth_kwargs
        )

    def test_ten_crop(self):
        fn_kwargs = meth_kwargs = {"size": (5,)}
205
        self._test_op_list_output(
vfdev's avatar
vfdev committed
206
207
208
            "ten_crop", "TenCrop", out_length=10, fn_kwargs=fn_kwargs, meth_kwargs=meth_kwargs
        )
        fn_kwargs = meth_kwargs = {"size": [5, ]}
209
        self._test_op_list_output(
vfdev's avatar
vfdev committed
210
211
212
            "ten_crop", "TenCrop", out_length=10, fn_kwargs=fn_kwargs, meth_kwargs=meth_kwargs
        )
        fn_kwargs = meth_kwargs = {"size": (4, 5)}
213
        self._test_op_list_output(
vfdev's avatar
vfdev committed
214
215
216
            "ten_crop", "TenCrop", out_length=10, fn_kwargs=fn_kwargs, meth_kwargs=meth_kwargs
        )
        fn_kwargs = meth_kwargs = {"size": [4, 5]}
217
        self._test_op_list_output(
vfdev's avatar
vfdev committed
218
219
220
            "ten_crop", "TenCrop", out_length=10, fn_kwargs=fn_kwargs, meth_kwargs=meth_kwargs
        )

vfdev's avatar
vfdev committed
221
    def test_resize(self):
222
        tensor, _ = self._create_data(height=34, width=36, device=self.device)
vfdev's avatar
vfdev committed
223
224
225
226
227
228
        script_fn = torch.jit.script(F.resize)

        for dt in [None, torch.float32, torch.float64]:
            if dt is not None:
                # This is a trivial cast to float of uint8 data to test all cases
                tensor = tensor.to(dt)
229
            for size in [32, 34, [32, ], [32, 32], (32, 32), [34, 35]]:
vfdev's avatar
vfdev committed
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
                for interpolation in [BILINEAR, BICUBIC, NEAREST]:

                    resized_tensor = F.resize(tensor, size=size, interpolation=interpolation)

                    if isinstance(size, int):
                        script_size = [size, ]
                    else:
                        script_size = size

                    s_resized_tensor = script_fn(tensor, size=script_size, interpolation=interpolation)
                    self.assertTrue(s_resized_tensor.equal(resized_tensor))

                    transform = T.Resize(size=script_size, interpolation=interpolation)
                    resized_tensor = transform(tensor)
                    script_transform = torch.jit.script(transform)
                    s_resized_tensor = script_transform(tensor)
                    self.assertTrue(s_resized_tensor.equal(resized_tensor))

248
    def test_resized_crop(self):
249
        tensor = torch.randint(0, 255, size=(3, 44, 56), dtype=torch.uint8, device=self.device)
250

251
252
        for scale in [(0.7, 1.2), [0.7, 1.2]]:
            for ratio in [(0.75, 1.333), [0.75, 1.333]]:
253
                for size in [(32, ), [44, ], [32, ], [32, 32], (32, 32), [44, 55]]:
254
255
256
257
258
259
260
261
262
263
264
265
266
                    for interpolation in [NEAREST, BILINEAR, BICUBIC]:
                        transform = T.RandomResizedCrop(
                            size=size, scale=scale, ratio=ratio, interpolation=interpolation
                        )
                        s_transform = torch.jit.script(transform)

                        torch.manual_seed(12)
                        out1 = transform(tensor)
                        torch.manual_seed(12)
                        out2 = s_transform(tensor)
                        self.assertTrue(out1.equal(out2))

    def test_random_affine(self):
267
        tensor = torch.randint(0, 255, size=(3, 44, 56), dtype=torch.uint8, device=self.device)
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284

        for shear in [15, 10.0, (5.0, 10.0), [-15, 15], [-10.0, 10.0, -11.0, 11.0]]:
            for scale in [(0.7, 1.2), [0.7, 1.2]]:
                for translate in [(0.1, 0.2), [0.2, 0.1]]:
                    for degrees in [45, 35.0, (-45, 45), [-90.0, 90.0]]:
                        for interpolation in [NEAREST, BILINEAR]:
                            transform = T.RandomAffine(
                                degrees=degrees, translate=translate,
                                scale=scale, shear=shear, resample=interpolation
                            )
                            s_transform = torch.jit.script(transform)

                            torch.manual_seed(12)
                            out1 = transform(tensor)
                            torch.manual_seed(12)
                            out2 = s_transform(tensor)
                            self.assertTrue(out1.equal(out2))
285

286
    def test_random_rotate(self):
287
        tensor = torch.randint(0, 255, size=(3, 44, 56), dtype=torch.uint8, device=self.device)
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303

        for center in [(0, 0), [10, 10], None, (56, 44)]:
            for expand in [True, False]:
                for degrees in [45, 35.0, (-45, 45), [-90.0, 90.0]]:
                    for interpolation in [NEAREST, BILINEAR]:
                        transform = T.RandomRotation(
                            degrees=degrees, resample=interpolation, expand=expand, center=center
                        )
                        s_transform = torch.jit.script(transform)

                        torch.manual_seed(12)
                        out1 = transform(tensor)
                        torch.manual_seed(12)
                        out2 = s_transform(tensor)
                        self.assertTrue(out1.equal(out2))

304
    def test_random_perspective(self):
305
        tensor = torch.randint(0, 255, size=(3, 44, 56), dtype=torch.uint8, device=self.device)
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320

        for distortion_scale in np.linspace(0.1, 1.0, num=20):
            for interpolation in [NEAREST, BILINEAR]:
                transform = T.RandomPerspective(
                    distortion_scale=distortion_scale,
                    interpolation=interpolation
                )
                s_transform = torch.jit.script(transform)

                torch.manual_seed(12)
                out1 = transform(tensor)
                torch.manual_seed(12)
                out2 = s_transform(tensor)
                self.assertTrue(out1.equal(out2))

321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
    def test_to_grayscale(self):

        meth_kwargs = {"num_output_channels": 1}
        tol = 1.0 + 1e-10
        self._test_class_op(
            "Grayscale", meth_kwargs=meth_kwargs, test_exact_match=False, tol=tol, agg_method="max"
        )

        meth_kwargs = {"num_output_channels": 3}
        self._test_class_op(
            "Grayscale", meth_kwargs=meth_kwargs, test_exact_match=False, tol=tol, agg_method="max"
        )

        meth_kwargs = {}
        self._test_class_op(
            "RandomGrayscale", meth_kwargs=meth_kwargs, test_exact_match=False, tol=tol, agg_method="max"
        )

339

340
341
342
343
344
345
346
@unittest.skipIf(not torch.cuda.is_available(), reason="Skip if no CUDA device")
class CUDATester(Tester):

    def setUp(self):
        self.device = "cuda"


347
348
if __name__ == '__main__':
    unittest.main()