Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
4import os
5import random
7import numpy
8import PIL.Image
9import PIL.ImageDraw
10import PIL.ImageOps
11import pkg_resources
12import torch
13import torchvision.transforms.functional
15from ..data.transforms import (
16 CenterCrop,
17 ColorJitter,
18 Compose,
19 Crop,
20 Pad,
21 RandomHorizontalFlip,
22 RandomRotation,
23 RandomVerticalFlip,
24 Resize,
25 ResizeCrop,
26 SingleAutoLevel16to8,
27 ToTensor,
28)
31def _create_img(size):
32 t = torch.randn(size)
33 pil = torchvision.transforms.functional.to_pil_image(t)
34 return pil
37def test_center_crop():
39 # parameters
40 im_size = (3, 22, 20) # (planes, height, width)
41 crop_size = (10, 12) # (height, width)
43 # test
44 bh = (im_size[1] - crop_size[0]) // 2
45 bw = (im_size[2] - crop_size[1]) // 2
46 idx = (slice(bh, -bh), slice(bw, -bw), slice(0, im_size[0]))
47 transforms = CenterCrop(crop_size)
48 img, gt, mask = [_create_img(im_size) for i in range(3)]
49 assert img.size == (im_size[2], im_size[1]) # confirms the above
50 img_t, gt_t, mask_t = transforms(img, gt, mask)
51 assert img_t.size == (crop_size[1], crop_size[0]) # confirms the above
52 # notice that PIL->array does array.transpose(1, 2, 0)
53 # so it creates an array that is (height, width, planes)
54 assert numpy.all(numpy.array(img_t) == numpy.array(img)[idx])
55 assert numpy.all(numpy.array(gt_t) == numpy.array(gt)[idx])
56 assert numpy.all(numpy.array(mask_t) == numpy.array(mask)[idx])
59def test_center_crop_uneven():
61 # parameters - WARNING: notice that modern implementations of centercrop in
62 # torchvision (noticed with version 0.10.x) may provide slightly different
63 # outputs depending on the multiplicity of pixels that need to be cropped
64 # from both sides. In the current configuration, the output is such that
65 # the centercrop is 0.5 pixel to the right/bottom from an actual center
66 # crop. With other configurations, it may be that the crop is 0.5 pixel to
67 # the left/top part of the image, which makes testing this function a bit
68 # hard. If you have issues with this function, try different
69 # configurations here, or to change the setting of "idx" a few lines down.
70 im_size = (3, 21, 20) # (planes, height, width)
71 crop_size = (10, 13) # (height, width)
73 # test
74 bh = (im_size[1] - crop_size[0]) // 2
75 bw = (im_size[2] - crop_size[1]) // 2
76 # when the crop size is uneven, this is what happens - notice here that the
77 # image height is uneven, and the crop width as well - the attributions of
78 # extra pixels will depend on what is uneven (original image or crop)
79 # crop is 0.5 pixel to the right/bottom:
80 idx = (slice(bh + 1, -bh), slice(bw + 1, -bw), slice(0, im_size[0]))
81 # crop is 0.5 pixel to the left/top:
82 # idx = (slice(bh, -(bh + 1)), slice(bw, -(bw + 1)), slice(0, im_size[0]))
83 transforms = CenterCrop(crop_size)
84 img, gt, mask = [_create_img(im_size) for i in range(3)]
85 assert img.size == (im_size[2], im_size[1]) # confirms the above
86 img_t, gt_t, mask_t = transforms(img, gt, mask)
87 assert img_t.size == (crop_size[1], crop_size[0]) # confirms the above
88 # notice that PIL->array does array.transpose(1, 2, 0)
89 # so it creates an array that is (height, width, planes)
90 assert numpy.all(numpy.array(img_t) == numpy.array(img)[idx])
91 assert numpy.all(numpy.array(gt_t) == numpy.array(gt)[idx])
92 assert numpy.all(numpy.array(mask_t) == numpy.array(mask)[idx])
95def test_pad_default():
97 # parameters
98 im_size = (3, 22, 20) # (planes, height, width)
99 pad_size = 2
101 # test
102 idx = (
103 slice(pad_size, -pad_size),
104 slice(pad_size, -pad_size),
105 slice(0, im_size[0]),
106 )
107 transforms = Pad(pad_size)
108 img, gt, mask = [_create_img(im_size) for i in range(3)]
109 assert img.size == (im_size[2], im_size[1]) # confirms the above
110 img_t, gt_t, mask_t = transforms(img, gt, mask)
111 # notice that PIL->array does array.transpose(1, 2, 0)
112 # so it creates an array that is (height, width, planes)
113 assert numpy.all(numpy.array(img_t)[idx] == numpy.array(img))
114 assert numpy.all(numpy.array(gt_t)[idx] == numpy.array(gt))
115 assert numpy.all(numpy.array(mask_t)[idx] == numpy.array(mask))
117 # checks that the border introduced with padding is all about "fill"
118 img_t = numpy.array(img_t)
119 img_t[idx] = 0
120 # border_size_plane = img_t[:, :, 0].size - numpy.array(img)[:, :, 0].size
121 assert img_t.sum() == 0
123 gt_t = numpy.array(gt_t)
124 gt_t[idx] = 0
125 assert gt_t.sum() == 0
127 mask_t = numpy.array(mask_t)
128 mask_t[idx] = 0
129 assert mask_t.sum() == 0
132def test_pad_2tuple():
134 # parameters
135 im_size = (3, 22, 20) # (planes, height, width)
136 pad_size = (1, 2) # left/right, top/bottom
137 fill = (3, 4, 5)
139 # test
140 idx = (
141 slice(pad_size[1], -pad_size[1]),
142 slice(pad_size[0], -pad_size[0]),
143 slice(0, im_size[0]),
144 )
145 transforms = Pad(pad_size, fill)
146 img, gt, mask = [_create_img(im_size) for i in range(3)]
147 assert img.size == (im_size[2], im_size[1]) # confirms the above
148 img_t, gt_t, mask_t = transforms(img, gt, mask)
149 # notice that PIL->array does array.transpose(1, 2, 0)
150 # so it creates an array that is (height, width, planes)
151 assert numpy.all(numpy.array(img_t)[idx] == numpy.array(img))
152 assert numpy.all(numpy.array(gt_t)[idx] == numpy.array(gt))
153 assert numpy.all(numpy.array(mask_t)[idx] == numpy.array(mask))
155 # checks that the border introduced with padding is all about "fill"
156 img_t = numpy.array(img_t)
157 img_t[idx] = 0
158 border_size_plane = img_t[:, :, 0].size - numpy.array(img)[:, :, 0].size
159 expected_sum = sum((fill[k] * border_size_plane) for k in range(3))
160 assert img_t.sum() == expected_sum
162 gt_t = numpy.array(gt_t)
163 gt_t[idx] = 0
164 assert gt_t.sum() == expected_sum
166 mask_t = numpy.array(mask_t)
167 mask_t[idx] = 0
168 assert mask_t.sum() == expected_sum
171def test_pad_4tuple():
173 # parameters
174 im_size = (3, 22, 20) # (planes, height, width)
175 pad_size = (1, 2, 3, 4) # left, top, right, bottom
176 fill = (3, 4, 5)
178 # test
179 idx = (
180 slice(pad_size[1], -pad_size[3]),
181 slice(pad_size[0], -pad_size[2]),
182 slice(0, im_size[0]),
183 )
184 transforms = Pad(pad_size, fill)
185 img, gt, mask = [_create_img(im_size) for i in range(3)]
186 assert img.size == (im_size[2], im_size[1]) # confirms the above
187 img_t, gt_t, mask_t = transforms(img, gt, mask)
188 # notice that PIL->array does array.transpose(1, 2, 0)
189 # so it creates an array that is (height, width, planes)
190 assert numpy.all(numpy.array(img_t)[idx] == numpy.array(img))
191 assert numpy.all(numpy.array(gt_t)[idx] == numpy.array(gt))
192 assert numpy.all(numpy.array(mask_t)[idx] == numpy.array(mask))
194 # checks that the border introduced with padding is all about "fill"
195 img_t = numpy.array(img_t)
196 img_t[idx] = 0
197 border_size_plane = img_t[:, :, 0].size - numpy.array(img)[:, :, 0].size
198 expected_sum = sum((fill[k] * border_size_plane) for k in range(3))
199 assert img_t.sum() == expected_sum
201 gt_t = numpy.array(gt_t)
202 gt_t[idx] = 0
203 assert gt_t.sum() == expected_sum
205 mask_t = numpy.array(mask_t)
206 mask_t[idx] = 0
207 assert mask_t.sum() == expected_sum
210def test_resize_downscale_w():
212 # parameters
213 im_size = (3, 22, 20) # (planes, height, width)
214 new_size = 10 # (smallest edge)
216 # test
217 transforms = Resize(new_size)
218 img, gt, mask = [_create_img(im_size) for i in range(3)]
219 assert img.size == (im_size[2], im_size[1]) # confirms the above
220 img_t, gt_t, mask_t = transforms(img, gt, mask)
221 new_size = (new_size, (new_size * im_size[1]) / im_size[2])
222 assert img_t.size == new_size
223 assert gt_t.size == new_size
224 assert mask_t.size == new_size
227def test_resize_downscale_hw():
229 # parameters
230 im_size = (3, 22, 20) # (planes, height, width)
231 new_size = (10, 12) # (height, width)
233 # test
234 transforms = Resize(new_size)
235 img, gt, mask = [_create_img(im_size) for i in range(3)]
236 assert img.size == (im_size[2], im_size[1]) # confirms the above
237 img_t, gt_t, mask_t = transforms(img, gt, mask)
238 assert img_t.size == (new_size[1], new_size[0])
239 assert gt_t.size == (new_size[1], new_size[0])
240 assert mask_t.size == (new_size[1], new_size[0])
243def test_crop():
245 # parameters
246 im_size = (3, 22, 20) # (planes, height, width)
247 crop_size = (3, 2, 10, 12) # (upper, left, height, width)
249 # test
250 idx = (
251 slice(crop_size[0], crop_size[0] + crop_size[2]),
252 slice(crop_size[1], crop_size[1] + crop_size[3]),
253 slice(0, im_size[0]),
254 )
255 transforms = Crop(*crop_size)
256 img, gt, mask = [_create_img(im_size) for i in range(3)]
257 assert img.size == (im_size[2], im_size[1]) # confirms the above
258 img_t, gt_t, mask_t = transforms(img, gt, mask)
259 # notice that PIL->array does array.transpose(1, 2, 0)
260 # so it creates an array that is (height, width, planes)
261 assert numpy.all(numpy.array(img_t) == numpy.array(img)[idx])
262 assert numpy.all(numpy.array(gt_t) == numpy.array(gt)[idx])
263 assert numpy.all(numpy.array(mask_t) == numpy.array(mask)[idx])
266def test_to_tensor():
268 transforms = ToTensor()
269 img, gt, mask = [_create_img((3, 5, 5)) for i in range(3)]
270 gt = gt.convert("1", dither=None)
271 mask = mask.convert("1", dither=None)
272 img_t, gt_t, mask_t = transforms(img, gt, mask)
273 assert img_t.dtype == torch.float32
274 assert gt_t.dtype == torch.float32
275 assert mask_t.dtype == torch.float32
278def test_horizontal_flip():
280 transforms = RandomHorizontalFlip(p=1)
282 im_size = (3, 24, 42) # (planes, height, width)
283 img, gt, mask = [_create_img(im_size) for i in range(3)]
284 img_t, gt_t, mask_t = transforms(img, gt, mask)
286 # notice that PIL->array does array.transpose(1, 2, 0)
287 # so it creates an array that is (height, width, planes)
288 assert numpy.all(numpy.flip(img_t, axis=1) == numpy.array(img))
289 assert numpy.all(numpy.flip(gt_t, axis=1) == numpy.array(gt))
290 assert numpy.all(numpy.flip(mask_t, axis=1) == numpy.array(mask))
293def test_vertical_flip():
295 transforms = RandomVerticalFlip(p=1)
297 im_size = (3, 24, 42) # (planes, height, width)
298 img, gt, mask = [_create_img(im_size) for i in range(3)]
299 img_t, gt_t, mask_t = transforms(img, gt, mask)
301 # notice that PIL->array does array.transpose(1, 2, 0)
302 # so it creates an array that is (height, width, planes)
303 assert numpy.all(numpy.flip(img_t, axis=0) == numpy.array(img))
304 assert numpy.all(numpy.flip(gt_t, axis=0) == numpy.array(gt))
305 assert numpy.all(numpy.flip(mask_t, axis=0) == numpy.array(mask))
308def test_rotation():
310 im_size = (3, 24, 42) # (planes, height, width)
311 transforms = RandomRotation(degrees=90, p=1)
312 img = _create_img(im_size)
314 # asserts all images are rotated the same
315 # and they are different from the original
316 random.seed(42)
317 img1_t, img2_t, img3_t = transforms(img, img, img)
318 assert img1_t.size == (im_size[2], im_size[1])
319 assert numpy.all(numpy.array(img1_t) == numpy.array(img2_t))
320 assert numpy.all(numpy.array(img1_t) == numpy.array(img3_t))
321 assert numpy.any(numpy.array(img1_t) != numpy.array(img))
323 # asserts two random transforms are not the same
324 (img_t2,) = transforms(img)
325 assert numpy.any(numpy.array(img_t2) != numpy.array(img1_t))
328def test_color_jitter():
330 im_size = (3, 24, 42) # (planes, height, width)
331 transforms = ColorJitter(p=1)
332 img = _create_img(im_size)
334 # asserts only the first image is jittered
335 # and it is different from the original
336 # all others match the input data
337 random.seed(42)
338 img1_t, img2_t, img3_t = transforms(img, img, img)
339 assert img1_t.size == (im_size[2], im_size[1])
340 assert numpy.any(numpy.array(img1_t) != numpy.array(img))
341 assert numpy.any(numpy.array(img1_t) != numpy.array(img2_t))
342 assert numpy.all(numpy.array(img2_t) == numpy.array(img3_t))
343 assert numpy.all(numpy.array(img2_t) == numpy.array(img))
345 # asserts two random transforms are not the same
346 img1_t2, img2_t2, img3_t2 = transforms(img, img, img)
347 assert numpy.any(numpy.array(img1_t2) != numpy.array(img1_t))
348 assert numpy.all(numpy.array(img2_t2) == numpy.array(img))
349 assert numpy.all(numpy.array(img3_t2) == numpy.array(img))
352def test_compose():
354 transforms = Compose(
355 [
356 RandomVerticalFlip(p=1),
357 RandomHorizontalFlip(p=1),
358 RandomVerticalFlip(p=1),
359 RandomHorizontalFlip(p=1),
360 ]
361 )
363 img, gt, mask = [_create_img((3, 24, 42)) for i in range(3)]
364 img_t, gt_t, mask_t = transforms(img, gt, mask)
365 assert numpy.all(numpy.array(img_t) == numpy.array(img))
366 assert numpy.all(numpy.array(gt_t) == numpy.array(gt))
367 assert numpy.all(numpy.array(mask_t) == numpy.array(mask))
370def test_16bit_autolevel():
372 path = pkg_resources.resource_filename(
373 __name__, os.path.join("data", "img-16bit.png")
374 )
375 # the way to load a 16-bit PNG image correctly, according to:
376 # https://stackoverflow.com/questions/32622658/read-16-bit-png-image-file-using-python
377 # https://github.com/python-pillow/Pillow/issues/3011
378 img = PIL.Image.fromarray(
379 numpy.array(PIL.Image.open(path)).astype("uint16")
380 )
381 assert img.mode == "I;16"
382 assert img.getextrema() == (0, 65281)
384 timg = SingleAutoLevel16to8()(img)
385 assert timg.mode == "L"
386 assert timg.getextrema() == (0, 255)
387 # timg.show()
388 # import ipdb; ipdb.set_trace()
391def test_ResizeCrop():
393 # parameters
394 im_size = (3, 128, 140) # (planes, height, width)
395 mask_gt_size = (1, 128, 140) # (planes, height, width)
396 crop_size = (30, 30, 91, 91) # (left,up , right ,down)
397 size_after_crop = (61, 61)
399 idx = (slice(crop_size[0], crop_size[2]), slice(crop_size[1], crop_size[3]))
401 # Create random image and a mask with a circle inside
402 img, gt = _create_img(im_size), _create_img(mask_gt_size)
403 mask = PIL.Image.new("L", (140, 128), "black")
404 dr = PIL.ImageDraw.Draw(mask)
405 dr.ellipse((30, 30, 90, 90), "white")
407 # Test
408 transform = ResizeCrop()
409 img_, gt_, mask_ = transform(img, gt, mask)
411 assert img_.size == size_after_crop
412 assert gt_.size == size_after_crop
413 assert mask_.size == size_after_crop
415 assert img_.mode == "RGB"
416 assert gt_.mode == "L"
417 assert mask_.mode == "L"
419 assert numpy.all(numpy.array(img_) == numpy.array(img)[idx])
420 assert numpy.all(numpy.array(gt_) == numpy.array(gt)[idx])
421 assert numpy.all(numpy.array(mask_) == numpy.array(mask)[idx])