deepdoctection 0.42.1__py3-none-any.whl → 0.43.1__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.
Potentially problematic release.
This version of deepdoctection might be problematic. Click here for more details.
- deepdoctection/__init__.py +4 -2
- deepdoctection/analyzer/__init__.py +2 -1
- deepdoctection/analyzer/config.py +919 -0
- deepdoctection/analyzer/dd.py +36 -62
- deepdoctection/analyzer/factory.py +311 -141
- deepdoctection/configs/conf_dd_one.yaml +100 -44
- deepdoctection/configs/profiles.jsonl +32 -0
- deepdoctection/dataflow/__init__.py +9 -6
- deepdoctection/dataflow/base.py +33 -15
- deepdoctection/dataflow/common.py +96 -75
- deepdoctection/dataflow/custom.py +36 -29
- deepdoctection/dataflow/custom_serialize.py +135 -91
- deepdoctection/dataflow/parallel_map.py +33 -31
- deepdoctection/dataflow/serialize.py +15 -10
- deepdoctection/dataflow/stats.py +41 -28
- deepdoctection/datapoint/__init__.py +4 -6
- deepdoctection/datapoint/annotation.py +104 -66
- deepdoctection/datapoint/box.py +190 -130
- deepdoctection/datapoint/convert.py +66 -39
- deepdoctection/datapoint/image.py +151 -95
- deepdoctection/datapoint/view.py +383 -236
- deepdoctection/datasets/__init__.py +2 -6
- deepdoctection/datasets/adapter.py +11 -11
- deepdoctection/datasets/base.py +118 -81
- deepdoctection/datasets/dataflow_builder.py +18 -12
- deepdoctection/datasets/info.py +76 -57
- deepdoctection/datasets/instances/__init__.py +6 -2
- deepdoctection/datasets/instances/doclaynet.py +17 -14
- deepdoctection/datasets/instances/fintabnet.py +16 -22
- deepdoctection/datasets/instances/funsd.py +11 -6
- deepdoctection/datasets/instances/iiitar13k.py +9 -9
- deepdoctection/datasets/instances/layouttest.py +9 -9
- deepdoctection/datasets/instances/publaynet.py +9 -9
- deepdoctection/datasets/instances/pubtables1m.py +13 -13
- deepdoctection/datasets/instances/pubtabnet.py +13 -15
- deepdoctection/datasets/instances/rvlcdip.py +8 -8
- deepdoctection/datasets/instances/xfund.py +11 -9
- deepdoctection/datasets/registry.py +18 -11
- deepdoctection/datasets/save.py +12 -11
- deepdoctection/eval/__init__.py +3 -2
- deepdoctection/eval/accmetric.py +72 -52
- deepdoctection/eval/base.py +29 -10
- deepdoctection/eval/cocometric.py +14 -12
- deepdoctection/eval/eval.py +56 -41
- deepdoctection/eval/registry.py +6 -3
- deepdoctection/eval/tedsmetric.py +24 -9
- deepdoctection/eval/tp_eval_callback.py +13 -12
- deepdoctection/extern/__init__.py +1 -1
- deepdoctection/extern/base.py +176 -97
- deepdoctection/extern/d2detect.py +127 -92
- deepdoctection/extern/deskew.py +19 -10
- deepdoctection/extern/doctrocr.py +162 -108
- deepdoctection/extern/fastlang.py +25 -17
- deepdoctection/extern/hfdetr.py +137 -60
- deepdoctection/extern/hflayoutlm.py +329 -248
- deepdoctection/extern/hflm.py +67 -33
- deepdoctection/extern/model.py +108 -762
- deepdoctection/extern/pdftext.py +37 -12
- deepdoctection/extern/pt/nms.py +15 -1
- deepdoctection/extern/pt/ptutils.py +13 -9
- deepdoctection/extern/tessocr.py +87 -54
- deepdoctection/extern/texocr.py +29 -14
- deepdoctection/extern/tp/tfutils.py +36 -8
- deepdoctection/extern/tp/tpcompat.py +54 -16
- deepdoctection/extern/tp/tpfrcnn/config/config.py +20 -4
- deepdoctection/extern/tpdetect.py +4 -2
- deepdoctection/mapper/__init__.py +1 -1
- deepdoctection/mapper/cats.py +117 -76
- deepdoctection/mapper/cocostruct.py +35 -17
- deepdoctection/mapper/d2struct.py +56 -29
- deepdoctection/mapper/hfstruct.py +32 -19
- deepdoctection/mapper/laylmstruct.py +221 -185
- deepdoctection/mapper/maputils.py +71 -35
- deepdoctection/mapper/match.py +76 -62
- deepdoctection/mapper/misc.py +68 -44
- deepdoctection/mapper/pascalstruct.py +13 -12
- deepdoctection/mapper/prodigystruct.py +33 -19
- deepdoctection/mapper/pubstruct.py +42 -32
- deepdoctection/mapper/tpstruct.py +39 -19
- deepdoctection/mapper/xfundstruct.py +20 -13
- deepdoctection/pipe/__init__.py +1 -2
- deepdoctection/pipe/anngen.py +104 -62
- deepdoctection/pipe/base.py +226 -107
- deepdoctection/pipe/common.py +206 -123
- deepdoctection/pipe/concurrency.py +74 -47
- deepdoctection/pipe/doctectionpipe.py +108 -47
- deepdoctection/pipe/language.py +41 -24
- deepdoctection/pipe/layout.py +45 -18
- deepdoctection/pipe/lm.py +146 -78
- deepdoctection/pipe/order.py +205 -119
- deepdoctection/pipe/refine.py +111 -63
- deepdoctection/pipe/registry.py +1 -1
- deepdoctection/pipe/segment.py +213 -142
- deepdoctection/pipe/sub_layout.py +76 -46
- deepdoctection/pipe/text.py +52 -33
- deepdoctection/pipe/transform.py +8 -6
- deepdoctection/train/d2_frcnn_train.py +87 -69
- deepdoctection/train/hf_detr_train.py +72 -40
- deepdoctection/train/hf_layoutlm_train.py +85 -46
- deepdoctection/train/tp_frcnn_train.py +56 -28
- deepdoctection/utils/concurrency.py +59 -16
- deepdoctection/utils/context.py +40 -19
- deepdoctection/utils/develop.py +26 -17
- deepdoctection/utils/env_info.py +86 -37
- deepdoctection/utils/error.py +16 -10
- deepdoctection/utils/file_utils.py +246 -71
- deepdoctection/utils/fs.py +162 -43
- deepdoctection/utils/identifier.py +29 -16
- deepdoctection/utils/logger.py +49 -32
- deepdoctection/utils/metacfg.py +83 -21
- deepdoctection/utils/pdf_utils.py +119 -62
- deepdoctection/utils/settings.py +24 -10
- deepdoctection/utils/tqdm.py +10 -5
- deepdoctection/utils/transform.py +182 -46
- deepdoctection/utils/utils.py +61 -28
- deepdoctection/utils/viz.py +150 -104
- deepdoctection-0.43.1.dist-info/METADATA +376 -0
- deepdoctection-0.43.1.dist-info/RECORD +149 -0
- deepdoctection/analyzer/_config.py +0 -146
- deepdoctection-0.42.1.dist-info/METADATA +0 -431
- deepdoctection-0.42.1.dist-info/RECORD +0 -148
- {deepdoctection-0.42.1.dist-info → deepdoctection-0.43.1.dist-info}/WHEEL +0 -0
- {deepdoctection-0.42.1.dist-info → deepdoctection-0.43.1.dist-info}/licenses/LICENSE +0 -0
- {deepdoctection-0.42.1.dist-info → deepdoctection-0.43.1.dist-info}/top_level.txt +0 -0
deepdoctection/pipe/common.py
CHANGED
|
@@ -46,18 +46,22 @@ elif os.environ.get("DD_USE_TF"):
|
|
|
46
46
|
@pipeline_component_registry.register("ImageCroppingService")
|
|
47
47
|
class ImageCroppingService(PipelineComponent):
|
|
48
48
|
"""
|
|
49
|
-
Crop sub images given by bounding boxes of some annotations.
|
|
50
|
-
|
|
49
|
+
Crop sub images given by bounding boxes of some annotations.
|
|
50
|
+
|
|
51
|
+
This service is not necessary for `ImageLayoutService` and is more intended for saved files where sub images are
|
|
51
52
|
generally not stored.
|
|
53
|
+
|
|
52
54
|
"""
|
|
53
55
|
|
|
54
56
|
def __init__(
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
self,
|
|
58
|
+
category_names: Optional[Union[TypeOrStr, Sequence[TypeOrStr]]] = None,
|
|
59
|
+
service_ids: Optional[Sequence[str]] = None,
|
|
58
60
|
) -> None:
|
|
59
61
|
"""
|
|
60
|
-
:
|
|
62
|
+
Args:
|
|
63
|
+
category_names: A single name or a list of category names to crop.
|
|
64
|
+
service_ids: Optional list of service IDs.
|
|
61
65
|
"""
|
|
62
66
|
if category_names is None:
|
|
63
67
|
self.category_names = None
|
|
@@ -86,42 +90,46 @@ class ImageCroppingService(PipelineComponent):
|
|
|
86
90
|
|
|
87
91
|
class IntersectionMatcher:
|
|
88
92
|
"""
|
|
89
|
-
Objects of two object classes can be assigned to one another by determining their pairwise intersection.
|
|
90
|
-
above a limit, a relation is created between them.
|
|
93
|
+
Objects of two object classes can be assigned to one another by determining their pairwise intersection.
|
|
94
|
+
If this is above a limit, a relation is created between them.
|
|
95
|
+
|
|
91
96
|
The parent object class (based on its category) and the child object class are defined for the service.
|
|
92
97
|
|
|
93
98
|
Either `iou` (intersection-over-union) or `ioa` (intersection-over-area) can be selected as the matching rule.
|
|
94
99
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
relationship_key=Relationships.CHILD)
|
|
100
|
+
Example:
|
|
101
|
+
```python
|
|
102
|
+
matcher = IntersectionMatcher(matching_rule="ioa", threshold=0.7)
|
|
103
|
+
match_service = MatchingService(parent_categories=["text","title"],
|
|
104
|
+
child_categories="word",
|
|
105
|
+
matcher=matcher,
|
|
106
|
+
relationship_key=Relationships.CHILD)
|
|
107
|
+
```
|
|
104
108
|
|
|
105
|
-
|
|
106
|
-
|
|
109
|
+
Assigning means that text and title annotation will receive a relationship called `CHILD` which is a list
|
|
110
|
+
of annotation ids of mapped words.
|
|
107
111
|
"""
|
|
108
112
|
|
|
109
113
|
def __init__(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
114
|
+
self,
|
|
115
|
+
matching_rule: Literal["iou", "ioa"],
|
|
116
|
+
threshold: float,
|
|
117
|
+
use_weighted_intersections: bool = False,
|
|
118
|
+
max_parent_only: bool = False,
|
|
115
119
|
) -> None:
|
|
116
120
|
"""
|
|
117
|
-
:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
121
|
+
Args:
|
|
122
|
+
matching_rule: `iou` or `ioa`.
|
|
123
|
+
threshold: iou/ioa threshold. Value between [0,1].
|
|
124
|
+
use_weighted_intersections: This is currently only implemented for matching_rule `ioa`. Instead of using
|
|
125
|
+
the ioa_matrix it will use mat weighted ioa in order to take into account that intersections with more
|
|
126
|
+
cells will likely decrease the ioa value. By multiplying the ioa with the number of all intersection for
|
|
127
|
+
each child this value calibrate the ioa.
|
|
128
|
+
max_parent_only: Will assign to each child at most one parent with maximum ioa.
|
|
129
|
+
|
|
130
|
+
Raises:
|
|
131
|
+
ValueError: If `matching_rule` is not `iou` or `ioa`.
|
|
132
|
+
"""
|
|
125
133
|
|
|
126
134
|
if matching_rule not in ("iou", "ioa"):
|
|
127
135
|
raise ValueError("segment rule must be either iou or ioa")
|
|
@@ -131,25 +139,27 @@ class IntersectionMatcher:
|
|
|
131
139
|
self.max_parent_only = max_parent_only
|
|
132
140
|
|
|
133
141
|
def match(
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
142
|
+
self,
|
|
143
|
+
dp: Image,
|
|
144
|
+
parent_categories: Optional[Union[TypeOrStr, Sequence[TypeOrStr]]] = None,
|
|
145
|
+
child_categories: Optional[Union[TypeOrStr, Sequence[TypeOrStr]]] = None,
|
|
146
|
+
parent_ann_service_ids: Optional[Union[str, Sequence[str]]] = None,
|
|
147
|
+
child_ann_service_ids: Optional[Union[str, Sequence[str]]] = None,
|
|
140
148
|
) -> list[tuple[str, str]]:
|
|
141
149
|
"""
|
|
142
|
-
The matching algorithm
|
|
143
|
-
|
|
144
|
-
:
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
150
|
+
The matching algorithm.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
dp: `Image` datapoint.
|
|
154
|
+
parent_categories: List of categories to be used as parent class. Will generate a child-relationship.
|
|
155
|
+
child_categories: List of categories to be used for a child class.
|
|
156
|
+
parent_ann_service_ids: Additional filter condition. If some ids are selected, it will ignore all other
|
|
157
|
+
parent candidates which are not in the list.
|
|
158
|
+
child_ann_service_ids: Additional filter condition. If some ids are selected, it will ignore all other
|
|
159
|
+
children candidates which are not in the list.
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
A list of tuples with parent and child annotation ids.
|
|
153
163
|
"""
|
|
154
164
|
child_index, parent_index, child_anns, parent_anns = match_anns_by_intersection(
|
|
155
165
|
dp,
|
|
@@ -177,37 +187,38 @@ class NeighbourMatcher:
|
|
|
177
187
|
"""
|
|
178
188
|
Objects of two object classes can be assigned to one another by determining their pairwise distance.
|
|
179
189
|
|
|
180
|
-
|
|
181
|
-
|
|
190
|
+
Example:
|
|
191
|
+
```python
|
|
182
192
|
matcher = NeighbourMatcher()
|
|
183
|
-
|
|
184
193
|
match_service = MatchingService(parent_categories=["figure"],
|
|
185
194
|
child_categories="caption",
|
|
186
195
|
matcher=matcher,
|
|
187
196
|
relationship_key=Relationships.LAYOUT_LINK)
|
|
188
|
-
|
|
197
|
+
```
|
|
189
198
|
"""
|
|
190
199
|
|
|
191
200
|
def match(
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
201
|
+
self,
|
|
202
|
+
dp: Image,
|
|
203
|
+
parent_categories: Optional[Union[TypeOrStr, Sequence[TypeOrStr]]] = None,
|
|
204
|
+
child_categories: Optional[Union[TypeOrStr, Sequence[TypeOrStr]]] = None,
|
|
205
|
+
parent_ann_service_ids: Optional[Union[str, Sequence[str]]] = None,
|
|
206
|
+
child_ann_service_ids: Optional[Union[str, Sequence[str]]] = None,
|
|
198
207
|
) -> list[tuple[str, str]]:
|
|
199
208
|
"""
|
|
200
|
-
The matching algorithm
|
|
201
|
-
|
|
202
|
-
:
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
209
|
+
The matching algorithm.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
dp: `Image` datapoint.
|
|
213
|
+
parent_categories: List of categories to be used as parent class. Will generate a child-relationship.
|
|
214
|
+
child_categories: List of categories to be used for a child class.
|
|
215
|
+
parent_ann_service_ids: Additional filter condition. If some ids are selected, it will ignore all other
|
|
216
|
+
parent candidates which are not in the list.
|
|
217
|
+
child_ann_service_ids: Additional filter condition. If some ids are selected, it will ignore all other
|
|
218
|
+
children candidates which are not in the list.
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
A list of tuples with parent and child annotation ids.
|
|
211
222
|
"""
|
|
212
223
|
|
|
213
224
|
return [
|
|
@@ -225,8 +236,17 @@ class NeighbourMatcher:
|
|
|
225
236
|
@dataclass
|
|
226
237
|
class FamilyCompound:
|
|
227
238
|
"""
|
|
228
|
-
A family compound is a set of parent and child categories that are related by a relationship key.
|
|
229
|
-
categories will receive a relationship to the child categories.
|
|
239
|
+
A family compound is a set of parent and child categories that are related by a relationship key.
|
|
240
|
+
The parent categories will receive a relationship to the child categories.
|
|
241
|
+
|
|
242
|
+
Attributes:
|
|
243
|
+
relationship_key: The relationship key.
|
|
244
|
+
parent_categories: Parent categories.
|
|
245
|
+
child_categories: Child categories.
|
|
246
|
+
parent_ann_service_ids: Parent annotation service IDs.
|
|
247
|
+
child_ann_service_ids: Child annotation service IDs.
|
|
248
|
+
create_synthetic_parent: Whether to create a synthetic parent.
|
|
249
|
+
synthetic_parent: The synthetic parent.
|
|
230
250
|
"""
|
|
231
251
|
|
|
232
252
|
relationship_key: Relationships
|
|
@@ -255,18 +275,23 @@ class FamilyCompound:
|
|
|
255
275
|
@pipeline_component_registry.register("MatchingService")
|
|
256
276
|
class MatchingService(PipelineComponent):
|
|
257
277
|
"""
|
|
258
|
-
A service to match annotations of two categories by intersection or distance.
|
|
259
|
-
|
|
278
|
+
A service to match annotations of two categories by intersection or distance.
|
|
279
|
+
|
|
280
|
+
The matched annotations will be assigned a relationship. The parent category will receive a
|
|
281
|
+
relationship to the child category.
|
|
282
|
+
|
|
283
|
+
|
|
260
284
|
"""
|
|
261
285
|
|
|
262
286
|
def __init__(
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
287
|
+
self,
|
|
288
|
+
family_compounds: Sequence[FamilyCompound],
|
|
289
|
+
matcher: Union[IntersectionMatcher, NeighbourMatcher],
|
|
266
290
|
) -> None:
|
|
267
291
|
"""
|
|
268
|
-
:
|
|
269
|
-
|
|
292
|
+
Args:
|
|
293
|
+
family_compounds: A list of `FamilyCompound`.
|
|
294
|
+
matcher: A matcher object.
|
|
270
295
|
"""
|
|
271
296
|
self.family_compounds = family_compounds
|
|
272
297
|
self.matcher = matcher
|
|
@@ -274,10 +299,10 @@ class MatchingService(PipelineComponent):
|
|
|
274
299
|
|
|
275
300
|
def serve(self, dp: Image) -> None:
|
|
276
301
|
"""
|
|
277
|
-
|
|
278
|
-
- generates child relationship at parent level
|
|
302
|
+
Generates pairwise match-score by intersection and generates child relationship at parent level.
|
|
279
303
|
|
|
280
|
-
:
|
|
304
|
+
Args:
|
|
305
|
+
dp: `Image` datapoint.
|
|
281
306
|
"""
|
|
282
307
|
for family_compound in self.family_compounds:
|
|
283
308
|
matched_pairs = self.matcher.match(
|
|
@@ -300,18 +325,22 @@ class MatchingService(PipelineComponent):
|
|
|
300
325
|
detect_result_list = []
|
|
301
326
|
for child_ann in child_anns:
|
|
302
327
|
if child_ann.annotation_id not in child_ann_ids:
|
|
303
|
-
detect_result_list.append(
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
328
|
+
detect_result_list.append(
|
|
329
|
+
DetectionResult(
|
|
330
|
+
class_name=family_compound.synthetic_parent,
|
|
331
|
+
box=child_ann.get_bounding_box(dp.image_id).to_list(mode="xyxy"),
|
|
332
|
+
absolute_coords=child_ann.get_bounding_box(dp.image_id).absolute_coords,
|
|
333
|
+
relationships={family_compound.relationship_key: child_ann.annotation_id},
|
|
334
|
+
)
|
|
335
|
+
)
|
|
308
336
|
for detect_result in detect_result_list:
|
|
309
337
|
annotation_id = self.dp_manager.set_image_annotation(detect_result)
|
|
310
338
|
if annotation_id is not None and detect_result.relationships is not None:
|
|
311
|
-
self.dp_manager.set_relationship_annotation(
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
339
|
+
self.dp_manager.set_relationship_annotation(
|
|
340
|
+
family_compound.relationship_key,
|
|
341
|
+
annotation_id,
|
|
342
|
+
detect_result.relationships.get(family_compound.relationship_key, None),
|
|
343
|
+
)
|
|
315
344
|
|
|
316
345
|
def clone(self) -> PipelineComponent:
|
|
317
346
|
return self.__class__(self.family_compounds, self.matcher)
|
|
@@ -336,31 +365,44 @@ class MatchingService(PipelineComponent):
|
|
|
336
365
|
@pipeline_component_registry.register("PageParsingService")
|
|
337
366
|
class PageParsingService(PipelineComponent):
|
|
338
367
|
"""
|
|
339
|
-
A "pseudo" pipeline component that can be added to a pipeline to convert `Image`s into `Page` formats.
|
|
340
|
-
|
|
368
|
+
A "pseudo" pipeline component that can be added to a pipeline to convert `Image`s into `Page` formats.
|
|
369
|
+
|
|
370
|
+
It allows a custom parsing depending on customizing options of other pipeline components.
|
|
371
|
+
|
|
372
|
+
Info:
|
|
373
|
+
This component is not meant to be used in the `serve` method.
|
|
341
374
|
"""
|
|
342
375
|
|
|
343
376
|
def __init__(
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
377
|
+
self,
|
|
378
|
+
text_container: TypeOrStr,
|
|
379
|
+
floating_text_block_categories: Optional[Union[TypeOrStr, Sequence[TypeOrStr]]] = None,
|
|
380
|
+
residual_text_block_categories: Optional[Union[TypeOrStr, Sequence[TypeOrStr]]] = None,
|
|
381
|
+
include_residual_text_container: bool = True,
|
|
348
382
|
):
|
|
349
383
|
"""
|
|
350
|
-
:
|
|
351
|
-
|
|
352
|
-
|
|
384
|
+
Args:
|
|
385
|
+
text_container: Name of an image annotation that has a `CHARS` sub category. These annotations will be
|
|
386
|
+
ordered within all text blocks.
|
|
387
|
+
floating_text_block_categories: Name of image annotation that have a relation with text containers.
|
|
388
|
+
residual_text_block_categories: Name of image annotation that have a relation with text containers.
|
|
389
|
+
include_residual_text_container: Whether to include residual text container.
|
|
353
390
|
"""
|
|
354
391
|
self.name = "page_parser"
|
|
355
392
|
if isinstance(floating_text_block_categories, (str, ObjectTypes)):
|
|
356
393
|
floating_text_block_categories = (get_type(floating_text_block_categories),)
|
|
357
394
|
if floating_text_block_categories is None:
|
|
358
|
-
floating_text_block_categories = IMAGE_DEFAULTS
|
|
395
|
+
floating_text_block_categories = IMAGE_DEFAULTS.FLOATING_TEXT_BLOCK_CATEGORIES
|
|
396
|
+
if residual_text_block_categories is None:
|
|
397
|
+
residual_text_block_categories = IMAGE_DEFAULTS.RESIDUAL_TEXT_BLOCK_CATEGORIES
|
|
359
398
|
|
|
360
399
|
self.text_container = get_type(text_container)
|
|
361
400
|
self.floating_text_block_categories = tuple(
|
|
362
401
|
(get_type(text_block) for text_block in floating_text_block_categories)
|
|
363
402
|
)
|
|
403
|
+
self.residual_text_block_categories = tuple(
|
|
404
|
+
get_type(text_block) for text_block in residual_text_block_categories
|
|
405
|
+
)
|
|
364
406
|
self.include_residual_text_container = include_residual_text_container
|
|
365
407
|
self._init_sanity_checks()
|
|
366
408
|
super().__init__(self.name)
|
|
@@ -370,14 +412,19 @@ class PageParsingService(PipelineComponent):
|
|
|
370
412
|
|
|
371
413
|
def pass_datapoint(self, dp: Image) -> Page:
|
|
372
414
|
"""
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
:
|
|
415
|
+
Converts `Image` to `Page`.
|
|
416
|
+
|
|
417
|
+
Args:
|
|
418
|
+
dp: `Image`.
|
|
419
|
+
|
|
420
|
+
Returns:
|
|
421
|
+
`Page`.
|
|
376
422
|
"""
|
|
377
423
|
return Page.from_image(
|
|
378
424
|
dp,
|
|
379
425
|
text_container=self.text_container,
|
|
380
426
|
floating_text_block_categories=self.floating_text_block_categories,
|
|
427
|
+
residual_text_block_categories=self.residual_text_block_categories,
|
|
381
428
|
include_residual_text_container=self.include_residual_text_container,
|
|
382
429
|
)
|
|
383
430
|
|
|
@@ -389,15 +436,22 @@ class PageParsingService(PipelineComponent):
|
|
|
389
436
|
|
|
390
437
|
def get_meta_annotation(self) -> MetaAnnotation:
|
|
391
438
|
"""
|
|
392
|
-
|
|
439
|
+
Returns:
|
|
440
|
+
`MetaAnnotation`. No new annotations are generated here.
|
|
393
441
|
"""
|
|
394
442
|
return MetaAnnotation(image_annotations=(), sub_categories={}, relationships={}, summaries=())
|
|
395
443
|
|
|
396
444
|
def clone(self) -> PageParsingService:
|
|
397
|
-
"""
|
|
445
|
+
"""
|
|
446
|
+
Clone the `PageParsingService`.
|
|
447
|
+
|
|
448
|
+
Returns:
|
|
449
|
+
A cloned `PageParsingService`.
|
|
450
|
+
"""
|
|
398
451
|
return self.__class__(
|
|
399
452
|
deepcopy(self.text_container),
|
|
400
453
|
deepcopy(self.floating_text_block_categories),
|
|
454
|
+
deepcopy(self.residual_text_block_categories),
|
|
401
455
|
self.include_residual_text_container,
|
|
402
456
|
)
|
|
403
457
|
|
|
@@ -409,15 +463,18 @@ class PageParsingService(PipelineComponent):
|
|
|
409
463
|
class AnnotationNmsService(PipelineComponent):
|
|
410
464
|
"""
|
|
411
465
|
A service to pass `ImageAnnotation` to a non-maximum suppression (NMS) process for given pairs of categories.
|
|
466
|
+
|
|
412
467
|
`ImageAnnotation`s are subjected to NMS process in groups:
|
|
413
468
|
If `nms_pairs=[[LayoutType.text, LayoutType.table],[LayoutType.title, LayoutType.table]]` all `ImageAnnotation`
|
|
414
469
|
subject to these categories are being selected and identified as one category.
|
|
415
470
|
After NMS the discarded image annotation will be deactivated.
|
|
416
471
|
|
|
417
|
-
|
|
418
|
-
|
|
472
|
+
Example:
|
|
473
|
+
```python
|
|
419
474
|
AnnotationNmsService(nms_pairs=[[LayoutType.text, LayoutType.table],[LayoutType.title, LayoutType.table]],
|
|
420
|
-
thresholds=[0.7,0.7])
|
|
475
|
+
thresholds=[0.7,0.7])
|
|
476
|
+
```
|
|
477
|
+
For each pair a threshold has to be provided.
|
|
421
478
|
|
|
422
479
|
For a pair of categories, one can also select a category which has always priority even if the score is lower.
|
|
423
480
|
This is useful if one expects some categories to be larger and want to keep them.
|
|
@@ -426,15 +483,20 @@ class AnnotationNmsService(PipelineComponent):
|
|
|
426
483
|
"""
|
|
427
484
|
|
|
428
485
|
def __init__(
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
486
|
+
self,
|
|
487
|
+
nms_pairs: Sequence[Sequence[TypeOrStr]],
|
|
488
|
+
thresholds: Union[float, Sequence[float]],
|
|
489
|
+
priority: Optional[Sequence[Union[Optional[TypeOrStr]]]] = None,
|
|
433
490
|
):
|
|
434
491
|
"""
|
|
435
|
-
:
|
|
436
|
-
|
|
437
|
-
|
|
492
|
+
Args:
|
|
493
|
+
nms_pairs: Groups of categories, either as string or by `ObjectType`.
|
|
494
|
+
thresholds: Suppression threshold. If only one value is provided, it will apply the threshold to all
|
|
495
|
+
pairs. If a list is provided, make sure to add as many list elements as `nms_pairs`.
|
|
496
|
+
priority: Optional list of categories which have always priority.
|
|
497
|
+
|
|
498
|
+
Raises:
|
|
499
|
+
AssertionError: If the length of `nms_pairs` and `thresholds` or `priority` do not match.
|
|
438
500
|
"""
|
|
439
501
|
self.nms_pairs = [[get_type(val) for val in pair] for pair in nms_pairs]
|
|
440
502
|
if isinstance(thresholds, float):
|
|
@@ -456,6 +518,13 @@ class AnnotationNmsService(PipelineComponent):
|
|
|
456
518
|
super().__init__("nms")
|
|
457
519
|
|
|
458
520
|
def serve(self, dp: Image) -> None:
|
|
521
|
+
"""
|
|
522
|
+
Args:
|
|
523
|
+
dp: `Image`.
|
|
524
|
+
|
|
525
|
+
Returns:
|
|
526
|
+
None.
|
|
527
|
+
"""
|
|
459
528
|
for pair, threshold, prio in zip(self.nms_pairs, self.threshold, self.priority):
|
|
460
529
|
anns = dp.get_annotation(category_names=pair)
|
|
461
530
|
ann_ids_to_keep = nms_image_annotations(anns, threshold, dp.image_id, prio)
|
|
@@ -476,27 +545,41 @@ class AnnotationNmsService(PipelineComponent):
|
|
|
476
545
|
@pipeline_component_registry.register("ImageParsingService")
|
|
477
546
|
class ImageParsingService:
|
|
478
547
|
"""
|
|
479
|
-
A super light service that calls `to_image` when processing datapoints.
|
|
480
|
-
|
|
548
|
+
A super light service that calls `to_image` when processing datapoints.
|
|
549
|
+
|
|
550
|
+
Might be useful if you build a pipeline that is not derived from `DoctectionPipe`.
|
|
551
|
+
|
|
481
552
|
"""
|
|
482
553
|
|
|
483
554
|
def __init__(self, dpi: Optional[int] = None):
|
|
484
555
|
"""
|
|
485
|
-
:
|
|
556
|
+
Args:
|
|
557
|
+
dpi: dpi resolution when converting PDFs into pixel values.
|
|
486
558
|
"""
|
|
487
559
|
self.name = "image"
|
|
488
560
|
self.dpi = dpi
|
|
489
561
|
|
|
490
562
|
def pass_datapoint(self, dp: Union[str, Mapping[str, Union[str, bytes]]]) -> Optional[Image]:
|
|
491
|
-
"""
|
|
563
|
+
"""
|
|
564
|
+
Pass a datapoint.
|
|
565
|
+
|
|
566
|
+
Args:
|
|
567
|
+
dp: A datapoint, either a string or a mapping.
|
|
568
|
+
|
|
569
|
+
Returns:
|
|
570
|
+
`Image` or None.
|
|
571
|
+
"""
|
|
492
572
|
return to_image(dp, self.dpi)
|
|
493
573
|
|
|
494
574
|
def predict_dataflow(self, df: DataFlow) -> DataFlow:
|
|
495
575
|
"""
|
|
496
|
-
Mapping a datapoint via `pass_datapoint` within a dataflow pipeline
|
|
576
|
+
Mapping a datapoint via `pass_datapoint` within a dataflow pipeline.
|
|
577
|
+
|
|
578
|
+
Args:
|
|
579
|
+
df: An input `DataFlow`.
|
|
497
580
|
|
|
498
|
-
:
|
|
499
|
-
|
|
581
|
+
Returns:
|
|
582
|
+
An output `DataFlow`.
|
|
500
583
|
"""
|
|
501
584
|
return MapData(df, self.pass_datapoint)
|
|
502
585
|
|