Publishing 2019 R1 content
[platform/upstream/dldt.git] / tools / accuracy_checker / tests / test_preprocessor.py
1 """
2 Copyright (c) 2019 Intel Corporation
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8       http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 """
16
17 import cv2
18 import numpy as np
19 import pytest
20
21 from accuracy_checker.config import ConfigError
22 from accuracy_checker.preprocessor import (
23     Crop,
24     Normalize,
25     Preprocessor,
26     Resize,
27     Flip,
28     BgrToRgb,
29     CropRect,
30     ExtendAroundRect,
31     PointAligner
32 )
33 from accuracy_checker.preprocessor.preprocessing_executor import PreprocessingExecutor
34 from accuracy_checker.dataset import DataRepresentation
35
36
37 class TestResize:
38     def test_default_resize(self, mocker):
39         cv2_resize_mock = mocker.patch('accuracy_checker.preprocessor.preprocessors.cv2.resize')
40         resize = Preprocessor.provide('resize', {'type': 'resize', 'size': 200})
41
42         input_mock = mocker.Mock()
43         resize(DataRepresentation(input_mock))
44
45         assert not resize.use_pil
46         assert resize.dst_width == 200
47         assert resize.dst_height == 200
48         cv2_resize_mock.assert_called_once_with(
49             input_mock, (200, 200), interpolation=Resize.OPENCV_INTERPOLATION['LINEAR']
50         )
51
52     def test_custom_resize(self, mocker):
53         cv2_resize_mock = mocker.patch('accuracy_checker.preprocessor.preprocessors.cv2.resize')
54
55         resize = Preprocessor.provide(
56             'resize', {'type': 'resize', 'dst_width': 126, 'dst_height': 128, 'interpolation': 'CUBIC'}
57         )
58
59         input_mock = mocker.Mock()
60         resize(DataRepresentation(input_mock))
61
62         assert not resize.use_pil
63         assert resize.dst_width == 126
64         assert resize.dst_height == 128
65         cv2_resize_mock.assert_called_once_with(
66             input_mock, (126, 128),
67             interpolation=Resize.OPENCV_INTERPOLATION['CUBIC']
68         )
69
70     def test_resize_without_save_aspect_ratio(self):
71         name = 'mock_preprocessor'
72         config = {'type': 'resize', 'dst_width': 150, 'dst_height': 150}
73         input_image = np.ones((100, 50, 3))
74         resize = Preprocessor.provide('resize', config, name)
75
76         result = resize(DataRepresentation(input_image)).data
77
78         assert result.shape == (150, 150, 3)
79
80     def test_resize_save_aspect_ratio_unknown_raise_config_error(self):
81         with pytest.raises(ConfigError):
82             Preprocessor.provide(
83                 'resize', {'type': 'resize', 'dst_width': 100, 'dst_height': 150, 'aspect_ratio_scale': 'unknown'}
84             )
85
86     def test_resize_save_aspect_ratio_height(self):
87         input_image = np.ones((100, 50, 3))
88         resize = Preprocessor.provide('resize', {
89             'type': 'resize', 'dst_width': 100, 'dst_height': 150,
90             'interpolation': 'CUBIC', 'aspect_ratio_scale': 'height'
91         })
92         result = resize(DataRepresentation(input_image)).data
93
94         assert result.shape == (300, 100, 3)
95
96     def test_resize_save_aspect_ratio_width(self):
97         input_image = np.ones((100, 50, 3))
98         resize = Preprocessor.provide('resize', {
99             'type': 'resize', 'dst_width': 150, 'dst_height': 150, 'aspect_ratio_scale': 'width'
100         })
101         result = resize(DataRepresentation(input_image)).data
102
103         assert result.shape == (150, 75, 3)
104
105     def test_resize_save_aspect_ratio_for_greater_dim(self):
106         input_image = np.ones((100, 50, 3))
107         resize = Preprocessor.provide('resize', {
108             'type': 'resize',
109             'dst_width': 100,
110             'dst_height': 150,
111             'aspect_ratio_scale': 'greater'
112         })
113         result = resize(DataRepresentation(input_image)).data
114
115         assert result.shape == (300, 100, 3)
116
117     def test_resize_to_negative_size_raise_config_error(self):
118         with pytest.raises(ConfigError):
119             Preprocessor.provide('resize', {'type': 'resize', 'size': -100})
120
121     def test_resize_to_negative_destination_width_raise_config_error(self):
122         with pytest.raises(ConfigError):
123             Preprocessor.provide('resize', {'type': 'resize', 'dst_width': -100, 'dst_height': 100})
124
125     def test_resize_to_negative_destination_height_raise_config_error(self):
126         with pytest.raises(ConfigError):
127             Preprocessor.provide('resize', {'type': 'resize', 'dst_width': 100, 'dst_height': -100})
128
129     def test_resize_with_both_provided_size_and_dst_height_dst_width_warn(self):
130         input_image = np.ones((100, 50, 3))
131
132         with pytest.warns(None) as warnings:
133             resize = Preprocessor.provide(
134                 'resize', {'type': 'resize', 'dst_width': 100, 'dst_height': 100, 'size': 200}
135             )
136             assert len(warnings) == 1
137             result = resize(DataRepresentation(input_image)).data
138             assert result.shape == (200, 200, 3)
139
140     def test_resize_provided_only_dst_height_raise_config_error(self):
141         with pytest.raises(ValueError):
142             Preprocessor.provide('resize', {'type': 'resize', 'dst_height': 100})
143
144     def test_resize_provided_only_dst_width_raise_config_error(self):
145         with pytest.raises(ValueError):
146             Preprocessor.provide('resize', {'type': 'resize', 'dst_width': 100})
147
148
149 class TestNormalization:
150     def test_normalization_without_mean_and_std_raise_config_error(self):
151         with pytest.raises(ConfigError):
152             Preprocessor.provide('normalization', {'type': 'normalization'})
153
154     def test_custom_normalization_with_mean(self):
155         normalization = Preprocessor.provide('normalization', {'type': 'normalization', 'mean': '(1, 2, 3)'})
156         source = np.full_like((3, 300, 300), 100)
157         input_ref = source.copy() - (1, 2, 3)
158         result = normalization(DataRepresentation(source))
159
160         assert normalization.mean == (1, 2, 3)
161         assert normalization.std is None
162         assert np.all(input_ref == result.data)
163         assert result.metadata == {'image_size': (3,)}
164
165     def test_custom_normalization_with_precomputed_mean(self):
166         normalization = Preprocessor.provide('normalization', {'type': 'normalization', 'mean': 'cifar10'})
167
168         source = np.full_like((3, 300, 300), 100)
169         input_ref = source.copy() - normalization.PRECOMPUTED_MEANS['cifar10']
170         result = normalization(DataRepresentation(source))
171
172         assert normalization.mean == normalization.PRECOMPUTED_MEANS['cifar10']
173         assert normalization.std is None
174         assert np.all(input_ref == result.data)
175         assert result.metadata == {'image_size': (3,)}
176
177     def test_custom_normalization_with_mean_as_scalar(self):
178         normalization = Preprocessor.provide('normalization', {'type': 'normalization', 'mean': '1'})
179
180         source = np.full_like((3, 300, 300), 100)
181         input_ref = source.copy() - 1
182         result = normalization(DataRepresentation(source))
183
184         assert normalization.mean == (1.0, )
185         assert normalization.std is None
186         assert np.all(input_ref == result.data)
187         assert result.metadata == {'image_size': (3,)}
188
189     def test_custom_normalization_with_std(self):
190         normalization = Preprocessor.provide('normalization', {'type': 'normalization', 'std': '(1, 2, 3)'})
191
192         source = np.full_like((3, 300, 300), 100)
193         input_ref = source.copy() / (1, 2, 3)
194         result = normalization(DataRepresentation(source))
195
196         assert normalization.mean is None
197         assert normalization.std == (1, 2, 3)
198         assert np.all(input_ref == result.data)
199         assert result.metadata == {'image_size': (3,)}
200
201     def test_custom_normalization_with_precomputed_std(self):
202         normalization = Preprocessor.provide('normalization', {'type': 'normalization', 'std': 'cifar10'})
203
204         source = np.full_like((3, 300, 300), 100)
205         input_ref = source.copy() / normalization.PRECOMPUTED_STDS['cifar10']
206         result = normalization(DataRepresentation(source))
207
208         assert normalization.mean is None
209         assert normalization.std == normalization.PRECOMPUTED_STDS['cifar10']
210         assert np.all(input_ref == result.data)
211         assert result.metadata == {'image_size': (3,)}
212
213     def test_custom_normalization_with_std_as_scalar(self):
214         normalization = Preprocessor.provide('normalization', {'type': 'normalization', 'std': '2'})
215         source = np.full_like((3, 300, 300), 100)
216         input_ref = source.copy() / 2
217         result = normalization(DataRepresentation(source))
218
219         assert normalization.mean is None
220         assert normalization.std == (2.0, )
221         assert np.all(input_ref == result.data)
222         assert result.metadata == {'image_size': (3,)}
223
224     def test_custom_normalization_with_mean_and_std(self):
225         normalization = Preprocessor.provide(
226             'normalization', {'type': 'normalization', 'mean': '(1, 2, 3)', 'std': '(4, 5, 6)'}
227         )
228
229         input_ = np.full_like((3, 300, 300), 100)
230         input_ref = (input_ - (1, 2, 3)) / (4, 5, 6)
231         result = normalization(DataRepresentation(input_))
232
233         assert normalization.mean == (1, 2, 3)
234         assert normalization.std == (4, 5, 6)
235         assert np.all(input_ref == result.data)
236         assert result.metadata == {'image_size': (3,)}
237
238     def test_custom_normalization_with_mean_and_std_as_scalars(self):
239         normalization = Preprocessor.provide('normalization', {'type': 'normalization', 'mean': '2', 'std': '5'})
240
241         input_ = np.full_like((3, 300, 300), 100)
242         input_ref = (input_ - (2, )) / (5, )
243         result = normalization(DataRepresentation(input_))
244
245         assert normalization.mean == (2, )
246         assert normalization.std == (5, )
247         assert np.all(input_ref == result.data)
248         assert result.metadata == {'image_size': (3,)}
249
250     def test_normalization_with_zero_in_std_values_raise_config_error(self):
251         with pytest.raises(ConfigError):
252             Preprocessor.provide('normalization', {'type': 'normalization', 'std': '(4, 0, 6)'})
253
254     def test_normalization_with_zero_as_std_value_raise_config_error(self):
255         with pytest.raises(ConfigError):
256             Preprocessor.provide('normalization', {'type': 'normalization', 'std': '0'})
257
258     def test_normalization_with_not_channel_wise_mean_list_raise_config_error(self):
259         with pytest.raises(ConfigError):
260             Preprocessor.provide('normalization', {'type': 'normalization', 'mean': '3, 2'})
261
262     def test_normalization_with_not_channel_wise_std_list_raise_config_error(self):
263         with pytest.raises(ConfigError):
264             Preprocessor.provide('normalization', {'type': 'normalization', 'std': '3, 2'})
265
266     def test_normalization_with_unknown_precomputed_mean_raise_config_error(self):
267         with pytest.raises(ValueError):
268             Preprocessor.provide('normalization', {'type': 'normalization', 'mean': 'unknown'})
269
270     def test_normalization_with_unknown_precomputed_std_raise_config_error(self):
271         with pytest.raises(ValueError):
272             Preprocessor.provide('normalization', {'type': 'normalization', 'std': 'unknown'})
273
274
275 class TestPreprocessingEvaluator:
276     def test_preprocessing_evaluator(self):
277         config = [{'type': 'normalization', 'mean': '(1, 2, 3)'}, {'type': 'resize', 'size': 200}]
278         preprocessor = PreprocessingExecutor(config)
279
280         assert 2 == len(preprocessor.processors)
281         assert isinstance(preprocessor.processors[0], Normalize)
282         assert isinstance(preprocessor.processors[1], Resize)
283         assert preprocessor.processors[0].mean == (1, 2, 3)
284         assert preprocessor.processors[1].dst_width == 200
285
286
287 class TestCrop:
288     def test_crop_higher(self):
289         crop = Crop({'dst_width': 50, 'dst_height': 33, 'type': 'crop'})
290         image = np.zeros((100, 100, 3))
291         image_rep = crop(DataRepresentation(image))
292
293         assert image_rep.data.shape == (33, 50, 3)
294         assert image_rep.metadata == {'image_size': (100, 100, 3)}
295
296     def test_crop_to_size(self):
297         crop = Crop({'size': 50, 'type': 'crop'})
298         image = np.zeros((100, 100, 3))
299         image_rep = crop(DataRepresentation(image))
300
301         assert image_rep.data.shape == (50, 50, 3)
302         assert image_rep.metadata == {'image_size': (100, 100, 3)}
303
304     def test_crop_higher_non_symmetric(self):
305         crop = Crop({'dst_width': 50, 'dst_height': 12, 'type': 'crop'})
306         image = np.zeros((70, 50, 3))
307         image_rep = crop(DataRepresentation(image))
308
309         assert image_rep.data.shape == (12, 50, 3)
310         assert image_rep.metadata == {'image_size': (70, 50, 3)}
311
312     def test_crop_less(self):
313         crop = Crop({'dst_width': 151, 'dst_height': 42, 'type': 'crop'})
314         image = np.zeros((30, 30, 3))
315         image_rep = crop(DataRepresentation(image))
316
317         assert image_rep.data.shape == (42, 151, 3)
318         assert image_rep.metadata == {'image_size': (30, 30, 3)}
319
320     def test_crop_less_non_symmetric(self):
321         crop = Crop({'dst_width': 42, 'dst_height': 151, 'type': 'crop'})
322         image = np.zeros((30, 40, 3))
323         image_rep = crop(DataRepresentation(image))
324
325         assert image_rep.data.shape == (151, 42, 3)
326         assert image_rep.metadata == {'image_size': (30, 40, 3)}
327
328     def test_crop_to_negative_size_raise_config_error(self):
329         with pytest.raises(ConfigError):
330             Crop({'size': -151, 'type': 'crop'})
331
332     def test_crop_to_negative_destination_width_raise_config_error(self):
333         with pytest.raises(ConfigError):
334             Crop({'dst_width': -100, 'dst_height': 100, 'type': 'crop'})
335
336     def test_crop_to_negative_destination_height_raise_config_error(self):
337         with pytest.raises(ConfigError):
338             Crop({'dst_width': 100, 'dst_height': -100, 'type': 'crop'})
339
340     def test_crop_with_both_provided_size_and_dst_height_dst_width_warn(self):
341         image = np.zeros((30, 40, 3))
342         with pytest.warns(None) as warnings:
343             crop = Crop({'dst_width': 100, 'dst_height': 100, 'size': 200, 'type': 'crop'})
344             assert len(warnings) == 1
345             result = crop.process(DataRepresentation(image))
346             assert result.data.shape == (200, 200, 3)
347             assert result.metadata == {'image_size': (30, 40, 3)}
348
349
350 class TestFlip:
351     def test_horizontal_flip(self):
352         image = np.random.randint(0, 255, (30, 40, 3))
353         expected_image = cv2.flip(image, 0)
354         flip = Flip({'type': 'flip', 'mode': 'horizontal'})
355         assert np.array_equal(expected_image, flip.process(DataRepresentation(image)).data)
356
357     def test_vertical_flip(self):
358         image = np.random.randint(0, 255, (30, 40, 3))
359         expected_image = cv2.flip(image, 1)
360         flip = Flip({'type': 'flip', 'mode': 'vertical'})
361         assert np.array_equal(expected_image, flip.process(DataRepresentation(image)).data)
362
363     def test_flip_raise_config_error_if_mode_not_provided(self):
364         with pytest.raises(ConfigError):
365             Flip({'type': 'flip'})
366
367     def test_flip_raise_config_error_if_mode_unknown(self):
368         with pytest.raises(ConfigError):
369             Flip({'type': 'flip', 'mode': 'unknown'})
370
371
372 class TestBGRtoRGB:
373     def test_bgr_to_rgb(self):
374         image = np.random.randint(0, 255, (30, 40, 3)).astype(np.uint8)
375         expected_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
376         bgr_to_rgb = BgrToRgb({'type': 'bgr_to_rgb'})
377         assert np.array_equal(expected_image, bgr_to_rgb.process(DataRepresentation(image)).data)
378
379
380 class TestCropRect:
381     def test_crop_rect_if_rect_not_provided(self):
382         image = np.zeros((30, 40, 3))
383         crop_rect = CropRect({'type': 'crop_rect'})
384         assert np.array_equal(image, crop_rect(image, {}))
385
386     def test_crop_rect_if_rect_equal_image(self):
387         image = np.zeros((30, 40, 3))
388         crop_rect = CropRect({'type': 'crop_rect'})
389         assert np.array_equal(image, crop_rect(DataRepresentation(image), {'rect': [0, 0, 40, 30]}).data)
390
391     def test_crop_rect(self):
392         image = np.zeros((30, 40, 3))
393         image[:, 20:, :] = 1
394         expected_image = np.ones((30, 20, 3))
395         crop_rect = CropRect({'type': 'crop_rect'})
396         assert np.array_equal(expected_image, crop_rect(DataRepresentation(image), {'rect': [20, 0, 40, 30]}).data)
397
398     def test_crop_rect_negative_coordinates_of_rect(self):
399         image = np.zeros((30, 40, 3))
400         image[:, 20:, :] = 1
401         expected_image = image
402         crop_rect = CropRect({'type': 'crop_rect'})
403         assert np.array_equal(expected_image, crop_rect(DataRepresentation(image), {'rect': [-20, 0, 40, 30]}).data)
404
405     def test_crop_rect_more_image_size_coordinates_of_rect(self):
406         image = np.zeros((30, 40, 3))
407         image[:, 20:, :] = 1
408         expected_image = np.ones((30, 20, 3))
409         crop_rect = CropRect({'type': 'crop_rect'})
410         assert np.array_equal(expected_image, crop_rect(DataRepresentation(image), {'rect': [20, 0, 40, 50]}).data)
411
412
413 class TestExtendAroundRect:
414     def test_default_extend_around_rect_without_rect(self):
415         image = np.random.randint(0, 255, (30, 40, 3)).astype(np.uint8)
416         expected_image = image
417         extend_image_around_rect = ExtendAroundRect({'type': 'extend_around_rect'})
418         assert np.array_equal(expected_image, extend_image_around_rect(DataRepresentation(image), {}).data)
419
420     def test_default_extend_around_rect(self):
421         image = np.random.randint(0, 255, (30, 40, 3)).astype(np.uint8)
422         expected_image = image
423         extend_image_around_rect = ExtendAroundRect({'type': 'extend_around_rect'})
424         assert np.array_equal(
425             expected_image, extend_image_around_rect(DataRepresentation(image), {'rect': [20, 0, 40, 30]}).data
426         )
427
428     def test_extend_around_rect_with_positive_augmentation(self):
429         image = np.random.randint(0, 255, (30, 40, 3)).astype(np.uint8)
430         expected_image = cv2.copyMakeBorder(image, int(15.5), int(31), int(0), int(11), cv2.BORDER_REPLICATE)
431         extend_image_around_rect = ExtendAroundRect({'type': 'extend_around_rect', 'augmentation_param': 0.5})
432         assert np.array_equal(
433             expected_image, extend_image_around_rect(DataRepresentation(image), {'rect': [20, 0, 40, 30]}).data
434         )
435
436     def test_extend_around_rect_with_negative_augmentation(self):
437         image = np.random.randint(0, 255, (30, 40, 3)).astype(np.uint8)
438         expected_image = image
439         extend_image_around_rect = ExtendAroundRect({'type': 'extend_around_rect', 'augmentation_param': -0.5})
440         assert np.array_equal(
441             expected_image, extend_image_around_rect(DataRepresentation(image), {'rect': [20, 0, 40, 30]}).data
442         )
443
444     def test_extend_around_rect_with_rect_equal_image(self):
445         image = np.random.randint(0, 255, (30, 40, 3)).astype(np.uint8)
446         expected_image = cv2.copyMakeBorder(image, int(15.5), int(31), int(20.5), int(41), cv2.BORDER_REPLICATE)
447         extend_image_around_rect = ExtendAroundRect({'type': 'extend_around_rect', 'augmentation_param': 0.5})
448         assert np.array_equal(
449             expected_image, extend_image_around_rect(DataRepresentation(image), {'rect': [0, 0, 40, 30]}).data
450         )
451
452     def test_extend_around_rect_negative_coordinates_of_rect(self):
453         image = np.random.randint(0, 255, (30, 40, 3)).astype(np.uint8)
454         expected_image = cv2.copyMakeBorder(image, int(15.5), int(31), int(20.5), int(41), cv2.BORDER_REPLICATE)
455         extend_image_around_rect = ExtendAroundRect({'type': 'extend_around_rect', 'augmentation_param': 0.5})
456         assert np.array_equal(
457             expected_image, extend_image_around_rect(DataRepresentation(image), {'rect': [-20, 0, 40, 30]}).data
458         )
459
460     def test_extend_around_rect_more_image_size_coordinates_of_rect(self):
461         image = np.random.randint(0, 255, (30, 40, 3)).astype(np.uint8)
462         expected_image = cv2.copyMakeBorder(image, int(15.5), int(31), int(0), int(11), cv2.BORDER_REPLICATE)
463         extend_image_around_rect = ExtendAroundRect({'type': 'extend_around_rect', 'augmentation_param': 0.5})
464         assert np.array_equal(
465             expected_image, extend_image_around_rect(DataRepresentation(image), {'rect': [20, 0, 40, 50]}).data
466         )
467
468
469 class TestPointAlignment:
470     def test_point_alignment_width_negative_size_raise_config_error(self):
471         with pytest.raises(ConfigError):
472             PointAligner({'type': 'point_alignment', 'size': -100})
473
474     def test_point_alignment_negative_destination_width_raise_config_error(self):
475         with pytest.raises(ConfigError):
476             PointAligner({'type': 'point_alignment', 'dst_width': -100, 'dst_height': 100})
477
478     def test_point_alignment_to_negative_destination_height_raise_config_error(self):
479         with pytest.raises(ValueError):
480             PointAligner({'type': 'point_alignment', 'dst_width': 100, 'dst_height': -100})
481
482     def test_point_alignment_provided_only_dst_height_raise_config_error(self):
483         with pytest.raises(ValueError):
484             PointAligner({'type': 'point_alignment', 'dst_height': 100})
485
486     def test_point_alignment_provided_only_dst_width_raise_config_error(self):
487         with pytest.raises(ValueError):
488             PointAligner({'type': 'point_alignment', 'dst_width': 100})
489
490     def test_point_alignment_both_provided_size_and_dst_height_dst_width_warn(self):
491         input_image = np.ones((100, 50, 3))
492
493         with pytest.warns(None) as warnings:
494             point_aligner = PointAligner({'type': 'point_alignment', 'dst_width': 100, 'dst_height': 100, 'size': 200})
495             assert len(warnings) == 1
496             result = point_aligner(DataRepresentation(input_image), {}).data
497             assert result.shape == (100, 50, 3)
498
499     def test_point_alignment_not_provided_points_im_meta(self):
500         input_image = np.ones((100, 50, 3))
501
502         point_aligner = PointAligner({'type': 'point_alignment', 'dst_width': 100, 'dst_height': 100})
503         result = point_aligner(DataRepresentation(input_image), {}).data
504         assert result.shape == (100, 50, 3)
505
506     def test_point_alignment_default_use_normalization(self):
507         image = np.random.randint(0, 255, (40, 40, 3)).astype(np.uint8)
508
509         point_aligner = PointAligner({'type': 'point_alignment', 'dst_width': 40, 'dst_height': 40})
510         result = point_aligner(
511             DataRepresentation(image), {'keypoints': PointAligner.ref_landmarks.reshape(-1).tolist()}
512         ).data
513         transformation_matrix = point_aligner.transformation_from_points(
514             point_aligner.ref_landmarks * 40, point_aligner.ref_landmarks
515         )
516         expected_result = cv2.warpAffine(image, transformation_matrix, (40, 40), flags=cv2.WARP_INVERSE_MAP)
517
518         assert np.array_equal(result, expected_result)
519
520     def test_point_alignment_use_normalization(self):
521         image = np.random.randint(0, 255, (40, 40, 3)).astype(np.uint8)
522
523         point_aligner = PointAligner({'type': 'point_alignment', 'dst_width': 40, 'dst_height': 40, 'normalize': True})
524         result = point_aligner(
525             DataRepresentation(image), {'keypoints': PointAligner.ref_landmarks.reshape(-1).tolist()}
526         ).data
527         transformation_matrix = point_aligner.transformation_from_points(
528             point_aligner.ref_landmarks * 40, point_aligner.ref_landmarks
529         )
530         expected_result = cv2.warpAffine(image, transformation_matrix, (40, 40), flags=cv2.WARP_INVERSE_MAP)
531
532         assert np.array_equal(result, expected_result)
533
534     def test_point_alignment_without_normalization(self):
535         image = np.random.randint(0, 255, (40, 40, 3)).astype(np.uint8)
536
537         point_aligner = PointAligner({'type': 'point_alignment', 'dst_width': 40, 'dst_height': 40, 'normalize': False})
538         result = point_aligner(
539             DataRepresentation(image), {'keypoints': PointAligner.ref_landmarks.reshape(-1).tolist()}
540         ).data
541         transformation_matrix = point_aligner.transformation_from_points(
542             point_aligner.ref_landmarks * 40, point_aligner.ref_landmarks * 40
543         )
544         expected_result = cv2.warpAffine(image, transformation_matrix, (40, 40), flags=cv2.WARP_INVERSE_MAP)
545
546         assert np.array_equal(result, expected_result)
547
548     def test_point_alignment_with_drawing_points(self):
549         image = np.random.randint(0, 255, (40, 40, 3)).astype(np.uint8)
550
551         point_aligner = PointAligner({
552             'type': 'point_alignment', 'dst_width': 40, 'dst_height': 40, 'draw_points': True
553         })
554         result = point_aligner(
555             DataRepresentation(image), {'keypoints': PointAligner.ref_landmarks.reshape(-1).tolist()}
556         ).data
557         transformation_matrix = point_aligner.transformation_from_points(
558             point_aligner.ref_landmarks * 40, point_aligner.ref_landmarks
559         )
560         expected_result = image
561         for point in PointAligner.ref_landmarks:
562             cv2.circle(expected_result, (int(point[0]), int(point[1])), 5, (255, 0, 0), -1)
563         expected_result = cv2.warpAffine(expected_result, transformation_matrix, (40, 40), flags=cv2.WARP_INVERSE_MAP)
564
565         assert np.array_equal(result, expected_result)
566
567     def test_point_alignment_with_resizing(self):
568         image = np.random.randint(0, 255, (80, 80, 3)).astype(np.uint8)
569
570         point_aligner = PointAligner({'type': 'point_alignment', 'size': 40})
571         result = point_aligner(
572             DataRepresentation(image), {'keypoints': PointAligner.ref_landmarks.reshape(-1).tolist()}
573         ).data
574         transformation_matrix = point_aligner.transformation_from_points(
575             point_aligner.ref_landmarks * 40, point_aligner.ref_landmarks * 0.5
576         )
577         expected_result = cv2.resize(image, (40, 40))
578         expected_result = cv2.warpAffine(expected_result, transformation_matrix, (40, 40), flags=cv2.WARP_INVERSE_MAP)
579
580         assert np.array_equal(result, expected_result)
581
582
583 class TestPreprocessorExtraArgs:
584     def test_resize_raise_config_error_on_extra_args(self):
585         with pytest.raises(ConfigError):
586             Preprocessor.provide('resize', {'type': 'resize', 'size': 1, 'something_extra': 'extra'})
587
588     def test_normalization_raise_config_error_on_extra_args(self):
589         with pytest.raises(ConfigError):
590             Preprocessor.provide('normalization', {'type': 'normalization', 'mean': 0, 'something_extra': 'extra'})
591
592     def test_bgr_to_rgb_raise_config_error_on_extra_args(self):
593         with pytest.raises(ConfigError):
594             Preprocessor.provide('bgr_to_rgb',  {'type': 'bgr_to_rgb', 'something_extra': 'extra'})
595
596     def test_flip_raise_config_error_on_extra_args(self):
597         with pytest.raises(ConfigError):
598             Preprocessor.provide('flip', {'type': 'flip', 'something_extra': 'extra'})
599
600     def test_crop_accuracy_raise_config_error_on_extra_args(self):
601         with pytest.raises(ConfigError):
602             Preprocessor.provide('crop', {'type': 'crop', 'size': 1, 'something_extra': 'extra'})
603
604     def test_extend_around_rect_raise_config_error_on_extra_args(self):
605         with pytest.raises(ConfigError):
606             Preprocessor.provide('extend_around_rect', {'type': 'extend_around_rect', 'something_extra': 'extra'})
607
608     def test_point_alignment_raise_config_error_on_extra_args(self):
609         with pytest.raises(ConfigError):
610             Preprocessor.provide('point_alignment', {'type': 'point_alignment', 'something_extra': 'extra'})