onnxtr 0.5.1__py3-none-any.whl → 0.6.0__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.
- onnxtr/contrib/__init__.py +1 -0
- onnxtr/contrib/artefacts.py +6 -8
- onnxtr/contrib/base.py +7 -16
- onnxtr/file_utils.py +1 -3
- onnxtr/io/elements.py +45 -59
- onnxtr/io/html.py +0 -2
- onnxtr/io/image.py +1 -4
- onnxtr/io/pdf.py +3 -5
- onnxtr/io/reader.py +4 -10
- onnxtr/models/_utils.py +10 -17
- onnxtr/models/builder.py +17 -30
- onnxtr/models/classification/models/mobilenet.py +7 -12
- onnxtr/models/classification/predictor/base.py +6 -7
- onnxtr/models/classification/zoo.py +25 -11
- onnxtr/models/detection/_utils/base.py +3 -7
- onnxtr/models/detection/core.py +2 -8
- onnxtr/models/detection/models/differentiable_binarization.py +10 -17
- onnxtr/models/detection/models/fast.py +10 -17
- onnxtr/models/detection/models/linknet.py +10 -17
- onnxtr/models/detection/postprocessor/base.py +3 -9
- onnxtr/models/detection/predictor/base.py +4 -5
- onnxtr/models/detection/zoo.py +20 -6
- onnxtr/models/engine.py +9 -9
- onnxtr/models/factory/hub.py +3 -7
- onnxtr/models/predictor/base.py +29 -30
- onnxtr/models/predictor/predictor.py +4 -5
- onnxtr/models/preprocessor/base.py +8 -12
- onnxtr/models/recognition/core.py +0 -1
- onnxtr/models/recognition/models/crnn.py +11 -23
- onnxtr/models/recognition/models/master.py +9 -15
- onnxtr/models/recognition/models/parseq.py +8 -12
- onnxtr/models/recognition/models/sar.py +8 -12
- onnxtr/models/recognition/models/vitstr.py +9 -15
- onnxtr/models/recognition/predictor/_utils.py +6 -9
- onnxtr/models/recognition/predictor/base.py +3 -3
- onnxtr/models/recognition/utils.py +2 -7
- onnxtr/models/recognition/zoo.py +19 -7
- onnxtr/models/zoo.py +7 -9
- onnxtr/transforms/base.py +17 -6
- onnxtr/utils/common_types.py +7 -8
- onnxtr/utils/data.py +7 -11
- onnxtr/utils/fonts.py +1 -6
- onnxtr/utils/geometry.py +18 -49
- onnxtr/utils/multithreading.py +3 -5
- onnxtr/utils/reconstitution.py +6 -8
- onnxtr/utils/repr.py +1 -2
- onnxtr/utils/visualization.py +12 -21
- onnxtr/utils/vocabs.py +1 -2
- onnxtr/version.py +1 -1
- {onnxtr-0.5.1.dist-info → onnxtr-0.6.0.dist-info}/METADATA +70 -41
- onnxtr-0.6.0.dist-info/RECORD +75 -0
- {onnxtr-0.5.1.dist-info → onnxtr-0.6.0.dist-info}/WHEEL +1 -1
- onnxtr-0.5.1.dist-info/RECORD +0 -75
- {onnxtr-0.5.1.dist-info → onnxtr-0.6.0.dist-info}/LICENSE +0 -0
- {onnxtr-0.5.1.dist-info → onnxtr-0.6.0.dist-info}/top_level.txt +0 -0
- {onnxtr-0.5.1.dist-info → onnxtr-0.6.0.dist-info}/zip-safe +0 -0
onnxtr/utils/geometry.py
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
|
|
6
6
|
from copy import deepcopy
|
|
7
7
|
from math import ceil
|
|
8
|
-
from typing import List, Optional, Tuple, Union
|
|
9
8
|
|
|
10
9
|
import cv2
|
|
11
10
|
import numpy as np
|
|
@@ -34,11 +33,9 @@ def bbox_to_polygon(bbox: BoundingBox) -> Polygon4P:
|
|
|
34
33
|
"""Convert a bounding box to a polygon
|
|
35
34
|
|
|
36
35
|
Args:
|
|
37
|
-
----
|
|
38
36
|
bbox: a bounding box
|
|
39
37
|
|
|
40
38
|
Returns:
|
|
41
|
-
-------
|
|
42
39
|
a polygon
|
|
43
40
|
"""
|
|
44
41
|
return bbox[0], (bbox[1][0], bbox[0][1]), (bbox[0][0], bbox[1][1]), bbox[1]
|
|
@@ -48,29 +45,27 @@ def polygon_to_bbox(polygon: Polygon4P) -> BoundingBox:
|
|
|
48
45
|
"""Convert a polygon to a bounding box
|
|
49
46
|
|
|
50
47
|
Args:
|
|
51
|
-
----
|
|
52
48
|
polygon: a polygon
|
|
53
49
|
|
|
54
50
|
Returns:
|
|
55
|
-
-------
|
|
56
51
|
a bounding box
|
|
57
52
|
"""
|
|
58
53
|
x, y = zip(*polygon)
|
|
59
54
|
return (min(x), min(y)), (max(x), max(y))
|
|
60
55
|
|
|
61
56
|
|
|
62
|
-
def detach_scores(boxes:
|
|
57
|
+
def detach_scores(boxes: list[np.ndarray]) -> tuple[list[np.ndarray], list[np.ndarray]]:
|
|
63
58
|
"""Detach the objectness scores from box predictions
|
|
59
|
+
|
|
64
60
|
Args:
|
|
65
|
-
----
|
|
66
61
|
boxes: list of arrays with boxes of shape (N, 5) or (N, 5, 2)
|
|
62
|
+
|
|
67
63
|
Returns:
|
|
68
|
-
-------
|
|
69
64
|
a tuple of two lists: the first one contains the boxes without the objectness scores,
|
|
70
65
|
the second one contains the objectness scores
|
|
71
66
|
"""
|
|
72
67
|
|
|
73
|
-
def _detach(boxes: np.ndarray) ->
|
|
68
|
+
def _detach(boxes: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
|
|
74
69
|
if boxes.ndim == 2:
|
|
75
70
|
return boxes[:, :-1], boxes[:, -1]
|
|
76
71
|
return boxes[:, :-1], boxes[:, -1, -1]
|
|
@@ -83,12 +78,10 @@ def shape_translate(data: np.ndarray, format: str) -> np.ndarray:
|
|
|
83
78
|
"""Translate the shape of the input data to the desired format
|
|
84
79
|
|
|
85
80
|
Args:
|
|
86
|
-
----
|
|
87
81
|
data: input data in shape (B, C, H, W) or (B, H, W, C) or (C, H, W) or (H, W, C)
|
|
88
82
|
format: target format ('BCHW', 'BHWC', 'CHW', or 'HWC')
|
|
89
83
|
|
|
90
84
|
Returns:
|
|
91
|
-
-------
|
|
92
85
|
the reshaped data
|
|
93
86
|
"""
|
|
94
87
|
# Get the current shape
|
|
@@ -120,11 +113,10 @@ def shape_translate(data: np.ndarray, format: str) -> np.ndarray:
|
|
|
120
113
|
return data
|
|
121
114
|
|
|
122
115
|
|
|
123
|
-
def resolve_enclosing_bbox(bboxes:
|
|
116
|
+
def resolve_enclosing_bbox(bboxes: list[BoundingBox] | np.ndarray) -> BoundingBox | np.ndarray:
|
|
124
117
|
"""Compute enclosing bbox either from:
|
|
125
118
|
|
|
126
119
|
Args:
|
|
127
|
-
----
|
|
128
120
|
bboxes: boxes in one of the following formats:
|
|
129
121
|
|
|
130
122
|
- an array of boxes: (*, 4), where boxes have this shape:
|
|
@@ -133,7 +125,6 @@ def resolve_enclosing_bbox(bboxes: Union[List[BoundingBox], np.ndarray]) -> Unio
|
|
|
133
125
|
- a list of BoundingBox
|
|
134
126
|
|
|
135
127
|
Returns:
|
|
136
|
-
-------
|
|
137
128
|
a (1, 4) array (enclosing boxarray), or a BoundingBox
|
|
138
129
|
"""
|
|
139
130
|
if isinstance(bboxes, np.ndarray):
|
|
@@ -144,11 +135,10 @@ def resolve_enclosing_bbox(bboxes: Union[List[BoundingBox], np.ndarray]) -> Unio
|
|
|
144
135
|
return (min(x), min(y)), (max(x), max(y))
|
|
145
136
|
|
|
146
137
|
|
|
147
|
-
def resolve_enclosing_rbbox(rbboxes:
|
|
138
|
+
def resolve_enclosing_rbbox(rbboxes: list[np.ndarray], intermed_size: int = 1024) -> np.ndarray:
|
|
148
139
|
"""Compute enclosing rotated bbox either from:
|
|
149
140
|
|
|
150
141
|
Args:
|
|
151
|
-
----
|
|
152
142
|
rbboxes: boxes in one of the following formats:
|
|
153
143
|
|
|
154
144
|
- an array of boxes: (*, 4, 2), where boxes have this shape:
|
|
@@ -158,26 +148,23 @@ def resolve_enclosing_rbbox(rbboxes: List[np.ndarray], intermed_size: int = 1024
|
|
|
158
148
|
intermed_size: size of the intermediate image
|
|
159
149
|
|
|
160
150
|
Returns:
|
|
161
|
-
-------
|
|
162
151
|
a (4, 2) array (enclosing rotated box)
|
|
163
152
|
"""
|
|
164
153
|
cloud: np.ndarray = np.concatenate(rbboxes, axis=0)
|
|
165
154
|
# Convert to absolute for minAreaRect
|
|
166
155
|
cloud *= intermed_size
|
|
167
156
|
rect = cv2.minAreaRect(cloud.astype(np.int32))
|
|
168
|
-
return cv2.boxPoints(rect) / intermed_size
|
|
157
|
+
return cv2.boxPoints(rect) / intermed_size
|
|
169
158
|
|
|
170
159
|
|
|
171
160
|
def rotate_abs_points(points: np.ndarray, angle: float = 0.0) -> np.ndarray:
|
|
172
161
|
"""Rotate points counter-clockwise.
|
|
173
162
|
|
|
174
163
|
Args:
|
|
175
|
-
----
|
|
176
164
|
points: array of size (N, 2)
|
|
177
165
|
angle: angle between -90 and +90 degrees
|
|
178
166
|
|
|
179
167
|
Returns:
|
|
180
|
-
-------
|
|
181
168
|
Rotated points
|
|
182
169
|
"""
|
|
183
170
|
angle_rad = angle * np.pi / 180.0 # compute radian angle for np functions
|
|
@@ -187,16 +174,14 @@ def rotate_abs_points(points: np.ndarray, angle: float = 0.0) -> np.ndarray:
|
|
|
187
174
|
return np.matmul(points, rotation_mat.T)
|
|
188
175
|
|
|
189
176
|
|
|
190
|
-
def compute_expanded_shape(img_shape:
|
|
177
|
+
def compute_expanded_shape(img_shape: tuple[int, int], angle: float) -> tuple[int, int]:
|
|
191
178
|
"""Compute the shape of an expanded rotated image
|
|
192
179
|
|
|
193
180
|
Args:
|
|
194
|
-
----
|
|
195
181
|
img_shape: the height and width of the image
|
|
196
182
|
angle: angle between -90 and +90 degrees
|
|
197
183
|
|
|
198
184
|
Returns:
|
|
199
|
-
-------
|
|
200
185
|
the height and width of the rotated image
|
|
201
186
|
"""
|
|
202
187
|
points: np.ndarray = np.array([
|
|
@@ -213,21 +198,19 @@ def compute_expanded_shape(img_shape: Tuple[int, int], angle: float) -> Tuple[in
|
|
|
213
198
|
def rotate_abs_geoms(
|
|
214
199
|
geoms: np.ndarray,
|
|
215
200
|
angle: float,
|
|
216
|
-
img_shape:
|
|
201
|
+
img_shape: tuple[int, int],
|
|
217
202
|
expand: bool = True,
|
|
218
203
|
) -> np.ndarray:
|
|
219
204
|
"""Rotate a batch of bounding boxes or polygons by an angle around the
|
|
220
205
|
image center.
|
|
221
206
|
|
|
222
207
|
Args:
|
|
223
|
-
----
|
|
224
208
|
geoms: (N, 4) or (N, 4, 2) array of ABSOLUTE coordinate boxes
|
|
225
209
|
angle: anti-clockwise rotation angle in degrees
|
|
226
210
|
img_shape: the height and width of the image
|
|
227
211
|
expand: whether the image should be padded to avoid information loss
|
|
228
212
|
|
|
229
213
|
Returns:
|
|
230
|
-
-------
|
|
231
214
|
A batch of rotated polygons (N, 4, 2)
|
|
232
215
|
"""
|
|
233
216
|
# Switch to polygons
|
|
@@ -253,19 +236,17 @@ def rotate_abs_geoms(
|
|
|
253
236
|
return rotated_polys
|
|
254
237
|
|
|
255
238
|
|
|
256
|
-
def remap_boxes(loc_preds: np.ndarray, orig_shape:
|
|
239
|
+
def remap_boxes(loc_preds: np.ndarray, orig_shape: tuple[int, int], dest_shape: tuple[int, int]) -> np.ndarray:
|
|
257
240
|
"""Remaps a batch of rotated locpred (N, 4, 2) expressed for an origin_shape to a destination_shape.
|
|
258
241
|
This does not impact the absolute shape of the boxes, but allow to calculate the new relative RotatedBbox
|
|
259
242
|
coordinates after a resizing of the image.
|
|
260
243
|
|
|
261
244
|
Args:
|
|
262
|
-
----
|
|
263
245
|
loc_preds: (N, 4, 2) array of RELATIVE loc_preds
|
|
264
246
|
orig_shape: shape of the origin image
|
|
265
247
|
dest_shape: shape of the destination image
|
|
266
248
|
|
|
267
249
|
Returns:
|
|
268
|
-
-------
|
|
269
250
|
A batch of rotated loc_preds (N, 4, 2) expressed in the destination referencial
|
|
270
251
|
"""
|
|
271
252
|
if len(dest_shape) != 2:
|
|
@@ -284,9 +265,9 @@ def remap_boxes(loc_preds: np.ndarray, orig_shape: Tuple[int, int], dest_shape:
|
|
|
284
265
|
def rotate_boxes(
|
|
285
266
|
loc_preds: np.ndarray,
|
|
286
267
|
angle: float,
|
|
287
|
-
orig_shape:
|
|
268
|
+
orig_shape: tuple[int, int],
|
|
288
269
|
min_angle: float = 1.0,
|
|
289
|
-
target_shape:
|
|
270
|
+
target_shape: tuple[int, int] | None = None,
|
|
290
271
|
) -> np.ndarray:
|
|
291
272
|
"""Rotate a batch of straight bounding boxes (xmin, ymin, xmax, ymax, c) or rotated bounding boxes
|
|
292
273
|
(4, 2) of an angle, if angle > min_angle, around the center of the page.
|
|
@@ -294,7 +275,6 @@ def rotate_boxes(
|
|
|
294
275
|
is done to remove the padding that is created by rotate_page(expand=True)
|
|
295
276
|
|
|
296
277
|
Args:
|
|
297
|
-
----
|
|
298
278
|
loc_preds: (N, 4) or (N, 4, 2) array of RELATIVE boxes
|
|
299
279
|
angle: angle between -90 and +90 degrees
|
|
300
280
|
orig_shape: shape of the origin image
|
|
@@ -302,7 +282,6 @@ def rotate_boxes(
|
|
|
302
282
|
target_shape: shape of the destination image
|
|
303
283
|
|
|
304
284
|
Returns:
|
|
305
|
-
-------
|
|
306
285
|
A batch of rotated boxes (N, 4, 2): or a batch of straight bounding boxes
|
|
307
286
|
"""
|
|
308
287
|
# Change format of the boxes to rotated boxes
|
|
@@ -349,20 +328,18 @@ def rotate_image(
|
|
|
349
328
|
"""Rotate an image counterclockwise by an given angle.
|
|
350
329
|
|
|
351
330
|
Args:
|
|
352
|
-
----
|
|
353
331
|
image: numpy tensor to rotate
|
|
354
332
|
angle: rotation angle in degrees, between -90 and +90
|
|
355
333
|
expand: whether the image should be padded before the rotation
|
|
356
334
|
preserve_origin_shape: if expand is set to True, resizes the final output to the original image size
|
|
357
335
|
|
|
358
336
|
Returns:
|
|
359
|
-
-------
|
|
360
337
|
Rotated array, padded by 0 by default.
|
|
361
338
|
"""
|
|
362
339
|
# Compute the expanded padding
|
|
363
340
|
exp_img: np.ndarray
|
|
364
341
|
if expand:
|
|
365
|
-
exp_shape = compute_expanded_shape(image.shape[:2], angle)
|
|
342
|
+
exp_shape = compute_expanded_shape(image.shape[:2], angle)
|
|
366
343
|
h_pad, w_pad = (
|
|
367
344
|
int(max(0, ceil(exp_shape[0] - image.shape[0]))),
|
|
368
345
|
int(max(0, ceil(exp_shape[1] - image.shape[1]))),
|
|
@@ -383,7 +360,7 @@ def rotate_image(
|
|
|
383
360
|
# Pad height
|
|
384
361
|
else:
|
|
385
362
|
h_pad, w_pad = int(rot_img.shape[1] * image.shape[0] / image.shape[1] - rot_img.shape[0]), 0
|
|
386
|
-
rot_img = np.pad(rot_img, ((h_pad // 2, h_pad - h_pad // 2), (w_pad // 2, w_pad - w_pad // 2), (0, 0)))
|
|
363
|
+
rot_img = np.pad(rot_img, ((h_pad // 2, h_pad - h_pad // 2), (w_pad // 2, w_pad - w_pad // 2), (0, 0)))
|
|
387
364
|
if preserve_origin_shape:
|
|
388
365
|
# rescale
|
|
389
366
|
rot_img = cv2.resize(rot_img, image.shape[:-1][::-1], interpolation=cv2.INTER_LINEAR)
|
|
@@ -395,11 +372,9 @@ def remove_image_padding(image: np.ndarray) -> np.ndarray:
|
|
|
395
372
|
"""Remove black border padding from an image
|
|
396
373
|
|
|
397
374
|
Args:
|
|
398
|
-
----
|
|
399
375
|
image: numpy tensor to remove padding from
|
|
400
376
|
|
|
401
377
|
Returns:
|
|
402
|
-
-------
|
|
403
378
|
Image with padding removed
|
|
404
379
|
"""
|
|
405
380
|
# Find the bounding box of the non-black region
|
|
@@ -429,16 +404,14 @@ def estimate_page_angle(polys: np.ndarray) -> float:
|
|
|
429
404
|
return 0.0
|
|
430
405
|
|
|
431
406
|
|
|
432
|
-
def convert_to_relative_coords(geoms: np.ndarray, img_shape:
|
|
407
|
+
def convert_to_relative_coords(geoms: np.ndarray, img_shape: tuple[int, int]) -> np.ndarray:
|
|
433
408
|
"""Convert a geometry to relative coordinates
|
|
434
409
|
|
|
435
410
|
Args:
|
|
436
|
-
----
|
|
437
411
|
geoms: a set of polygons of shape (N, 4, 2) or of straight boxes of shape (N, 4)
|
|
438
412
|
img_shape: the height and width of the image
|
|
439
413
|
|
|
440
414
|
Returns:
|
|
441
|
-
-------
|
|
442
415
|
the updated geometry
|
|
443
416
|
"""
|
|
444
417
|
# Polygon
|
|
@@ -456,18 +429,16 @@ def convert_to_relative_coords(geoms: np.ndarray, img_shape: Tuple[int, int]) ->
|
|
|
456
429
|
raise ValueError(f"invalid format for arg `geoms`: {geoms.shape}")
|
|
457
430
|
|
|
458
431
|
|
|
459
|
-
def extract_crops(img: np.ndarray, boxes: np.ndarray, channels_last: bool = True) ->
|
|
432
|
+
def extract_crops(img: np.ndarray, boxes: np.ndarray, channels_last: bool = True) -> list[np.ndarray]:
|
|
460
433
|
"""Created cropped images from list of bounding boxes
|
|
461
434
|
|
|
462
435
|
Args:
|
|
463
|
-
----
|
|
464
436
|
img: input image
|
|
465
437
|
boxes: bounding boxes of shape (N, 4) where N is the number of boxes, and the relative
|
|
466
438
|
coordinates (xmin, ymin, xmax, ymax)
|
|
467
439
|
channels_last: whether the channel dimensions is the last one instead of the last one
|
|
468
440
|
|
|
469
441
|
Returns:
|
|
470
|
-
-------
|
|
471
442
|
list of cropped images
|
|
472
443
|
"""
|
|
473
444
|
if boxes.shape[0] == 0:
|
|
@@ -492,11 +463,10 @@ def extract_crops(img: np.ndarray, boxes: np.ndarray, channels_last: bool = True
|
|
|
492
463
|
|
|
493
464
|
def extract_rcrops(
|
|
494
465
|
img: np.ndarray, polys: np.ndarray, dtype=np.float32, channels_last: bool = True, assume_horizontal: bool = False
|
|
495
|
-
) ->
|
|
466
|
+
) -> list[np.ndarray]:
|
|
496
467
|
"""Created cropped images from list of rotated bounding boxes
|
|
497
468
|
|
|
498
469
|
Args:
|
|
499
|
-
----
|
|
500
470
|
img: input image
|
|
501
471
|
polys: bounding boxes of shape (N, 4, 2)
|
|
502
472
|
dtype: target data type of bounding boxes
|
|
@@ -504,7 +474,6 @@ def extract_rcrops(
|
|
|
504
474
|
assume_horizontal: whether the boxes are assumed to be only horizontally oriented
|
|
505
475
|
|
|
506
476
|
Returns:
|
|
507
|
-
-------
|
|
508
477
|
list of cropped images
|
|
509
478
|
"""
|
|
510
479
|
if polys.shape[0] == 0:
|
|
@@ -603,4 +572,4 @@ def extract_rcrops(
|
|
|
603
572
|
for idx in range(_boxes.shape[0])
|
|
604
573
|
]
|
|
605
574
|
|
|
606
|
-
return crops
|
|
575
|
+
return crops
|
onnxtr/utils/multithreading.py
CHANGED
|
@@ -6,15 +6,16 @@
|
|
|
6
6
|
|
|
7
7
|
import multiprocessing as mp
|
|
8
8
|
import os
|
|
9
|
+
from collections.abc import Callable, Iterable, Iterator
|
|
9
10
|
from multiprocessing.pool import ThreadPool
|
|
10
|
-
from typing import Any
|
|
11
|
+
from typing import Any
|
|
11
12
|
|
|
12
13
|
from onnxtr.file_utils import ENV_VARS_TRUE_VALUES
|
|
13
14
|
|
|
14
15
|
__all__ = ["multithread_exec"]
|
|
15
16
|
|
|
16
17
|
|
|
17
|
-
def multithread_exec(func: Callable[[Any], Any], seq: Iterable[Any], threads:
|
|
18
|
+
def multithread_exec(func: Callable[[Any], Any], seq: Iterable[Any], threads: int | None = None) -> Iterator[Any]:
|
|
18
19
|
"""Execute a given function in parallel for each element of a given sequence
|
|
19
20
|
|
|
20
21
|
>>> from onnxtr.utils.multithreading import multithread_exec
|
|
@@ -22,17 +23,14 @@ def multithread_exec(func: Callable[[Any], Any], seq: Iterable[Any], threads: Op
|
|
|
22
23
|
>>> results = multithread_exec(lambda x: x ** 2, entries)
|
|
23
24
|
|
|
24
25
|
Args:
|
|
25
|
-
----
|
|
26
26
|
func: function to be executed on each element of the iterable
|
|
27
27
|
seq: iterable
|
|
28
28
|
threads: number of workers to be used for multiprocessing
|
|
29
29
|
|
|
30
30
|
Returns:
|
|
31
|
-
-------
|
|
32
31
|
iterator of the function's results using the iterable as inputs
|
|
33
32
|
|
|
34
33
|
Notes:
|
|
35
|
-
-----
|
|
36
34
|
This function uses ThreadPool from multiprocessing package, which uses `/dev/shm` directory for shared memory.
|
|
37
35
|
If you do not have write permissions for this directory (if you run `onnxtr` on AWS Lambda for instance),
|
|
38
36
|
you might want to disable multiprocessing. To achieve that, set 'ONNXTR_MULTIPROCESSING_DISABLE' to 'TRUE'.
|
onnxtr/utils/reconstitution.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# This program is licensed under the Apache License 2.0.
|
|
4
4
|
# See LICENSE or go to <https://opensource.org/licenses/Apache-2.0> for full license details.
|
|
5
5
|
import logging
|
|
6
|
-
from typing import Any
|
|
6
|
+
from typing import Any
|
|
7
7
|
|
|
8
8
|
import numpy as np
|
|
9
9
|
from anyascii import anyascii
|
|
@@ -18,7 +18,7 @@ __all__ = ["synthesize_page"]
|
|
|
18
18
|
ROTATION_WARNING = False
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
def _warn_rotation(entry:
|
|
21
|
+
def _warn_rotation(entry: dict[str, Any]) -> None: # pragma: no cover
|
|
22
22
|
global ROTATION_WARNING
|
|
23
23
|
if not ROTATION_WARNING and len(entry["geometry"]) == 4:
|
|
24
24
|
logging.warning("Polygons with larger rotations will lead to inaccurate rendering")
|
|
@@ -27,11 +27,11 @@ def _warn_rotation(entry: Dict[str, Any]) -> None: # pragma: no cover
|
|
|
27
27
|
|
|
28
28
|
def _synthesize(
|
|
29
29
|
response: Image.Image,
|
|
30
|
-
entry:
|
|
30
|
+
entry: dict[str, Any],
|
|
31
31
|
w: int,
|
|
32
32
|
h: int,
|
|
33
33
|
draw_proba: bool = False,
|
|
34
|
-
font_family:
|
|
34
|
+
font_family: str | None = None,
|
|
35
35
|
smoothing_factor: float = 0.75,
|
|
36
36
|
min_font_size: int = 6,
|
|
37
37
|
max_font_size: int = 50,
|
|
@@ -111,9 +111,9 @@ def _synthesize(
|
|
|
111
111
|
|
|
112
112
|
|
|
113
113
|
def synthesize_page(
|
|
114
|
-
page:
|
|
114
|
+
page: dict[str, Any],
|
|
115
115
|
draw_proba: bool = False,
|
|
116
|
-
font_family:
|
|
116
|
+
font_family: str | None = None,
|
|
117
117
|
smoothing_factor: float = 0.95,
|
|
118
118
|
min_font_size: int = 8,
|
|
119
119
|
max_font_size: int = 50,
|
|
@@ -121,7 +121,6 @@ def synthesize_page(
|
|
|
121
121
|
"""Draw a the content of the element page (OCR response) on a blank page.
|
|
122
122
|
|
|
123
123
|
Args:
|
|
124
|
-
----
|
|
125
124
|
page: exported Page object to represent
|
|
126
125
|
draw_proba: if True, draw words in colors to represent confidence. Blue: p=1, red: p=0
|
|
127
126
|
font_family: family of the font
|
|
@@ -130,7 +129,6 @@ def synthesize_page(
|
|
|
130
129
|
max_font_size: maximum font size
|
|
131
130
|
|
|
132
131
|
Returns:
|
|
133
|
-
-------
|
|
134
132
|
the synthesized page
|
|
135
133
|
"""
|
|
136
134
|
# Draw template
|
onnxtr/utils/repr.py
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
|
|
6
6
|
# Adapted from https://github.com/pytorch/torch/blob/master/torch/nn/modules/module.py
|
|
7
7
|
|
|
8
|
-
from typing import List
|
|
9
8
|
|
|
10
9
|
__all__ = ["NestedObject"]
|
|
11
10
|
|
|
@@ -25,7 +24,7 @@ def _addindent(s_, num_spaces):
|
|
|
25
24
|
class NestedObject:
|
|
26
25
|
"""Base class for all nested objects in onnxtr"""
|
|
27
26
|
|
|
28
|
-
_children_names:
|
|
27
|
+
_children_names: list[str]
|
|
29
28
|
|
|
30
29
|
def extra_repr(self) -> str:
|
|
31
30
|
return ""
|
onnxtr/utils/visualization.py
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# See LICENSE or go to <https://opensource.org/licenses/Apache-2.0> for full license details.
|
|
5
5
|
|
|
6
6
|
from copy import deepcopy
|
|
7
|
-
from typing import Any
|
|
7
|
+
from typing import Any
|
|
8
8
|
|
|
9
9
|
import cv2
|
|
10
10
|
import matplotlib.patches as patches
|
|
@@ -19,9 +19,9 @@ __all__ = ["visualize_page", "draw_boxes"]
|
|
|
19
19
|
|
|
20
20
|
def rect_patch(
|
|
21
21
|
geometry: BoundingBox,
|
|
22
|
-
page_dimensions:
|
|
23
|
-
label:
|
|
24
|
-
color:
|
|
22
|
+
page_dimensions: tuple[int, int],
|
|
23
|
+
label: str | None = None,
|
|
24
|
+
color: tuple[float, float, float] = (0, 0, 0),
|
|
25
25
|
alpha: float = 0.3,
|
|
26
26
|
linewidth: int = 2,
|
|
27
27
|
fill: bool = True,
|
|
@@ -30,7 +30,6 @@ def rect_patch(
|
|
|
30
30
|
"""Create a matplotlib rectangular patch for the element
|
|
31
31
|
|
|
32
32
|
Args:
|
|
33
|
-
----
|
|
34
33
|
geometry: bounding box of the element
|
|
35
34
|
page_dimensions: dimensions of the Page in format (height, width)
|
|
36
35
|
label: label to display when hovered
|
|
@@ -41,7 +40,6 @@ def rect_patch(
|
|
|
41
40
|
preserve_aspect_ratio: pass True if you passed True to the predictor
|
|
42
41
|
|
|
43
42
|
Returns:
|
|
44
|
-
-------
|
|
45
43
|
a rectangular Patch
|
|
46
44
|
"""
|
|
47
45
|
if len(geometry) != 2 or any(not isinstance(elt, tuple) or len(elt) != 2 for elt in geometry):
|
|
@@ -70,9 +68,9 @@ def rect_patch(
|
|
|
70
68
|
|
|
71
69
|
def polygon_patch(
|
|
72
70
|
geometry: np.ndarray,
|
|
73
|
-
page_dimensions:
|
|
74
|
-
label:
|
|
75
|
-
color:
|
|
71
|
+
page_dimensions: tuple[int, int],
|
|
72
|
+
label: str | None = None,
|
|
73
|
+
color: tuple[float, float, float] = (0, 0, 0),
|
|
76
74
|
alpha: float = 0.3,
|
|
77
75
|
linewidth: int = 2,
|
|
78
76
|
fill: bool = True,
|
|
@@ -81,7 +79,6 @@ def polygon_patch(
|
|
|
81
79
|
"""Create a matplotlib polygon patch for the element
|
|
82
80
|
|
|
83
81
|
Args:
|
|
84
|
-
----
|
|
85
82
|
geometry: bounding box of the element
|
|
86
83
|
page_dimensions: dimensions of the Page in format (height, width)
|
|
87
84
|
label: label to display when hovered
|
|
@@ -92,7 +89,6 @@ def polygon_patch(
|
|
|
92
89
|
preserve_aspect_ratio: pass True if you passed True to the predictor
|
|
93
90
|
|
|
94
91
|
Returns:
|
|
95
|
-
-------
|
|
96
92
|
a polygon Patch
|
|
97
93
|
"""
|
|
98
94
|
if not geometry.shape == (4, 2):
|
|
@@ -114,20 +110,18 @@ def polygon_patch(
|
|
|
114
110
|
|
|
115
111
|
|
|
116
112
|
def create_obj_patch(
|
|
117
|
-
geometry:
|
|
118
|
-
page_dimensions:
|
|
113
|
+
geometry: BoundingBox | Polygon4P | np.ndarray,
|
|
114
|
+
page_dimensions: tuple[int, int],
|
|
119
115
|
**kwargs: Any,
|
|
120
116
|
) -> patches.Patch:
|
|
121
117
|
"""Create a matplotlib patch for the element
|
|
122
118
|
|
|
123
119
|
Args:
|
|
124
|
-
----
|
|
125
120
|
geometry: bounding box (straight or rotated) of the element
|
|
126
121
|
page_dimensions: dimensions of the page in format (height, width)
|
|
127
122
|
**kwargs: keyword arguments for the patch
|
|
128
123
|
|
|
129
124
|
Returns:
|
|
130
|
-
-------
|
|
131
125
|
a matplotlib Patch
|
|
132
126
|
"""
|
|
133
127
|
if isinstance(geometry, tuple):
|
|
@@ -141,7 +135,7 @@ def create_obj_patch(
|
|
|
141
135
|
|
|
142
136
|
|
|
143
137
|
def visualize_page(
|
|
144
|
-
page:
|
|
138
|
+
page: dict[str, Any],
|
|
145
139
|
image: np.ndarray,
|
|
146
140
|
words_only: bool = True,
|
|
147
141
|
display_artefacts: bool = True,
|
|
@@ -163,7 +157,6 @@ def visualize_page(
|
|
|
163
157
|
>>> plt.show()
|
|
164
158
|
|
|
165
159
|
Args:
|
|
166
|
-
----
|
|
167
160
|
page: the exported Page of a Document
|
|
168
161
|
image: np array of the page, needs to have the same shape than page['dimensions']
|
|
169
162
|
words_only: whether only words should be displayed
|
|
@@ -174,7 +167,6 @@ def visualize_page(
|
|
|
174
167
|
**kwargs: keyword arguments for the polygon patch
|
|
175
168
|
|
|
176
169
|
Returns:
|
|
177
|
-
-------
|
|
178
170
|
the matplotlib figure
|
|
179
171
|
"""
|
|
180
172
|
# Get proper scale and aspect ratio
|
|
@@ -187,7 +179,7 @@ def visualize_page(
|
|
|
187
179
|
ax.axis("off")
|
|
188
180
|
|
|
189
181
|
if interactive:
|
|
190
|
-
artists:
|
|
182
|
+
artists: list[patches.Patch] = [] # instantiate an empty list of patches (to be drawn on the page)
|
|
191
183
|
|
|
192
184
|
for block in page["blocks"]:
|
|
193
185
|
if not words_only:
|
|
@@ -266,11 +258,10 @@ def visualize_page(
|
|
|
266
258
|
return fig
|
|
267
259
|
|
|
268
260
|
|
|
269
|
-
def draw_boxes(boxes: np.ndarray, image: np.ndarray, color:
|
|
261
|
+
def draw_boxes(boxes: np.ndarray, image: np.ndarray, color: tuple[int, int, int] | None = None, **kwargs) -> None:
|
|
270
262
|
"""Draw an array of relative straight boxes on an image
|
|
271
263
|
|
|
272
264
|
Args:
|
|
273
|
-
----
|
|
274
265
|
boxes: array of relative boxes, of shape (*, 4)
|
|
275
266
|
image: np array, float32 or uint8
|
|
276
267
|
color: color to use for bounding box edges
|
onnxtr/utils/vocabs.py
CHANGED
|
@@ -4,12 +4,11 @@
|
|
|
4
4
|
# See LICENSE or go to <https://opensource.org/licenses/Apache-2.0> for full license details.
|
|
5
5
|
|
|
6
6
|
import string
|
|
7
|
-
from typing import Dict
|
|
8
7
|
|
|
9
8
|
__all__ = ["VOCABS"]
|
|
10
9
|
|
|
11
10
|
|
|
12
|
-
VOCABS:
|
|
11
|
+
VOCABS: dict[str, str] = {
|
|
13
12
|
"digits": string.digits,
|
|
14
13
|
"ascii_letters": string.ascii_letters,
|
|
15
14
|
"punctuation": string.punctuation,
|
onnxtr/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = 'v0.
|
|
1
|
+
__version__ = 'v0.6.0'
|