dtlpy 1.115.44__py3-none-any.whl → 1.116.6__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.
- dtlpy/__init__.py +491 -491
- dtlpy/__version__.py +1 -1
- dtlpy/assets/__init__.py +26 -26
- dtlpy/assets/code_server/config.yaml +2 -2
- dtlpy/assets/code_server/installation.sh +24 -24
- dtlpy/assets/code_server/launch.json +13 -13
- dtlpy/assets/code_server/settings.json +2 -2
- dtlpy/assets/main.py +53 -53
- dtlpy/assets/main_partial.py +18 -18
- dtlpy/assets/mock.json +11 -11
- dtlpy/assets/model_adapter.py +83 -83
- dtlpy/assets/package.json +61 -61
- dtlpy/assets/package_catalog.json +29 -29
- dtlpy/assets/package_gitignore +307 -307
- dtlpy/assets/service_runners/__init__.py +33 -33
- dtlpy/assets/service_runners/converter.py +96 -96
- dtlpy/assets/service_runners/multi_method.py +49 -49
- dtlpy/assets/service_runners/multi_method_annotation.py +54 -54
- dtlpy/assets/service_runners/multi_method_dataset.py +55 -55
- dtlpy/assets/service_runners/multi_method_item.py +52 -52
- dtlpy/assets/service_runners/multi_method_json.py +52 -52
- dtlpy/assets/service_runners/single_method.py +37 -37
- dtlpy/assets/service_runners/single_method_annotation.py +43 -43
- dtlpy/assets/service_runners/single_method_dataset.py +43 -43
- dtlpy/assets/service_runners/single_method_item.py +41 -41
- dtlpy/assets/service_runners/single_method_json.py +42 -42
- dtlpy/assets/service_runners/single_method_multi_input.py +45 -45
- dtlpy/assets/voc_annotation_template.xml +23 -23
- dtlpy/caches/base_cache.py +32 -32
- dtlpy/caches/cache.py +473 -473
- dtlpy/caches/dl_cache.py +201 -201
- dtlpy/caches/filesystem_cache.py +89 -89
- dtlpy/caches/redis_cache.py +84 -84
- dtlpy/dlp/__init__.py +20 -20
- dtlpy/dlp/cli_utilities.py +367 -367
- dtlpy/dlp/command_executor.py +764 -764
- dtlpy/dlp/dlp +1 -1
- dtlpy/dlp/dlp.bat +1 -1
- dtlpy/dlp/dlp.py +128 -128
- dtlpy/dlp/parser.py +651 -651
- dtlpy/entities/__init__.py +83 -83
- dtlpy/entities/analytic.py +347 -347
- dtlpy/entities/annotation.py +1879 -1879
- dtlpy/entities/annotation_collection.py +699 -699
- dtlpy/entities/annotation_definitions/__init__.py +20 -20
- dtlpy/entities/annotation_definitions/base_annotation_definition.py +100 -100
- dtlpy/entities/annotation_definitions/box.py +195 -195
- dtlpy/entities/annotation_definitions/classification.py +67 -67
- dtlpy/entities/annotation_definitions/comparison.py +72 -72
- dtlpy/entities/annotation_definitions/cube.py +204 -204
- dtlpy/entities/annotation_definitions/cube_3d.py +149 -149
- dtlpy/entities/annotation_definitions/description.py +32 -32
- dtlpy/entities/annotation_definitions/ellipse.py +124 -124
- dtlpy/entities/annotation_definitions/free_text.py +62 -62
- dtlpy/entities/annotation_definitions/gis.py +69 -69
- dtlpy/entities/annotation_definitions/note.py +139 -139
- dtlpy/entities/annotation_definitions/point.py +117 -117
- dtlpy/entities/annotation_definitions/polygon.py +182 -182
- dtlpy/entities/annotation_definitions/polyline.py +111 -111
- dtlpy/entities/annotation_definitions/pose.py +92 -92
- dtlpy/entities/annotation_definitions/ref_image.py +86 -86
- dtlpy/entities/annotation_definitions/segmentation.py +240 -240
- dtlpy/entities/annotation_definitions/subtitle.py +34 -34
- dtlpy/entities/annotation_definitions/text.py +85 -85
- dtlpy/entities/annotation_definitions/undefined_annotation.py +74 -74
- dtlpy/entities/app.py +220 -220
- dtlpy/entities/app_module.py +107 -107
- dtlpy/entities/artifact.py +174 -174
- dtlpy/entities/assignment.py +399 -399
- dtlpy/entities/base_entity.py +214 -214
- dtlpy/entities/bot.py +113 -113
- dtlpy/entities/codebase.py +292 -292
- dtlpy/entities/collection.py +38 -38
- dtlpy/entities/command.py +169 -169
- dtlpy/entities/compute.py +449 -449
- dtlpy/entities/dataset.py +1299 -1299
- dtlpy/entities/directory_tree.py +44 -44
- dtlpy/entities/dpk.py +470 -470
- dtlpy/entities/driver.py +235 -235
- dtlpy/entities/execution.py +397 -397
- dtlpy/entities/feature.py +124 -124
- dtlpy/entities/feature_set.py +145 -145
- dtlpy/entities/filters.py +798 -798
- dtlpy/entities/gis_item.py +107 -107
- dtlpy/entities/integration.py +184 -184
- dtlpy/entities/item.py +959 -959
- dtlpy/entities/label.py +123 -123
- dtlpy/entities/links.py +85 -85
- dtlpy/entities/message.py +175 -175
- dtlpy/entities/model.py +684 -684
- dtlpy/entities/node.py +1005 -1005
- dtlpy/entities/ontology.py +810 -803
- dtlpy/entities/organization.py +287 -287
- dtlpy/entities/package.py +657 -657
- dtlpy/entities/package_defaults.py +5 -5
- dtlpy/entities/package_function.py +185 -185
- dtlpy/entities/package_module.py +113 -113
- dtlpy/entities/package_slot.py +118 -118
- dtlpy/entities/paged_entities.py +299 -299
- dtlpy/entities/pipeline.py +624 -624
- dtlpy/entities/pipeline_execution.py +279 -279
- dtlpy/entities/project.py +394 -394
- dtlpy/entities/prompt_item.py +505 -505
- dtlpy/entities/recipe.py +301 -301
- dtlpy/entities/reflect_dict.py +102 -102
- dtlpy/entities/resource_execution.py +138 -138
- dtlpy/entities/service.py +963 -963
- dtlpy/entities/service_driver.py +117 -117
- dtlpy/entities/setting.py +294 -294
- dtlpy/entities/task.py +495 -495
- dtlpy/entities/time_series.py +143 -143
- dtlpy/entities/trigger.py +426 -426
- dtlpy/entities/user.py +118 -118
- dtlpy/entities/webhook.py +124 -124
- dtlpy/examples/__init__.py +19 -19
- dtlpy/examples/add_labels.py +135 -135
- dtlpy/examples/add_metadata_to_item.py +21 -21
- dtlpy/examples/annotate_items_using_model.py +65 -65
- dtlpy/examples/annotate_video_using_model_and_tracker.py +75 -75
- dtlpy/examples/annotations_convert_to_voc.py +9 -9
- dtlpy/examples/annotations_convert_to_yolo.py +9 -9
- dtlpy/examples/convert_annotation_types.py +51 -51
- dtlpy/examples/converter.py +143 -143
- dtlpy/examples/copy_annotations.py +22 -22
- dtlpy/examples/copy_folder.py +31 -31
- dtlpy/examples/create_annotations.py +51 -51
- dtlpy/examples/create_video_annotations.py +83 -83
- dtlpy/examples/delete_annotations.py +26 -26
- dtlpy/examples/filters.py +113 -113
- dtlpy/examples/move_item.py +23 -23
- dtlpy/examples/play_video_annotation.py +13 -13
- dtlpy/examples/show_item_and_mask.py +53 -53
- dtlpy/examples/triggers.py +49 -49
- dtlpy/examples/upload_batch_of_items.py +20 -20
- dtlpy/examples/upload_items_and_custom_format_annotations.py +55 -55
- dtlpy/examples/upload_items_with_modalities.py +43 -43
- dtlpy/examples/upload_segmentation_annotations_from_mask_image.py +44 -44
- dtlpy/examples/upload_yolo_format_annotations.py +70 -70
- dtlpy/exceptions.py +125 -125
- dtlpy/miscellaneous/__init__.py +20 -20
- dtlpy/miscellaneous/dict_differ.py +95 -95
- dtlpy/miscellaneous/git_utils.py +217 -217
- dtlpy/miscellaneous/json_utils.py +14 -14
- dtlpy/miscellaneous/list_print.py +105 -105
- dtlpy/miscellaneous/zipping.py +130 -130
- dtlpy/ml/__init__.py +20 -20
- dtlpy/ml/base_feature_extractor_adapter.py +27 -27
- dtlpy/ml/base_model_adapter.py +1257 -1230
- dtlpy/ml/metrics.py +461 -461
- dtlpy/ml/predictions_utils.py +274 -274
- dtlpy/ml/summary_writer.py +57 -57
- dtlpy/ml/train_utils.py +60 -60
- dtlpy/new_instance.py +252 -252
- dtlpy/repositories/__init__.py +56 -56
- dtlpy/repositories/analytics.py +85 -85
- dtlpy/repositories/annotations.py +916 -916
- dtlpy/repositories/apps.py +383 -383
- dtlpy/repositories/artifacts.py +452 -452
- dtlpy/repositories/assignments.py +599 -599
- dtlpy/repositories/bots.py +213 -213
- dtlpy/repositories/codebases.py +559 -559
- dtlpy/repositories/collections.py +332 -332
- dtlpy/repositories/commands.py +152 -152
- dtlpy/repositories/compositions.py +61 -61
- dtlpy/repositories/computes.py +439 -439
- dtlpy/repositories/datasets.py +1504 -1504
- dtlpy/repositories/downloader.py +976 -923
- dtlpy/repositories/dpks.py +433 -433
- dtlpy/repositories/drivers.py +482 -482
- dtlpy/repositories/executions.py +815 -815
- dtlpy/repositories/feature_sets.py +226 -226
- dtlpy/repositories/features.py +255 -255
- dtlpy/repositories/integrations.py +484 -484
- dtlpy/repositories/items.py +912 -912
- dtlpy/repositories/messages.py +94 -94
- dtlpy/repositories/models.py +1000 -1000
- dtlpy/repositories/nodes.py +80 -80
- dtlpy/repositories/ontologies.py +511 -511
- dtlpy/repositories/organizations.py +525 -525
- dtlpy/repositories/packages.py +1941 -1941
- dtlpy/repositories/pipeline_executions.py +451 -451
- dtlpy/repositories/pipelines.py +640 -640
- dtlpy/repositories/projects.py +539 -539
- dtlpy/repositories/recipes.py +419 -399
- dtlpy/repositories/resource_executions.py +137 -137
- dtlpy/repositories/schema.py +120 -120
- dtlpy/repositories/service_drivers.py +213 -213
- dtlpy/repositories/services.py +1704 -1704
- dtlpy/repositories/settings.py +339 -339
- dtlpy/repositories/tasks.py +1477 -1477
- dtlpy/repositories/times_series.py +278 -278
- dtlpy/repositories/triggers.py +536 -536
- dtlpy/repositories/upload_element.py +257 -257
- dtlpy/repositories/uploader.py +661 -661
- dtlpy/repositories/webhooks.py +249 -249
- dtlpy/services/__init__.py +22 -22
- dtlpy/services/aihttp_retry.py +131 -131
- dtlpy/services/api_client.py +1785 -1785
- dtlpy/services/api_reference.py +40 -40
- dtlpy/services/async_utils.py +133 -133
- dtlpy/services/calls_counter.py +44 -44
- dtlpy/services/check_sdk.py +68 -68
- dtlpy/services/cookie.py +115 -115
- dtlpy/services/create_logger.py +156 -156
- dtlpy/services/events.py +84 -84
- dtlpy/services/logins.py +235 -235
- dtlpy/services/reporter.py +256 -256
- dtlpy/services/service_defaults.py +91 -91
- dtlpy/utilities/__init__.py +20 -20
- dtlpy/utilities/annotations/__init__.py +16 -16
- dtlpy/utilities/annotations/annotation_converters.py +269 -269
- dtlpy/utilities/base_package_runner.py +285 -264
- dtlpy/utilities/converter.py +1650 -1650
- dtlpy/utilities/dataset_generators/__init__.py +1 -1
- dtlpy/utilities/dataset_generators/dataset_generator.py +670 -670
- dtlpy/utilities/dataset_generators/dataset_generator_tensorflow.py +23 -23
- dtlpy/utilities/dataset_generators/dataset_generator_torch.py +21 -21
- dtlpy/utilities/local_development/__init__.py +1 -1
- dtlpy/utilities/local_development/local_session.py +179 -179
- dtlpy/utilities/reports/__init__.py +2 -2
- dtlpy/utilities/reports/figures.py +343 -343
- dtlpy/utilities/reports/report.py +71 -71
- dtlpy/utilities/videos/__init__.py +17 -17
- dtlpy/utilities/videos/video_player.py +598 -598
- dtlpy/utilities/videos/videos.py +470 -470
- {dtlpy-1.115.44.data → dtlpy-1.116.6.data}/scripts/dlp +1 -1
- dtlpy-1.116.6.data/scripts/dlp.bat +2 -0
- {dtlpy-1.115.44.data → dtlpy-1.116.6.data}/scripts/dlp.py +128 -128
- {dtlpy-1.115.44.dist-info → dtlpy-1.116.6.dist-info}/METADATA +186 -186
- dtlpy-1.116.6.dist-info/RECORD +239 -0
- {dtlpy-1.115.44.dist-info → dtlpy-1.116.6.dist-info}/WHEEL +1 -1
- {dtlpy-1.115.44.dist-info → dtlpy-1.116.6.dist-info}/licenses/LICENSE +200 -200
- tests/features/environment.py +551 -551
- dtlpy/assets/__pycache__/__init__.cpython-310.pyc +0 -0
- dtlpy-1.115.44.data/scripts/dlp.bat +0 -2
- dtlpy-1.115.44.dist-info/RECORD +0 -240
- {dtlpy-1.115.44.dist-info → dtlpy-1.116.6.dist-info}/entry_points.txt +0 -0
- {dtlpy-1.115.44.dist-info → dtlpy-1.116.6.dist-info}/top_level.txt +0 -0
|
@@ -1,117 +1,117 @@
|
|
|
1
|
-
import numpy as np
|
|
2
|
-
|
|
3
|
-
from . import BaseAnnotationDefinition
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class Point(BaseAnnotationDefinition):
|
|
7
|
-
"""
|
|
8
|
-
Point annotation object
|
|
9
|
-
"""
|
|
10
|
-
type = "point"
|
|
11
|
-
|
|
12
|
-
def __init__(self, x, y, label, attributes=None, description=None):
|
|
13
|
-
super().__init__(description=description, attributes=attributes)
|
|
14
|
-
self.y = y
|
|
15
|
-
self.x = x
|
|
16
|
-
self.label = label
|
|
17
|
-
|
|
18
|
-
@property
|
|
19
|
-
def geo(self):
|
|
20
|
-
return [self.x, self.y]
|
|
21
|
-
|
|
22
|
-
@property
|
|
23
|
-
def left(self):
|
|
24
|
-
return self.x
|
|
25
|
-
|
|
26
|
-
@property
|
|
27
|
-
def top(self):
|
|
28
|
-
return self.y
|
|
29
|
-
|
|
30
|
-
@property
|
|
31
|
-
def right(self):
|
|
32
|
-
return self.y
|
|
33
|
-
|
|
34
|
-
@property
|
|
35
|
-
def bottom(self):
|
|
36
|
-
return self.x
|
|
37
|
-
|
|
38
|
-
@property
|
|
39
|
-
def z(self):
|
|
40
|
-
return 0
|
|
41
|
-
|
|
42
|
-
def show(self, image, thickness, with_text, height, width, annotation_format, color, alpha=1):
|
|
43
|
-
"""
|
|
44
|
-
Show annotation as ndarray
|
|
45
|
-
:param image: empty or image to draw on
|
|
46
|
-
:param thickness:
|
|
47
|
-
:param with_text: not required
|
|
48
|
-
:param height: item height
|
|
49
|
-
:param width: item width
|
|
50
|
-
:param annotation_format: options: list(dl.ViewAnnotationOptions)
|
|
51
|
-
:param color: color
|
|
52
|
-
:param alpha: opacity value [0 1], default 1
|
|
53
|
-
:return: ndarray
|
|
54
|
-
"""
|
|
55
|
-
try:
|
|
56
|
-
import cv2
|
|
57
|
-
except (ImportError, ModuleNotFoundError):
|
|
58
|
-
self.logger.error(
|
|
59
|
-
'Import Error! Cant import cv2. Annotations operations will be limited. import manually and fix errors')
|
|
60
|
-
raise
|
|
61
|
-
|
|
62
|
-
# point cant have thickness 1
|
|
63
|
-
if thickness is None or thickness == -1:
|
|
64
|
-
thickness = 5
|
|
65
|
-
|
|
66
|
-
# create image to draw on
|
|
67
|
-
if alpha != 1:
|
|
68
|
-
overlay = image.copy()
|
|
69
|
-
else:
|
|
70
|
-
overlay = image
|
|
71
|
-
|
|
72
|
-
# draw annotation
|
|
73
|
-
overlay = cv2.circle(
|
|
74
|
-
img=overlay,
|
|
75
|
-
center=(int(np.round(self.x)), int(np.round(self.y))),
|
|
76
|
-
radius=thickness,
|
|
77
|
-
color=color,
|
|
78
|
-
thickness=thickness,
|
|
79
|
-
lineType=cv2.LINE_AA,
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
if not isinstance(color, int) and len(color) == 4 and color[3] != 255:
|
|
83
|
-
# add with opacity
|
|
84
|
-
image = cv2.addWeighted(src1=overlay,
|
|
85
|
-
alpha=alpha,
|
|
86
|
-
src2=image,
|
|
87
|
-
beta=1 - alpha,
|
|
88
|
-
gamma=0)
|
|
89
|
-
else:
|
|
90
|
-
image = overlay
|
|
91
|
-
|
|
92
|
-
if with_text:
|
|
93
|
-
image = self.add_text_to_image(image=image, annotation=self)
|
|
94
|
-
return image
|
|
95
|
-
|
|
96
|
-
def to_coordinates(self, color):
|
|
97
|
-
return {"x": float(self.x), "y": float(self.y), "z": float(self.z)}
|
|
98
|
-
|
|
99
|
-
@staticmethod
|
|
100
|
-
def from_coordinates(coordinates):
|
|
101
|
-
return coordinates["x"], coordinates["y"]
|
|
102
|
-
|
|
103
|
-
@classmethod
|
|
104
|
-
def from_json(cls, _json):
|
|
105
|
-
if "coordinates" in _json:
|
|
106
|
-
x, y = cls.from_coordinates(_json["coordinates"])
|
|
107
|
-
elif "data" in _json:
|
|
108
|
-
x, y = cls.from_coordinates(_json["data"])
|
|
109
|
-
else:
|
|
110
|
-
raise ValueError('can not find "coordinates" or "data" in annotation. id: {}'.format(_json["id"]))
|
|
111
|
-
|
|
112
|
-
return cls(
|
|
113
|
-
x=x,
|
|
114
|
-
y=y,
|
|
115
|
-
label=_json["label"],
|
|
116
|
-
attributes=_json.get("attributes", None),
|
|
117
|
-
)
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from . import BaseAnnotationDefinition
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Point(BaseAnnotationDefinition):
|
|
7
|
+
"""
|
|
8
|
+
Point annotation object
|
|
9
|
+
"""
|
|
10
|
+
type = "point"
|
|
11
|
+
|
|
12
|
+
def __init__(self, x, y, label, attributes=None, description=None):
|
|
13
|
+
super().__init__(description=description, attributes=attributes)
|
|
14
|
+
self.y = y
|
|
15
|
+
self.x = x
|
|
16
|
+
self.label = label
|
|
17
|
+
|
|
18
|
+
@property
|
|
19
|
+
def geo(self):
|
|
20
|
+
return [self.x, self.y]
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def left(self):
|
|
24
|
+
return self.x
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def top(self):
|
|
28
|
+
return self.y
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def right(self):
|
|
32
|
+
return self.y
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def bottom(self):
|
|
36
|
+
return self.x
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def z(self):
|
|
40
|
+
return 0
|
|
41
|
+
|
|
42
|
+
def show(self, image, thickness, with_text, height, width, annotation_format, color, alpha=1):
|
|
43
|
+
"""
|
|
44
|
+
Show annotation as ndarray
|
|
45
|
+
:param image: empty or image to draw on
|
|
46
|
+
:param thickness:
|
|
47
|
+
:param with_text: not required
|
|
48
|
+
:param height: item height
|
|
49
|
+
:param width: item width
|
|
50
|
+
:param annotation_format: options: list(dl.ViewAnnotationOptions)
|
|
51
|
+
:param color: color
|
|
52
|
+
:param alpha: opacity value [0 1], default 1
|
|
53
|
+
:return: ndarray
|
|
54
|
+
"""
|
|
55
|
+
try:
|
|
56
|
+
import cv2
|
|
57
|
+
except (ImportError, ModuleNotFoundError):
|
|
58
|
+
self.logger.error(
|
|
59
|
+
'Import Error! Cant import cv2. Annotations operations will be limited. import manually and fix errors')
|
|
60
|
+
raise
|
|
61
|
+
|
|
62
|
+
# point cant have thickness 1
|
|
63
|
+
if thickness is None or thickness == -1:
|
|
64
|
+
thickness = 5
|
|
65
|
+
|
|
66
|
+
# create image to draw on
|
|
67
|
+
if alpha != 1:
|
|
68
|
+
overlay = image.copy()
|
|
69
|
+
else:
|
|
70
|
+
overlay = image
|
|
71
|
+
|
|
72
|
+
# draw annotation
|
|
73
|
+
overlay = cv2.circle(
|
|
74
|
+
img=overlay,
|
|
75
|
+
center=(int(np.round(self.x)), int(np.round(self.y))),
|
|
76
|
+
radius=thickness,
|
|
77
|
+
color=color,
|
|
78
|
+
thickness=thickness,
|
|
79
|
+
lineType=cv2.LINE_AA,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
if not isinstance(color, int) and len(color) == 4 and color[3] != 255:
|
|
83
|
+
# add with opacity
|
|
84
|
+
image = cv2.addWeighted(src1=overlay,
|
|
85
|
+
alpha=alpha,
|
|
86
|
+
src2=image,
|
|
87
|
+
beta=1 - alpha,
|
|
88
|
+
gamma=0)
|
|
89
|
+
else:
|
|
90
|
+
image = overlay
|
|
91
|
+
|
|
92
|
+
if with_text:
|
|
93
|
+
image = self.add_text_to_image(image=image, annotation=self)
|
|
94
|
+
return image
|
|
95
|
+
|
|
96
|
+
def to_coordinates(self, color):
|
|
97
|
+
return {"x": float(self.x), "y": float(self.y), "z": float(self.z)}
|
|
98
|
+
|
|
99
|
+
@staticmethod
|
|
100
|
+
def from_coordinates(coordinates):
|
|
101
|
+
return coordinates["x"], coordinates["y"]
|
|
102
|
+
|
|
103
|
+
@classmethod
|
|
104
|
+
def from_json(cls, _json):
|
|
105
|
+
if "coordinates" in _json:
|
|
106
|
+
x, y = cls.from_coordinates(_json["coordinates"])
|
|
107
|
+
elif "data" in _json:
|
|
108
|
+
x, y = cls.from_coordinates(_json["data"])
|
|
109
|
+
else:
|
|
110
|
+
raise ValueError('can not find "coordinates" or "data" in annotation. id: {}'.format(_json["id"]))
|
|
111
|
+
|
|
112
|
+
return cls(
|
|
113
|
+
x=x,
|
|
114
|
+
y=y,
|
|
115
|
+
label=_json["label"],
|
|
116
|
+
attributes=_json.get("attributes", None),
|
|
117
|
+
)
|
|
@@ -1,182 +1,182 @@
|
|
|
1
|
-
import numpy as np
|
|
2
|
-
import logging
|
|
3
|
-
|
|
4
|
-
from . import BaseAnnotationDefinition
|
|
5
|
-
|
|
6
|
-
logger = logging.getLogger(name='dtlpy')
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class Polygon(BaseAnnotationDefinition):
|
|
10
|
-
"""
|
|
11
|
-
Polygon annotation object
|
|
12
|
-
"""
|
|
13
|
-
type = "segment"
|
|
14
|
-
|
|
15
|
-
def __init__(self, geo, label, attributes=None, description=None):
|
|
16
|
-
super().__init__(description=description, attributes=attributes)
|
|
17
|
-
self.geo = geo
|
|
18
|
-
self.label = label
|
|
19
|
-
|
|
20
|
-
@property
|
|
21
|
-
def x(self):
|
|
22
|
-
return self.geo[:, 0]
|
|
23
|
-
|
|
24
|
-
@property
|
|
25
|
-
def y(self):
|
|
26
|
-
return self.geo[:, 1]
|
|
27
|
-
|
|
28
|
-
@property
|
|
29
|
-
def left(self):
|
|
30
|
-
return np.min(self.x)
|
|
31
|
-
|
|
32
|
-
@property
|
|
33
|
-
def top(self):
|
|
34
|
-
return np.min(self.y)
|
|
35
|
-
|
|
36
|
-
@property
|
|
37
|
-
def right(self):
|
|
38
|
-
return np.max(self.x)
|
|
39
|
-
|
|
40
|
-
@property
|
|
41
|
-
def bottom(self):
|
|
42
|
-
return np.max(self.y)
|
|
43
|
-
|
|
44
|
-
def to_coordinates(self, color):
|
|
45
|
-
return [[{"x": float(x), "y": float(y)} for x, y in self.geo]]
|
|
46
|
-
|
|
47
|
-
@staticmethod
|
|
48
|
-
def from_coordinates(coordinates):
|
|
49
|
-
return np.asarray([[pt["x"], pt["y"]] for pt in coordinates])
|
|
50
|
-
|
|
51
|
-
def show(self, image, thickness, with_text, height, width, annotation_format, color, alpha=1):
|
|
52
|
-
"""
|
|
53
|
-
Show annotation as ndarray
|
|
54
|
-
:param image: empty or image to draw on
|
|
55
|
-
:param thickness:
|
|
56
|
-
:param with_text: not required
|
|
57
|
-
:param height: item height
|
|
58
|
-
:param width: item width
|
|
59
|
-
:param annotation_format: options: list(dl.ViewAnnotationOptions)
|
|
60
|
-
:param color: color
|
|
61
|
-
:param alpha: opacity value [0 1], default 1
|
|
62
|
-
:return: ndarray
|
|
63
|
-
"""
|
|
64
|
-
try:
|
|
65
|
-
import cv2
|
|
66
|
-
except (ImportError, ModuleNotFoundError):
|
|
67
|
-
self.logger.error(
|
|
68
|
-
'Import Error! Cant import cv2. Annotations operations will be limited. import manually and fix errors')
|
|
69
|
-
raise
|
|
70
|
-
|
|
71
|
-
if thickness is None:
|
|
72
|
-
thickness = 2
|
|
73
|
-
|
|
74
|
-
# create image to draw on
|
|
75
|
-
if alpha != 1:
|
|
76
|
-
overlay = image.copy()
|
|
77
|
-
else:
|
|
78
|
-
overlay = image
|
|
79
|
-
|
|
80
|
-
overlay = cv2.drawContours(
|
|
81
|
-
image=overlay,
|
|
82
|
-
contours=[np.round(self.geo).astype(int)],
|
|
83
|
-
contourIdx=-1,
|
|
84
|
-
color=color,
|
|
85
|
-
thickness=thickness,
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
if not isinstance(color, int) and len(color) == 4 and color[3] != 255:
|
|
89
|
-
# add with opacity
|
|
90
|
-
image = cv2.addWeighted(src1=overlay,
|
|
91
|
-
alpha=alpha,
|
|
92
|
-
src2=image,
|
|
93
|
-
beta=1 - alpha,
|
|
94
|
-
gamma=0)
|
|
95
|
-
else:
|
|
96
|
-
image = overlay
|
|
97
|
-
|
|
98
|
-
if with_text:
|
|
99
|
-
image = self.add_text_to_image(image=image, annotation=self)
|
|
100
|
-
return image
|
|
101
|
-
|
|
102
|
-
@classmethod
|
|
103
|
-
def from_segmentation(cls, mask, label, attributes=None, epsilon=None, max_instances=1, min_area=0):
|
|
104
|
-
"""
|
|
105
|
-
Convert binary mask to Polygon
|
|
106
|
-
|
|
107
|
-
:param mask: binary mask (0,1)
|
|
108
|
-
:param label: annotation label
|
|
109
|
-
:param attributes: annotations list of attributes
|
|
110
|
-
:param epsilon: from opencv: specifying the approximation accuracy. This is the maximum distance between the original curve and its approximation. if 0 all points are returns
|
|
111
|
-
:param max_instances: number of max instances to return. if None all wil be returned
|
|
112
|
-
:param min_area: remove polygons with area lower thn this threshold (pixels)
|
|
113
|
-
|
|
114
|
-
:return: Polygon annotation
|
|
115
|
-
"""
|
|
116
|
-
try:
|
|
117
|
-
import cv2
|
|
118
|
-
except (ImportError, ModuleNotFoundError):
|
|
119
|
-
raise ImportError('opencv not found. Must install to perform this function')
|
|
120
|
-
|
|
121
|
-
# mask float
|
|
122
|
-
mask = 1. * mask
|
|
123
|
-
# normalize to 1
|
|
124
|
-
mask /= np.max(mask)
|
|
125
|
-
# threshold the mask
|
|
126
|
-
ret, thresh = cv2.threshold(mask, 0.5, 255, 0)
|
|
127
|
-
# find contours
|
|
128
|
-
major, minor, _ = cv2.__version__.split(".")
|
|
129
|
-
if int(major) > 3:
|
|
130
|
-
contours, hierarchy = cv2.findContours(thresh.astype(np.uint8), cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
|
|
131
|
-
else:
|
|
132
|
-
_, contours, hierarchy = cv2.findContours(thresh.astype(np.uint8), cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
|
|
133
|
-
|
|
134
|
-
if len(contours) == 0:
|
|
135
|
-
# no contours were found
|
|
136
|
-
new_pts_list = []
|
|
137
|
-
else:
|
|
138
|
-
# calculate contours area
|
|
139
|
-
areas = np.asarray([cv2.contourArea(cnt) for cnt in contours])
|
|
140
|
-
# take onr contour with maximum area
|
|
141
|
-
sorted_areas_inds = areas.argsort()[::-1]
|
|
142
|
-
filtered_contours = [contours[s_ind] for s_ind in
|
|
143
|
-
sorted_areas_inds if areas[s_ind] > min_area] # filter by area size
|
|
144
|
-
filtered_contours = filtered_contours[:max_instances] # take only the first max_instance of the results
|
|
145
|
-
# estimate contour to reduce number of points
|
|
146
|
-
new_pts_list = list()
|
|
147
|
-
for curve in filtered_contours:
|
|
148
|
-
if epsilon is None:
|
|
149
|
-
epsilon = 0.0005 * cv2.arcLength(curve=curve,
|
|
150
|
-
closed=True)
|
|
151
|
-
estimated_polygon = np.squeeze(cv2.approxPolyDP(curve=curve,
|
|
152
|
-
epsilon=epsilon,
|
|
153
|
-
closed=True))
|
|
154
|
-
if len(estimated_polygon.shape) == 1:
|
|
155
|
-
new_pts_list.append(np.squeeze(curve))
|
|
156
|
-
else:
|
|
157
|
-
new_pts_list.append(estimated_polygon)
|
|
158
|
-
polygons = [cls(geo=new_pts,
|
|
159
|
-
label=label,
|
|
160
|
-
attributes=attributes,
|
|
161
|
-
) for new_pts in new_pts_list]
|
|
162
|
-
|
|
163
|
-
if len(polygons) == 1:
|
|
164
|
-
polygons = polygons[0]
|
|
165
|
-
return polygons
|
|
166
|
-
|
|
167
|
-
@classmethod
|
|
168
|
-
def from_json(cls, _json):
|
|
169
|
-
if "coordinates" in _json:
|
|
170
|
-
geo = cls.from_coordinates(coordinates=_json["coordinates"][0])
|
|
171
|
-
elif "data" in _json:
|
|
172
|
-
geo = cls.from_coordinates(coordinates=_json["data"][0])
|
|
173
|
-
else:
|
|
174
|
-
raise ValueError(
|
|
175
|
-
'can not find "coordinates" or "data" in annotation. id: %s'
|
|
176
|
-
% _json["id"]
|
|
177
|
-
)
|
|
178
|
-
return cls(
|
|
179
|
-
geo=geo,
|
|
180
|
-
label=_json["label"],
|
|
181
|
-
attributes=_json.get("attributes", None),
|
|
182
|
-
)
|
|
1
|
+
import numpy as np
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
from . import BaseAnnotationDefinition
|
|
5
|
+
|
|
6
|
+
logger = logging.getLogger(name='dtlpy')
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Polygon(BaseAnnotationDefinition):
|
|
10
|
+
"""
|
|
11
|
+
Polygon annotation object
|
|
12
|
+
"""
|
|
13
|
+
type = "segment"
|
|
14
|
+
|
|
15
|
+
def __init__(self, geo, label, attributes=None, description=None):
|
|
16
|
+
super().__init__(description=description, attributes=attributes)
|
|
17
|
+
self.geo = geo
|
|
18
|
+
self.label = label
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def x(self):
|
|
22
|
+
return self.geo[:, 0]
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def y(self):
|
|
26
|
+
return self.geo[:, 1]
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def left(self):
|
|
30
|
+
return np.min(self.x)
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def top(self):
|
|
34
|
+
return np.min(self.y)
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def right(self):
|
|
38
|
+
return np.max(self.x)
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def bottom(self):
|
|
42
|
+
return np.max(self.y)
|
|
43
|
+
|
|
44
|
+
def to_coordinates(self, color):
|
|
45
|
+
return [[{"x": float(x), "y": float(y)} for x, y in self.geo]]
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def from_coordinates(coordinates):
|
|
49
|
+
return np.asarray([[pt["x"], pt["y"]] for pt in coordinates])
|
|
50
|
+
|
|
51
|
+
def show(self, image, thickness, with_text, height, width, annotation_format, color, alpha=1):
|
|
52
|
+
"""
|
|
53
|
+
Show annotation as ndarray
|
|
54
|
+
:param image: empty or image to draw on
|
|
55
|
+
:param thickness:
|
|
56
|
+
:param with_text: not required
|
|
57
|
+
:param height: item height
|
|
58
|
+
:param width: item width
|
|
59
|
+
:param annotation_format: options: list(dl.ViewAnnotationOptions)
|
|
60
|
+
:param color: color
|
|
61
|
+
:param alpha: opacity value [0 1], default 1
|
|
62
|
+
:return: ndarray
|
|
63
|
+
"""
|
|
64
|
+
try:
|
|
65
|
+
import cv2
|
|
66
|
+
except (ImportError, ModuleNotFoundError):
|
|
67
|
+
self.logger.error(
|
|
68
|
+
'Import Error! Cant import cv2. Annotations operations will be limited. import manually and fix errors')
|
|
69
|
+
raise
|
|
70
|
+
|
|
71
|
+
if thickness is None:
|
|
72
|
+
thickness = 2
|
|
73
|
+
|
|
74
|
+
# create image to draw on
|
|
75
|
+
if alpha != 1:
|
|
76
|
+
overlay = image.copy()
|
|
77
|
+
else:
|
|
78
|
+
overlay = image
|
|
79
|
+
|
|
80
|
+
overlay = cv2.drawContours(
|
|
81
|
+
image=overlay,
|
|
82
|
+
contours=[np.round(self.geo).astype(int)],
|
|
83
|
+
contourIdx=-1,
|
|
84
|
+
color=color,
|
|
85
|
+
thickness=thickness,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
if not isinstance(color, int) and len(color) == 4 and color[3] != 255:
|
|
89
|
+
# add with opacity
|
|
90
|
+
image = cv2.addWeighted(src1=overlay,
|
|
91
|
+
alpha=alpha,
|
|
92
|
+
src2=image,
|
|
93
|
+
beta=1 - alpha,
|
|
94
|
+
gamma=0)
|
|
95
|
+
else:
|
|
96
|
+
image = overlay
|
|
97
|
+
|
|
98
|
+
if with_text:
|
|
99
|
+
image = self.add_text_to_image(image=image, annotation=self)
|
|
100
|
+
return image
|
|
101
|
+
|
|
102
|
+
@classmethod
|
|
103
|
+
def from_segmentation(cls, mask, label, attributes=None, epsilon=None, max_instances=1, min_area=0):
|
|
104
|
+
"""
|
|
105
|
+
Convert binary mask to Polygon
|
|
106
|
+
|
|
107
|
+
:param mask: binary mask (0,1)
|
|
108
|
+
:param label: annotation label
|
|
109
|
+
:param attributes: annotations list of attributes
|
|
110
|
+
:param epsilon: from opencv: specifying the approximation accuracy. This is the maximum distance between the original curve and its approximation. if 0 all points are returns
|
|
111
|
+
:param max_instances: number of max instances to return. if None all wil be returned
|
|
112
|
+
:param min_area: remove polygons with area lower thn this threshold (pixels)
|
|
113
|
+
|
|
114
|
+
:return: Polygon annotation
|
|
115
|
+
"""
|
|
116
|
+
try:
|
|
117
|
+
import cv2
|
|
118
|
+
except (ImportError, ModuleNotFoundError):
|
|
119
|
+
raise ImportError('opencv not found. Must install to perform this function')
|
|
120
|
+
|
|
121
|
+
# mask float
|
|
122
|
+
mask = 1. * mask
|
|
123
|
+
# normalize to 1
|
|
124
|
+
mask /= np.max(mask)
|
|
125
|
+
# threshold the mask
|
|
126
|
+
ret, thresh = cv2.threshold(mask, 0.5, 255, 0)
|
|
127
|
+
# find contours
|
|
128
|
+
major, minor, _ = cv2.__version__.split(".")
|
|
129
|
+
if int(major) > 3:
|
|
130
|
+
contours, hierarchy = cv2.findContours(thresh.astype(np.uint8), cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
|
|
131
|
+
else:
|
|
132
|
+
_, contours, hierarchy = cv2.findContours(thresh.astype(np.uint8), cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
|
|
133
|
+
|
|
134
|
+
if len(contours) == 0:
|
|
135
|
+
# no contours were found
|
|
136
|
+
new_pts_list = []
|
|
137
|
+
else:
|
|
138
|
+
# calculate contours area
|
|
139
|
+
areas = np.asarray([cv2.contourArea(cnt) for cnt in contours])
|
|
140
|
+
# take onr contour with maximum area
|
|
141
|
+
sorted_areas_inds = areas.argsort()[::-1]
|
|
142
|
+
filtered_contours = [contours[s_ind] for s_ind in
|
|
143
|
+
sorted_areas_inds if areas[s_ind] > min_area] # filter by area size
|
|
144
|
+
filtered_contours = filtered_contours[:max_instances] # take only the first max_instance of the results
|
|
145
|
+
# estimate contour to reduce number of points
|
|
146
|
+
new_pts_list = list()
|
|
147
|
+
for curve in filtered_contours:
|
|
148
|
+
if epsilon is None:
|
|
149
|
+
epsilon = 0.0005 * cv2.arcLength(curve=curve,
|
|
150
|
+
closed=True)
|
|
151
|
+
estimated_polygon = np.squeeze(cv2.approxPolyDP(curve=curve,
|
|
152
|
+
epsilon=epsilon,
|
|
153
|
+
closed=True))
|
|
154
|
+
if len(estimated_polygon.shape) == 1:
|
|
155
|
+
new_pts_list.append(np.squeeze(curve))
|
|
156
|
+
else:
|
|
157
|
+
new_pts_list.append(estimated_polygon)
|
|
158
|
+
polygons = [cls(geo=new_pts,
|
|
159
|
+
label=label,
|
|
160
|
+
attributes=attributes,
|
|
161
|
+
) for new_pts in new_pts_list]
|
|
162
|
+
|
|
163
|
+
if len(polygons) == 1:
|
|
164
|
+
polygons = polygons[0]
|
|
165
|
+
return polygons
|
|
166
|
+
|
|
167
|
+
@classmethod
|
|
168
|
+
def from_json(cls, _json):
|
|
169
|
+
if "coordinates" in _json:
|
|
170
|
+
geo = cls.from_coordinates(coordinates=_json["coordinates"][0])
|
|
171
|
+
elif "data" in _json:
|
|
172
|
+
geo = cls.from_coordinates(coordinates=_json["data"][0])
|
|
173
|
+
else:
|
|
174
|
+
raise ValueError(
|
|
175
|
+
'can not find "coordinates" or "data" in annotation. id: %s'
|
|
176
|
+
% _json["id"]
|
|
177
|
+
)
|
|
178
|
+
return cls(
|
|
179
|
+
geo=geo,
|
|
180
|
+
label=_json["label"],
|
|
181
|
+
attributes=_json.get("attributes", None),
|
|
182
|
+
)
|