deepdoctection 0.31__py3-none-any.whl → 0.33__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 +16 -29
- deepdoctection/analyzer/dd.py +70 -59
- deepdoctection/configs/conf_dd_one.yaml +34 -31
- deepdoctection/dataflow/common.py +9 -5
- deepdoctection/dataflow/custom.py +5 -5
- deepdoctection/dataflow/custom_serialize.py +75 -18
- deepdoctection/dataflow/parallel_map.py +3 -3
- deepdoctection/dataflow/serialize.py +4 -4
- deepdoctection/dataflow/stats.py +3 -3
- deepdoctection/datapoint/annotation.py +41 -56
- deepdoctection/datapoint/box.py +9 -8
- deepdoctection/datapoint/convert.py +6 -6
- deepdoctection/datapoint/image.py +56 -44
- deepdoctection/datapoint/view.py +245 -150
- deepdoctection/datasets/__init__.py +1 -4
- deepdoctection/datasets/adapter.py +35 -26
- deepdoctection/datasets/base.py +14 -12
- deepdoctection/datasets/dataflow_builder.py +3 -3
- deepdoctection/datasets/info.py +24 -26
- deepdoctection/datasets/instances/doclaynet.py +51 -51
- deepdoctection/datasets/instances/fintabnet.py +46 -46
- deepdoctection/datasets/instances/funsd.py +25 -24
- deepdoctection/datasets/instances/iiitar13k.py +13 -10
- deepdoctection/datasets/instances/layouttest.py +4 -3
- deepdoctection/datasets/instances/publaynet.py +5 -5
- deepdoctection/datasets/instances/pubtables1m.py +24 -21
- deepdoctection/datasets/instances/pubtabnet.py +32 -30
- deepdoctection/datasets/instances/rvlcdip.py +30 -30
- deepdoctection/datasets/instances/xfund.py +26 -26
- deepdoctection/datasets/save.py +6 -6
- deepdoctection/eval/__init__.py +1 -4
- deepdoctection/eval/accmetric.py +32 -33
- deepdoctection/eval/base.py +8 -9
- deepdoctection/eval/cocometric.py +15 -13
- deepdoctection/eval/eval.py +41 -37
- deepdoctection/eval/tedsmetric.py +30 -23
- deepdoctection/eval/tp_eval_callback.py +16 -19
- deepdoctection/extern/__init__.py +2 -7
- deepdoctection/extern/base.py +339 -134
- deepdoctection/extern/d2detect.py +85 -113
- deepdoctection/extern/deskew.py +14 -11
- deepdoctection/extern/doctrocr.py +141 -130
- deepdoctection/extern/fastlang.py +27 -18
- deepdoctection/extern/hfdetr.py +71 -62
- deepdoctection/extern/hflayoutlm.py +504 -211
- deepdoctection/extern/hflm.py +230 -0
- deepdoctection/extern/model.py +488 -302
- deepdoctection/extern/pdftext.py +23 -19
- deepdoctection/extern/pt/__init__.py +1 -3
- deepdoctection/extern/pt/nms.py +6 -2
- deepdoctection/extern/pt/ptutils.py +29 -19
- deepdoctection/extern/tessocr.py +39 -38
- deepdoctection/extern/texocr.py +18 -18
- deepdoctection/extern/tp/tfutils.py +57 -9
- deepdoctection/extern/tp/tpcompat.py +21 -14
- deepdoctection/extern/tp/tpfrcnn/__init__.py +20 -0
- deepdoctection/extern/tp/tpfrcnn/common.py +7 -3
- deepdoctection/extern/tp/tpfrcnn/config/__init__.py +20 -0
- deepdoctection/extern/tp/tpfrcnn/config/config.py +13 -10
- deepdoctection/extern/tp/tpfrcnn/modeling/__init__.py +20 -0
- deepdoctection/extern/tp/tpfrcnn/modeling/backbone.py +18 -8
- deepdoctection/extern/tp/tpfrcnn/modeling/generalized_rcnn.py +12 -6
- deepdoctection/extern/tp/tpfrcnn/modeling/model_box.py +14 -9
- deepdoctection/extern/tp/tpfrcnn/modeling/model_cascade.py +8 -5
- deepdoctection/extern/tp/tpfrcnn/modeling/model_fpn.py +22 -17
- deepdoctection/extern/tp/tpfrcnn/modeling/model_frcnn.py +21 -14
- deepdoctection/extern/tp/tpfrcnn/modeling/model_mrcnn.py +19 -11
- deepdoctection/extern/tp/tpfrcnn/modeling/model_rpn.py +15 -10
- deepdoctection/extern/tp/tpfrcnn/predict.py +9 -4
- deepdoctection/extern/tp/tpfrcnn/preproc.py +12 -8
- deepdoctection/extern/tp/tpfrcnn/utils/__init__.py +20 -0
- deepdoctection/extern/tp/tpfrcnn/utils/box_ops.py +10 -2
- deepdoctection/extern/tpdetect.py +45 -53
- deepdoctection/mapper/__init__.py +3 -8
- deepdoctection/mapper/cats.py +27 -29
- deepdoctection/mapper/cocostruct.py +10 -10
- deepdoctection/mapper/d2struct.py +27 -26
- deepdoctection/mapper/hfstruct.py +13 -8
- deepdoctection/mapper/laylmstruct.py +178 -37
- deepdoctection/mapper/maputils.py +12 -11
- deepdoctection/mapper/match.py +2 -2
- deepdoctection/mapper/misc.py +11 -9
- deepdoctection/mapper/pascalstruct.py +4 -4
- deepdoctection/mapper/prodigystruct.py +5 -5
- deepdoctection/mapper/pubstruct.py +84 -92
- deepdoctection/mapper/tpstruct.py +5 -5
- deepdoctection/mapper/xfundstruct.py +33 -33
- deepdoctection/pipe/__init__.py +1 -1
- deepdoctection/pipe/anngen.py +12 -14
- deepdoctection/pipe/base.py +52 -106
- deepdoctection/pipe/common.py +72 -59
- deepdoctection/pipe/concurrency.py +16 -11
- deepdoctection/pipe/doctectionpipe.py +24 -21
- deepdoctection/pipe/language.py +20 -25
- deepdoctection/pipe/layout.py +20 -16
- deepdoctection/pipe/lm.py +75 -105
- deepdoctection/pipe/order.py +194 -89
- deepdoctection/pipe/refine.py +111 -124
- deepdoctection/pipe/segment.py +156 -161
- deepdoctection/pipe/{cell.py → sub_layout.py} +50 -40
- deepdoctection/pipe/text.py +37 -36
- deepdoctection/pipe/transform.py +19 -16
- deepdoctection/train/__init__.py +6 -12
- deepdoctection/train/d2_frcnn_train.py +48 -41
- deepdoctection/train/hf_detr_train.py +41 -30
- deepdoctection/train/hf_layoutlm_train.py +153 -135
- deepdoctection/train/tp_frcnn_train.py +32 -31
- deepdoctection/utils/concurrency.py +1 -1
- deepdoctection/utils/context.py +13 -6
- deepdoctection/utils/develop.py +4 -4
- deepdoctection/utils/env_info.py +87 -125
- deepdoctection/utils/file_utils.py +6 -11
- deepdoctection/utils/fs.py +22 -18
- deepdoctection/utils/identifier.py +2 -2
- deepdoctection/utils/logger.py +16 -15
- deepdoctection/utils/metacfg.py +7 -7
- deepdoctection/utils/mocks.py +93 -0
- deepdoctection/utils/pdf_utils.py +11 -11
- deepdoctection/utils/settings.py +185 -181
- deepdoctection/utils/tqdm.py +1 -1
- deepdoctection/utils/transform.py +14 -9
- deepdoctection/utils/types.py +104 -0
- deepdoctection/utils/utils.py +7 -7
- deepdoctection/utils/viz.py +74 -72
- {deepdoctection-0.31.dist-info → deepdoctection-0.33.dist-info}/METADATA +30 -21
- deepdoctection-0.33.dist-info/RECORD +146 -0
- {deepdoctection-0.31.dist-info → deepdoctection-0.33.dist-info}/WHEEL +1 -1
- deepdoctection/utils/detection_types.py +0 -68
- deepdoctection-0.31.dist-info/RECORD +0 -144
- {deepdoctection-0.31.dist-info → deepdoctection-0.33.dist-info}/LICENSE +0 -0
- {deepdoctection-0.31.dist-info → deepdoctection-0.33.dist-info}/top_level.txt +0 -0
deepdoctection/pipe/refine.py
CHANGED
|
@@ -19,11 +19,13 @@
|
|
|
19
19
|
Module for refining methods of table segmentation. The refining methods lead ultimately to a table structure which
|
|
20
20
|
enables html table representations
|
|
21
21
|
"""
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
22
24
|
from collections import defaultdict
|
|
23
25
|
from copy import copy
|
|
24
26
|
from dataclasses import asdict
|
|
25
27
|
from itertools import chain, product
|
|
26
|
-
from typing import DefaultDict,
|
|
28
|
+
from typing import DefaultDict, Optional, Sequence, Union
|
|
27
29
|
|
|
28
30
|
import networkx as nx # type: ignore
|
|
29
31
|
|
|
@@ -32,16 +34,15 @@ from ..datapoint.box import merge_boxes
|
|
|
32
34
|
from ..datapoint.image import Image
|
|
33
35
|
from ..extern.base import DetectionResult
|
|
34
36
|
from ..mapper.maputils import MappingContextManager
|
|
35
|
-
from ..utils.
|
|
36
|
-
from ..utils.
|
|
37
|
-
from
|
|
38
|
-
from .base import PipelineComponent
|
|
37
|
+
from ..utils.error import ImageError
|
|
38
|
+
from ..utils.settings import CellType, LayoutType, ObjectTypes, Relationships, TableType, get_type
|
|
39
|
+
from .base import MetaAnnotation, PipelineComponent
|
|
39
40
|
from .registry import pipeline_component_registry
|
|
40
41
|
|
|
41
42
|
__all__ = ["TableSegmentationRefinementService", "generate_html_string"]
|
|
42
43
|
|
|
43
44
|
|
|
44
|
-
def tiles_to_cells(dp: Image, table: ImageAnnotation) ->
|
|
45
|
+
def tiles_to_cells(dp: Image, table: ImageAnnotation) -> list[tuple[tuple[int, int], str]]:
|
|
45
46
|
"""
|
|
46
47
|
Creation of a table parquet: A table is divided into a tile parquet with the (number of rows) x
|
|
47
48
|
(the number of columns) tiles.
|
|
@@ -53,17 +54,17 @@ def tiles_to_cells(dp: Image, table: ImageAnnotation) -> List[Tuple[Tuple[int, i
|
|
|
53
54
|
:return: Image
|
|
54
55
|
"""
|
|
55
56
|
|
|
56
|
-
cell_ann_ids = table.get_relationship(Relationships.
|
|
57
|
+
cell_ann_ids = table.get_relationship(Relationships.CHILD)
|
|
57
58
|
cells = dp.get_annotation(
|
|
58
|
-
category_names=[LayoutType.
|
|
59
|
+
category_names=[LayoutType.CELL, CellType.HEADER, CellType.BODY], annotation_ids=cell_ann_ids
|
|
59
60
|
)
|
|
60
61
|
tile_to_cells = []
|
|
61
62
|
|
|
62
63
|
for cell in cells:
|
|
63
|
-
row_number =
|
|
64
|
-
col_number =
|
|
65
|
-
rs =
|
|
66
|
-
cs =
|
|
64
|
+
row_number = cell.get_sub_category(CellType.ROW_NUMBER).category_id
|
|
65
|
+
col_number = cell.get_sub_category(CellType.COLUMN_NUMBER).category_id
|
|
66
|
+
rs = cell.get_sub_category(CellType.ROW_SPAN).category_id
|
|
67
|
+
cs = cell.get_sub_category(CellType.COLUMN_SPAN).category_id
|
|
67
68
|
for k in range(rs):
|
|
68
69
|
for l in range(cs):
|
|
69
70
|
assert cell.annotation_id is not None, cell.annotation_id
|
|
@@ -73,15 +74,15 @@ def tiles_to_cells(dp: Image, table: ImageAnnotation) -> List[Tuple[Tuple[int, i
|
|
|
73
74
|
|
|
74
75
|
|
|
75
76
|
def connected_component_tiles(
|
|
76
|
-
tile_to_cell_list:
|
|
77
|
-
) ->
|
|
77
|
+
tile_to_cell_list: list[tuple[tuple[int, int], str]]
|
|
78
|
+
) -> tuple[list[set[tuple[int, int]]], DefaultDict[tuple[int, int], list[str]]]:
|
|
78
79
|
"""
|
|
79
80
|
The assignment of bricks to their cell occupancy induces a graph, with bricks as corners and cell edges. Cells that
|
|
80
81
|
lie on top of several bricks connect the underlying bricks. The graph generated according to this procedure is
|
|
81
82
|
usually multiple connected. The related components and the tile/cell ids assignment are determined.
|
|
82
83
|
|
|
83
|
-
:param tile_to_cell_list:
|
|
84
|
-
:return:
|
|
84
|
+
:param tile_to_cell_list: list of tuples with tile position and cell ids
|
|
85
|
+
:return: list of set with tiles that belong to the same connected component and a dict with tiles as keys and
|
|
85
86
|
assigned list of cell ids as values.
|
|
86
87
|
"""
|
|
87
88
|
cell_to_tile_list = [(cell_position[1], cell_position[0]) for cell_position in tile_to_cell_list]
|
|
@@ -107,7 +108,7 @@ def connected_component_tiles(
|
|
|
107
108
|
connected_components_tiles = []
|
|
108
109
|
|
|
109
110
|
for component in connected_components_cell:
|
|
110
|
-
tiles:
|
|
111
|
+
tiles: set[tuple[int, int]] = set()
|
|
111
112
|
for cell in component:
|
|
112
113
|
tiles = tiles.union(set(cell_to_tile_dict[cell])) # type: ignore
|
|
113
114
|
connected_components_tiles.append(tiles)
|
|
@@ -115,7 +116,7 @@ def connected_component_tiles(
|
|
|
115
116
|
return connected_components_tiles, tile_to_cell_dict
|
|
116
117
|
|
|
117
118
|
|
|
118
|
-
def _missing_tile(inputs:
|
|
119
|
+
def _missing_tile(inputs: set[tuple[int, int]]) -> Optional[tuple[int, int]]:
|
|
119
120
|
min_x, min_y, max_x, max_y = (
|
|
120
121
|
min(a[0] for a in inputs),
|
|
121
122
|
min(a[1] for a in inputs),
|
|
@@ -131,15 +132,15 @@ def _missing_tile(inputs: Set[Tuple[int, int]]) -> Optional[Tuple[int, int]]:
|
|
|
131
132
|
|
|
132
133
|
|
|
133
134
|
def _find_component(
|
|
134
|
-
tile:
|
|
135
|
-
) -> Optional[
|
|
135
|
+
tile: tuple[int, int], reduced_connected_tiles: list[set[tuple[int, int]]]
|
|
136
|
+
) -> Optional[set[tuple[int, int]]]:
|
|
136
137
|
for comp in reduced_connected_tiles:
|
|
137
138
|
if tile in comp:
|
|
138
139
|
return comp
|
|
139
140
|
return None
|
|
140
141
|
|
|
141
142
|
|
|
142
|
-
def _merge_components(reduced_connected_tiles:
|
|
143
|
+
def _merge_components(reduced_connected_tiles: list[set[tuple[int, int]]]) -> list[set[tuple[int, int]]]:
|
|
143
144
|
new_reduced_connected_tiles = []
|
|
144
145
|
for connected_tile in reduced_connected_tiles:
|
|
145
146
|
out = _missing_tile(connected_tile)
|
|
@@ -161,17 +162,17 @@ def _merge_components(reduced_connected_tiles: List[Set[Tuple[int, int]]]) -> Li
|
|
|
161
162
|
return new_reduced_connected_tiles
|
|
162
163
|
|
|
163
164
|
|
|
164
|
-
def generate_rectangle_tiling(connected_components_tiles:
|
|
165
|
+
def generate_rectangle_tiling(connected_components_tiles: list[set[tuple[int, int]]]) -> list[set[tuple[int, int]]]:
|
|
165
166
|
"""
|
|
166
167
|
The determined connected components imply that all cells have to be combined which are above a connected component.
|
|
167
168
|
In addition, however, it must also be taken into account that cells must be rectangular. This means that related
|
|
168
169
|
components have to be combined whose combined cells above do not create a rectangular tiling. All tiles are combined
|
|
169
170
|
in such a way that all cells above them combine to form a rectangular scheme.
|
|
170
171
|
|
|
171
|
-
:param connected_components_tiles:
|
|
172
|
-
:return:
|
|
172
|
+
:param connected_components_tiles: list of set with tiles that belong to the same connected component
|
|
173
|
+
:return: list of sets with tiles, the cells on top of which together form a rectangular scheme
|
|
173
174
|
"""
|
|
174
|
-
rectangle_tiling:
|
|
175
|
+
rectangle_tiling: list[set[tuple[int, int]]] = []
|
|
175
176
|
inputs = connected_components_tiles
|
|
176
177
|
|
|
177
178
|
while rectangle_tiling != inputs:
|
|
@@ -183,25 +184,25 @@ def generate_rectangle_tiling(connected_components_tiles: List[Set[Tuple[int, in
|
|
|
183
184
|
|
|
184
185
|
|
|
185
186
|
def rectangle_cells(
|
|
186
|
-
rectangle_tiling:
|
|
187
|
-
) ->
|
|
187
|
+
rectangle_tiling: list[set[tuple[int, int]]], tile_to_cell_dict: DefaultDict[tuple[int, int], list[str]]
|
|
188
|
+
) -> list[set[str]]:
|
|
188
189
|
"""
|
|
189
190
|
All cells are determined that are located above combined connected components and form a rectangular scheme.
|
|
190
191
|
|
|
191
|
-
:param rectangle_tiling:
|
|
192
|
+
:param rectangle_tiling: list of sets with tiles, the cells on top of which together form a rectangular scheme
|
|
192
193
|
:param tile_to_cell_dict: Dict with tiles as keys and assigned list of cell ids as values.
|
|
193
|
-
:return:
|
|
194
|
+
:return: list of set of cell ids that form a rectangular scheme
|
|
194
195
|
"""
|
|
195
|
-
rectangle_tiling_cells:
|
|
196
|
+
rectangle_tiling_cells: list[set[str]] = []
|
|
196
197
|
for rect_tiling_component in rectangle_tiling:
|
|
197
|
-
rect_cell_component:
|
|
198
|
+
rect_cell_component: set[str] = set()
|
|
198
199
|
for el in rect_tiling_component:
|
|
199
200
|
rect_cell_component = rect_cell_component.union(set(tile_to_cell_dict[el]))
|
|
200
201
|
rectangle_tiling_cells.append(rect_cell_component)
|
|
201
202
|
return rectangle_tiling_cells
|
|
202
203
|
|
|
203
204
|
|
|
204
|
-
def _tiling_to_cell_position(inputs:
|
|
205
|
+
def _tiling_to_cell_position(inputs: set[tuple[int, int]]) -> tuple[int, int, int, int]:
|
|
205
206
|
row_number = min(a[0] for a in inputs)
|
|
206
207
|
col_number = min(a[1] for a in inputs)
|
|
207
208
|
row_span = max(abs(a[0] - b[0]) + 1 for a in inputs for b in inputs)
|
|
@@ -210,8 +211,8 @@ def _tiling_to_cell_position(inputs: Set[Tuple[int, int]]) -> Tuple[int, int, in
|
|
|
210
211
|
|
|
211
212
|
|
|
212
213
|
def _html_cell(
|
|
213
|
-
cell_position: Union[
|
|
214
|
-
) ->
|
|
214
|
+
cell_position: Union[tuple[int, int, int, int], tuple[()]], position_filled_list: list[tuple[int, int]]
|
|
215
|
+
) -> list[str]:
|
|
215
216
|
"""
|
|
216
217
|
Html table cell string generation
|
|
217
218
|
"""
|
|
@@ -238,12 +239,12 @@ def _html_cell(
|
|
|
238
239
|
|
|
239
240
|
|
|
240
241
|
def _html_row(
|
|
241
|
-
row_list:
|
|
242
|
-
position_filled_list:
|
|
242
|
+
row_list: list[tuple[int, int, int, int]],
|
|
243
|
+
position_filled_list: list[tuple[int, int]],
|
|
243
244
|
this_row: int,
|
|
244
245
|
number_of_cols: int,
|
|
245
|
-
row_ann_id_list:
|
|
246
|
-
) ->
|
|
246
|
+
row_ann_id_list: list[str],
|
|
247
|
+
) -> list[str]:
|
|
247
248
|
"""
|
|
248
249
|
Html table row string generation
|
|
249
250
|
"""
|
|
@@ -275,16 +276,16 @@ def _html_row(
|
|
|
275
276
|
|
|
276
277
|
|
|
277
278
|
def _html_table(
|
|
278
|
-
table_list:
|
|
279
|
-
cells_ann_list:
|
|
279
|
+
table_list: list[tuple[int, list[tuple[int, int, int, int]]]],
|
|
280
|
+
cells_ann_list: list[tuple[int, list[str]]],
|
|
280
281
|
number_of_rows: int,
|
|
281
282
|
number_of_cols: int,
|
|
282
|
-
) ->
|
|
283
|
+
) -> list[str]:
|
|
283
284
|
"""
|
|
284
285
|
Html table string generation
|
|
285
286
|
"""
|
|
286
287
|
html = ["<table>"]
|
|
287
|
-
position_filled:
|
|
288
|
+
position_filled: list[tuple[int, int]] = []
|
|
288
289
|
for idx in range(1, number_of_rows + 1):
|
|
289
290
|
row_idx = list(filter(lambda x: x[0] == idx, table_list))[0][1] # pylint:disable=W0640
|
|
290
291
|
row_ann_ids = list(filter(lambda x: x[0] == idx, cells_ann_list))[0][1] # pylint:disable=W0640
|
|
@@ -294,7 +295,7 @@ def _html_table(
|
|
|
294
295
|
return html
|
|
295
296
|
|
|
296
297
|
|
|
297
|
-
def generate_html_string(table: ImageAnnotation) ->
|
|
298
|
+
def generate_html_string(table: ImageAnnotation) -> list[str]:
|
|
298
299
|
"""
|
|
299
300
|
Takes the table segmentation by using table cells row number, column numbers etc. and generates a html
|
|
300
301
|
representation.
|
|
@@ -307,36 +308,36 @@ def generate_html_string(table: ImageAnnotation) -> List[str]:
|
|
|
307
308
|
table_image = table.image
|
|
308
309
|
cells = table_image.get_annotation(
|
|
309
310
|
category_names=[
|
|
310
|
-
LayoutType.
|
|
311
|
-
CellType.
|
|
312
|
-
CellType.
|
|
313
|
-
CellType.
|
|
314
|
-
CellType.
|
|
315
|
-
CellType.
|
|
316
|
-
CellType.
|
|
311
|
+
LayoutType.CELL,
|
|
312
|
+
CellType.HEADER,
|
|
313
|
+
CellType.BODY,
|
|
314
|
+
CellType.SPANNING,
|
|
315
|
+
CellType.ROW_HEADER,
|
|
316
|
+
CellType.COLUMN_HEADER,
|
|
317
|
+
CellType.PROJECTED_ROW_HEADER,
|
|
317
318
|
]
|
|
318
319
|
)
|
|
319
|
-
number_of_rows =
|
|
320
|
-
number_of_cols =
|
|
320
|
+
number_of_rows = table_image.summary.get_sub_category(TableType.NUMBER_OF_ROWS).category_id
|
|
321
|
+
number_of_cols = table_image.summary.get_sub_category(TableType.NUMBER_OF_COLUMNS).category_id
|
|
321
322
|
table_list = []
|
|
322
323
|
cells_ann_list = []
|
|
323
324
|
for row_number in range(1, number_of_rows + 1):
|
|
324
325
|
cells_of_row = list(
|
|
325
326
|
sorted(
|
|
326
327
|
filter(
|
|
327
|
-
lambda cell: cell.get_sub_category(CellType.
|
|
328
|
-
==
|
|
328
|
+
lambda cell: cell.get_sub_category(CellType.ROW_NUMBER).category_id
|
|
329
|
+
== row_number, # pylint: disable=W0640
|
|
329
330
|
cells,
|
|
330
331
|
),
|
|
331
|
-
key=lambda cell: cell.get_sub_category(CellType.
|
|
332
|
+
key=lambda cell: cell.get_sub_category(CellType.COLUMN_NUMBER).category_id,
|
|
332
333
|
)
|
|
333
334
|
)
|
|
334
335
|
row_list = [
|
|
335
336
|
(
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
337
|
+
cell.get_sub_category(CellType.ROW_NUMBER).category_id,
|
|
338
|
+
cell.get_sub_category(CellType.COLUMN_NUMBER).category_id,
|
|
339
|
+
cell.get_sub_category(CellType.ROW_SPAN).category_id,
|
|
340
|
+
cell.get_sub_category(CellType.COLUMN_SPAN).category_id,
|
|
340
341
|
)
|
|
341
342
|
for cell in cells_of_row
|
|
342
343
|
]
|
|
@@ -398,19 +399,13 @@ class TableSegmentationRefinementService(PipelineComponent):
|
|
|
398
399
|
|
|
399
400
|
"""
|
|
400
401
|
|
|
401
|
-
def __init__(self) -> None:
|
|
402
|
-
self.
|
|
403
|
-
self.
|
|
404
|
-
LayoutType.cell,
|
|
405
|
-
CellType.column_header,
|
|
406
|
-
CellType.projected_row_header,
|
|
407
|
-
CellType.spanning,
|
|
408
|
-
CellType.row_header,
|
|
409
|
-
]
|
|
402
|
+
def __init__(self, table_name: Sequence[ObjectTypes], cell_names: Sequence[ObjectTypes]) -> None:
|
|
403
|
+
self.table_name = table_name
|
|
404
|
+
self.cell_names = cell_names
|
|
410
405
|
super().__init__("table_segment_refine")
|
|
411
406
|
|
|
412
407
|
def serve(self, dp: Image) -> None:
|
|
413
|
-
tables = dp.get_annotation(category_names=self.
|
|
408
|
+
tables = dp.get_annotation(category_names=self.table_name)
|
|
414
409
|
for table in tables:
|
|
415
410
|
if table.image is None:
|
|
416
411
|
raise ImageError("table.image cannot be None")
|
|
@@ -427,23 +422,23 @@ class TableSegmentationRefinementService(PipelineComponent):
|
|
|
427
422
|
det_result = DetectionResult(
|
|
428
423
|
box=merged_box.to_list(mode="xyxy"),
|
|
429
424
|
score=-1.0,
|
|
430
|
-
class_id=
|
|
425
|
+
class_id=cells[0].category_id,
|
|
431
426
|
class_name=get_type(cells[0].category_name),
|
|
432
427
|
)
|
|
433
428
|
new_cell_ann_id = self.dp_manager.set_image_annotation(det_result, table.annotation_id)
|
|
434
429
|
if new_cell_ann_id is not None:
|
|
435
430
|
row_number, col_number, row_span, col_span = _tiling_to_cell_position(tiling)
|
|
436
431
|
self.dp_manager.set_category_annotation(
|
|
437
|
-
CellType.
|
|
432
|
+
CellType.ROW_NUMBER, row_number, CellType.ROW_NUMBER, new_cell_ann_id
|
|
438
433
|
)
|
|
439
434
|
self.dp_manager.set_category_annotation(
|
|
440
|
-
CellType.
|
|
435
|
+
CellType.COLUMN_NUMBER, col_number, CellType.COLUMN_NUMBER, new_cell_ann_id
|
|
441
436
|
)
|
|
442
437
|
self.dp_manager.set_category_annotation(
|
|
443
|
-
CellType.
|
|
438
|
+
CellType.ROW_SPAN, row_span, CellType.ROW_SPAN, new_cell_ann_id
|
|
444
439
|
)
|
|
445
440
|
self.dp_manager.set_category_annotation(
|
|
446
|
-
CellType.
|
|
441
|
+
CellType.COLUMN_SPAN, col_span, CellType.COLUMN_SPAN, new_cell_ann_id
|
|
447
442
|
)
|
|
448
443
|
else:
|
|
449
444
|
# DetectionResult cannot be dumped, hence merged_box must already exist. Hence, it must
|
|
@@ -458,67 +453,59 @@ class TableSegmentationRefinementService(PipelineComponent):
|
|
|
458
453
|
for cell in cells:
|
|
459
454
|
cell.deactivate()
|
|
460
455
|
|
|
461
|
-
cells = table.image.get_annotation(category_names=self.
|
|
462
|
-
number_of_rows = max(
|
|
463
|
-
number_of_cols = max(
|
|
464
|
-
max_row_span = max(
|
|
465
|
-
max_col_span = max(
|
|
456
|
+
cells = table.image.get_annotation(category_names=self.cell_names)
|
|
457
|
+
number_of_rows = max(cell.get_sub_category(CellType.ROW_NUMBER).category_id for cell in cells)
|
|
458
|
+
number_of_cols = max(cell.get_sub_category(CellType.COLUMN_NUMBER).category_id for cell in cells)
|
|
459
|
+
max_row_span = max(cell.get_sub_category(CellType.ROW_SPAN).category_id for cell in cells)
|
|
460
|
+
max_col_span = max(cell.get_sub_category(CellType.COLUMN_SPAN).category_id for cell in cells)
|
|
466
461
|
# TODO: the summaries should be sub categories of the underlying ann
|
|
467
|
-
if
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
)
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
table.image.summary.remove_sub_category(TableType.max_col_span)
|
|
478
|
-
else:
|
|
479
|
-
raise AnnotationError(
|
|
480
|
-
"Table summary does not contain sub categories TableType.number_of_rows, "
|
|
481
|
-
"TableType.number_of_columns, TableType.max_row_span, TableType.max_col_span"
|
|
482
|
-
)
|
|
462
|
+
if (
|
|
463
|
+
TableType.NUMBER_OF_ROWS in table.image.summary.sub_categories
|
|
464
|
+
and TableType.NUMBER_OF_COLUMNS in table.image.summary.sub_categories
|
|
465
|
+
and TableType.MAX_ROW_SPAN in table.image.summary.sub_categories
|
|
466
|
+
and TableType.MAX_COL_SPAN in table.image.summary.sub_categories
|
|
467
|
+
):
|
|
468
|
+
table.image.summary.remove_sub_category(TableType.NUMBER_OF_ROWS)
|
|
469
|
+
table.image.summary.remove_sub_category(TableType.NUMBER_OF_COLUMNS)
|
|
470
|
+
table.image.summary.remove_sub_category(TableType.MAX_ROW_SPAN)
|
|
471
|
+
table.image.summary.remove_sub_category(TableType.MAX_COL_SPAN)
|
|
483
472
|
|
|
484
473
|
self.dp_manager.set_summary_annotation(
|
|
485
|
-
TableType.
|
|
474
|
+
TableType.NUMBER_OF_ROWS, TableType.NUMBER_OF_ROWS, number_of_rows, annotation_id=table.annotation_id
|
|
486
475
|
)
|
|
487
476
|
self.dp_manager.set_summary_annotation(
|
|
488
|
-
TableType.
|
|
489
|
-
TableType.
|
|
477
|
+
TableType.NUMBER_OF_COLUMNS,
|
|
478
|
+
TableType.NUMBER_OF_COLUMNS,
|
|
490
479
|
number_of_cols,
|
|
491
480
|
annotation_id=table.annotation_id,
|
|
492
481
|
)
|
|
493
482
|
self.dp_manager.set_summary_annotation(
|
|
494
|
-
TableType.
|
|
483
|
+
TableType.MAX_ROW_SPAN, TableType.MAX_ROW_SPAN, max_row_span, annotation_id=table.annotation_id
|
|
495
484
|
)
|
|
496
485
|
self.dp_manager.set_summary_annotation(
|
|
497
|
-
TableType.
|
|
486
|
+
TableType.MAX_COL_SPAN, TableType.MAX_COL_SPAN, max_col_span, annotation_id=table.annotation_id
|
|
498
487
|
)
|
|
499
488
|
html = generate_html_string(table)
|
|
500
|
-
self.dp_manager.set_container_annotation(TableType.
|
|
501
|
-
|
|
502
|
-
def clone(self) ->
|
|
503
|
-
return self.__class__()
|
|
504
|
-
|
|
505
|
-
def get_meta_annotation(self) ->
|
|
506
|
-
return
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
},
|
|
520
|
-
),
|
|
521
|
-
("relationships", {}),
|
|
522
|
-
("summaries", []),
|
|
523
|
-
]
|
|
489
|
+
self.dp_manager.set_container_annotation(TableType.HTML, -1, TableType.HTML, table.annotation_id, html)
|
|
490
|
+
|
|
491
|
+
def clone(self) -> TableSegmentationRefinementService:
|
|
492
|
+
return self.__class__(self.table_name, self.cell_names)
|
|
493
|
+
|
|
494
|
+
def get_meta_annotation(self) -> MetaAnnotation:
|
|
495
|
+
return MetaAnnotation(
|
|
496
|
+
image_annotations=(),
|
|
497
|
+
sub_categories={
|
|
498
|
+
LayoutType.CELL: {
|
|
499
|
+
CellType.ROW_NUMBER,
|
|
500
|
+
CellType.COLUMN_NUMBER,
|
|
501
|
+
CellType.ROW_SPAN,
|
|
502
|
+
CellType.COLUMN_SPAN,
|
|
503
|
+
},
|
|
504
|
+
LayoutType.TABLE: {TableType.HTML},
|
|
505
|
+
},
|
|
506
|
+
relationships={},
|
|
507
|
+
summaries=(),
|
|
524
508
|
)
|
|
509
|
+
|
|
510
|
+
def clear_predictor(self) -> None:
|
|
511
|
+
pass
|