deepdoctection 0.30__py3-none-any.whl → 0.32__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 +38 -29
- deepdoctection/analyzer/dd.py +36 -29
- deepdoctection/configs/conf_dd_one.yaml +34 -31
- deepdoctection/dataflow/base.py +0 -19
- deepdoctection/dataflow/custom.py +4 -3
- deepdoctection/dataflow/custom_serialize.py +14 -5
- deepdoctection/dataflow/parallel_map.py +12 -11
- deepdoctection/dataflow/serialize.py +5 -4
- deepdoctection/datapoint/annotation.py +35 -13
- deepdoctection/datapoint/box.py +3 -5
- deepdoctection/datapoint/convert.py +3 -1
- deepdoctection/datapoint/image.py +79 -36
- deepdoctection/datapoint/view.py +152 -49
- deepdoctection/datasets/__init__.py +1 -4
- deepdoctection/datasets/adapter.py +6 -3
- deepdoctection/datasets/base.py +86 -11
- deepdoctection/datasets/dataflow_builder.py +1 -1
- deepdoctection/datasets/info.py +4 -4
- deepdoctection/datasets/instances/doclaynet.py +3 -2
- deepdoctection/datasets/instances/fintabnet.py +2 -1
- deepdoctection/datasets/instances/funsd.py +2 -1
- deepdoctection/datasets/instances/iiitar13k.py +5 -2
- deepdoctection/datasets/instances/layouttest.py +4 -8
- deepdoctection/datasets/instances/publaynet.py +2 -2
- deepdoctection/datasets/instances/pubtables1m.py +6 -3
- deepdoctection/datasets/instances/pubtabnet.py +2 -1
- deepdoctection/datasets/instances/rvlcdip.py +2 -1
- deepdoctection/datasets/instances/xfund.py +2 -1
- deepdoctection/eval/__init__.py +1 -4
- deepdoctection/eval/accmetric.py +1 -1
- deepdoctection/eval/base.py +5 -4
- deepdoctection/eval/cocometric.py +2 -1
- deepdoctection/eval/eval.py +19 -15
- deepdoctection/eval/tedsmetric.py +14 -11
- deepdoctection/eval/tp_eval_callback.py +14 -7
- deepdoctection/extern/__init__.py +2 -7
- deepdoctection/extern/base.py +39 -13
- deepdoctection/extern/d2detect.py +182 -90
- deepdoctection/extern/deskew.py +36 -9
- deepdoctection/extern/doctrocr.py +265 -83
- deepdoctection/extern/fastlang.py +49 -9
- deepdoctection/extern/hfdetr.py +106 -55
- deepdoctection/extern/hflayoutlm.py +441 -122
- deepdoctection/extern/hflm.py +225 -0
- deepdoctection/extern/model.py +56 -47
- deepdoctection/extern/pdftext.py +10 -5
- deepdoctection/extern/pt/__init__.py +1 -3
- deepdoctection/extern/pt/nms.py +6 -2
- deepdoctection/extern/pt/ptutils.py +27 -18
- deepdoctection/extern/tessocr.py +134 -22
- deepdoctection/extern/texocr.py +6 -2
- deepdoctection/extern/tp/tfutils.py +43 -9
- deepdoctection/extern/tp/tpcompat.py +14 -11
- 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 +9 -6
- deepdoctection/extern/tp/tpfrcnn/modeling/__init__.py +20 -0
- deepdoctection/extern/tp/tpfrcnn/modeling/backbone.py +17 -7
- deepdoctection/extern/tp/tpfrcnn/modeling/generalized_rcnn.py +12 -6
- deepdoctection/extern/tp/tpfrcnn/modeling/model_box.py +9 -4
- deepdoctection/extern/tp/tpfrcnn/modeling/model_cascade.py +8 -5
- deepdoctection/extern/tp/tpfrcnn/modeling/model_fpn.py +16 -11
- deepdoctection/extern/tp/tpfrcnn/modeling/model_frcnn.py +17 -10
- deepdoctection/extern/tp/tpfrcnn/modeling/model_mrcnn.py +14 -8
- deepdoctection/extern/tp/tpfrcnn/modeling/model_rpn.py +15 -10
- deepdoctection/extern/tp/tpfrcnn/predict.py +9 -4
- deepdoctection/extern/tp/tpfrcnn/preproc.py +8 -9
- deepdoctection/extern/tp/tpfrcnn/utils/__init__.py +20 -0
- deepdoctection/extern/tp/tpfrcnn/utils/box_ops.py +10 -2
- deepdoctection/extern/tpdetect.py +54 -30
- deepdoctection/mapper/__init__.py +3 -8
- deepdoctection/mapper/d2struct.py +9 -7
- deepdoctection/mapper/hfstruct.py +7 -2
- deepdoctection/mapper/laylmstruct.py +164 -21
- deepdoctection/mapper/maputils.py +16 -3
- deepdoctection/mapper/misc.py +6 -3
- deepdoctection/mapper/prodigystruct.py +1 -1
- deepdoctection/mapper/pubstruct.py +10 -10
- deepdoctection/mapper/tpstruct.py +3 -3
- deepdoctection/pipe/__init__.py +1 -1
- deepdoctection/pipe/anngen.py +35 -8
- deepdoctection/pipe/base.py +53 -19
- deepdoctection/pipe/common.py +23 -13
- deepdoctection/pipe/concurrency.py +2 -1
- deepdoctection/pipe/doctectionpipe.py +2 -2
- deepdoctection/pipe/language.py +3 -2
- deepdoctection/pipe/layout.py +6 -3
- deepdoctection/pipe/lm.py +34 -66
- deepdoctection/pipe/order.py +142 -35
- deepdoctection/pipe/refine.py +26 -24
- deepdoctection/pipe/segment.py +21 -16
- deepdoctection/pipe/{cell.py → sub_layout.py} +30 -9
- deepdoctection/pipe/text.py +14 -8
- deepdoctection/pipe/transform.py +16 -9
- deepdoctection/train/__init__.py +6 -12
- deepdoctection/train/d2_frcnn_train.py +36 -28
- deepdoctection/train/hf_detr_train.py +26 -17
- deepdoctection/train/hf_layoutlm_train.py +133 -111
- deepdoctection/train/tp_frcnn_train.py +21 -19
- deepdoctection/utils/__init__.py +3 -0
- deepdoctection/utils/concurrency.py +1 -1
- deepdoctection/utils/context.py +2 -2
- deepdoctection/utils/env_info.py +41 -84
- deepdoctection/utils/error.py +84 -0
- deepdoctection/utils/file_utils.py +4 -15
- deepdoctection/utils/fs.py +7 -7
- deepdoctection/utils/logger.py +1 -0
- deepdoctection/utils/mocks.py +93 -0
- deepdoctection/utils/pdf_utils.py +5 -4
- deepdoctection/utils/settings.py +6 -1
- deepdoctection/utils/transform.py +1 -1
- deepdoctection/utils/utils.py +0 -6
- deepdoctection/utils/viz.py +48 -5
- {deepdoctection-0.30.dist-info → deepdoctection-0.32.dist-info}/METADATA +57 -73
- deepdoctection-0.32.dist-info/RECORD +146 -0
- {deepdoctection-0.30.dist-info → deepdoctection-0.32.dist-info}/WHEEL +1 -1
- deepdoctection-0.30.dist-info/RECORD +0 -143
- {deepdoctection-0.30.dist-info → deepdoctection-0.32.dist-info}/LICENSE +0 -0
- {deepdoctection-0.30.dist-info → deepdoctection-0.32.dist-info}/top_level.txt +0 -0
|
@@ -24,6 +24,7 @@ from dataclasses import dataclass, field
|
|
|
24
24
|
from typing import Any, Dict, List, Optional, Union, no_type_check
|
|
25
25
|
|
|
26
26
|
from ..utils.detection_types import JsonDict
|
|
27
|
+
from ..utils.error import AnnotationError, UUIDError
|
|
27
28
|
from ..utils.identifier import get_uuid, is_uuid_like
|
|
28
29
|
from ..utils.logger import LoggingRecord, logger
|
|
29
30
|
from ..utils.settings import DefaultType, ObjectTypes, SummaryType, TypeOrStr, get_type
|
|
@@ -36,7 +37,16 @@ def ann_from_dict(cls, **kwargs):
|
|
|
36
37
|
"""
|
|
37
38
|
A factory function to create subclasses of annotations from a given dict
|
|
38
39
|
"""
|
|
39
|
-
|
|
40
|
+
_init_kwargs = {
|
|
41
|
+
"external_id": kwargs.get("external_id"),
|
|
42
|
+
"category_name": kwargs.get("category_name"),
|
|
43
|
+
"category_id": kwargs.get("category_id"),
|
|
44
|
+
"score": kwargs.get("score"),
|
|
45
|
+
"service_id": kwargs.get("service_id"),
|
|
46
|
+
"model_id": kwargs.get("model_id"),
|
|
47
|
+
"session_id": kwargs.get("session_id"),
|
|
48
|
+
}
|
|
49
|
+
ann = cls(**_init_kwargs)
|
|
40
50
|
ann.active = kwargs.get("active")
|
|
41
51
|
ann._annotation_id = kwargs.get("_annotation_id") # pylint: disable=W0212
|
|
42
52
|
if isinstance(kwargs.get("sub_categories"), dict):
|
|
@@ -74,11 +84,17 @@ class Annotation(ABC):
|
|
|
74
84
|
id will not depend on the defining attributes.
|
|
75
85
|
|
|
76
86
|
`_annotation_id`: Unique id for annotations. Will always be given as string representation of a md5-hash.
|
|
87
|
+
`service_id`: Service that generated the annotation. This will be the name of a pipeline component
|
|
88
|
+
`model_id`: Model that generated the annotation. This will be the name of particular model
|
|
89
|
+
`session_id`: Session id for the annotation. This will be the id of the session in which the annotation was created.
|
|
77
90
|
"""
|
|
78
91
|
|
|
79
92
|
active: bool = field(default=True, init=False, repr=True)
|
|
80
93
|
external_id: Optional[Union[str, int]] = field(default=None, init=True, repr=False)
|
|
81
94
|
_annotation_id: Optional[str] = field(default=None, init=False, repr=True)
|
|
95
|
+
service_id: Optional[str] = field(default=None)
|
|
96
|
+
model_id: Optional[str] = field(default=None)
|
|
97
|
+
session_id: Optional[str] = field(default=None)
|
|
82
98
|
|
|
83
99
|
def __post_init__(self) -> None:
|
|
84
100
|
"""
|
|
@@ -101,7 +117,7 @@ class Annotation(ABC):
|
|
|
101
117
|
"""
|
|
102
118
|
if self._annotation_id:
|
|
103
119
|
return self._annotation_id
|
|
104
|
-
raise
|
|
120
|
+
raise AnnotationError("Dump annotation first or pass external_id to create an annotation id")
|
|
105
121
|
|
|
106
122
|
@annotation_id.setter
|
|
107
123
|
def annotation_id(self, input_id: str) -> None:
|
|
@@ -109,13 +125,13 @@ class Annotation(ABC):
|
|
|
109
125
|
annotation_id setter
|
|
110
126
|
"""
|
|
111
127
|
if self._annotation_id is not None:
|
|
112
|
-
raise
|
|
128
|
+
raise AnnotationError("Annotation_id already defined and cannot be reset")
|
|
113
129
|
if is_uuid_like(input_id):
|
|
114
130
|
self._annotation_id = input_id
|
|
115
131
|
elif isinstance(input_id, property):
|
|
116
132
|
pass
|
|
117
133
|
else:
|
|
118
|
-
raise
|
|
134
|
+
raise AnnotationError("Annotation_id must be uuid3 string")
|
|
119
135
|
|
|
120
136
|
@abstractmethod
|
|
121
137
|
def get_defining_attributes(self) -> List[str]:
|
|
@@ -126,13 +142,13 @@ class Annotation(ABC):
|
|
|
126
142
|
|
|
127
143
|
:return: A list of attributes.
|
|
128
144
|
"""
|
|
129
|
-
raise NotImplementedError
|
|
145
|
+
raise NotImplementedError()
|
|
130
146
|
|
|
131
147
|
def _assert_attributes_have_str(self, state_id: bool = False) -> None:
|
|
132
148
|
defining_attributes = self.get_state_attributes() if state_id else self.get_defining_attributes()
|
|
133
149
|
for attr in defining_attributes:
|
|
134
150
|
if not hasattr(eval("self." + attr), "__str__"): # pylint: disable=W0123
|
|
135
|
-
raise
|
|
151
|
+
raise AnnotationError(f"Attribute {attr} must have __str__ method")
|
|
136
152
|
|
|
137
153
|
@staticmethod
|
|
138
154
|
def set_annotation_id(annotation: "CategoryAnnotation", *container_id_context: Optional[str]) -> str:
|
|
@@ -179,7 +195,7 @@ class Annotation(ABC):
|
|
|
179
195
|
|
|
180
196
|
:return: Annotation instance
|
|
181
197
|
"""
|
|
182
|
-
raise NotImplementedError
|
|
198
|
+
raise NotImplementedError()
|
|
183
199
|
|
|
184
200
|
@staticmethod
|
|
185
201
|
@abstractmethod
|
|
@@ -189,7 +205,7 @@ class Annotation(ABC):
|
|
|
189
205
|
|
|
190
206
|
:return: A list of attributes.
|
|
191
207
|
"""
|
|
192
|
-
raise NotImplementedError
|
|
208
|
+
raise NotImplementedError()
|
|
193
209
|
|
|
194
210
|
@property
|
|
195
211
|
def state_id(self) -> str:
|
|
@@ -290,7 +306,12 @@ class CategoryAnnotation(Annotation):
|
|
|
290
306
|
"""
|
|
291
307
|
|
|
292
308
|
if sub_category_name in self.sub_categories:
|
|
293
|
-
raise
|
|
309
|
+
raise AnnotationError(
|
|
310
|
+
f"sub category {sub_category_name} already defined: "
|
|
311
|
+
f"annotation_id: {self.annotation_id}, "
|
|
312
|
+
f"category_name: {self.category_name}, "
|
|
313
|
+
f"category_id: {self.category_id}"
|
|
314
|
+
)
|
|
294
315
|
|
|
295
316
|
if self._annotation_id is not None:
|
|
296
317
|
if annotation._annotation_id is None: # pylint: disable=W0212
|
|
@@ -333,7 +354,7 @@ class CategoryAnnotation(Annotation):
|
|
|
333
354
|
:param annotation_id: An annotation id
|
|
334
355
|
"""
|
|
335
356
|
if not is_uuid_like(annotation_id):
|
|
336
|
-
raise
|
|
357
|
+
raise UUIDError("Annotation_id must be uuid")
|
|
337
358
|
|
|
338
359
|
key_type = get_type(key)
|
|
339
360
|
if key not in self.relationships:
|
|
@@ -436,14 +457,14 @@ class ImageAnnotation(CategoryAnnotation):
|
|
|
436
457
|
box = self.bounding_box
|
|
437
458
|
if box:
|
|
438
459
|
return box
|
|
439
|
-
raise
|
|
460
|
+
raise AnnotationError(f"bounding_box has not been initialized for {self.annotation_id}")
|
|
440
461
|
|
|
441
462
|
def get_summary(self, key: ObjectTypes) -> CategoryAnnotation:
|
|
442
463
|
"""Get summary sub categories from `image`. Raises `ValueError` if `key` is not available"""
|
|
443
464
|
if self.image:
|
|
444
465
|
if self.image.summary:
|
|
445
466
|
return self.image.summary.get_sub_category(key)
|
|
446
|
-
raise
|
|
467
|
+
raise AnnotationError(f"Summary does not exist for {self.annotation_id} and key: {key}")
|
|
447
468
|
|
|
448
469
|
|
|
449
470
|
@dataclass
|
|
@@ -483,5 +504,6 @@ class ContainerAnnotation(CategoryAnnotation):
|
|
|
483
504
|
@classmethod
|
|
484
505
|
def from_dict(cls, **kwargs: JsonDict) -> "SummaryAnnotation":
|
|
485
506
|
container_ann = ann_from_dict(cls, **kwargs)
|
|
486
|
-
|
|
507
|
+
value = kwargs.get("value", "")
|
|
508
|
+
container_ann.value = value if isinstance(value, str) else list(value)
|
|
487
509
|
return container_ann
|
deepdoctection/datapoint/box.py
CHANGED
|
@@ -25,13 +25,15 @@ from typing import List, Optional, Sequence, no_type_check
|
|
|
25
25
|
|
|
26
26
|
import numpy as np
|
|
27
27
|
import numpy.typing as npt
|
|
28
|
+
from lazy_imports import try_import
|
|
28
29
|
from numpy import float32
|
|
29
30
|
|
|
30
31
|
from ..utils.detection_types import ImageType
|
|
32
|
+
from ..utils.error import BoundingBoxError
|
|
31
33
|
from ..utils.file_utils import cocotools_available
|
|
32
34
|
from ..utils.logger import LoggingRecord, logger
|
|
33
35
|
|
|
34
|
-
|
|
36
|
+
with try_import() as import_guard:
|
|
35
37
|
import pycocotools.mask as coco_mask
|
|
36
38
|
|
|
37
39
|
|
|
@@ -140,10 +142,6 @@ def iou(boxes1: npt.NDArray[float32], boxes2: npt.NDArray[float32]) -> npt.NDArr
|
|
|
140
142
|
return np_iou(boxes1, boxes2)
|
|
141
143
|
|
|
142
144
|
|
|
143
|
-
class BoundingBoxError(BaseException):
|
|
144
|
-
"""Special exception only for `BoundingBox`"""
|
|
145
|
-
|
|
146
|
-
|
|
147
145
|
@dataclass
|
|
148
146
|
class BoundingBox:
|
|
149
147
|
"""
|
|
@@ -32,6 +32,7 @@ from pypdf import PdfReader
|
|
|
32
32
|
|
|
33
33
|
from ..utils.detection_types import ImageType
|
|
34
34
|
from ..utils.develop import deprecated
|
|
35
|
+
from ..utils.error import DependencyError
|
|
35
36
|
from ..utils.pdf_utils import pdf_to_np_array
|
|
36
37
|
from ..utils.viz import viz_handler
|
|
37
38
|
|
|
@@ -121,7 +122,8 @@ def convert_pdf_bytes_to_np_array(pdf_bytes: bytes, dpi: Optional[int] = None) -
|
|
|
121
122
|
"""
|
|
122
123
|
from pdf2image import convert_from_bytes # type: ignore # pylint: disable=C0415, E0401
|
|
123
124
|
|
|
124
|
-
|
|
125
|
+
if which("pdftoppm") is None:
|
|
126
|
+
raise DependencyError("convert_pdf_bytes_to_np_array requires poppler to be installed")
|
|
125
127
|
|
|
126
128
|
with BytesIO(pdf_bytes) as pdf_file:
|
|
127
129
|
pdf = PdfReader(pdf_file).pages[0]
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"""
|
|
19
19
|
Dataclass Image
|
|
20
20
|
"""
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
21
23
|
import json
|
|
22
24
|
from dataclasses import dataclass, field
|
|
23
25
|
from os import environ
|
|
@@ -28,6 +30,7 @@ import numpy as np
|
|
|
28
30
|
from numpy import uint8
|
|
29
31
|
|
|
30
32
|
from ..utils.detection_types import ImageType, JsonDict, Pathlike
|
|
33
|
+
from ..utils.error import AnnotationError, BoundingBoxError, ImageError, UUIDError
|
|
31
34
|
from ..utils.identifier import get_uuid, is_uuid_like
|
|
32
35
|
from ..utils.settings import ObjectTypes, get_type
|
|
33
36
|
from .annotation import Annotation, BoundingBox, ImageAnnotation, SummaryAnnotation
|
|
@@ -108,7 +111,7 @@ class Image:
|
|
|
108
111
|
"""
|
|
109
112
|
if self._image_id is not None:
|
|
110
113
|
return self._image_id
|
|
111
|
-
raise
|
|
114
|
+
raise ImageError("image_id not set")
|
|
112
115
|
|
|
113
116
|
@image_id.setter
|
|
114
117
|
def image_id(self, input_id: str) -> None:
|
|
@@ -116,13 +119,13 @@ class Image:
|
|
|
116
119
|
image_id setter
|
|
117
120
|
"""
|
|
118
121
|
if self._image_id is not None:
|
|
119
|
-
raise
|
|
122
|
+
raise ImageError("image_id already defined and cannot be reset")
|
|
120
123
|
if is_uuid_like(input_id):
|
|
121
124
|
self._image_id = input_id
|
|
122
125
|
elif isinstance(input_id, property):
|
|
123
126
|
pass
|
|
124
127
|
else:
|
|
125
|
-
raise
|
|
128
|
+
raise UUIDError("image_id must be uuid3 string")
|
|
126
129
|
|
|
127
130
|
@property
|
|
128
131
|
def image(self) -> Optional[ImageType]:
|
|
@@ -153,7 +156,7 @@ class Image:
|
|
|
153
156
|
self._self_embedding()
|
|
154
157
|
else:
|
|
155
158
|
if not isinstance(image, np.ndarray):
|
|
156
|
-
raise
|
|
159
|
+
raise ImageError(f"Cannot load image is of type: {type(image)}")
|
|
157
160
|
self._image = image.astype(uint8)
|
|
158
161
|
self.set_width_height(self._image.shape[1], self._image.shape[0])
|
|
159
162
|
self._self_embedding()
|
|
@@ -201,7 +204,7 @@ class Image:
|
|
|
201
204
|
self._bbox = None
|
|
202
205
|
self.embeddings.pop(self.image_id)
|
|
203
206
|
|
|
204
|
-
def get_image(self) ->
|
|
207
|
+
def get_image(self) -> _Img: # type: ignore # pylint: disable=E0602
|
|
205
208
|
"""
|
|
206
209
|
Get the image either in base64 string representation or as np.array.
|
|
207
210
|
|
|
@@ -248,7 +251,7 @@ class Image:
|
|
|
248
251
|
width
|
|
249
252
|
"""
|
|
250
253
|
if self._bbox is None:
|
|
251
|
-
raise
|
|
254
|
+
raise ImageError("Width not available. Call set_width_height first")
|
|
252
255
|
return self._bbox.width
|
|
253
256
|
|
|
254
257
|
@property
|
|
@@ -257,7 +260,7 @@ class Image:
|
|
|
257
260
|
height
|
|
258
261
|
"""
|
|
259
262
|
if self._bbox is None:
|
|
260
|
-
raise
|
|
263
|
+
raise ImageError("Height not available. Call set_width_height first")
|
|
261
264
|
return self._bbox.height
|
|
262
265
|
|
|
263
266
|
def set_width_height(self, width: float, height: float) -> None:
|
|
@@ -281,7 +284,7 @@ class Image:
|
|
|
281
284
|
:param bounding_box: bounding box of this image in terms of the embedding image.
|
|
282
285
|
"""
|
|
283
286
|
if not isinstance(bounding_box, BoundingBox):
|
|
284
|
-
raise
|
|
287
|
+
raise BoundingBoxError(f"Bounding box must be of type BoundingBox, is of type {type(bounding_box)}")
|
|
285
288
|
self.embeddings[image_id] = bounding_box
|
|
286
289
|
|
|
287
290
|
def get_embedding(self, image_id: str) -> BoundingBox:
|
|
@@ -307,14 +310,14 @@ class Image:
|
|
|
307
310
|
:param annotation: image annotation to store
|
|
308
311
|
"""
|
|
309
312
|
if not isinstance(annotation, ImageAnnotation):
|
|
310
|
-
raise
|
|
313
|
+
raise AnnotationError(
|
|
311
314
|
f"Annotation must be of type ImageAnnotation: "
|
|
312
315
|
f"{annotation.annotation_id} but is of type {str(type(annotation))}"
|
|
313
316
|
)
|
|
314
317
|
if annotation._annotation_id is None: # pylint: disable=W0212
|
|
315
318
|
annotation.annotation_id = self.define_annotation_id(annotation)
|
|
316
319
|
if annotation.annotation_id in self._annotation_ids:
|
|
317
|
-
raise
|
|
320
|
+
raise ImageError(f"Cannot dump annotation with already taken " f"id {annotation.annotation_id}")
|
|
318
321
|
self._annotation_ids.append(annotation.annotation_id)
|
|
319
322
|
self.annotations.append(annotation)
|
|
320
323
|
|
|
@@ -322,7 +325,10 @@ class Image:
|
|
|
322
325
|
self,
|
|
323
326
|
category_names: Optional[Union[str, ObjectTypes, Sequence[Union[str, ObjectTypes]]]] = None,
|
|
324
327
|
annotation_ids: Optional[Union[str, Sequence[str]]] = None,
|
|
325
|
-
|
|
328
|
+
service_id: Optional[Union[str, Sequence[str]]] = None,
|
|
329
|
+
model_id: Optional[Union[str, Sequence[str]]] = None,
|
|
330
|
+
session_ids: Optional[Union[str, Sequence[str]]] = None,
|
|
331
|
+
ignore_inactive: bool = True,
|
|
326
332
|
) -> List[ImageAnnotation]:
|
|
327
333
|
"""
|
|
328
334
|
Selection of annotations from the annotation container. Filter conditions can be defined by specifying
|
|
@@ -333,47 +339,80 @@ class Image:
|
|
|
333
339
|
|
|
334
340
|
:param category_names: A single name or list of names
|
|
335
341
|
:param annotation_ids: A single id or list of ids
|
|
336
|
-
:param
|
|
342
|
+
:param service_id: A single service name or list of service names
|
|
343
|
+
:param model_id: A single model name or list of model names
|
|
344
|
+
:param session_ids: A single session id or list of session ids
|
|
345
|
+
:param ignore_inactive: If set to `True` only active annotations are returned.
|
|
346
|
+
|
|
337
347
|
:return: A (possibly empty) list of Annotations
|
|
338
348
|
"""
|
|
339
349
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
350
|
+
if category_names is not None:
|
|
351
|
+
category_names = (
|
|
352
|
+
[get_type(cat_name) for cat_name in category_names]
|
|
353
|
+
if isinstance(category_names, (list, set))
|
|
354
|
+
else [get_type(category_names)] # type:ignore
|
|
355
|
+
)
|
|
345
356
|
|
|
346
|
-
|
|
357
|
+
ann_ids = [annotation_ids] if isinstance(annotation_ids, str) else annotation_ids
|
|
358
|
+
service_id = [service_id] if isinstance(service_id, str) else service_id
|
|
359
|
+
model_id = [model_id] if isinstance(model_id, str) else model_id
|
|
360
|
+
session_id = [session_ids] if isinstance(session_ids, str) else session_ids
|
|
347
361
|
|
|
348
|
-
if
|
|
349
|
-
|
|
350
|
-
|
|
362
|
+
if ignore_inactive:
|
|
363
|
+
anns = filter(lambda x: x.active, self.annotations)
|
|
364
|
+
else:
|
|
365
|
+
anns = self.annotations # type:ignore
|
|
351
366
|
|
|
352
|
-
if
|
|
353
|
-
anns = filter(lambda x: x.category_name in
|
|
367
|
+
if category_names is not None:
|
|
368
|
+
anns = filter(lambda x: x.category_name in category_names, anns) # type:ignore
|
|
354
369
|
|
|
355
370
|
if ann_ids is not None:
|
|
356
371
|
anns = filter(lambda x: x.annotation_id in ann_ids, anns) # type:ignore
|
|
357
372
|
|
|
373
|
+
if service_id is not None:
|
|
374
|
+
anns = filter(lambda x: x.service_id in service_id, anns) # type:ignore
|
|
375
|
+
|
|
376
|
+
if model_id is not None:
|
|
377
|
+
anns = filter(lambda x: x.model_id in model_id, anns) # type:ignore
|
|
378
|
+
|
|
379
|
+
if session_id is not None:
|
|
380
|
+
anns = filter(lambda x: x.session_id in session_id, anns) # type:ignore
|
|
381
|
+
|
|
358
382
|
return list(anns)
|
|
359
383
|
|
|
360
384
|
def get_annotation_iter(
|
|
361
385
|
self,
|
|
362
386
|
category_names: Optional[Union[str, ObjectTypes, Sequence[Union[str, ObjectTypes]]]] = None,
|
|
363
387
|
annotation_ids: Optional[Union[str, Sequence[str]]] = None,
|
|
364
|
-
|
|
388
|
+
service_id: Optional[Union[str, Sequence[str]]] = None,
|
|
389
|
+
model_id: Optional[Union[str, Sequence[str]]] = None,
|
|
390
|
+
session_ids: Optional[Union[str, Sequence[str]]] = None,
|
|
391
|
+
ignore_inactive: bool = True,
|
|
365
392
|
) -> Iterable[ImageAnnotation]:
|
|
366
393
|
"""
|
|
367
394
|
Get annotation as an iterator. Same as `get_annotation` but returns an iterator instead of a list.
|
|
368
395
|
|
|
369
396
|
:param category_names: A single name or list of names
|
|
370
397
|
:param annotation_ids: A single id or list of ids
|
|
371
|
-
:param
|
|
398
|
+
:param service_id: A single service name or list of service names
|
|
399
|
+
:param model_id: A single model name or list of model names
|
|
400
|
+
:param session_ids: A single session id or list of session ids
|
|
401
|
+
:param ignore_inactive: If set to `True` only active annotations are returned.
|
|
372
402
|
|
|
373
403
|
:return: A (possibly empty) list of annotations
|
|
374
404
|
"""
|
|
375
405
|
|
|
376
|
-
return iter(
|
|
406
|
+
return iter(
|
|
407
|
+
self.get_annotation(
|
|
408
|
+
category_names=category_names,
|
|
409
|
+
annotation_ids=annotation_ids,
|
|
410
|
+
service_id=service_id,
|
|
411
|
+
model_id=model_id,
|
|
412
|
+
session_ids=session_ids,
|
|
413
|
+
ignore_inactive=ignore_inactive,
|
|
414
|
+
)
|
|
415
|
+
)
|
|
377
416
|
|
|
378
417
|
def as_dict(self) -> Dict[str, Any]:
|
|
379
418
|
"""
|
|
@@ -439,7 +478,7 @@ class Image:
|
|
|
439
478
|
new_image = Image(file_name=self.file_name, location=self.location, external_id=annotation_id)
|
|
440
479
|
|
|
441
480
|
if self._bbox is None or ann.bounding_box is None:
|
|
442
|
-
raise
|
|
481
|
+
raise ImageError(f"Bounding box for image and ImageAnnotation ({annotation_id}) must be set")
|
|
443
482
|
|
|
444
483
|
new_bounding_box = intersection_box(self._bbox, ann.bounding_box, self.width, self.height)
|
|
445
484
|
if new_bounding_box.absolute_coords:
|
|
@@ -454,7 +493,7 @@ class Image:
|
|
|
454
493
|
if crop_image and self.image is not None:
|
|
455
494
|
new_image.image = crop_box_from_image(self.image, ann.bounding_box, self.width, self.height)
|
|
456
495
|
elif crop_image and self.image is None:
|
|
457
|
-
raise
|
|
496
|
+
raise ImageError("crop_image = True requires self.image to be not None")
|
|
458
497
|
|
|
459
498
|
ann.image = new_image
|
|
460
499
|
|
|
@@ -472,7 +511,7 @@ class Image:
|
|
|
472
511
|
|
|
473
512
|
ann = self.get_annotation(annotation_ids=annotation_id)[0]
|
|
474
513
|
if ann.image is None:
|
|
475
|
-
raise
|
|
514
|
+
raise ImageError("When adding sub images to ImageAnnotation then ImageAnnotation.image must not be None")
|
|
476
515
|
assert ann.bounding_box is not None
|
|
477
516
|
box = ann.bounding_box.to_list("xyxy")
|
|
478
517
|
proposals = self.get_annotation(category_names)
|
|
@@ -485,7 +524,7 @@ class Image:
|
|
|
485
524
|
sub_images = self.get_annotation(annotation_ids=selected_ids.tolist())
|
|
486
525
|
for sub_image in sub_images:
|
|
487
526
|
if sub_image.image is None:
|
|
488
|
-
raise
|
|
527
|
+
raise ImageError(
|
|
489
528
|
"When setting an embedding to ImageAnnotation then ImageAnnotation.image must not be None"
|
|
490
529
|
)
|
|
491
530
|
sub_image.image.set_embedding(
|
|
@@ -494,16 +533,20 @@ class Image:
|
|
|
494
533
|
)
|
|
495
534
|
ann.image.dump(sub_image)
|
|
496
535
|
|
|
497
|
-
def remove_image_from_lower_hierachy(self) -> None:
|
|
536
|
+
def remove_image_from_lower_hierachy(self, pixel_values_only: bool = False) -> None:
|
|
498
537
|
"""Will remove all images from image annotations."""
|
|
499
538
|
for ann in self.annotations:
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
539
|
+
if pixel_values_only:
|
|
540
|
+
if ann.image is not None:
|
|
541
|
+
ann.image.clear_image()
|
|
542
|
+
else:
|
|
543
|
+
absolute_bounding_box = ann.get_bounding_box(self.image_id)
|
|
544
|
+
ann.bounding_box = absolute_bounding_box
|
|
545
|
+
ann.image = None
|
|
503
546
|
|
|
504
547
|
@classmethod
|
|
505
548
|
@no_type_check
|
|
506
|
-
def from_dict(cls, **kwargs) ->
|
|
549
|
+
def from_dict(cls, **kwargs) -> Image:
|
|
507
550
|
"""
|
|
508
551
|
Create `Image` instance from dict.
|
|
509
552
|
|
|
@@ -534,7 +577,7 @@ class Image:
|
|
|
534
577
|
|
|
535
578
|
@classmethod
|
|
536
579
|
@no_type_check
|
|
537
|
-
def from_file(cls, file_path: str) ->
|
|
580
|
+
def from_file(cls, file_path: str) -> Image:
|
|
538
581
|
"""
|
|
539
582
|
Create `Image` instance from .json file.
|
|
540
583
|
|