dtlpy 1.113.10__py3-none-any.whl → 1.114.13__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 +488 -488
- dtlpy/__version__.py +1 -1
- dtlpy/assets/__init__.py +26 -26
- dtlpy/assets/__pycache__/__init__.cpython-38.pyc +0 -0
- 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 +311 -311
- 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 +296 -296
- dtlpy/entities/collection.py +38 -38
- dtlpy/entities/command.py +169 -169
- dtlpy/entities/compute.py +442 -442
- dtlpy/entities/dataset.py +1285 -1285
- dtlpy/entities/directory_tree.py +44 -44
- dtlpy/entities/dpk.py +470 -470
- dtlpy/entities/driver.py +222 -222
- dtlpy/entities/execution.py +397 -397
- dtlpy/entities/feature.py +124 -124
- dtlpy/entities/feature_set.py +145 -145
- dtlpy/entities/filters.py +641 -641
- dtlpy/entities/gis_item.py +107 -107
- dtlpy/entities/integration.py +184 -184
- dtlpy/entities/item.py +953 -953
- dtlpy/entities/label.py +123 -123
- dtlpy/entities/links.py +85 -85
- dtlpy/entities/message.py +175 -175
- dtlpy/entities/model.py +694 -691
- dtlpy/entities/node.py +1005 -1005
- dtlpy/entities/ontology.py +803 -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 +290 -267
- dtlpy/entities/pipeline.py +593 -593
- dtlpy/entities/pipeline_execution.py +279 -279
- dtlpy/entities/project.py +394 -394
- dtlpy/entities/prompt_item.py +499 -499
- dtlpy/entities/recipe.py +301 -301
- dtlpy/entities/reflect_dict.py +102 -102
- dtlpy/entities/resource_execution.py +138 -138
- dtlpy/entities/service.py +958 -958
- dtlpy/entities/service_driver.py +117 -117
- dtlpy/entities/setting.py +294 -294
- dtlpy/entities/task.py +491 -491
- 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 +945 -940
- 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 -348
- dtlpy/repositories/commands.py +158 -158
- dtlpy/repositories/compositions.py +61 -61
- dtlpy/repositories/computes.py +434 -406
- dtlpy/repositories/datasets.py +1291 -1291
- dtlpy/repositories/downloader.py +895 -895
- dtlpy/repositories/dpks.py +433 -433
- dtlpy/repositories/drivers.py +266 -266
- dtlpy/repositories/executions.py +817 -817
- dtlpy/repositories/feature_sets.py +226 -226
- dtlpy/repositories/features.py +238 -238
- dtlpy/repositories/integrations.py +484 -484
- dtlpy/repositories/items.py +909 -915
- dtlpy/repositories/messages.py +94 -94
- dtlpy/repositories/models.py +877 -867
- 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 +448 -448
- dtlpy/repositories/pipelines.py +642 -642
- dtlpy/repositories/projects.py +539 -539
- dtlpy/repositories/recipes.py +399 -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 +1124 -1124
- dtlpy/repositories/times_series.py +278 -278
- dtlpy/repositories/triggers.py +536 -536
- dtlpy/repositories/upload_element.py +257 -257
- dtlpy/repositories/uploader.py +651 -651
- dtlpy/repositories/webhooks.py +249 -249
- dtlpy/services/__init__.py +22 -22
- dtlpy/services/aihttp_retry.py +131 -131
- dtlpy/services/api_client.py +1782 -1782
- 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 +264 -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.113.10.data → dtlpy-1.114.13.data}/scripts/dlp +1 -1
- dtlpy-1.114.13.data/scripts/dlp.bat +2 -0
- {dtlpy-1.113.10.data → dtlpy-1.114.13.data}/scripts/dlp.py +128 -128
- {dtlpy-1.113.10.dist-info → dtlpy-1.114.13.dist-info}/LICENSE +200 -200
- {dtlpy-1.113.10.dist-info → dtlpy-1.114.13.dist-info}/METADATA +172 -172
- dtlpy-1.114.13.dist-info/RECORD +240 -0
- {dtlpy-1.113.10.dist-info → dtlpy-1.114.13.dist-info}/WHEEL +1 -1
- tests/features/environment.py +551 -550
- dtlpy-1.113.10.data/scripts/dlp.bat +0 -2
- dtlpy-1.113.10.dist-info/RECORD +0 -244
- tests/assets/__init__.py +0 -0
- tests/assets/models_flow/__init__.py +0 -0
- tests/assets/models_flow/failedmain.py +0 -52
- tests/assets/models_flow/main.py +0 -62
- tests/assets/models_flow/main_model.py +0 -54
- {dtlpy-1.113.10.dist-info → dtlpy-1.114.13.dist-info}/entry_points.txt +0 -0
- {dtlpy-1.113.10.dist-info → dtlpy-1.114.13.dist-info}/top_level.txt +0 -0
|
@@ -1,240 +1,240 @@
|
|
|
1
|
-
import numpy as np
|
|
2
|
-
import base64
|
|
3
|
-
import io
|
|
4
|
-
from PIL import Image
|
|
5
|
-
|
|
6
|
-
from . import BaseAnnotationDefinition
|
|
7
|
-
|
|
8
|
-
from .box import Box
|
|
9
|
-
from .polygon import Polygon
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class Segmentation(BaseAnnotationDefinition):
|
|
13
|
-
"""
|
|
14
|
-
Segmentation annotation object
|
|
15
|
-
"""
|
|
16
|
-
type = "binary"
|
|
17
|
-
|
|
18
|
-
def __init__(self, geo: np.ndarray, label: str, attributes=None, description=None, color=None):
|
|
19
|
-
super().__init__(description=description, attributes=attributes)
|
|
20
|
-
self._geo = geo
|
|
21
|
-
self._coordinates = None
|
|
22
|
-
self.label = label
|
|
23
|
-
self._color = color
|
|
24
|
-
|
|
25
|
-
@property
|
|
26
|
-
def geo(self) -> np.ndarray:
|
|
27
|
-
if self._geo is None:
|
|
28
|
-
self._geo = self.from_coordinates(self._coordinates)
|
|
29
|
-
if self._color is None:
|
|
30
|
-
color = None
|
|
31
|
-
fill_coordinates = self._geo.nonzero()
|
|
32
|
-
if len(fill_coordinates) > 0 and len(fill_coordinates[0]) > 0 and len(fill_coordinates[1]) > 0:
|
|
33
|
-
color = self._geo[fill_coordinates[0][0]][fill_coordinates[1][0]]
|
|
34
|
-
self._color = color
|
|
35
|
-
self._geo = (self._geo[:, :, 3] > 127).astype(float)
|
|
36
|
-
return self._geo
|
|
37
|
-
|
|
38
|
-
@geo.setter
|
|
39
|
-
def geo(self, geo: np.ndarray):
|
|
40
|
-
self._geo = geo
|
|
41
|
-
self._coordinates = None
|
|
42
|
-
|
|
43
|
-
@property
|
|
44
|
-
def x(self):
|
|
45
|
-
return
|
|
46
|
-
|
|
47
|
-
@property
|
|
48
|
-
def y(self):
|
|
49
|
-
return
|
|
50
|
-
|
|
51
|
-
@property
|
|
52
|
-
def pts(self):
|
|
53
|
-
return np.where(self.geo > 0)
|
|
54
|
-
|
|
55
|
-
@property
|
|
56
|
-
def left(self):
|
|
57
|
-
left = 0
|
|
58
|
-
if len(self.pts[1]) > 0:
|
|
59
|
-
left = np.min(self.pts[1])
|
|
60
|
-
return left
|
|
61
|
-
|
|
62
|
-
@property
|
|
63
|
-
def top(self):
|
|
64
|
-
top = 0
|
|
65
|
-
if len(self.pts[0]) > 0:
|
|
66
|
-
top = np.min(self.pts[0])
|
|
67
|
-
return top
|
|
68
|
-
|
|
69
|
-
@property
|
|
70
|
-
def right(self):
|
|
71
|
-
right = 0
|
|
72
|
-
if len(self.pts[1]) > 0:
|
|
73
|
-
right = np.max(self.pts[1])
|
|
74
|
-
return right
|
|
75
|
-
|
|
76
|
-
@property
|
|
77
|
-
def bottom(self):
|
|
78
|
-
bottom = 0
|
|
79
|
-
if len(self.pts[0]) > 0:
|
|
80
|
-
bottom = np.max(self.pts[0])
|
|
81
|
-
return bottom
|
|
82
|
-
|
|
83
|
-
def show(self, image, thickness, with_text, height, width, annotation_format, color, alpha=1):
|
|
84
|
-
"""
|
|
85
|
-
Show annotation as ndarray
|
|
86
|
-
:param image: empty or image to draw on
|
|
87
|
-
:param thickness:
|
|
88
|
-
:param with_text: not required
|
|
89
|
-
:param height: item height
|
|
90
|
-
:param width: item width
|
|
91
|
-
:param annotation_format: options: list(dl.ViewAnnotationOptions)
|
|
92
|
-
:param color: color
|
|
93
|
-
:param alpha: opacity value [0 1], default 1
|
|
94
|
-
:return: ndarray
|
|
95
|
-
"""
|
|
96
|
-
try:
|
|
97
|
-
import cv2
|
|
98
|
-
except (ImportError, ModuleNotFoundError):
|
|
99
|
-
raise ImportError('opencv not found. Must install to perform this function')
|
|
100
|
-
|
|
101
|
-
if alpha != 1:
|
|
102
|
-
overlay = image.copy()
|
|
103
|
-
else:
|
|
104
|
-
overlay = image
|
|
105
|
-
if color is None:
|
|
106
|
-
if self._color:
|
|
107
|
-
color = self._color
|
|
108
|
-
else:
|
|
109
|
-
color = (255, 255, 255)
|
|
110
|
-
# draw annotation
|
|
111
|
-
overlay[np.where(self.geo)] = color
|
|
112
|
-
|
|
113
|
-
if not isinstance(color, int) and len(color) == 4 and color[3] != 255:
|
|
114
|
-
# add with opacity
|
|
115
|
-
image = cv2.addWeighted(src1=overlay,
|
|
116
|
-
alpha=alpha,
|
|
117
|
-
src2=image,
|
|
118
|
-
beta=1 - alpha,
|
|
119
|
-
gamma=0)
|
|
120
|
-
else:
|
|
121
|
-
image = overlay
|
|
122
|
-
|
|
123
|
-
if with_text:
|
|
124
|
-
image = self.add_text_to_image(image=image, annotation=self)
|
|
125
|
-
return image
|
|
126
|
-
|
|
127
|
-
def to_coordinates(self, color=None):
|
|
128
|
-
"""
|
|
129
|
-
Convert segmentation to coordinates
|
|
130
|
-
`self._color` is ignored if `color` is provided
|
|
131
|
-
|
|
132
|
-
:param color: color
|
|
133
|
-
:return: coordinates
|
|
134
|
-
"""
|
|
135
|
-
need_encode = False
|
|
136
|
-
if color is not None and self._color is not None:
|
|
137
|
-
# if input color is not the same as the annotation's color - need to re-encode
|
|
138
|
-
if self._color != color:
|
|
139
|
-
need_encode = True
|
|
140
|
-
|
|
141
|
-
if need_encode or self._coordinates is None:
|
|
142
|
-
if color is None:
|
|
143
|
-
if self._color is not None:
|
|
144
|
-
color = self._color
|
|
145
|
-
else:
|
|
146
|
-
color = (255, 255, 255)
|
|
147
|
-
max_val = np.max(self.geo)
|
|
148
|
-
if max_val > 1:
|
|
149
|
-
self.geo = self.geo / max_val
|
|
150
|
-
png_ann = np.stack((color[0] * self.geo,
|
|
151
|
-
color[1] * self.geo,
|
|
152
|
-
color[2] * self.geo,
|
|
153
|
-
255 * self.geo),
|
|
154
|
-
axis=2).astype(np.uint8)
|
|
155
|
-
pil_img = Image.fromarray(png_ann)
|
|
156
|
-
buff = io.BytesIO()
|
|
157
|
-
pil_img.save(buff, format="PNG")
|
|
158
|
-
new_image_string = base64.b64encode(buff.getvalue()).decode("utf-8")
|
|
159
|
-
self._coordinates = "data:image/png;base64,%s" % new_image_string
|
|
160
|
-
return self._coordinates
|
|
161
|
-
|
|
162
|
-
def to_box(self):
|
|
163
|
-
"""
|
|
164
|
-
|
|
165
|
-
:return: Box annotations list to each separated segmentation
|
|
166
|
-
"""
|
|
167
|
-
polygons = Polygon.from_segmentation(mask=self.geo, label=self.label,
|
|
168
|
-
attributes=self.attributes, max_instances=None)
|
|
169
|
-
|
|
170
|
-
if not isinstance(polygons, list):
|
|
171
|
-
polygons = [polygons]
|
|
172
|
-
|
|
173
|
-
boxes = [Box(left=polygon.left,
|
|
174
|
-
top=polygon.top,
|
|
175
|
-
right=polygon.right,
|
|
176
|
-
bottom=polygon.bottom,
|
|
177
|
-
label=polygon.label,
|
|
178
|
-
attributes=polygon.attributes) for polygon in polygons]
|
|
179
|
-
|
|
180
|
-
return boxes
|
|
181
|
-
|
|
182
|
-
@classmethod
|
|
183
|
-
def from_polygon(cls, geo, label, shape, attributes=None):
|
|
184
|
-
"""
|
|
185
|
-
|
|
186
|
-
:param geo: list of x,y coordinates of the polygon ([[x,y],[x,y]...]
|
|
187
|
-
:param label: annotation's label
|
|
188
|
-
:param shape: image shape (h,w)
|
|
189
|
-
:param attributes:
|
|
190
|
-
:return:
|
|
191
|
-
"""
|
|
192
|
-
try:
|
|
193
|
-
import cv2
|
|
194
|
-
except (ImportError, ModuleNotFoundError):
|
|
195
|
-
raise ImportError('opencv not found. Must install to perform this function')
|
|
196
|
-
|
|
197
|
-
thickness = -1
|
|
198
|
-
|
|
199
|
-
# plot polygon on a blank mask with thickness -1 to fill the polyline
|
|
200
|
-
mask = np.zeros(shape=shape, dtype=np.uint8)
|
|
201
|
-
mask = cv2.drawContours(image=mask,
|
|
202
|
-
contours=[np.asarray(geo).astype('int')],
|
|
203
|
-
contourIdx=-1,
|
|
204
|
-
color=1,
|
|
205
|
-
thickness=thickness)
|
|
206
|
-
|
|
207
|
-
return cls(
|
|
208
|
-
geo=mask,
|
|
209
|
-
label=label,
|
|
210
|
-
attributes=attributes,
|
|
211
|
-
)
|
|
212
|
-
|
|
213
|
-
@staticmethod
|
|
214
|
-
def from_coordinates(coordinates):
|
|
215
|
-
if isinstance(coordinates, dict):
|
|
216
|
-
data = coordinates["data"][22:]
|
|
217
|
-
elif isinstance(coordinates, str):
|
|
218
|
-
data = coordinates[22:]
|
|
219
|
-
else:
|
|
220
|
-
raise TypeError('unknown binary data type')
|
|
221
|
-
decode = base64.b64decode(data)
|
|
222
|
-
mask = np.array(Image.open(io.BytesIO(decode)))
|
|
223
|
-
return mask
|
|
224
|
-
|
|
225
|
-
@classmethod
|
|
226
|
-
def from_json(cls, _json):
|
|
227
|
-
if "coordinates" in _json:
|
|
228
|
-
coordinates = _json["coordinates"]
|
|
229
|
-
elif "data" in _json:
|
|
230
|
-
coordinates = _json["data"]
|
|
231
|
-
else:
|
|
232
|
-
raise ValueError('can not find "coordinates" or "data" in annotation. id: {}'.format(_json["id"]))
|
|
233
|
-
inst = cls(
|
|
234
|
-
geo=None,
|
|
235
|
-
label=_json["label"],
|
|
236
|
-
attributes=_json.get("attributes", None),
|
|
237
|
-
color=None
|
|
238
|
-
)
|
|
239
|
-
inst._coordinates = coordinates
|
|
240
|
-
return inst
|
|
1
|
+
import numpy as np
|
|
2
|
+
import base64
|
|
3
|
+
import io
|
|
4
|
+
from PIL import Image
|
|
5
|
+
|
|
6
|
+
from . import BaseAnnotationDefinition
|
|
7
|
+
|
|
8
|
+
from .box import Box
|
|
9
|
+
from .polygon import Polygon
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Segmentation(BaseAnnotationDefinition):
|
|
13
|
+
"""
|
|
14
|
+
Segmentation annotation object
|
|
15
|
+
"""
|
|
16
|
+
type = "binary"
|
|
17
|
+
|
|
18
|
+
def __init__(self, geo: np.ndarray, label: str, attributes=None, description=None, color=None):
|
|
19
|
+
super().__init__(description=description, attributes=attributes)
|
|
20
|
+
self._geo = geo
|
|
21
|
+
self._coordinates = None
|
|
22
|
+
self.label = label
|
|
23
|
+
self._color = color
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def geo(self) -> np.ndarray:
|
|
27
|
+
if self._geo is None:
|
|
28
|
+
self._geo = self.from_coordinates(self._coordinates)
|
|
29
|
+
if self._color is None:
|
|
30
|
+
color = None
|
|
31
|
+
fill_coordinates = self._geo.nonzero()
|
|
32
|
+
if len(fill_coordinates) > 0 and len(fill_coordinates[0]) > 0 and len(fill_coordinates[1]) > 0:
|
|
33
|
+
color = self._geo[fill_coordinates[0][0]][fill_coordinates[1][0]]
|
|
34
|
+
self._color = color
|
|
35
|
+
self._geo = (self._geo[:, :, 3] > 127).astype(float)
|
|
36
|
+
return self._geo
|
|
37
|
+
|
|
38
|
+
@geo.setter
|
|
39
|
+
def geo(self, geo: np.ndarray):
|
|
40
|
+
self._geo = geo
|
|
41
|
+
self._coordinates = None
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def x(self):
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def y(self):
|
|
49
|
+
return
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def pts(self):
|
|
53
|
+
return np.where(self.geo > 0)
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def left(self):
|
|
57
|
+
left = 0
|
|
58
|
+
if len(self.pts[1]) > 0:
|
|
59
|
+
left = np.min(self.pts[1])
|
|
60
|
+
return left
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def top(self):
|
|
64
|
+
top = 0
|
|
65
|
+
if len(self.pts[0]) > 0:
|
|
66
|
+
top = np.min(self.pts[0])
|
|
67
|
+
return top
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def right(self):
|
|
71
|
+
right = 0
|
|
72
|
+
if len(self.pts[1]) > 0:
|
|
73
|
+
right = np.max(self.pts[1])
|
|
74
|
+
return right
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def bottom(self):
|
|
78
|
+
bottom = 0
|
|
79
|
+
if len(self.pts[0]) > 0:
|
|
80
|
+
bottom = np.max(self.pts[0])
|
|
81
|
+
return bottom
|
|
82
|
+
|
|
83
|
+
def show(self, image, thickness, with_text, height, width, annotation_format, color, alpha=1):
|
|
84
|
+
"""
|
|
85
|
+
Show annotation as ndarray
|
|
86
|
+
:param image: empty or image to draw on
|
|
87
|
+
:param thickness:
|
|
88
|
+
:param with_text: not required
|
|
89
|
+
:param height: item height
|
|
90
|
+
:param width: item width
|
|
91
|
+
:param annotation_format: options: list(dl.ViewAnnotationOptions)
|
|
92
|
+
:param color: color
|
|
93
|
+
:param alpha: opacity value [0 1], default 1
|
|
94
|
+
:return: ndarray
|
|
95
|
+
"""
|
|
96
|
+
try:
|
|
97
|
+
import cv2
|
|
98
|
+
except (ImportError, ModuleNotFoundError):
|
|
99
|
+
raise ImportError('opencv not found. Must install to perform this function')
|
|
100
|
+
|
|
101
|
+
if alpha != 1:
|
|
102
|
+
overlay = image.copy()
|
|
103
|
+
else:
|
|
104
|
+
overlay = image
|
|
105
|
+
if color is None:
|
|
106
|
+
if self._color:
|
|
107
|
+
color = self._color
|
|
108
|
+
else:
|
|
109
|
+
color = (255, 255, 255)
|
|
110
|
+
# draw annotation
|
|
111
|
+
overlay[np.where(self.geo)] = color
|
|
112
|
+
|
|
113
|
+
if not isinstance(color, int) and len(color) == 4 and color[3] != 255:
|
|
114
|
+
# add with opacity
|
|
115
|
+
image = cv2.addWeighted(src1=overlay,
|
|
116
|
+
alpha=alpha,
|
|
117
|
+
src2=image,
|
|
118
|
+
beta=1 - alpha,
|
|
119
|
+
gamma=0)
|
|
120
|
+
else:
|
|
121
|
+
image = overlay
|
|
122
|
+
|
|
123
|
+
if with_text:
|
|
124
|
+
image = self.add_text_to_image(image=image, annotation=self)
|
|
125
|
+
return image
|
|
126
|
+
|
|
127
|
+
def to_coordinates(self, color=None):
|
|
128
|
+
"""
|
|
129
|
+
Convert segmentation to coordinates
|
|
130
|
+
`self._color` is ignored if `color` is provided
|
|
131
|
+
|
|
132
|
+
:param color: color
|
|
133
|
+
:return: coordinates
|
|
134
|
+
"""
|
|
135
|
+
need_encode = False
|
|
136
|
+
if color is not None and self._color is not None:
|
|
137
|
+
# if input color is not the same as the annotation's color - need to re-encode
|
|
138
|
+
if self._color != color:
|
|
139
|
+
need_encode = True
|
|
140
|
+
|
|
141
|
+
if need_encode or self._coordinates is None:
|
|
142
|
+
if color is None:
|
|
143
|
+
if self._color is not None:
|
|
144
|
+
color = self._color
|
|
145
|
+
else:
|
|
146
|
+
color = (255, 255, 255)
|
|
147
|
+
max_val = np.max(self.geo)
|
|
148
|
+
if max_val > 1:
|
|
149
|
+
self.geo = self.geo / max_val
|
|
150
|
+
png_ann = np.stack((color[0] * self.geo,
|
|
151
|
+
color[1] * self.geo,
|
|
152
|
+
color[2] * self.geo,
|
|
153
|
+
255 * self.geo),
|
|
154
|
+
axis=2).astype(np.uint8)
|
|
155
|
+
pil_img = Image.fromarray(png_ann)
|
|
156
|
+
buff = io.BytesIO()
|
|
157
|
+
pil_img.save(buff, format="PNG")
|
|
158
|
+
new_image_string = base64.b64encode(buff.getvalue()).decode("utf-8")
|
|
159
|
+
self._coordinates = "data:image/png;base64,%s" % new_image_string
|
|
160
|
+
return self._coordinates
|
|
161
|
+
|
|
162
|
+
def to_box(self):
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
:return: Box annotations list to each separated segmentation
|
|
166
|
+
"""
|
|
167
|
+
polygons = Polygon.from_segmentation(mask=self.geo, label=self.label,
|
|
168
|
+
attributes=self.attributes, max_instances=None)
|
|
169
|
+
|
|
170
|
+
if not isinstance(polygons, list):
|
|
171
|
+
polygons = [polygons]
|
|
172
|
+
|
|
173
|
+
boxes = [Box(left=polygon.left,
|
|
174
|
+
top=polygon.top,
|
|
175
|
+
right=polygon.right,
|
|
176
|
+
bottom=polygon.bottom,
|
|
177
|
+
label=polygon.label,
|
|
178
|
+
attributes=polygon.attributes) for polygon in polygons]
|
|
179
|
+
|
|
180
|
+
return boxes
|
|
181
|
+
|
|
182
|
+
@classmethod
|
|
183
|
+
def from_polygon(cls, geo, label, shape, attributes=None):
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
:param geo: list of x,y coordinates of the polygon ([[x,y],[x,y]...]
|
|
187
|
+
:param label: annotation's label
|
|
188
|
+
:param shape: image shape (h,w)
|
|
189
|
+
:param attributes:
|
|
190
|
+
:return:
|
|
191
|
+
"""
|
|
192
|
+
try:
|
|
193
|
+
import cv2
|
|
194
|
+
except (ImportError, ModuleNotFoundError):
|
|
195
|
+
raise ImportError('opencv not found. Must install to perform this function')
|
|
196
|
+
|
|
197
|
+
thickness = -1
|
|
198
|
+
|
|
199
|
+
# plot polygon on a blank mask with thickness -1 to fill the polyline
|
|
200
|
+
mask = np.zeros(shape=shape, dtype=np.uint8)
|
|
201
|
+
mask = cv2.drawContours(image=mask,
|
|
202
|
+
contours=[np.asarray(geo).astype('int')],
|
|
203
|
+
contourIdx=-1,
|
|
204
|
+
color=1,
|
|
205
|
+
thickness=thickness)
|
|
206
|
+
|
|
207
|
+
return cls(
|
|
208
|
+
geo=mask,
|
|
209
|
+
label=label,
|
|
210
|
+
attributes=attributes,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
@staticmethod
|
|
214
|
+
def from_coordinates(coordinates):
|
|
215
|
+
if isinstance(coordinates, dict):
|
|
216
|
+
data = coordinates["data"][22:]
|
|
217
|
+
elif isinstance(coordinates, str):
|
|
218
|
+
data = coordinates[22:]
|
|
219
|
+
else:
|
|
220
|
+
raise TypeError('unknown binary data type')
|
|
221
|
+
decode = base64.b64decode(data)
|
|
222
|
+
mask = np.array(Image.open(io.BytesIO(decode)))
|
|
223
|
+
return mask
|
|
224
|
+
|
|
225
|
+
@classmethod
|
|
226
|
+
def from_json(cls, _json):
|
|
227
|
+
if "coordinates" in _json:
|
|
228
|
+
coordinates = _json["coordinates"]
|
|
229
|
+
elif "data" in _json:
|
|
230
|
+
coordinates = _json["data"]
|
|
231
|
+
else:
|
|
232
|
+
raise ValueError('can not find "coordinates" or "data" in annotation. id: {}'.format(_json["id"]))
|
|
233
|
+
inst = cls(
|
|
234
|
+
geo=None,
|
|
235
|
+
label=_json["label"],
|
|
236
|
+
attributes=_json.get("attributes", None),
|
|
237
|
+
color=None
|
|
238
|
+
)
|
|
239
|
+
inst._coordinates = coordinates
|
|
240
|
+
return inst
|
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
from . import BaseAnnotationDefinition
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class Subtitle(BaseAnnotationDefinition):
|
|
5
|
-
"""
|
|
6
|
-
Subtitle annotation object
|
|
7
|
-
"""
|
|
8
|
-
type = "subtitle"
|
|
9
|
-
|
|
10
|
-
def __init__(self, text, label, attributes=None, description=None):
|
|
11
|
-
super().__init__(description=description, attributes=attributes)
|
|
12
|
-
self.text = text
|
|
13
|
-
self.label = label
|
|
14
|
-
|
|
15
|
-
def to_coordinates(self, color):
|
|
16
|
-
return {"text": self.text}
|
|
17
|
-
|
|
18
|
-
@staticmethod
|
|
19
|
-
def from_coordinates(coordinates):
|
|
20
|
-
return coordinates["text"]
|
|
21
|
-
|
|
22
|
-
@classmethod
|
|
23
|
-
def from_json(cls, _json):
|
|
24
|
-
if "coordinates" in _json:
|
|
25
|
-
text = cls.from_coordinates(coordinates=_json["coordinates"])
|
|
26
|
-
elif "data" in _json:
|
|
27
|
-
text = cls.from_coordinates(coordinates=_json["data"])
|
|
28
|
-
else:
|
|
29
|
-
raise ValueError('Bad json, "coordinates" or "data" not found')
|
|
30
|
-
return cls(
|
|
31
|
-
text=text,
|
|
32
|
-
label=_json["label"],
|
|
33
|
-
attributes=_json.get("attributes", None)
|
|
34
|
-
)
|
|
1
|
+
from . import BaseAnnotationDefinition
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Subtitle(BaseAnnotationDefinition):
|
|
5
|
+
"""
|
|
6
|
+
Subtitle annotation object
|
|
7
|
+
"""
|
|
8
|
+
type = "subtitle"
|
|
9
|
+
|
|
10
|
+
def __init__(self, text, label, attributes=None, description=None):
|
|
11
|
+
super().__init__(description=description, attributes=attributes)
|
|
12
|
+
self.text = text
|
|
13
|
+
self.label = label
|
|
14
|
+
|
|
15
|
+
def to_coordinates(self, color):
|
|
16
|
+
return {"text": self.text}
|
|
17
|
+
|
|
18
|
+
@staticmethod
|
|
19
|
+
def from_coordinates(coordinates):
|
|
20
|
+
return coordinates["text"]
|
|
21
|
+
|
|
22
|
+
@classmethod
|
|
23
|
+
def from_json(cls, _json):
|
|
24
|
+
if "coordinates" in _json:
|
|
25
|
+
text = cls.from_coordinates(coordinates=_json["coordinates"])
|
|
26
|
+
elif "data" in _json:
|
|
27
|
+
text = cls.from_coordinates(coordinates=_json["data"])
|
|
28
|
+
else:
|
|
29
|
+
raise ValueError('Bad json, "coordinates" or "data" not found')
|
|
30
|
+
return cls(
|
|
31
|
+
text=text,
|
|
32
|
+
label=_json["label"],
|
|
33
|
+
attributes=_json.get("attributes", None)
|
|
34
|
+
)
|