deepdoctection 0.32__py3-none-any.whl → 0.34__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 +8 -25
- deepdoctection/analyzer/dd.py +84 -71
- 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 +78 -56
- deepdoctection/datapoint/box.py +7 -7
- deepdoctection/datapoint/convert.py +6 -6
- deepdoctection/datapoint/image.py +157 -75
- deepdoctection/datapoint/view.py +175 -151
- deepdoctection/datasets/adapter.py +30 -24
- deepdoctection/datasets/base.py +10 -10
- deepdoctection/datasets/dataflow_builder.py +3 -3
- deepdoctection/datasets/info.py +23 -25
- deepdoctection/datasets/instances/doclaynet.py +48 -49
- deepdoctection/datasets/instances/fintabnet.py +44 -45
- deepdoctection/datasets/instances/funsd.py +23 -23
- deepdoctection/datasets/instances/iiitar13k.py +8 -8
- deepdoctection/datasets/instances/layouttest.py +2 -2
- deepdoctection/datasets/instances/publaynet.py +3 -3
- deepdoctection/datasets/instances/pubtables1m.py +18 -18
- deepdoctection/datasets/instances/pubtabnet.py +30 -29
- deepdoctection/datasets/instances/rvlcdip.py +28 -29
- deepdoctection/datasets/instances/xfund.py +51 -30
- deepdoctection/datasets/save.py +6 -6
- deepdoctection/eval/accmetric.py +32 -33
- deepdoctection/eval/base.py +8 -9
- deepdoctection/eval/cocometric.py +13 -12
- deepdoctection/eval/eval.py +32 -26
- deepdoctection/eval/tedsmetric.py +16 -12
- deepdoctection/eval/tp_eval_callback.py +7 -16
- deepdoctection/extern/base.py +339 -134
- deepdoctection/extern/d2detect.py +69 -89
- deepdoctection/extern/deskew.py +11 -10
- deepdoctection/extern/doctrocr.py +81 -64
- deepdoctection/extern/fastlang.py +23 -16
- deepdoctection/extern/hfdetr.py +53 -38
- deepdoctection/extern/hflayoutlm.py +216 -155
- deepdoctection/extern/hflm.py +35 -30
- deepdoctection/extern/model.py +433 -255
- deepdoctection/extern/pdftext.py +15 -15
- deepdoctection/extern/pt/ptutils.py +4 -2
- deepdoctection/extern/tessocr.py +39 -38
- deepdoctection/extern/texocr.py +14 -16
- deepdoctection/extern/tp/tfutils.py +16 -2
- deepdoctection/extern/tp/tpcompat.py +11 -7
- deepdoctection/extern/tp/tpfrcnn/config/config.py +4 -4
- deepdoctection/extern/tp/tpfrcnn/modeling/backbone.py +1 -1
- deepdoctection/extern/tp/tpfrcnn/modeling/model_box.py +5 -5
- deepdoctection/extern/tp/tpfrcnn/modeling/model_fpn.py +6 -6
- deepdoctection/extern/tp/tpfrcnn/modeling/model_frcnn.py +4 -4
- deepdoctection/extern/tp/tpfrcnn/modeling/model_mrcnn.py +5 -3
- deepdoctection/extern/tp/tpfrcnn/preproc.py +5 -5
- deepdoctection/extern/tpdetect.py +40 -45
- deepdoctection/mapper/cats.py +36 -40
- deepdoctection/mapper/cocostruct.py +16 -12
- deepdoctection/mapper/d2struct.py +22 -22
- deepdoctection/mapper/hfstruct.py +7 -7
- deepdoctection/mapper/laylmstruct.py +22 -24
- deepdoctection/mapper/maputils.py +9 -10
- deepdoctection/mapper/match.py +33 -2
- deepdoctection/mapper/misc.py +6 -7
- deepdoctection/mapper/pascalstruct.py +4 -4
- deepdoctection/mapper/prodigystruct.py +6 -6
- deepdoctection/mapper/pubstruct.py +84 -92
- deepdoctection/mapper/tpstruct.py +3 -3
- deepdoctection/mapper/xfundstruct.py +33 -33
- deepdoctection/pipe/anngen.py +39 -14
- deepdoctection/pipe/base.py +68 -99
- deepdoctection/pipe/common.py +181 -85
- deepdoctection/pipe/concurrency.py +14 -10
- deepdoctection/pipe/doctectionpipe.py +24 -21
- deepdoctection/pipe/language.py +20 -25
- deepdoctection/pipe/layout.py +18 -16
- deepdoctection/pipe/lm.py +49 -47
- deepdoctection/pipe/order.py +63 -65
- deepdoctection/pipe/refine.py +102 -109
- deepdoctection/pipe/segment.py +157 -162
- deepdoctection/pipe/sub_layout.py +50 -40
- deepdoctection/pipe/text.py +37 -36
- deepdoctection/pipe/transform.py +19 -16
- deepdoctection/train/d2_frcnn_train.py +27 -25
- deepdoctection/train/hf_detr_train.py +22 -18
- deepdoctection/train/hf_layoutlm_train.py +49 -48
- deepdoctection/train/tp_frcnn_train.py +10 -11
- deepdoctection/utils/concurrency.py +1 -1
- deepdoctection/utils/context.py +13 -6
- deepdoctection/utils/develop.py +4 -4
- deepdoctection/utils/env_info.py +52 -14
- deepdoctection/utils/file_utils.py +6 -11
- deepdoctection/utils/fs.py +41 -14
- deepdoctection/utils/identifier.py +2 -2
- deepdoctection/utils/logger.py +15 -15
- deepdoctection/utils/metacfg.py +7 -7
- deepdoctection/utils/pdf_utils.py +39 -14
- deepdoctection/utils/settings.py +188 -182
- 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 +70 -69
- {deepdoctection-0.32.dist-info → deepdoctection-0.34.dist-info}/METADATA +7 -4
- deepdoctection-0.34.dist-info/RECORD +146 -0
- {deepdoctection-0.32.dist-info → deepdoctection-0.34.dist-info}/WHEEL +1 -1
- deepdoctection/utils/detection_types.py +0 -68
- deepdoctection-0.32.dist-info/RECORD +0 -146
- {deepdoctection-0.32.dist-info → deepdoctection-0.34.dist-info}/LICENSE +0 -0
- {deepdoctection-0.32.dist-info → deepdoctection-0.34.dist-info}/top_level.txt +0 -0
|
@@ -79,8 +79,8 @@ def sample_fast_rcnn_targets(boxes, gt_boxes, gt_labels, frcnn_fg_thresh, frcnn_
|
|
|
79
79
|
proposal_metrics(iou)
|
|
80
80
|
|
|
81
81
|
# add ground truth as proposals as well
|
|
82
|
-
boxes = tf.concat([boxes, gt_boxes], axis=0) # (n+m) x 4
|
|
83
|
-
iou = tf.concat([iou, tf.eye(tf.shape(gt_boxes)[0])], axis=0) # (n+m) x m
|
|
82
|
+
boxes = tf.concat([boxes, gt_boxes], axis=0) # (n+m) x 4 # pylint: disable=E1123
|
|
83
|
+
iou = tf.concat([iou, tf.eye(tf.shape(gt_boxes)[0])], axis=0) # (n+m) x m # pylint: disable=E1123
|
|
84
84
|
# #proposal=n+m from now on
|
|
85
85
|
|
|
86
86
|
def sample_fg_bg(iou):
|
|
@@ -114,10 +114,10 @@ def sample_fast_rcnn_targets(boxes, gt_boxes, gt_labels, frcnn_fg_thresh, frcnn_
|
|
|
114
114
|
)
|
|
115
115
|
fg_inds_wrt_gt = tf.gather(best_iou_ind, fg_inds) # num_fg
|
|
116
116
|
|
|
117
|
-
all_indices = tf.concat([fg_inds, bg_inds], axis=0) # indices w.r.t all n+m proposal boxes
|
|
117
|
+
all_indices = tf.concat([fg_inds, bg_inds], axis=0) # indices w.r.t all n+m proposal boxes # pylint: disable=E1123
|
|
118
118
|
ret_boxes = tf.gather(boxes, all_indices)
|
|
119
119
|
|
|
120
|
-
ret_labels = tf.concat(
|
|
120
|
+
ret_labels = tf.concat( # pylint: disable=E1123
|
|
121
121
|
[tf.gather(gt_labels, fg_inds_wrt_gt), tf.zeros_like(bg_inds, dtype=tf.int64)],
|
|
122
122
|
axis=0,
|
|
123
123
|
)
|
|
@@ -47,7 +47,7 @@ def maskrcnn_loss(mask_logits, fg_labels, fg_target_masks):
|
|
|
47
47
|
|
|
48
48
|
# add some training visualizations to tensorboard
|
|
49
49
|
with tf.name_scope("mask_viz"):
|
|
50
|
-
viz = tf.concat([fg_target_masks, mask_probs], axis=1)
|
|
50
|
+
viz = tf.concat([fg_target_masks, mask_probs], axis=1) # pylint: disable=E1123
|
|
51
51
|
viz = tf.expand_dims(viz, 3)
|
|
52
52
|
viz = tf.cast(viz * 255, tf.uint8, name="viz")
|
|
53
53
|
tf.summary.image("mask_truth|pred", viz, max_outputs=10)
|
|
@@ -83,7 +83,7 @@ def maskrcnn_upXconv_head(feature, num_category, num_convs, norm=None, **kwargs)
|
|
|
83
83
|
with argscope(
|
|
84
84
|
[Conv2D, Conv2DTranspose],
|
|
85
85
|
data_format="channels_first",
|
|
86
|
-
kernel_initializer=tf.variance_scaling_initializer(
|
|
86
|
+
kernel_initializer=tf.variance_scaling_initializer( # pylint: disable=E1101
|
|
87
87
|
scale=2.0,
|
|
88
88
|
mode="fan_out",
|
|
89
89
|
distribution="untruncated_normal" if get_tf_version_tuple() >= (1, 12) else "normal",
|
|
@@ -127,5 +127,7 @@ def unpackbits_masks(masks):
|
|
|
127
127
|
assert masks.dtype == tf.uint8, masks
|
|
128
128
|
bits = tf.constant((128, 64, 32, 16, 8, 4, 2, 1), dtype=tf.uint8)
|
|
129
129
|
unpacked = tf.bitwise.bitwise_and(tf.expand_dims(masks, -1), bits) > 0
|
|
130
|
-
unpacked = tf.reshape(
|
|
130
|
+
unpacked = tf.reshape(
|
|
131
|
+
unpacked, tf.concat([tf.shape(masks)[:-1], [8 * tf.shape(masks)[-1]]], axis=0) # pylint: disable=E1123
|
|
132
|
+
) # pylint: disable=E1123
|
|
131
133
|
return unpacked
|
|
@@ -16,9 +16,9 @@ import numpy as np
|
|
|
16
16
|
from lazy_imports import try_import
|
|
17
17
|
|
|
18
18
|
from ....datapoint.convert import box_to_point4, point4_to_box
|
|
19
|
-
from ....utils.detection_types import ImageType, JsonDict
|
|
20
19
|
from ....utils.error import MalformedData
|
|
21
20
|
from ....utils.logger import log_once
|
|
21
|
+
from ....utils.types import JsonDict, PixelValues
|
|
22
22
|
from .common import filter_boxes_inside_shape, np_iou
|
|
23
23
|
from .modeling.model_fpn import get_all_anchors_fpn
|
|
24
24
|
from .utils.np_box_ops import area as np_area
|
|
@@ -214,15 +214,15 @@ def get_multilevel_rpn_anchor_input(
|
|
|
214
214
|
|
|
215
215
|
|
|
216
216
|
def get_anchor_labels(
|
|
217
|
-
anchors:
|
|
218
|
-
gt_boxes:
|
|
219
|
-
crowd_boxes:
|
|
217
|
+
anchors: PixelValues,
|
|
218
|
+
gt_boxes: PixelValues,
|
|
219
|
+
crowd_boxes: PixelValues,
|
|
220
220
|
batch_per_image: int,
|
|
221
221
|
front_ground_ratio: float,
|
|
222
222
|
positive_anchor_threshold: float,
|
|
223
223
|
negative_anchor_threshold: float,
|
|
224
224
|
crowd_overlap_threshold: float,
|
|
225
|
-
) -> (
|
|
225
|
+
) -> (PixelValues, PixelValues):
|
|
226
226
|
"""
|
|
227
227
|
Label each anchor as fg/bg/ignore.
|
|
228
228
|
|
|
@@ -18,17 +18,17 @@
|
|
|
18
18
|
"""
|
|
19
19
|
TP Faster RCNN model as predictor for deepdoctection pipeline
|
|
20
20
|
"""
|
|
21
|
+
from __future__ import annotations
|
|
21
22
|
|
|
22
23
|
from abc import ABC
|
|
23
|
-
from copy import copy
|
|
24
24
|
from pathlib import Path
|
|
25
|
-
from typing import
|
|
25
|
+
from typing import Mapping, Optional, Sequence, Union
|
|
26
26
|
|
|
27
|
-
from ..utils.detection_types import ImageType, Requirement
|
|
28
27
|
from ..utils.file_utils import get_tensorflow_requirement, get_tensorpack_requirement
|
|
29
28
|
from ..utils.metacfg import set_config_by_yaml
|
|
30
|
-
from ..utils.settings import ObjectTypes, TypeOrStr, get_type
|
|
31
|
-
from .
|
|
29
|
+
from ..utils.settings import DefaultType, ObjectTypes, TypeOrStr, get_type
|
|
30
|
+
from ..utils.types import PathLikeOrStr, PixelValues, Requirement
|
|
31
|
+
from .base import DetectionResult, ModelCategories, ObjectDetector
|
|
32
32
|
from .tp.tpcompat import TensorpackPredictor
|
|
33
33
|
from .tp.tpfrcnn.config.config import model_frcnn_config
|
|
34
34
|
from .tp.tpfrcnn.modeling.generalized_rcnn import ResNetFPNModel
|
|
@@ -38,41 +38,37 @@ from .tp.tpfrcnn.predict import tp_predict_image
|
|
|
38
38
|
class TPFrcnnDetectorMixin(ObjectDetector, ABC):
|
|
39
39
|
"""Base class for TP FRCNN detector. This class only implements the basic wrapper functions"""
|
|
40
40
|
|
|
41
|
-
def __init__(self, categories: Mapping[
|
|
42
|
-
|
|
41
|
+
def __init__(self, categories: Mapping[int, TypeOrStr], filter_categories: Optional[Sequence[TypeOrStr]] = None):
|
|
42
|
+
categories = {k: get_type(v) for k, v in categories.items()}
|
|
43
|
+
categories.update({0: get_type("background")})
|
|
44
|
+
self.categories = ModelCategories(categories)
|
|
43
45
|
if filter_categories:
|
|
44
|
-
filter_categories =
|
|
45
|
-
self.filter_categories = filter_categories
|
|
46
|
-
self._tp_categories = self._map_to_tp_categories(categories)
|
|
46
|
+
self.categories.filter_categories = tuple(get_type(cat) for cat in filter_categories)
|
|
47
47
|
|
|
48
|
-
def _map_category_names(self, detection_results:
|
|
48
|
+
def _map_category_names(self, detection_results: list[DetectionResult]) -> list[DetectionResult]:
|
|
49
49
|
"""
|
|
50
50
|
Populating category names to detection results
|
|
51
51
|
|
|
52
52
|
:param detection_results: list of detection results
|
|
53
53
|
:return: List of detection results with attribute class_name populated
|
|
54
54
|
"""
|
|
55
|
-
filtered_detection_result:
|
|
55
|
+
filtered_detection_result: list[DetectionResult] = []
|
|
56
56
|
for result in detection_results:
|
|
57
|
-
result.class_name = self.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
else:
|
|
57
|
+
result.class_name = self.categories.categories.get(
|
|
58
|
+
result.class_id if result.class_id else -1, DefaultType.DEFAULT_TYPE
|
|
59
|
+
)
|
|
60
|
+
if result.class_name != DefaultType.DEFAULT_TYPE:
|
|
62
61
|
filtered_detection_result.append(result)
|
|
63
62
|
return filtered_detection_result
|
|
64
63
|
|
|
65
64
|
@staticmethod
|
|
66
|
-
def
|
|
67
|
-
categories = {str(key): get_type(categories[val]) for key, val in enumerate(categories, 1)}
|
|
68
|
-
categories["0"] = get_type("background")
|
|
69
|
-
return categories # type: ignore
|
|
70
|
-
|
|
71
|
-
@staticmethod
|
|
72
|
-
def get_name(path_weights: str, architecture: str) -> str:
|
|
65
|
+
def get_name(path_weights: PathLikeOrStr, architecture: str) -> str:
|
|
73
66
|
"""Returns the name of the model"""
|
|
74
67
|
return f"Tensorpack_{architecture}" + "_".join(Path(path_weights).parts[-2:])
|
|
75
68
|
|
|
69
|
+
def get_category_names(self) -> tuple[ObjectTypes, ...]:
|
|
70
|
+
return self.categories.get_categories(as_dict=False)
|
|
71
|
+
|
|
76
72
|
|
|
77
73
|
class TPFrcnnDetector(TensorpackPredictor, TPFrcnnDetectorMixin):
|
|
78
74
|
"""
|
|
@@ -97,10 +93,10 @@ class TPFrcnnDetector(TensorpackPredictor, TPFrcnnDetectorMixin):
|
|
|
97
93
|
|
|
98
94
|
def __init__(
|
|
99
95
|
self,
|
|
100
|
-
path_yaml:
|
|
101
|
-
path_weights:
|
|
102
|
-
categories: Mapping[
|
|
103
|
-
config_overwrite: Optional[
|
|
96
|
+
path_yaml: PathLikeOrStr,
|
|
97
|
+
path_weights: PathLikeOrStr,
|
|
98
|
+
categories: Mapping[int, TypeOrStr],
|
|
99
|
+
config_overwrite: Optional[list[str]] = None,
|
|
104
100
|
ignore_mismatch: bool = False,
|
|
105
101
|
filter_categories: Optional[Sequence[TypeOrStr]] = None,
|
|
106
102
|
):
|
|
@@ -125,14 +121,10 @@ class TPFrcnnDetector(TensorpackPredictor, TPFrcnnDetectorMixin):
|
|
|
125
121
|
:param filter_categories: The model might return objects that are not supposed to be predicted and that should
|
|
126
122
|
be filtered. Pass a list of category names that must not be returned
|
|
127
123
|
"""
|
|
128
|
-
self.path_yaml = path_yaml
|
|
129
|
-
|
|
130
|
-
self.categories = copy(categories) # type: ignore
|
|
124
|
+
self.path_yaml = Path(path_yaml)
|
|
131
125
|
self.config_overwrite = config_overwrite
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
self.filter_categories = filter_categories
|
|
135
|
-
model = TPFrcnnDetector.get_wrapped_model(path_yaml, self.categories, config_overwrite)
|
|
126
|
+
|
|
127
|
+
model = TPFrcnnDetector.get_wrapped_model(path_yaml, categories, config_overwrite)
|
|
136
128
|
TensorpackPredictor.__init__(self, model, path_weights, ignore_mismatch)
|
|
137
129
|
TPFrcnnDetectorMixin.__init__(self, categories, filter_categories)
|
|
138
130
|
|
|
@@ -141,7 +133,7 @@ class TPFrcnnDetector(TensorpackPredictor, TPFrcnnDetectorMixin):
|
|
|
141
133
|
|
|
142
134
|
@staticmethod
|
|
143
135
|
def get_wrapped_model(
|
|
144
|
-
path_yaml:
|
|
136
|
+
path_yaml: PathLikeOrStr, categories: Mapping[int, TypeOrStr], config_overwrite: Union[list[str], None]
|
|
145
137
|
) -> ResNetFPNModel:
|
|
146
138
|
"""
|
|
147
139
|
Calls all necessary methods to build TP ResNetFPNModel
|
|
@@ -163,7 +155,7 @@ class TPFrcnnDetector(TensorpackPredictor, TPFrcnnDetectorMixin):
|
|
|
163
155
|
model_frcnn_config(config=hyper_param_config, categories=categories, print_summary=False)
|
|
164
156
|
return ResNetFPNModel(config=hyper_param_config)
|
|
165
157
|
|
|
166
|
-
def predict(self, np_img:
|
|
158
|
+
def predict(self, np_img: PixelValues) -> list[DetectionResult]:
|
|
167
159
|
"""
|
|
168
160
|
Prediction per image.
|
|
169
161
|
|
|
@@ -180,15 +172,18 @@ class TPFrcnnDetector(TensorpackPredictor, TPFrcnnDetectorMixin):
|
|
|
180
172
|
return self._map_category_names(detection_results)
|
|
181
173
|
|
|
182
174
|
@classmethod
|
|
183
|
-
def get_requirements(cls) ->
|
|
175
|
+
def get_requirements(cls) -> list[Requirement]:
|
|
184
176
|
return [get_tensorflow_requirement(), get_tensorpack_requirement()]
|
|
185
177
|
|
|
186
|
-
def clone(self) ->
|
|
178
|
+
def clone(self) -> TPFrcnnDetector:
|
|
187
179
|
return self.__class__(
|
|
188
|
-
self.path_yaml,
|
|
189
|
-
self.path_weights,
|
|
190
|
-
self.categories,
|
|
191
|
-
self.config_overwrite,
|
|
192
|
-
self.ignore_mismatch,
|
|
193
|
-
self.filter_categories,
|
|
180
|
+
path_yaml=self.path_yaml,
|
|
181
|
+
path_weights=self.path_weights,
|
|
182
|
+
categories=dict(self.categories.get_categories()),
|
|
183
|
+
config_overwrite=self.config_overwrite,
|
|
184
|
+
ignore_mismatch=self.ignore_mismatch,
|
|
185
|
+
filter_categories=self.categories.filter_categories,
|
|
194
186
|
)
|
|
187
|
+
|
|
188
|
+
def clear_model(self) -> None:
|
|
189
|
+
self.tp_predictor = None
|
deepdoctection/mapper/cats.py
CHANGED
|
@@ -21,19 +21,19 @@ builder method of a dataset.
|
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
23
|
from collections import defaultdict
|
|
24
|
-
from typing import Any,
|
|
24
|
+
from typing import Any, Literal, Mapping, Optional, Sequence, Union
|
|
25
25
|
|
|
26
|
-
from ..datapoint.annotation import CategoryAnnotation, ContainerAnnotation
|
|
26
|
+
from ..datapoint.annotation import DEFAULT_CATEGORY_ID, CategoryAnnotation, ContainerAnnotation
|
|
27
27
|
from ..datapoint.image import Image
|
|
28
|
-
from ..utils.settings import ObjectTypes, TypeOrStr, get_type
|
|
28
|
+
from ..utils.settings import ObjectTypes, SummaryType, TypeOrStr, get_type
|
|
29
29
|
from .maputils import LabelSummarizer, curry
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
@curry
|
|
33
33
|
def cat_to_sub_cat(
|
|
34
34
|
dp: Image,
|
|
35
|
-
categories_dict_names_as_key:
|
|
36
|
-
cat_to_sub_cat_dict: Optional[
|
|
35
|
+
categories_dict_names_as_key: dict[TypeOrStr, int],
|
|
36
|
+
cat_to_sub_cat_dict: Optional[dict[TypeOrStr, TypeOrStr]] = None,
|
|
37
37
|
) -> Image:
|
|
38
38
|
"""
|
|
39
39
|
Replace some category with its affiliated sub category of CategoryAnnotations. Suppose your category name is `foo`
|
|
@@ -49,13 +49,12 @@ def cat_to_sub_cat(
|
|
|
49
49
|
if cat_to_sub_cat_dict is None:
|
|
50
50
|
return dp
|
|
51
51
|
cat_to_sub_cat_dict_obj_type = {get_type(key): get_type(value) for key, value in cat_to_sub_cat_dict.items()}
|
|
52
|
-
|
|
53
|
-
for ann in dp.get_annotation_iter(category_names=list(cat_to_sub_cat_dict_obj_type.keys())):
|
|
52
|
+
for ann in dp.get_annotation(category_names=list(cat_to_sub_cat_dict_obj_type.keys())):
|
|
54
53
|
sub_cat_type = cat_to_sub_cat_dict_obj_type[get_type(ann.category_name)]
|
|
55
54
|
sub_cat = ann.get_sub_category(sub_cat_type)
|
|
56
55
|
if sub_cat:
|
|
57
56
|
ann.category_name = sub_cat.category_name
|
|
58
|
-
ann.category_id =
|
|
57
|
+
ann.category_id = categories_dict_names_as_key[ann.category_name]
|
|
59
58
|
|
|
60
59
|
return dp
|
|
61
60
|
|
|
@@ -63,7 +62,7 @@ def cat_to_sub_cat(
|
|
|
63
62
|
@curry
|
|
64
63
|
def re_assign_cat_ids(
|
|
65
64
|
dp: Image,
|
|
66
|
-
categories_dict_name_as_key: Optional[
|
|
65
|
+
categories_dict_name_as_key: Optional[dict[TypeOrStr, int]] = None,
|
|
67
66
|
cat_to_sub_cat_mapping: Optional[Mapping[ObjectTypes, Any]] = None,
|
|
68
67
|
) -> Image:
|
|
69
68
|
"""
|
|
@@ -89,13 +88,13 @@ def re_assign_cat_ids(
|
|
|
89
88
|
:return: Image
|
|
90
89
|
"""
|
|
91
90
|
|
|
92
|
-
|
|
93
|
-
for ann in dp.
|
|
91
|
+
ann_ids_to_remove: list[str] = []
|
|
92
|
+
for ann in dp.get_annotation():
|
|
94
93
|
if categories_dict_name_as_key is not None:
|
|
95
94
|
if ann.category_name in categories_dict_name_as_key:
|
|
96
95
|
ann.category_id = categories_dict_name_as_key[ann.category_name]
|
|
97
96
|
else:
|
|
98
|
-
|
|
97
|
+
ann_ids_to_remove.append(ann.annotation_id)
|
|
99
98
|
|
|
100
99
|
if cat_to_sub_cat_mapping:
|
|
101
100
|
if ann.category_name in cat_to_sub_cat_mapping:
|
|
@@ -103,17 +102,16 @@ def re_assign_cat_ids(
|
|
|
103
102
|
for key in sub_cat_keys_to_sub_cat_values:
|
|
104
103
|
sub_cat_values_dict = sub_cat_keys_to_sub_cat_values[key]
|
|
105
104
|
sub_category = ann.get_sub_category(key)
|
|
106
|
-
sub_category.category_id = sub_cat_values_dict.get(sub_category.category_name,
|
|
105
|
+
sub_category.category_id = sub_cat_values_dict.get(sub_category.category_name, DEFAULT_CATEGORY_ID)
|
|
107
106
|
|
|
108
|
-
|
|
109
|
-
dp.remove(ann)
|
|
107
|
+
dp.remove(annotation_ids=ann_ids_to_remove)
|
|
110
108
|
|
|
111
109
|
return dp
|
|
112
110
|
|
|
113
111
|
|
|
114
112
|
@curry
|
|
115
113
|
def filter_cat(
|
|
116
|
-
dp: Image, categories_as_list_filtered:
|
|
114
|
+
dp: Image, categories_as_list_filtered: list[TypeOrStr], categories_as_list_unfiltered: list[TypeOrStr]
|
|
117
115
|
) -> Image:
|
|
118
116
|
"""
|
|
119
117
|
Filters category annotations based on the on a list of categories to be kept and a list of all possible
|
|
@@ -132,7 +130,7 @@ def filter_cat(
|
|
|
132
130
|
remove_cats_mapper = remove_cats(category_names=cats_to_remove_list) # pylint: disable=E1120 # 259
|
|
133
131
|
dp = remove_cats_mapper(dp)
|
|
134
132
|
|
|
135
|
-
categories_dict_name_as_key = {v:
|
|
133
|
+
categories_dict_name_as_key = {v: k for k, v in enumerate(categories_as_list_filtered, 1)}
|
|
136
134
|
re_assign_cat_ids_mapper = re_assign_cat_ids( # pylint: disable=E1120
|
|
137
135
|
categories_dict_name_as_key=categories_dict_name_as_key
|
|
138
136
|
)
|
|
@@ -160,13 +158,13 @@ def filter_summary(
|
|
|
160
158
|
:return: Image or None
|
|
161
159
|
"""
|
|
162
160
|
for key, values in sub_cat_to_sub_cat_names_or_ids.items():
|
|
163
|
-
if mode == "name"
|
|
161
|
+
if mode == "name":
|
|
164
162
|
if dp.summary.get_sub_category(get_type(key)).category_name in values:
|
|
165
163
|
return dp
|
|
166
|
-
elif mode == "value"
|
|
164
|
+
elif mode == "value":
|
|
167
165
|
if dp.summary.get_sub_category(get_type(key)).value in values: # type: ignore
|
|
168
166
|
return dp
|
|
169
|
-
|
|
167
|
+
else:
|
|
170
168
|
if dp.summary.get_sub_category(get_type(key)).category_id in values:
|
|
171
169
|
return dp
|
|
172
170
|
return None
|
|
@@ -179,7 +177,7 @@ def image_to_cat_id(
|
|
|
179
177
|
sub_categories: Optional[Union[Mapping[TypeOrStr, TypeOrStr], Mapping[TypeOrStr, Sequence[TypeOrStr]]]] = None,
|
|
180
178
|
summary_sub_category_names: Optional[Union[TypeOrStr, Sequence[TypeOrStr]]] = None,
|
|
181
179
|
id_name_or_value: Literal["id", "name", "value"] = "id",
|
|
182
|
-
) ->
|
|
180
|
+
) -> tuple[dict[TypeOrStr, list[int]], str]:
|
|
183
181
|
"""
|
|
184
182
|
Extracts all category_ids, sub category information or summary sub category information with given names into a
|
|
185
183
|
defaultdict. This mapping is useful when running evaluation with e.g. an accuracy metric.
|
|
@@ -199,7 +197,7 @@ def image_to_cat_id(
|
|
|
199
197
|
|
|
200
198
|
will return
|
|
201
199
|
|
|
202
|
-
({'foo':[
|
|
200
|
+
({'foo':[1,1], 'bak':[2], 'baz':[3]}, image_id)
|
|
203
201
|
|
|
204
202
|
|
|
205
203
|
**Example 2:**
|
|
@@ -213,7 +211,7 @@ def image_to_cat_id(
|
|
|
213
211
|
|
|
214
212
|
will return
|
|
215
213
|
|
|
216
|
-
({'foo_sub_1':[
|
|
214
|
+
({'foo_sub_1':[5,6]}, image_id)
|
|
217
215
|
|
|
218
216
|
|
|
219
217
|
|
|
@@ -238,7 +236,7 @@ def image_to_cat_id(
|
|
|
238
236
|
if not summary_sub_category_names:
|
|
239
237
|
summary_sub_category_names = []
|
|
240
238
|
|
|
241
|
-
tmp_sub_category_names:
|
|
239
|
+
tmp_sub_category_names: dict[str, Sequence[str]] = {}
|
|
242
240
|
|
|
243
241
|
if sub_categories is not None:
|
|
244
242
|
for key, val in sub_categories.items():
|
|
@@ -250,15 +248,15 @@ def image_to_cat_id(
|
|
|
250
248
|
raise ValueError(f"id_name_or_value must be in ('id', 'name', 'value') but is {id_name_or_value}")
|
|
251
249
|
|
|
252
250
|
if category_names or sub_categories:
|
|
253
|
-
for ann in dp.
|
|
251
|
+
for ann in dp.get_annotation():
|
|
254
252
|
if ann.category_name in category_names:
|
|
255
|
-
cat_container[ann.category_name].append(
|
|
253
|
+
cat_container[ann.category_name].append(ann.category_id)
|
|
256
254
|
if ann.category_name in tmp_sub_category_names:
|
|
257
255
|
for sub_cat_name in tmp_sub_category_names[ann.category_name]:
|
|
258
256
|
sub_cat = ann.get_sub_category(get_type(sub_cat_name))
|
|
259
257
|
if sub_cat is not None:
|
|
260
258
|
if id_name_or_value == "id":
|
|
261
|
-
cat_container[sub_cat_name].append(
|
|
259
|
+
cat_container[sub_cat_name].append(sub_cat.category_id)
|
|
262
260
|
if id_name_or_value == "name":
|
|
263
261
|
cat_container[sub_cat_name].append(sub_cat.category_name) # type: ignore
|
|
264
262
|
if id_name_or_value == "value":
|
|
@@ -269,11 +267,11 @@ def image_to_cat_id(
|
|
|
269
267
|
)
|
|
270
268
|
cat_container[sub_cat_name].append(sub_cat.value) # type: ignore
|
|
271
269
|
|
|
272
|
-
if
|
|
270
|
+
if summary_sub_category_names:
|
|
273
271
|
for sub_cat_name in summary_sub_category_names:
|
|
274
272
|
sub_cat = dp.summary.get_sub_category(get_type(sub_cat_name))
|
|
275
273
|
if id_name_or_value == "id":
|
|
276
|
-
cat_container[sub_cat_name].append(
|
|
274
|
+
cat_container[sub_cat_name].append(sub_cat.category_id)
|
|
277
275
|
if id_name_or_value == "name":
|
|
278
276
|
cat_container[sub_cat_name].append(sub_cat.category_name) # type: ignore
|
|
279
277
|
if id_name_or_value == "value":
|
|
@@ -322,11 +320,11 @@ def remove_cats(
|
|
|
322
320
|
if isinstance(summary_sub_categories, str):
|
|
323
321
|
summary_sub_categories = [summary_sub_categories]
|
|
324
322
|
|
|
325
|
-
|
|
323
|
+
ann_ids_to_remove = []
|
|
326
324
|
|
|
327
|
-
for ann in dp.
|
|
325
|
+
for ann in dp.get_annotation():
|
|
328
326
|
if ann.category_name in category_names:
|
|
329
|
-
|
|
327
|
+
ann_ids_to_remove.append(ann.annotation_id)
|
|
330
328
|
if ann.category_name in sub_categories.keys():
|
|
331
329
|
sub_cats_to_remove = sub_categories[ann.category_name]
|
|
332
330
|
if isinstance(sub_cats_to_remove, str):
|
|
@@ -340,19 +338,17 @@ def remove_cats(
|
|
|
340
338
|
for relation in relationships_to_remove:
|
|
341
339
|
ann.remove_relationship(key=get_type(relation))
|
|
342
340
|
|
|
343
|
-
|
|
344
|
-
dp.remove(ann)
|
|
341
|
+
dp.remove(annotation_ids=ann_ids_to_remove)
|
|
345
342
|
|
|
346
343
|
if summary_sub_categories is not None:
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
dp.summary.remove_sub_category(get_type(sub_cat))
|
|
344
|
+
for sub_cat in summary_sub_categories:
|
|
345
|
+
dp.summary.remove_sub_category(get_type(sub_cat))
|
|
350
346
|
|
|
351
347
|
return dp
|
|
352
348
|
|
|
353
349
|
|
|
354
350
|
@curry
|
|
355
|
-
def add_summary(dp: Image, categories: Mapping[
|
|
351
|
+
def add_summary(dp: Image, categories: Mapping[int, ObjectTypes]) -> Image:
|
|
356
352
|
"""
|
|
357
353
|
Adding a summary with the number of categories in an image.
|
|
358
354
|
|
|
@@ -366,10 +362,10 @@ def add_summary(dp: Image, categories: Mapping[str, ObjectTypes]) -> Image:
|
|
|
366
362
|
for ann in anns:
|
|
367
363
|
summarizer.dump(ann.category_id)
|
|
368
364
|
summary_dict = summarizer.get_summary()
|
|
369
|
-
summary =
|
|
365
|
+
summary = CategoryAnnotation(category_name=SummaryType.SUMMARY)
|
|
370
366
|
for cat_id, val in summary_dict.items():
|
|
371
367
|
summary.dump_sub_category(
|
|
372
|
-
categories[cat_id], CategoryAnnotation(category_name=categories[cat_id], category_id=
|
|
368
|
+
categories[cat_id], CategoryAnnotation(category_name=categories[cat_id], category_id=val)
|
|
373
369
|
)
|
|
374
370
|
dp.summary = summary
|
|
375
371
|
return dp
|
|
@@ -20,21 +20,21 @@ Module for mapping annotations in coco style structure
|
|
|
20
20
|
"""
|
|
21
21
|
|
|
22
22
|
import os
|
|
23
|
-
from typing import
|
|
23
|
+
from typing import Mapping, Optional
|
|
24
24
|
|
|
25
25
|
from ..datapoint.annotation import CategoryAnnotation, ImageAnnotation
|
|
26
26
|
from ..datapoint.box import BoundingBox
|
|
27
27
|
from ..datapoint.image import Image
|
|
28
|
-
from ..utils.detection_types import JsonDict
|
|
29
28
|
from ..utils.fs import load_image_from_file
|
|
30
29
|
from ..utils.settings import ObjectTypes
|
|
30
|
+
from ..utils.types import CocoDatapointDict, JsonDict
|
|
31
31
|
from .maputils import MappingContextManager, curry, maybe_get_fake_score
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
@curry
|
|
35
35
|
def coco_to_image(
|
|
36
|
-
dp:
|
|
37
|
-
categories:
|
|
36
|
+
dp: CocoDatapointDict,
|
|
37
|
+
categories: dict[int, ObjectTypes],
|
|
38
38
|
load_image: bool,
|
|
39
39
|
filter_empty_image: bool,
|
|
40
40
|
fake_score: bool,
|
|
@@ -88,7 +88,7 @@ def coco_to_image(
|
|
|
88
88
|
bbox = BoundingBox(absolute_coords=True, ulx=x_1, uly=y_1, height=h, width=w)
|
|
89
89
|
|
|
90
90
|
annotation = ImageAnnotation(
|
|
91
|
-
category_name=categories[
|
|
91
|
+
category_name=categories[ann["category_id"]],
|
|
92
92
|
bounding_box=bbox,
|
|
93
93
|
category_id=ann["category_id"],
|
|
94
94
|
score=maybe_get_fake_score(fake_score),
|
|
@@ -98,8 +98,8 @@ def coco_to_image(
|
|
|
98
98
|
|
|
99
99
|
if coarse_sub_cat_name and coarse_mapping:
|
|
100
100
|
sub_cat = CategoryAnnotation(
|
|
101
|
-
category_name=categories[
|
|
102
|
-
category_id=
|
|
101
|
+
category_name=categories[coarse_mapping[ann["category_id"]]],
|
|
102
|
+
category_id=coarse_mapping[ann["category_id"]],
|
|
103
103
|
)
|
|
104
104
|
annotation.dump_sub_category(coarse_sub_cat_name, sub_cat)
|
|
105
105
|
|
|
@@ -109,7 +109,7 @@ def coco_to_image(
|
|
|
109
109
|
return image
|
|
110
110
|
|
|
111
111
|
|
|
112
|
-
def image_to_coco(dp: Image) ->
|
|
112
|
+
def image_to_coco(dp: Image) -> tuple[JsonDict, list[JsonDict]]:
|
|
113
113
|
"""
|
|
114
114
|
Converting an image back into the coco format. As images and anns are separated it will return a dict with the
|
|
115
115
|
image information and one for its annotations.
|
|
@@ -122,24 +122,28 @@ def image_to_coco(dp: Image) -> Tuple[JsonDict, List[JsonDict]]:
|
|
|
122
122
|
raise TypeError(f"datapoints must be of type Image, is of type {type(dp)}")
|
|
123
123
|
|
|
124
124
|
img: JsonDict = {}
|
|
125
|
-
anns:
|
|
125
|
+
anns: list[JsonDict] = []
|
|
126
126
|
|
|
127
127
|
img["id"] = int("".join([s for s in dp.image_id if s.isdigit()]))
|
|
128
128
|
img["width"] = dp.width
|
|
129
129
|
img["height"] = dp.height
|
|
130
130
|
img["file_name"] = dp.file_name
|
|
131
131
|
|
|
132
|
-
for img_ann in dp.
|
|
132
|
+
for img_ann in dp.get_annotation():
|
|
133
133
|
ann: JsonDict = {
|
|
134
134
|
"id": int("".join([s for s in img_ann.annotation_id if s.isdigit()])),
|
|
135
135
|
"image_id": img["id"],
|
|
136
|
-
"category_id":
|
|
136
|
+
"category_id": img_ann.category_id,
|
|
137
137
|
}
|
|
138
138
|
if img_ann.score:
|
|
139
139
|
ann["score"] = img_ann.score
|
|
140
140
|
ann["iscrowd"] = 0
|
|
141
141
|
bounding_box = img_ann.get_bounding_box(dp.image_id)
|
|
142
|
-
ann["area"] =
|
|
142
|
+
ann["area"] = (
|
|
143
|
+
bounding_box.area
|
|
144
|
+
if bounding_box.absolute_coords
|
|
145
|
+
else bounding_box.transform(dp.width, dp.height, absolute_coords=True).area
|
|
146
|
+
)
|
|
143
147
|
ann["bbox"] = bounding_box.to_list(mode="xywh")
|
|
144
148
|
anns.append(ann)
|
|
145
149
|
|