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,269 +1,269 @@
|
|
|
1
|
-
# https://github.com/AndrewCarterUK/pascal-voc-writer
|
|
2
|
-
import os
|
|
3
|
-
import shutil
|
|
4
|
-
import traceback
|
|
5
|
-
import tempfile
|
|
6
|
-
import logging
|
|
7
|
-
from PIL import Image
|
|
8
|
-
import json
|
|
9
|
-
from jinja2 import Environment, PackageLoader
|
|
10
|
-
from concurrent.futures import ThreadPoolExecutor
|
|
11
|
-
import dtlpy as dl
|
|
12
|
-
|
|
13
|
-
logger = logging.getLogger(name='dtlpy')
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class BaseConverterFromPlatform:
|
|
17
|
-
def __init__(self, project_name, dataset_name, output_directory, remote_path):
|
|
18
|
-
self.name = ''
|
|
19
|
-
self.project_name = project_name
|
|
20
|
-
self.dataset_name = dataset_name
|
|
21
|
-
self.remote_path = remote_path
|
|
22
|
-
self.output_directory = output_directory
|
|
23
|
-
self.params = None
|
|
24
|
-
|
|
25
|
-
# for threading outputs
|
|
26
|
-
self.errors = None
|
|
27
|
-
self.outputs = None
|
|
28
|
-
self.results = None
|
|
29
|
-
|
|
30
|
-
def convert_single_file(self, output_directory, item, annotations, params):
|
|
31
|
-
# for user to create
|
|
32
|
-
pass
|
|
33
|
-
|
|
34
|
-
def threading_wrapper(self, func, i_item,
|
|
35
|
-
# inputs for user
|
|
36
|
-
output_directory, item, annotations, params=None):
|
|
37
|
-
try:
|
|
38
|
-
self.outputs[i_item] = func(output_directory=output_directory,
|
|
39
|
-
item=item,
|
|
40
|
-
annotations=annotations,
|
|
41
|
-
params=params)
|
|
42
|
-
self.results[i_item] = True
|
|
43
|
-
except Exception as err:
|
|
44
|
-
logging.exception('Error running thread conversion')
|
|
45
|
-
self.errors[i_item] = traceback.format_exc()
|
|
46
|
-
self.results[i_item] = False
|
|
47
|
-
|
|
48
|
-
def run(self):
|
|
49
|
-
# create temp path to save dataloop annotations
|
|
50
|
-
local_annotations_path = os.path.join(tempfile.gettempdir(),
|
|
51
|
-
'dataloop_annotations_{}'.format(hash(os.times())))
|
|
52
|
-
if os.path.isdir(local_annotations_path):
|
|
53
|
-
raise IsADirectoryError('path already exists')
|
|
54
|
-
|
|
55
|
-
try:
|
|
56
|
-
# download annotations zip to local directory
|
|
57
|
-
project = dl.projects.get(project_name=self.project_name)
|
|
58
|
-
dataset = project.datasets.get(dataset_name=self.dataset_name)
|
|
59
|
-
|
|
60
|
-
dataset.items.download_annotations(dataset_name=self.dataset_name,
|
|
61
|
-
local_path=os.path.join(local_annotations_path, '*'))
|
|
62
|
-
|
|
63
|
-
# get labels to ids dictionary
|
|
64
|
-
if 'labels_dict' not in self.params:
|
|
65
|
-
self.params['labels_dict'] = {label: i_label for i_label, label in
|
|
66
|
-
enumerate(list(dataset.labels.keys()))}
|
|
67
|
-
|
|
68
|
-
output_annotations_path = os.path.join(self.output_directory, 'annotations')
|
|
69
|
-
# create output directories
|
|
70
|
-
if not os.path.isdir(self.output_directory):
|
|
71
|
-
os.makedirs(self.output_directory)
|
|
72
|
-
if not os.path.isdir(output_annotations_path):
|
|
73
|
-
os.makedirs(output_annotations_path)
|
|
74
|
-
|
|
75
|
-
# save labels
|
|
76
|
-
with open(os.path.join(self.output_directory, 'labels.txt'), 'w') as f:
|
|
77
|
-
f.write('\n'.join(['%s:%s' % (val, key) for key, val in self.params['labels_dict'].items()]))
|
|
78
|
-
|
|
79
|
-
# get all items (for width and height)
|
|
80
|
-
filters = dl.Filters()
|
|
81
|
-
filters.add(field='filename', values=self.remote_path)
|
|
82
|
-
filters.add(field='type', values='file')
|
|
83
|
-
pages = dataset.items.list(filters=filters)
|
|
84
|
-
|
|
85
|
-
# init workers and results lists
|
|
86
|
-
pool = ThreadPoolExecutor(max_workers=32)
|
|
87
|
-
i_item = -1
|
|
88
|
-
num_items = pages.items_count
|
|
89
|
-
self.outputs = [None for _ in range(num_items)]
|
|
90
|
-
self.results = [None for _ in range(num_items)]
|
|
91
|
-
self.errors = [None for _ in range(num_items)]
|
|
92
|
-
|
|
93
|
-
for page in pages:
|
|
94
|
-
for item in page:
|
|
95
|
-
i_item += 1
|
|
96
|
-
# create input annotations json
|
|
97
|
-
in_filepath = os.path.join(local_annotations_path, item.filename[1:])
|
|
98
|
-
name, ext = os.path.splitext(in_filepath)
|
|
99
|
-
in_filepath = name + '.json'
|
|
100
|
-
|
|
101
|
-
# check if annotations file exists
|
|
102
|
-
if not os.path.isfile(in_filepath):
|
|
103
|
-
self.results[i_item] = False
|
|
104
|
-
self.errors[i_item] = 'file not found: %s' % in_filepath
|
|
105
|
-
continue
|
|
106
|
-
|
|
107
|
-
with open(in_filepath, 'r', encoding="utf8") as f:
|
|
108
|
-
data = json.load(f)
|
|
109
|
-
|
|
110
|
-
pool.submit(self.threading_wrapper, **{'func': self.convert_single_file,
|
|
111
|
-
'i_item': i_item,
|
|
112
|
-
# input for "func"
|
|
113
|
-
'output_directory': output_annotations_path,
|
|
114
|
-
'item': item,
|
|
115
|
-
'annotations': data['annotations'],
|
|
116
|
-
'params': self.params})
|
|
117
|
-
print('Done')
|
|
118
|
-
pool.shutdown()
|
|
119
|
-
dummy = [logger.error(self.errors[i_job]) for i_job, suc in enumerate(self.results) if suc is False]
|
|
120
|
-
return self.outputs
|
|
121
|
-
except:
|
|
122
|
-
raise
|
|
123
|
-
finally:
|
|
124
|
-
# cleanup
|
|
125
|
-
if os.path.isdir(local_annotations_path):
|
|
126
|
-
shutil.rmtree(local_annotations_path)
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
class DtlpyToVoc(BaseConverterFromPlatform):
|
|
130
|
-
def __init__(self, **kwargs):
|
|
131
|
-
super().__init__(**kwargs)
|
|
132
|
-
|
|
133
|
-
# specific for voc
|
|
134
|
-
labels = dict()
|
|
135
|
-
# annotations template
|
|
136
|
-
environment = Environment(loader=PackageLoader('dtlpy', 'assets'),
|
|
137
|
-
keep_trailing_newline=True)
|
|
138
|
-
annotation_template = environment.get_template('voc_annotation_template.xml')
|
|
139
|
-
self.params = {'labels': labels,
|
|
140
|
-
'annotation_template': annotation_template}
|
|
141
|
-
|
|
142
|
-
@staticmethod
|
|
143
|
-
def new_annotation(path, width, height, depth=3, database='Unknown', segmented=0):
|
|
144
|
-
abspath = os.path.abspath(path)
|
|
145
|
-
annotation = {
|
|
146
|
-
'path': abspath,
|
|
147
|
-
'filename': os.path.basename(abspath),
|
|
148
|
-
'folder': os.path.basename(os.path.dirname(abspath)),
|
|
149
|
-
'width': width,
|
|
150
|
-
'height': height,
|
|
151
|
-
'depth': depth,
|
|
152
|
-
'database': database,
|
|
153
|
-
'segmented': segmented,
|
|
154
|
-
'objects': list()
|
|
155
|
-
}
|
|
156
|
-
return annotation
|
|
157
|
-
|
|
158
|
-
def convert_single_file(self, item, annotations, output_directory, params):
|
|
159
|
-
# output filepath for xml
|
|
160
|
-
out_filepath = os.path.join(output_directory, item.filename[1:])
|
|
161
|
-
# remove ext from output filepath
|
|
162
|
-
out_filepath, ext = os.path.splitext(out_filepath)
|
|
163
|
-
# add xml extension
|
|
164
|
-
out_filepath += '.xml'
|
|
165
|
-
if not os.path.isdir(os.path.dirname(out_filepath)):
|
|
166
|
-
os.makedirs(os.path.dirname(out_filepath), exist_ok=True)
|
|
167
|
-
|
|
168
|
-
width = item.width
|
|
169
|
-
height = item.height
|
|
170
|
-
depth = item.metadata['system'].get('channels', 3)
|
|
171
|
-
output_annotation = {
|
|
172
|
-
'path': item.filename,
|
|
173
|
-
'filename': os.path.basename(item.filename),
|
|
174
|
-
'folder': os.path.basename(os.path.dirname(item.filename)),
|
|
175
|
-
'width': width,
|
|
176
|
-
'height': height,
|
|
177
|
-
'depth': depth,
|
|
178
|
-
'database': 'Unknown',
|
|
179
|
-
'segmented': 0,
|
|
180
|
-
'objects': list()
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
for annotation in annotations:
|
|
184
|
-
if not annotation:
|
|
185
|
-
continue
|
|
186
|
-
if annotation['type'] != 'box':
|
|
187
|
-
continue
|
|
188
|
-
label = annotation['label']
|
|
189
|
-
coordinates = annotation['coordinates']
|
|
190
|
-
|
|
191
|
-
attributes = list()
|
|
192
|
-
# get attributes if exists
|
|
193
|
-
if 'attributes' in annotation:
|
|
194
|
-
attributes = annotation['attributes']
|
|
195
|
-
|
|
196
|
-
try:
|
|
197
|
-
left = int(coordinates[0]['x'])
|
|
198
|
-
top = int(coordinates[0]['y'])
|
|
199
|
-
right = int(coordinates[1]['x'])
|
|
200
|
-
bottom = int(coordinates[1]['y'])
|
|
201
|
-
except Exception as err:
|
|
202
|
-
print('coordinates', coordinates)
|
|
203
|
-
continue
|
|
204
|
-
|
|
205
|
-
output_annotation['objects'].append({'name': label,
|
|
206
|
-
'xmin': left,
|
|
207
|
-
'ymin': top,
|
|
208
|
-
'xmax': right,
|
|
209
|
-
'ymax': bottom,
|
|
210
|
-
'attributes': attributes,
|
|
211
|
-
})
|
|
212
|
-
with open(out_filepath, 'w') as file:
|
|
213
|
-
content = params['annotation_template'].render(**output_annotation)
|
|
214
|
-
file.write(content)
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
class DtlpyToYolo(BaseConverterFromPlatform):
|
|
218
|
-
def __init__(self, **kwargs):
|
|
219
|
-
super().__init__(**kwargs)
|
|
220
|
-
self.params = {'labels': dict()}
|
|
221
|
-
|
|
222
|
-
@staticmethod
|
|
223
|
-
def convert_bb(size, box):
|
|
224
|
-
dw = 1. / size[0]
|
|
225
|
-
dh = 1. / size[1]
|
|
226
|
-
x = (box[0] + box[1]) / 2.0
|
|
227
|
-
y = (box[2] + box[3]) / 2.0
|
|
228
|
-
w = box[1] - box[0]
|
|
229
|
-
h = box[3] - box[2]
|
|
230
|
-
x = x * dw
|
|
231
|
-
w = w * dw
|
|
232
|
-
y = y * dh
|
|
233
|
-
h = h * dh
|
|
234
|
-
return x, y, w, h
|
|
235
|
-
|
|
236
|
-
@staticmethod
|
|
237
|
-
def convert_single_file(item, annotations, output_directory, params):
|
|
238
|
-
# output filepath for xml
|
|
239
|
-
out_filepath = os.path.join(output_directory, item.filename[1:])
|
|
240
|
-
# remove ext from filepath
|
|
241
|
-
out_filepath, ext = os.path.splitext(out_filepath)
|
|
242
|
-
# add txt extension
|
|
243
|
-
out_filepath += '.txt'
|
|
244
|
-
|
|
245
|
-
if not os.path.isdir(os.path.dirname(out_filepath)):
|
|
246
|
-
os.makedirs(os.path.dirname(out_filepath), exist_ok=True)
|
|
247
|
-
|
|
248
|
-
width = item.width
|
|
249
|
-
height = item.height
|
|
250
|
-
yolo_annotations = list()
|
|
251
|
-
for annotation in annotations:
|
|
252
|
-
if annotation['type'] != 'box':
|
|
253
|
-
continue
|
|
254
|
-
label = annotation['label']
|
|
255
|
-
coordinates = annotation['coordinates']
|
|
256
|
-
try:
|
|
257
|
-
left = int(coordinates[0]['x'])
|
|
258
|
-
top = int(coordinates[0]['y'])
|
|
259
|
-
right = int(coordinates[1]['x'])
|
|
260
|
-
bottom = int(coordinates[1]['y'])
|
|
261
|
-
except Exception as err:
|
|
262
|
-
print('coords', coordinates)
|
|
263
|
-
continue
|
|
264
|
-
yolo_bb = DtlpyToYolo.convert_bb((width, height), (left, right, top, bottom))
|
|
265
|
-
yolo_annotations.append(
|
|
266
|
-
'%d %f %f %f %f' % (params['labels_dict'][label], yolo_bb[0], yolo_bb[1], yolo_bb[2], yolo_bb[3]))
|
|
267
|
-
|
|
268
|
-
with open(out_filepath, 'w') as f:
|
|
269
|
-
f.write('\n'.join(yolo_annotations))
|
|
1
|
+
# https://github.com/AndrewCarterUK/pascal-voc-writer
|
|
2
|
+
import os
|
|
3
|
+
import shutil
|
|
4
|
+
import traceback
|
|
5
|
+
import tempfile
|
|
6
|
+
import logging
|
|
7
|
+
from PIL import Image
|
|
8
|
+
import json
|
|
9
|
+
from jinja2 import Environment, PackageLoader
|
|
10
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
11
|
+
import dtlpy as dl
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(name='dtlpy')
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BaseConverterFromPlatform:
|
|
17
|
+
def __init__(self, project_name, dataset_name, output_directory, remote_path):
|
|
18
|
+
self.name = ''
|
|
19
|
+
self.project_name = project_name
|
|
20
|
+
self.dataset_name = dataset_name
|
|
21
|
+
self.remote_path = remote_path
|
|
22
|
+
self.output_directory = output_directory
|
|
23
|
+
self.params = None
|
|
24
|
+
|
|
25
|
+
# for threading outputs
|
|
26
|
+
self.errors = None
|
|
27
|
+
self.outputs = None
|
|
28
|
+
self.results = None
|
|
29
|
+
|
|
30
|
+
def convert_single_file(self, output_directory, item, annotations, params):
|
|
31
|
+
# for user to create
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
def threading_wrapper(self, func, i_item,
|
|
35
|
+
# inputs for user
|
|
36
|
+
output_directory, item, annotations, params=None):
|
|
37
|
+
try:
|
|
38
|
+
self.outputs[i_item] = func(output_directory=output_directory,
|
|
39
|
+
item=item,
|
|
40
|
+
annotations=annotations,
|
|
41
|
+
params=params)
|
|
42
|
+
self.results[i_item] = True
|
|
43
|
+
except Exception as err:
|
|
44
|
+
logging.exception('Error running thread conversion')
|
|
45
|
+
self.errors[i_item] = traceback.format_exc()
|
|
46
|
+
self.results[i_item] = False
|
|
47
|
+
|
|
48
|
+
def run(self):
|
|
49
|
+
# create temp path to save dataloop annotations
|
|
50
|
+
local_annotations_path = os.path.join(tempfile.gettempdir(),
|
|
51
|
+
'dataloop_annotations_{}'.format(hash(os.times())))
|
|
52
|
+
if os.path.isdir(local_annotations_path):
|
|
53
|
+
raise IsADirectoryError('path already exists')
|
|
54
|
+
|
|
55
|
+
try:
|
|
56
|
+
# download annotations zip to local directory
|
|
57
|
+
project = dl.projects.get(project_name=self.project_name)
|
|
58
|
+
dataset = project.datasets.get(dataset_name=self.dataset_name)
|
|
59
|
+
|
|
60
|
+
dataset.items.download_annotations(dataset_name=self.dataset_name,
|
|
61
|
+
local_path=os.path.join(local_annotations_path, '*'))
|
|
62
|
+
|
|
63
|
+
# get labels to ids dictionary
|
|
64
|
+
if 'labels_dict' not in self.params:
|
|
65
|
+
self.params['labels_dict'] = {label: i_label for i_label, label in
|
|
66
|
+
enumerate(list(dataset.labels.keys()))}
|
|
67
|
+
|
|
68
|
+
output_annotations_path = os.path.join(self.output_directory, 'annotations')
|
|
69
|
+
# create output directories
|
|
70
|
+
if not os.path.isdir(self.output_directory):
|
|
71
|
+
os.makedirs(self.output_directory)
|
|
72
|
+
if not os.path.isdir(output_annotations_path):
|
|
73
|
+
os.makedirs(output_annotations_path)
|
|
74
|
+
|
|
75
|
+
# save labels
|
|
76
|
+
with open(os.path.join(self.output_directory, 'labels.txt'), 'w') as f:
|
|
77
|
+
f.write('\n'.join(['%s:%s' % (val, key) for key, val in self.params['labels_dict'].items()]))
|
|
78
|
+
|
|
79
|
+
# get all items (for width and height)
|
|
80
|
+
filters = dl.Filters()
|
|
81
|
+
filters.add(field='filename', values=self.remote_path)
|
|
82
|
+
filters.add(field='type', values='file')
|
|
83
|
+
pages = dataset.items.list(filters=filters)
|
|
84
|
+
|
|
85
|
+
# init workers and results lists
|
|
86
|
+
pool = ThreadPoolExecutor(max_workers=32)
|
|
87
|
+
i_item = -1
|
|
88
|
+
num_items = pages.items_count
|
|
89
|
+
self.outputs = [None for _ in range(num_items)]
|
|
90
|
+
self.results = [None for _ in range(num_items)]
|
|
91
|
+
self.errors = [None for _ in range(num_items)]
|
|
92
|
+
|
|
93
|
+
for page in pages:
|
|
94
|
+
for item in page:
|
|
95
|
+
i_item += 1
|
|
96
|
+
# create input annotations json
|
|
97
|
+
in_filepath = os.path.join(local_annotations_path, item.filename[1:])
|
|
98
|
+
name, ext = os.path.splitext(in_filepath)
|
|
99
|
+
in_filepath = name + '.json'
|
|
100
|
+
|
|
101
|
+
# check if annotations file exists
|
|
102
|
+
if not os.path.isfile(in_filepath):
|
|
103
|
+
self.results[i_item] = False
|
|
104
|
+
self.errors[i_item] = 'file not found: %s' % in_filepath
|
|
105
|
+
continue
|
|
106
|
+
|
|
107
|
+
with open(in_filepath, 'r', encoding="utf8") as f:
|
|
108
|
+
data = json.load(f)
|
|
109
|
+
|
|
110
|
+
pool.submit(self.threading_wrapper, **{'func': self.convert_single_file,
|
|
111
|
+
'i_item': i_item,
|
|
112
|
+
# input for "func"
|
|
113
|
+
'output_directory': output_annotations_path,
|
|
114
|
+
'item': item,
|
|
115
|
+
'annotations': data['annotations'],
|
|
116
|
+
'params': self.params})
|
|
117
|
+
print('Done')
|
|
118
|
+
pool.shutdown()
|
|
119
|
+
dummy = [logger.error(self.errors[i_job]) for i_job, suc in enumerate(self.results) if suc is False]
|
|
120
|
+
return self.outputs
|
|
121
|
+
except:
|
|
122
|
+
raise
|
|
123
|
+
finally:
|
|
124
|
+
# cleanup
|
|
125
|
+
if os.path.isdir(local_annotations_path):
|
|
126
|
+
shutil.rmtree(local_annotations_path)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class DtlpyToVoc(BaseConverterFromPlatform):
|
|
130
|
+
def __init__(self, **kwargs):
|
|
131
|
+
super().__init__(**kwargs)
|
|
132
|
+
|
|
133
|
+
# specific for voc
|
|
134
|
+
labels = dict()
|
|
135
|
+
# annotations template
|
|
136
|
+
environment = Environment(loader=PackageLoader('dtlpy', 'assets'),
|
|
137
|
+
keep_trailing_newline=True)
|
|
138
|
+
annotation_template = environment.get_template('voc_annotation_template.xml')
|
|
139
|
+
self.params = {'labels': labels,
|
|
140
|
+
'annotation_template': annotation_template}
|
|
141
|
+
|
|
142
|
+
@staticmethod
|
|
143
|
+
def new_annotation(path, width, height, depth=3, database='Unknown', segmented=0):
|
|
144
|
+
abspath = os.path.abspath(path)
|
|
145
|
+
annotation = {
|
|
146
|
+
'path': abspath,
|
|
147
|
+
'filename': os.path.basename(abspath),
|
|
148
|
+
'folder': os.path.basename(os.path.dirname(abspath)),
|
|
149
|
+
'width': width,
|
|
150
|
+
'height': height,
|
|
151
|
+
'depth': depth,
|
|
152
|
+
'database': database,
|
|
153
|
+
'segmented': segmented,
|
|
154
|
+
'objects': list()
|
|
155
|
+
}
|
|
156
|
+
return annotation
|
|
157
|
+
|
|
158
|
+
def convert_single_file(self, item, annotations, output_directory, params):
|
|
159
|
+
# output filepath for xml
|
|
160
|
+
out_filepath = os.path.join(output_directory, item.filename[1:])
|
|
161
|
+
# remove ext from output filepath
|
|
162
|
+
out_filepath, ext = os.path.splitext(out_filepath)
|
|
163
|
+
# add xml extension
|
|
164
|
+
out_filepath += '.xml'
|
|
165
|
+
if not os.path.isdir(os.path.dirname(out_filepath)):
|
|
166
|
+
os.makedirs(os.path.dirname(out_filepath), exist_ok=True)
|
|
167
|
+
|
|
168
|
+
width = item.width
|
|
169
|
+
height = item.height
|
|
170
|
+
depth = item.metadata['system'].get('channels', 3)
|
|
171
|
+
output_annotation = {
|
|
172
|
+
'path': item.filename,
|
|
173
|
+
'filename': os.path.basename(item.filename),
|
|
174
|
+
'folder': os.path.basename(os.path.dirname(item.filename)),
|
|
175
|
+
'width': width,
|
|
176
|
+
'height': height,
|
|
177
|
+
'depth': depth,
|
|
178
|
+
'database': 'Unknown',
|
|
179
|
+
'segmented': 0,
|
|
180
|
+
'objects': list()
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
for annotation in annotations:
|
|
184
|
+
if not annotation:
|
|
185
|
+
continue
|
|
186
|
+
if annotation['type'] != 'box':
|
|
187
|
+
continue
|
|
188
|
+
label = annotation['label']
|
|
189
|
+
coordinates = annotation['coordinates']
|
|
190
|
+
|
|
191
|
+
attributes = list()
|
|
192
|
+
# get attributes if exists
|
|
193
|
+
if 'attributes' in annotation:
|
|
194
|
+
attributes = annotation['attributes']
|
|
195
|
+
|
|
196
|
+
try:
|
|
197
|
+
left = int(coordinates[0]['x'])
|
|
198
|
+
top = int(coordinates[0]['y'])
|
|
199
|
+
right = int(coordinates[1]['x'])
|
|
200
|
+
bottom = int(coordinates[1]['y'])
|
|
201
|
+
except Exception as err:
|
|
202
|
+
print('coordinates', coordinates)
|
|
203
|
+
continue
|
|
204
|
+
|
|
205
|
+
output_annotation['objects'].append({'name': label,
|
|
206
|
+
'xmin': left,
|
|
207
|
+
'ymin': top,
|
|
208
|
+
'xmax': right,
|
|
209
|
+
'ymax': bottom,
|
|
210
|
+
'attributes': attributes,
|
|
211
|
+
})
|
|
212
|
+
with open(out_filepath, 'w') as file:
|
|
213
|
+
content = params['annotation_template'].render(**output_annotation)
|
|
214
|
+
file.write(content)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class DtlpyToYolo(BaseConverterFromPlatform):
|
|
218
|
+
def __init__(self, **kwargs):
|
|
219
|
+
super().__init__(**kwargs)
|
|
220
|
+
self.params = {'labels': dict()}
|
|
221
|
+
|
|
222
|
+
@staticmethod
|
|
223
|
+
def convert_bb(size, box):
|
|
224
|
+
dw = 1. / size[0]
|
|
225
|
+
dh = 1. / size[1]
|
|
226
|
+
x = (box[0] + box[1]) / 2.0
|
|
227
|
+
y = (box[2] + box[3]) / 2.0
|
|
228
|
+
w = box[1] - box[0]
|
|
229
|
+
h = box[3] - box[2]
|
|
230
|
+
x = x * dw
|
|
231
|
+
w = w * dw
|
|
232
|
+
y = y * dh
|
|
233
|
+
h = h * dh
|
|
234
|
+
return x, y, w, h
|
|
235
|
+
|
|
236
|
+
@staticmethod
|
|
237
|
+
def convert_single_file(item, annotations, output_directory, params):
|
|
238
|
+
# output filepath for xml
|
|
239
|
+
out_filepath = os.path.join(output_directory, item.filename[1:])
|
|
240
|
+
# remove ext from filepath
|
|
241
|
+
out_filepath, ext = os.path.splitext(out_filepath)
|
|
242
|
+
# add txt extension
|
|
243
|
+
out_filepath += '.txt'
|
|
244
|
+
|
|
245
|
+
if not os.path.isdir(os.path.dirname(out_filepath)):
|
|
246
|
+
os.makedirs(os.path.dirname(out_filepath), exist_ok=True)
|
|
247
|
+
|
|
248
|
+
width = item.width
|
|
249
|
+
height = item.height
|
|
250
|
+
yolo_annotations = list()
|
|
251
|
+
for annotation in annotations:
|
|
252
|
+
if annotation['type'] != 'box':
|
|
253
|
+
continue
|
|
254
|
+
label = annotation['label']
|
|
255
|
+
coordinates = annotation['coordinates']
|
|
256
|
+
try:
|
|
257
|
+
left = int(coordinates[0]['x'])
|
|
258
|
+
top = int(coordinates[0]['y'])
|
|
259
|
+
right = int(coordinates[1]['x'])
|
|
260
|
+
bottom = int(coordinates[1]['y'])
|
|
261
|
+
except Exception as err:
|
|
262
|
+
print('coords', coordinates)
|
|
263
|
+
continue
|
|
264
|
+
yolo_bb = DtlpyToYolo.convert_bb((width, height), (left, right, top, bottom))
|
|
265
|
+
yolo_annotations.append(
|
|
266
|
+
'%d %f %f %f %f' % (params['labels_dict'][label], yolo_bb[0], yolo_bb[1], yolo_bb[2], yolo_bb[3]))
|
|
267
|
+
|
|
268
|
+
with open(out_filepath, 'w') as f:
|
|
269
|
+
f.write('\n'.join(yolo_annotations))
|