arkindex-base-worker 0.3.7rc5__py3-none-any.whl → 0.5.0a1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {arkindex_base_worker-0.3.7rc5.dist-info → arkindex_base_worker-0.5.0a1.dist-info}/METADATA +18 -19
- arkindex_base_worker-0.5.0a1.dist-info/RECORD +61 -0
- {arkindex_base_worker-0.3.7rc5.dist-info → arkindex_base_worker-0.5.0a1.dist-info}/WHEEL +1 -1
- {arkindex_base_worker-0.3.7rc5.dist-info → arkindex_base_worker-0.5.0a1.dist-info}/top_level.txt +2 -0
- arkindex_worker/cache.py +1 -1
- arkindex_worker/image.py +167 -2
- arkindex_worker/models.py +18 -0
- arkindex_worker/utils.py +98 -4
- arkindex_worker/worker/__init__.py +117 -218
- arkindex_worker/worker/base.py +39 -46
- arkindex_worker/worker/classification.py +34 -18
- arkindex_worker/worker/corpus.py +86 -0
- arkindex_worker/worker/dataset.py +89 -26
- arkindex_worker/worker/element.py +352 -91
- arkindex_worker/worker/entity.py +13 -11
- arkindex_worker/worker/image.py +21 -0
- arkindex_worker/worker/metadata.py +26 -16
- arkindex_worker/worker/process.py +92 -0
- arkindex_worker/worker/task.py +5 -4
- arkindex_worker/worker/training.py +25 -10
- arkindex_worker/worker/transcription.py +89 -68
- arkindex_worker/worker/version.py +3 -1
- hooks/pre_gen_project.py +3 -0
- tests/__init__.py +8 -0
- tests/conftest.py +47 -58
- tests/test_base_worker.py +212 -12
- tests/test_dataset_worker.py +294 -437
- tests/test_elements_worker/{test_classifications.py → test_classification.py} +216 -100
- tests/test_elements_worker/test_cli.py +3 -11
- tests/test_elements_worker/test_corpus.py +168 -0
- tests/test_elements_worker/test_dataset.py +106 -157
- tests/test_elements_worker/test_element.py +427 -0
- tests/test_elements_worker/test_element_create_multiple.py +715 -0
- tests/test_elements_worker/test_element_create_single.py +528 -0
- tests/test_elements_worker/test_element_list_children.py +969 -0
- tests/test_elements_worker/test_element_list_parents.py +530 -0
- tests/test_elements_worker/{test_entities.py → test_entity_create.py} +37 -195
- tests/test_elements_worker/test_entity_list_and_check.py +160 -0
- tests/test_elements_worker/test_image.py +66 -0
- tests/test_elements_worker/test_metadata.py +252 -161
- tests/test_elements_worker/test_process.py +89 -0
- tests/test_elements_worker/test_task.py +8 -18
- tests/test_elements_worker/test_training.py +17 -8
- tests/test_elements_worker/test_transcription_create.py +873 -0
- tests/test_elements_worker/test_transcription_create_with_elements.py +951 -0
- tests/test_elements_worker/test_transcription_list.py +450 -0
- tests/test_elements_worker/test_version.py +60 -0
- tests/test_elements_worker/test_worker.py +578 -293
- tests/test_image.py +542 -209
- tests/test_merge.py +1 -2
- tests/test_utils.py +89 -4
- worker-demo/tests/__init__.py +0 -0
- worker-demo/tests/conftest.py +32 -0
- worker-demo/tests/test_worker.py +12 -0
- worker-demo/worker_demo/__init__.py +6 -0
- worker-demo/worker_demo/worker.py +19 -0
- arkindex_base_worker-0.3.7rc5.dist-info/RECORD +0 -41
- tests/test_elements_worker/test_elements.py +0 -2713
- tests/test_elements_worker/test_transcriptions.py +0 -2119
- {arkindex_base_worker-0.3.7rc5.dist-info → arkindex_base_worker-0.5.0a1.dist-info}/LICENSE +0 -0
tests/test_image.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
import math
|
|
2
|
-
import unittest
|
|
3
3
|
import uuid
|
|
4
4
|
from io import BytesIO
|
|
5
5
|
from operator import attrgetter
|
|
@@ -9,6 +9,7 @@ import pytest
|
|
|
9
9
|
from PIL import Image, ImageChops, ImageOps
|
|
10
10
|
from requests import HTTPError
|
|
11
11
|
|
|
12
|
+
import arkindex_worker.image
|
|
12
13
|
from arkindex_worker.cache import CachedElement, create_tables, init_cache_db
|
|
13
14
|
from arkindex_worker.image import (
|
|
14
15
|
IIIF_FULL,
|
|
@@ -18,21 +19,64 @@ from arkindex_worker.image import (
|
|
|
18
19
|
download_tiles,
|
|
19
20
|
open_image,
|
|
20
21
|
polygon_bounding_box,
|
|
22
|
+
resized_images,
|
|
21
23
|
revert_orientation,
|
|
22
24
|
trim_polygon,
|
|
25
|
+
update_pillow_image_size_limit,
|
|
23
26
|
upload_image,
|
|
24
27
|
)
|
|
25
28
|
from arkindex_worker.models import Element
|
|
29
|
+
from tests import FIXTURES_DIR
|
|
26
30
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
ROTATED_MIRRORED_IMAGE = FIXTURES / "rotated_mirrored_image.jpg"
|
|
31
|
+
TILE = FIXTURES_DIR / "test_image.jpg"
|
|
32
|
+
FULL_IMAGE = FIXTURES_DIR / "tiled_image.jpg"
|
|
33
|
+
ROTATED_IMAGE = FIXTURES_DIR / "rotated_image.jpg"
|
|
34
|
+
MIRRORED_IMAGE = FIXTURES_DIR / "mirrored_image.jpg"
|
|
35
|
+
ROTATED_MIRRORED_IMAGE = FIXTURES_DIR / "rotated_mirrored_image.jpg"
|
|
33
36
|
TEST_IMAGE = {"width": 800, "height": 300}
|
|
34
37
|
|
|
35
38
|
|
|
39
|
+
@pytest.fixture
|
|
40
|
+
def mock_page():
|
|
41
|
+
class Page(Element):
|
|
42
|
+
@property
|
|
43
|
+
def crop(self):
|
|
44
|
+
# Image from Socface (https://socface.site.ined.fr/) project (AD026)
|
|
45
|
+
image = Image.open(FIXTURES_DIR / "AD026_6M_00505_0001_0373.jpg")
|
|
46
|
+
x, y, element_width, element_height = polygon_bounding_box(
|
|
47
|
+
self.zone.polygon
|
|
48
|
+
)
|
|
49
|
+
return image.crop(box=(x, y, x + element_width, y + element_height))
|
|
50
|
+
|
|
51
|
+
def open_image(
|
|
52
|
+
self,
|
|
53
|
+
*args,
|
|
54
|
+
max_width: int | None = None,
|
|
55
|
+
max_height: int | None = None,
|
|
56
|
+
use_full_image: bool | None = False,
|
|
57
|
+
**kwargs,
|
|
58
|
+
) -> Image.Image:
|
|
59
|
+
crop = self.crop.copy()
|
|
60
|
+
crop.thumbnail(
|
|
61
|
+
size=(
|
|
62
|
+
max_width or self.zone.image.width,
|
|
63
|
+
max_height or self.zone.image.height,
|
|
64
|
+
)
|
|
65
|
+
)
|
|
66
|
+
return crop
|
|
67
|
+
|
|
68
|
+
return Page(
|
|
69
|
+
id="page_id",
|
|
70
|
+
name="1",
|
|
71
|
+
zone={
|
|
72
|
+
"polygon": [[0, 0], [2000, 0], [2000, 3000], [0, 3000], [0, 0]],
|
|
73
|
+
"image": {"width": 2000, "height": 3000},
|
|
74
|
+
},
|
|
75
|
+
rotation_angle=0,
|
|
76
|
+
mirrored=False,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
36
80
|
def _root_mean_square(img_a, img_b):
|
|
37
81
|
"""
|
|
38
82
|
Get the root-mean-square difference between two images for fuzzy matching
|
|
@@ -45,10 +89,33 @@ def _root_mean_square(img_a, img_b):
|
|
|
45
89
|
)
|
|
46
90
|
|
|
47
91
|
|
|
92
|
+
@pytest.mark.parametrize(
|
|
93
|
+
("max_image_pixels", "expected_image_pixels"),
|
|
94
|
+
[
|
|
95
|
+
# Pillow Image size limit not updated
|
|
96
|
+
(None, Image.MAX_IMAGE_PIXELS),
|
|
97
|
+
# Pillow Image size limit set to None
|
|
98
|
+
("0", None),
|
|
99
|
+
(0, None),
|
|
100
|
+
# Update Pillow Image size limit
|
|
101
|
+
("1", 1),
|
|
102
|
+
(1, 1),
|
|
103
|
+
],
|
|
104
|
+
)
|
|
105
|
+
def test_update_pillow_image_size_limit(max_image_pixels, expected_image_pixels):
|
|
106
|
+
MAX_IMAGE_PIXELS = Image.MAX_IMAGE_PIXELS
|
|
107
|
+
|
|
108
|
+
@update_pillow_image_size_limit
|
|
109
|
+
def function() -> int | None:
|
|
110
|
+
return Image.MAX_IMAGE_PIXELS
|
|
111
|
+
|
|
112
|
+
assert function(max_image_pixels=max_image_pixels) == expected_image_pixels
|
|
113
|
+
assert Image.MAX_IMAGE_PIXELS == MAX_IMAGE_PIXELS
|
|
114
|
+
|
|
115
|
+
|
|
48
116
|
def test_download_tiles(responses):
|
|
49
117
|
expected = Image.open(FULL_IMAGE).convert("RGB")
|
|
50
|
-
|
|
51
|
-
tile_bytes = tile.read()
|
|
118
|
+
tile_bytes = TILE.read_bytes()
|
|
52
119
|
|
|
53
120
|
responses.add(
|
|
54
121
|
responses.GET,
|
|
@@ -76,9 +143,8 @@ def test_download_tiles_crop(responses):
|
|
|
76
143
|
"""
|
|
77
144
|
expected = Image.open(FULL_IMAGE).convert("RGB")
|
|
78
145
|
tile_bytes = BytesIO()
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
ImageOps.pad(Image.open(tile), (181, 241)).save(tile_bytes, format="jpeg")
|
|
146
|
+
# Add one extra pixel to each tile to return slightly bigger tiles
|
|
147
|
+
ImageOps.pad(Image.open(TILE), (181, 241)).save(tile_bytes, format="jpeg")
|
|
82
148
|
|
|
83
149
|
tile_bytes = tile_bytes.getvalue()
|
|
84
150
|
|
|
@@ -165,74 +231,33 @@ def test_open_image_rotate_mirror(rotation_angle, mirrored, expected_path):
|
|
|
165
231
|
assert _root_mean_square(expected, actual) <= 15.0
|
|
166
232
|
|
|
167
233
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
[
|
|
194
|
-
|
|
195
|
-
[197, 206],
|
|
196
|
-
[99, 200],
|
|
197
|
-
]
|
|
198
|
-
assert (
|
|
199
|
-
trim_polygon(bad_polygon, TEST_IMAGE["width"], TEST_IMAGE["height"])
|
|
200
|
-
== expected_polygon
|
|
201
|
-
)
|
|
202
|
-
|
|
203
|
-
def test_trim_polygon_good_polygon(self):
|
|
204
|
-
good_polygon = (
|
|
205
|
-
(12, 56),
|
|
206
|
-
(29, 60),
|
|
207
|
-
(35, 61),
|
|
208
|
-
(42, 59),
|
|
209
|
-
(58, 57),
|
|
210
|
-
(65, 61),
|
|
211
|
-
(72, 57),
|
|
212
|
-
(12, 56),
|
|
213
|
-
)
|
|
214
|
-
expected_polygon = [
|
|
215
|
-
[12, 56],
|
|
216
|
-
[29, 60],
|
|
217
|
-
[35, 61],
|
|
218
|
-
[42, 59],
|
|
219
|
-
[58, 57],
|
|
220
|
-
[65, 61],
|
|
221
|
-
[72, 57],
|
|
222
|
-
[12, 56],
|
|
223
|
-
]
|
|
224
|
-
assert (
|
|
225
|
-
trim_polygon(good_polygon, TEST_IMAGE["width"], TEST_IMAGE["height"])
|
|
226
|
-
== expected_polygon
|
|
227
|
-
)
|
|
228
|
-
|
|
229
|
-
def test_trim_polygon_invalid_polygon(self):
|
|
230
|
-
"""
|
|
231
|
-
An assertion error is raised the polygon input isn't a list or tuple
|
|
232
|
-
"""
|
|
233
|
-
bad_polygon = {
|
|
234
|
-
"polygon": [
|
|
235
|
-
[99, 200],
|
|
234
|
+
@pytest.mark.parametrize(
|
|
235
|
+
("polygon", "error"),
|
|
236
|
+
[
|
|
237
|
+
# Polygon input isn't a list or tuple
|
|
238
|
+
(
|
|
239
|
+
{
|
|
240
|
+
"polygon": [
|
|
241
|
+
[99, 200],
|
|
242
|
+
[25, 224],
|
|
243
|
+
[0, 0],
|
|
244
|
+
[0, 300],
|
|
245
|
+
[102, 300],
|
|
246
|
+
[260, 300],
|
|
247
|
+
[288, 295],
|
|
248
|
+
[296, 260],
|
|
249
|
+
[352, 259],
|
|
250
|
+
[106, 210],
|
|
251
|
+
[197, 206],
|
|
252
|
+
[99, 208],
|
|
253
|
+
]
|
|
254
|
+
},
|
|
255
|
+
"Input polygon must be a valid list or tuple of points.",
|
|
256
|
+
),
|
|
257
|
+
# Point coordinates are not integers
|
|
258
|
+
(
|
|
259
|
+
[
|
|
260
|
+
[9.9, 200],
|
|
236
261
|
[25, 224],
|
|
237
262
|
[0, 0],
|
|
238
263
|
[0, 300],
|
|
@@ -243,136 +268,161 @@ class TestTrimPolygon(unittest.TestCase):
|
|
|
243
268
|
[352, 259],
|
|
244
269
|
[106, 210],
|
|
245
270
|
[197, 206],
|
|
246
|
-
[99,
|
|
247
|
-
]
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
[
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
[
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
271
|
+
[99, 20.8],
|
|
272
|
+
],
|
|
273
|
+
"Polygon point coordinates must be integers.",
|
|
274
|
+
),
|
|
275
|
+
# Point coordinates are not lists or tuples
|
|
276
|
+
(
|
|
277
|
+
[
|
|
278
|
+
[12, 56],
|
|
279
|
+
[29, 60],
|
|
280
|
+
[35, 61],
|
|
281
|
+
"[42, 59]",
|
|
282
|
+
[58, 57],
|
|
283
|
+
[65, 61],
|
|
284
|
+
[72, 57],
|
|
285
|
+
[12, 56],
|
|
286
|
+
],
|
|
287
|
+
"Polygon points must be tuples or lists.",
|
|
288
|
+
),
|
|
289
|
+
# Point coordinates are not lists or tuples of length 2
|
|
290
|
+
(
|
|
291
|
+
[
|
|
292
|
+
[12, 56],
|
|
293
|
+
[29, 60, 3],
|
|
294
|
+
[35, 61],
|
|
295
|
+
[42, 59],
|
|
296
|
+
[58, 57],
|
|
297
|
+
[65, 61],
|
|
298
|
+
[72, 57],
|
|
299
|
+
[12, 56],
|
|
300
|
+
],
|
|
301
|
+
"Polygon points must be tuples or lists of 2 elements.",
|
|
302
|
+
),
|
|
303
|
+
# None of the polygon's points are inside the image
|
|
304
|
+
(
|
|
305
|
+
[
|
|
306
|
+
[999, 200],
|
|
307
|
+
[1097, 224],
|
|
308
|
+
[1020, 251],
|
|
309
|
+
[1232, 350],
|
|
310
|
+
[1312, 364],
|
|
311
|
+
[1325, 310],
|
|
312
|
+
[1318, 295],
|
|
313
|
+
[1296, 260],
|
|
314
|
+
[1352, 259],
|
|
315
|
+
[1006, 210],
|
|
316
|
+
[997, 206],
|
|
317
|
+
[999, 200],
|
|
318
|
+
],
|
|
319
|
+
"This polygon is entirely outside the image's bounds.",
|
|
320
|
+
),
|
|
321
|
+
],
|
|
322
|
+
)
|
|
323
|
+
def test_trim_polygon_errors(polygon, error):
|
|
324
|
+
with pytest.raises(AssertionError, match=error):
|
|
325
|
+
trim_polygon(polygon, TEST_IMAGE["width"], TEST_IMAGE["height"])
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def test_trim_polygon_negative_coordinates():
|
|
329
|
+
"""
|
|
330
|
+
Negative coordinates are ignored and replaced by 0 with no error being thrown
|
|
331
|
+
"""
|
|
332
|
+
polygon = [
|
|
333
|
+
[99, 200],
|
|
334
|
+
[25, 224],
|
|
335
|
+
[-8, -52],
|
|
336
|
+
[-12, 350],
|
|
337
|
+
[102, 364],
|
|
338
|
+
[260, 310],
|
|
339
|
+
[288, 295],
|
|
340
|
+
[296, 260],
|
|
341
|
+
[352, 259],
|
|
342
|
+
[106, 210],
|
|
343
|
+
[197, 206],
|
|
344
|
+
[99, 200],
|
|
345
|
+
]
|
|
346
|
+
expected_polygon = [
|
|
347
|
+
[99, 200],
|
|
348
|
+
[25, 224],
|
|
349
|
+
[0, 0],
|
|
350
|
+
[0, 300],
|
|
351
|
+
[102, 300],
|
|
352
|
+
[260, 300],
|
|
353
|
+
[288, 295],
|
|
354
|
+
[296, 260],
|
|
355
|
+
[352, 259],
|
|
356
|
+
[106, 210],
|
|
357
|
+
[197, 206],
|
|
358
|
+
[99, 200],
|
|
359
|
+
]
|
|
360
|
+
assert (
|
|
361
|
+
trim_polygon(polygon, TEST_IMAGE["width"], TEST_IMAGE["height"])
|
|
362
|
+
== expected_polygon
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def test_trim_polygon_partially_outside_image():
|
|
367
|
+
polygon = [
|
|
368
|
+
[99, 200],
|
|
369
|
+
[197, 224],
|
|
370
|
+
[120, 251],
|
|
371
|
+
[232, 350],
|
|
372
|
+
[312, 364],
|
|
373
|
+
[325, 310],
|
|
374
|
+
[318, 295],
|
|
375
|
+
[296, 260],
|
|
376
|
+
[352, 259],
|
|
377
|
+
[106, 210],
|
|
378
|
+
[197, 206],
|
|
379
|
+
[99, 200],
|
|
380
|
+
]
|
|
381
|
+
expected_polygon = [
|
|
382
|
+
[99, 200],
|
|
383
|
+
[197, 224],
|
|
384
|
+
[120, 251],
|
|
385
|
+
[232, 300],
|
|
386
|
+
[312, 300],
|
|
387
|
+
[325, 300],
|
|
388
|
+
[318, 295],
|
|
389
|
+
[296, 260],
|
|
390
|
+
[352, 259],
|
|
391
|
+
[106, 210],
|
|
392
|
+
[197, 206],
|
|
393
|
+
[99, 200],
|
|
394
|
+
]
|
|
395
|
+
assert (
|
|
396
|
+
trim_polygon(polygon, TEST_IMAGE["width"], TEST_IMAGE["height"])
|
|
397
|
+
== expected_polygon
|
|
398
|
+
)
|
|
291
399
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
]
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
"""
|
|
319
|
-
bad_polygon = [
|
|
320
|
-
[9.9, 200],
|
|
321
|
-
[25, 224],
|
|
322
|
-
[0, 0],
|
|
323
|
-
[0, 300],
|
|
324
|
-
[102, 300],
|
|
325
|
-
[260, 300],
|
|
326
|
-
[288, 295],
|
|
327
|
-
[296, 260],
|
|
328
|
-
[352, 259],
|
|
329
|
-
[106, 210],
|
|
330
|
-
[197, 206],
|
|
331
|
-
[99, 20.8],
|
|
332
|
-
]
|
|
333
|
-
with pytest.raises(
|
|
334
|
-
AssertionError, match="Polygon point coordinates must be integers."
|
|
335
|
-
):
|
|
336
|
-
trim_polygon(bad_polygon, TEST_IMAGE["width"], TEST_IMAGE["height"])
|
|
337
|
-
|
|
338
|
-
def test_trim_polygon_invalid_points_1(self):
|
|
339
|
-
"""
|
|
340
|
-
An assertion error is raised when point coordinates are not lists or tuples
|
|
341
|
-
"""
|
|
342
|
-
bad_polygon = [
|
|
343
|
-
[12, 56],
|
|
344
|
-
[29, 60],
|
|
345
|
-
[35, 61],
|
|
346
|
-
"[42, 59]",
|
|
347
|
-
[58, 57],
|
|
348
|
-
[65, 61],
|
|
349
|
-
[72, 57],
|
|
350
|
-
[12, 56],
|
|
351
|
-
]
|
|
352
|
-
with pytest.raises(
|
|
353
|
-
AssertionError, match="Polygon points must be tuples or lists."
|
|
354
|
-
):
|
|
355
|
-
trim_polygon(bad_polygon, TEST_IMAGE["width"], TEST_IMAGE["height"])
|
|
356
|
-
|
|
357
|
-
def test_trim_polygon_invalid_points_2(self):
|
|
358
|
-
"""
|
|
359
|
-
An assertion error is raised when point coordinates are not lists or tuples of length 2
|
|
360
|
-
"""
|
|
361
|
-
bad_polygon = [
|
|
362
|
-
[12, 56],
|
|
363
|
-
[29, 60, 3],
|
|
364
|
-
[35, 61],
|
|
365
|
-
[42, 59],
|
|
366
|
-
[58, 57],
|
|
367
|
-
[65, 61],
|
|
368
|
-
[72, 57],
|
|
369
|
-
[12, 56],
|
|
370
|
-
]
|
|
371
|
-
with pytest.raises(
|
|
372
|
-
AssertionError,
|
|
373
|
-
match="Polygon points must be tuples or lists of 2 elements.",
|
|
374
|
-
):
|
|
375
|
-
trim_polygon(bad_polygon, TEST_IMAGE["width"], TEST_IMAGE["height"])
|
|
400
|
+
|
|
401
|
+
def test_trim_polygon():
|
|
402
|
+
polygon = (
|
|
403
|
+
(12, 56),
|
|
404
|
+
(29, 60),
|
|
405
|
+
(35, 61),
|
|
406
|
+
(42, 59),
|
|
407
|
+
(58, 57),
|
|
408
|
+
(65, 61),
|
|
409
|
+
(72, 57),
|
|
410
|
+
(12, 56),
|
|
411
|
+
)
|
|
412
|
+
expected_polygon = [
|
|
413
|
+
[12, 56],
|
|
414
|
+
[29, 60],
|
|
415
|
+
[35, 61],
|
|
416
|
+
[42, 59],
|
|
417
|
+
[58, 57],
|
|
418
|
+
[65, 61],
|
|
419
|
+
[72, 57],
|
|
420
|
+
[12, 56],
|
|
421
|
+
]
|
|
422
|
+
assert (
|
|
423
|
+
trim_polygon(polygon, TEST_IMAGE["width"], TEST_IMAGE["height"])
|
|
424
|
+
== expected_polygon
|
|
425
|
+
)
|
|
376
426
|
|
|
377
427
|
|
|
378
428
|
@pytest.mark.parametrize(
|
|
@@ -584,3 +634,286 @@ def test_upload_image(responses):
|
|
|
584
634
|
|
|
585
635
|
assert len(responses.calls) == 1
|
|
586
636
|
assert list(map(attrgetter("request.url"), responses.calls)) == [dest_url]
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
@pytest.mark.parametrize(
|
|
640
|
+
(
|
|
641
|
+
"max_pixels_short",
|
|
642
|
+
"max_pixels_long",
|
|
643
|
+
"max_bytes",
|
|
644
|
+
"expected_sizes",
|
|
645
|
+
"expected_logs",
|
|
646
|
+
),
|
|
647
|
+
[
|
|
648
|
+
# No limits provided
|
|
649
|
+
(
|
|
650
|
+
None,
|
|
651
|
+
None,
|
|
652
|
+
None,
|
|
653
|
+
[(2000, 3000), (1000, 1500), (200, 300)],
|
|
654
|
+
[
|
|
655
|
+
(logging.INFO, "This element's image dimensions are (2000 x 3000)."),
|
|
656
|
+
(logging.WARNING, "The image was resized to (1000 x 1500)."),
|
|
657
|
+
(logging.WARNING, "The image was resized to (200 x 300)."),
|
|
658
|
+
],
|
|
659
|
+
),
|
|
660
|
+
# Image already under all three limits
|
|
661
|
+
(
|
|
662
|
+
10000,
|
|
663
|
+
10000,
|
|
664
|
+
4000000, # 4MB
|
|
665
|
+
[(2000, 3000), (1000, 1500), (200, 300)],
|
|
666
|
+
[
|
|
667
|
+
(logging.INFO, "This element's image dimensions are (2000 x 3000)."),
|
|
668
|
+
(logging.WARNING, "The image was resized to (1000 x 1500)."),
|
|
669
|
+
(logging.WARNING, "The image was resized to (200 x 300)."),
|
|
670
|
+
],
|
|
671
|
+
),
|
|
672
|
+
# Image above the "short side in pixels" limit
|
|
673
|
+
(
|
|
674
|
+
1000,
|
|
675
|
+
None,
|
|
676
|
+
None,
|
|
677
|
+
[(1000, 1500), (500, 750), (100, 150)],
|
|
678
|
+
[
|
|
679
|
+
(logging.INFO, "This element's image dimensions are (2000 x 3000)."),
|
|
680
|
+
(
|
|
681
|
+
logging.WARNING,
|
|
682
|
+
"Maximum image dimensions supported are (1000 x 3000).",
|
|
683
|
+
),
|
|
684
|
+
(logging.WARNING, "The image will be resized."),
|
|
685
|
+
(logging.WARNING, "The image was resized to (1000 x 1500)."),
|
|
686
|
+
(logging.WARNING, "The image was resized to (500 x 750)."),
|
|
687
|
+
(logging.WARNING, "The image was resized to (100 x 150)."),
|
|
688
|
+
],
|
|
689
|
+
),
|
|
690
|
+
# Image above the "long side in pixels" limit
|
|
691
|
+
(
|
|
692
|
+
None,
|
|
693
|
+
2000,
|
|
694
|
+
None,
|
|
695
|
+
[(1333, 2000), (667, 1000), (133, 200)],
|
|
696
|
+
[
|
|
697
|
+
(logging.INFO, "This element's image dimensions are (2000 x 3000)."),
|
|
698
|
+
(
|
|
699
|
+
logging.WARNING,
|
|
700
|
+
"Maximum image dimensions supported are (2000 x 2000).",
|
|
701
|
+
),
|
|
702
|
+
(logging.WARNING, "The image will be resized."),
|
|
703
|
+
(logging.WARNING, "The image was resized to (1333 x 2000)."),
|
|
704
|
+
(logging.WARNING, "The image was resized to (667 x 1000)."),
|
|
705
|
+
(logging.WARNING, "The image was resized to (133 x 200)."),
|
|
706
|
+
],
|
|
707
|
+
),
|
|
708
|
+
# Image above the "size in bytes" limit
|
|
709
|
+
(
|
|
710
|
+
None,
|
|
711
|
+
None,
|
|
712
|
+
100000, # 100kB
|
|
713
|
+
[(200, 300)],
|
|
714
|
+
[
|
|
715
|
+
(logging.INFO, "This element's image dimensions are (2000 x 3000)."),
|
|
716
|
+
(logging.WARNING, "The image size is 1.3 MB."),
|
|
717
|
+
(logging.WARNING, "Maximum image input size supported is 100.0 kB."),
|
|
718
|
+
(logging.WARNING, "The image will be resized."),
|
|
719
|
+
(logging.WARNING, "The image was resized to (1000 x 1500)."),
|
|
720
|
+
(logging.WARNING, "The image size is 214.2 kB."),
|
|
721
|
+
(logging.WARNING, "Maximum image input size supported is 100.0 kB."),
|
|
722
|
+
(logging.WARNING, "The image will be resized."),
|
|
723
|
+
(logging.WARNING, "The image was resized to (200 x 300)."),
|
|
724
|
+
],
|
|
725
|
+
),
|
|
726
|
+
# Image above all three limits
|
|
727
|
+
(
|
|
728
|
+
1000,
|
|
729
|
+
2000,
|
|
730
|
+
100000, # 100kB
|
|
731
|
+
[(500, 750), (100, 150)],
|
|
732
|
+
[
|
|
733
|
+
(logging.INFO, "This element's image dimensions are (2000 x 3000)."),
|
|
734
|
+
(
|
|
735
|
+
logging.WARNING,
|
|
736
|
+
"Maximum image dimensions supported are (1000 x 2000).",
|
|
737
|
+
),
|
|
738
|
+
(logging.WARNING, "The image will be resized."),
|
|
739
|
+
(logging.WARNING, "The image was resized to (1000 x 1500)."),
|
|
740
|
+
(logging.WARNING, "The image size is 214.2 kB."),
|
|
741
|
+
(logging.WARNING, "Maximum image input size supported is 100.0 kB."),
|
|
742
|
+
(logging.WARNING, "The image will be resized."),
|
|
743
|
+
(logging.WARNING, "The image was resized to (500 x 750)."),
|
|
744
|
+
(logging.WARNING, "The image was resized to (100 x 150)."),
|
|
745
|
+
],
|
|
746
|
+
),
|
|
747
|
+
# Image always above all three limits
|
|
748
|
+
(
|
|
749
|
+
50,
|
|
750
|
+
50,
|
|
751
|
+
50, # 50B
|
|
752
|
+
[],
|
|
753
|
+
[
|
|
754
|
+
(logging.INFO, "This element's image dimensions are (2000 x 3000)."),
|
|
755
|
+
(logging.WARNING, "Maximum image dimensions supported are (50 x 50)."),
|
|
756
|
+
(logging.WARNING, "The image will be resized."),
|
|
757
|
+
(logging.WARNING, "The image was resized to (33 x 50)."),
|
|
758
|
+
(logging.WARNING, "The image size is 1.0 kB."),
|
|
759
|
+
(logging.WARNING, "Maximum image input size supported is 50 Bytes."),
|
|
760
|
+
(logging.WARNING, "The image will be resized."),
|
|
761
|
+
(logging.WARNING, "The image was resized to (17 x 25)."),
|
|
762
|
+
(logging.WARNING, "The image size is 785 Bytes."),
|
|
763
|
+
(logging.WARNING, "Maximum image input size supported is 50 Bytes."),
|
|
764
|
+
(logging.WARNING, "The image will be resized."),
|
|
765
|
+
(logging.WARNING, "The image was resized to (3 x 5)."),
|
|
766
|
+
(logging.WARNING, "The image size is 689 Bytes."),
|
|
767
|
+
(logging.WARNING, "Maximum image input size supported is 50 Bytes."),
|
|
768
|
+
(logging.WARNING, "The image will be resized."),
|
|
769
|
+
],
|
|
770
|
+
),
|
|
771
|
+
],
|
|
772
|
+
)
|
|
773
|
+
def test_resized_images_portrait_format(
|
|
774
|
+
monkeypatch,
|
|
775
|
+
max_pixels_short,
|
|
776
|
+
max_pixels_long,
|
|
777
|
+
max_bytes,
|
|
778
|
+
expected_sizes,
|
|
779
|
+
expected_logs,
|
|
780
|
+
mock_page,
|
|
781
|
+
caplog,
|
|
782
|
+
):
|
|
783
|
+
monkeypatch.setattr(arkindex_worker.image, "IMAGE_RATIOS", [1.0, 0.5, 0.1])
|
|
784
|
+
|
|
785
|
+
# Short side is the width, long side is the height
|
|
786
|
+
assert mock_page.zone.image.width < mock_page.zone.image.height
|
|
787
|
+
|
|
788
|
+
assert [
|
|
789
|
+
Image.open(image).size
|
|
790
|
+
for image in resized_images(
|
|
791
|
+
element=mock_page,
|
|
792
|
+
max_pixels_short=max_pixels_short,
|
|
793
|
+
max_pixels_long=max_pixels_long,
|
|
794
|
+
max_bytes=max_bytes,
|
|
795
|
+
)
|
|
796
|
+
] == expected_sizes
|
|
797
|
+
|
|
798
|
+
assert [
|
|
799
|
+
(record.levelno, record.message) for record in caplog.records
|
|
800
|
+
] == expected_logs
|
|
801
|
+
|
|
802
|
+
|
|
803
|
+
@pytest.mark.parametrize(
|
|
804
|
+
("max_pixels_short", "max_pixels_long", "expected_sizes", "expected_logs"),
|
|
805
|
+
[
|
|
806
|
+
# Image above the "short side in pixels" limit
|
|
807
|
+
(
|
|
808
|
+
1000,
|
|
809
|
+
None,
|
|
810
|
+
[(1500, 1000), (750, 500), (150, 100)],
|
|
811
|
+
[
|
|
812
|
+
(logging.INFO, "This element's image dimensions are (3000 x 2000)."),
|
|
813
|
+
(
|
|
814
|
+
logging.WARNING,
|
|
815
|
+
"Maximum image dimensions supported are (3000 x 1000).",
|
|
816
|
+
),
|
|
817
|
+
(logging.WARNING, "The image will be resized."),
|
|
818
|
+
(logging.WARNING, "The image was resized to (1500 x 1000)."),
|
|
819
|
+
(logging.WARNING, "The image was resized to (750 x 500)."),
|
|
820
|
+
(logging.WARNING, "The image was resized to (150 x 100)."),
|
|
821
|
+
],
|
|
822
|
+
),
|
|
823
|
+
# Image above the "long side in pixels" limit
|
|
824
|
+
(
|
|
825
|
+
None,
|
|
826
|
+
2000,
|
|
827
|
+
[(2000, 1333), (1000, 667), (200, 133)],
|
|
828
|
+
[
|
|
829
|
+
(logging.INFO, "This element's image dimensions are (3000 x 2000)."),
|
|
830
|
+
(
|
|
831
|
+
logging.WARNING,
|
|
832
|
+
"Maximum image dimensions supported are (2000 x 2000).",
|
|
833
|
+
),
|
|
834
|
+
(logging.WARNING, "The image will be resized."),
|
|
835
|
+
(logging.WARNING, "The image was resized to (2000 x 1333)."),
|
|
836
|
+
(logging.WARNING, "The image was resized to (1000 x 667)."),
|
|
837
|
+
(logging.WARNING, "The image was resized to (200 x 133)."),
|
|
838
|
+
],
|
|
839
|
+
),
|
|
840
|
+
# Image above the two pixels limits
|
|
841
|
+
(
|
|
842
|
+
1000,
|
|
843
|
+
2000,
|
|
844
|
+
[(1500, 1000), (750, 500), (150, 100)],
|
|
845
|
+
[
|
|
846
|
+
(logging.INFO, "This element's image dimensions are (3000 x 2000)."),
|
|
847
|
+
(
|
|
848
|
+
logging.WARNING,
|
|
849
|
+
"Maximum image dimensions supported are (2000 x 1000).",
|
|
850
|
+
),
|
|
851
|
+
(logging.WARNING, "The image will be resized."),
|
|
852
|
+
(logging.WARNING, "The image was resized to (1500 x 1000)."),
|
|
853
|
+
(logging.WARNING, "The image was resized to (750 x 500)."),
|
|
854
|
+
(logging.WARNING, "The image was resized to (150 x 100)."),
|
|
855
|
+
],
|
|
856
|
+
),
|
|
857
|
+
],
|
|
858
|
+
)
|
|
859
|
+
def test_resized_images_landscape_format(
|
|
860
|
+
monkeypatch,
|
|
861
|
+
max_pixels_short,
|
|
862
|
+
max_pixels_long,
|
|
863
|
+
expected_sizes,
|
|
864
|
+
expected_logs,
|
|
865
|
+
mock_page,
|
|
866
|
+
caplog,
|
|
867
|
+
):
|
|
868
|
+
monkeypatch.setattr(arkindex_worker.image, "IMAGE_RATIOS", [1.0, 0.5, 0.1])
|
|
869
|
+
|
|
870
|
+
# Short side is the height, long side is the width
|
|
871
|
+
mock_page.zone = {
|
|
872
|
+
"polygon": [[0, 0], [3000, 0], [3000, 2000], [0, 2000], [0, 0]],
|
|
873
|
+
"image": {"width": 3000, "height": 2000},
|
|
874
|
+
}
|
|
875
|
+
assert mock_page.zone.image.height < mock_page.zone.image.width
|
|
876
|
+
|
|
877
|
+
assert [
|
|
878
|
+
Image.open(image).size
|
|
879
|
+
for image in resized_images(
|
|
880
|
+
element=mock_page,
|
|
881
|
+
max_pixels_short=max_pixels_short,
|
|
882
|
+
max_pixels_long=max_pixels_long,
|
|
883
|
+
max_bytes=None,
|
|
884
|
+
)
|
|
885
|
+
] == expected_sizes
|
|
886
|
+
|
|
887
|
+
assert [
|
|
888
|
+
(record.levelno, record.message) for record in caplog.records
|
|
889
|
+
] == expected_logs
|
|
890
|
+
|
|
891
|
+
|
|
892
|
+
def test_resized_images_use_base64(monkeypatch, mock_page, caplog):
|
|
893
|
+
monkeypatch.setattr(arkindex_worker.image, "IMAGE_RATIOS", [1.0, 0.5, 0.25, 0.1])
|
|
894
|
+
|
|
895
|
+
assert list(
|
|
896
|
+
map(
|
|
897
|
+
len,
|
|
898
|
+
resized_images(
|
|
899
|
+
element=mock_page,
|
|
900
|
+
max_pixels_short=None,
|
|
901
|
+
max_pixels_long=None,
|
|
902
|
+
max_bytes=100000,
|
|
903
|
+
use_base64=True,
|
|
904
|
+
),
|
|
905
|
+
)
|
|
906
|
+
) == [65280, 11892]
|
|
907
|
+
|
|
908
|
+
assert [(record.levelno, record.message) for record in caplog.records] == [
|
|
909
|
+
(logging.INFO, "This element's image dimensions are (2000 x 3000)."),
|
|
910
|
+
(logging.WARNING, "The image size is 1.7 MB."),
|
|
911
|
+
(logging.WARNING, "Maximum image input size supported is 100.0 kB."),
|
|
912
|
+
(logging.WARNING, "The image will be resized."),
|
|
913
|
+
(logging.WARNING, "The image was resized to (1000 x 1500)."),
|
|
914
|
+
(logging.WARNING, "The image size is 285.6 kB."),
|
|
915
|
+
(logging.WARNING, "Maximum image input size supported is 100.0 kB."),
|
|
916
|
+
(logging.WARNING, "The image will be resized."),
|
|
917
|
+
(logging.WARNING, "The image was resized to (500 x 750)."),
|
|
918
|
+
(logging.WARNING, "The image was resized to (200 x 300)."),
|
|
919
|
+
]
|