synapse-sdk 1.0.0b5__py3-none-any.whl → 2025.12.3__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.
- synapse_sdk/__init__.py +24 -0
- synapse_sdk/cli/code_server.py +305 -33
- synapse_sdk/clients/agent/__init__.py +2 -1
- synapse_sdk/clients/agent/container.py +143 -0
- synapse_sdk/clients/agent/ray.py +296 -38
- synapse_sdk/clients/backend/annotation.py +1 -1
- synapse_sdk/clients/backend/core.py +31 -4
- synapse_sdk/clients/backend/data_collection.py +82 -7
- synapse_sdk/clients/backend/hitl.py +1 -1
- synapse_sdk/clients/backend/ml.py +1 -1
- synapse_sdk/clients/base.py +211 -61
- synapse_sdk/loggers.py +46 -0
- synapse_sdk/plugins/README.md +1340 -0
- synapse_sdk/plugins/categories/base.py +59 -9
- synapse_sdk/plugins/categories/export/actions/__init__.py +3 -0
- synapse_sdk/plugins/categories/export/actions/export/__init__.py +28 -0
- synapse_sdk/plugins/categories/export/actions/export/action.py +165 -0
- synapse_sdk/plugins/categories/export/actions/export/enums.py +113 -0
- synapse_sdk/plugins/categories/export/actions/export/exceptions.py +53 -0
- synapse_sdk/plugins/categories/export/actions/export/models.py +74 -0
- synapse_sdk/plugins/categories/export/actions/export/run.py +195 -0
- synapse_sdk/plugins/categories/export/actions/export/utils.py +187 -0
- synapse_sdk/plugins/categories/export/templates/config.yaml +19 -1
- synapse_sdk/plugins/categories/export/templates/plugin/__init__.py +390 -0
- synapse_sdk/plugins/categories/export/templates/plugin/export.py +153 -177
- synapse_sdk/plugins/categories/neural_net/actions/train.py +1130 -32
- synapse_sdk/plugins/categories/neural_net/actions/tune.py +157 -4
- synapse_sdk/plugins/categories/neural_net/templates/config.yaml +7 -4
- synapse_sdk/plugins/categories/pre_annotation/actions/__init__.py +4 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation/__init__.py +3 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation/action.py +10 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/__init__.py +28 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/action.py +148 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/enums.py +269 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/exceptions.py +14 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/factory.py +76 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/models.py +100 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/orchestrator.py +248 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/run.py +64 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/__init__.py +17 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/annotation.py +265 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/base.py +170 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/extraction.py +83 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/metrics.py +92 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/preprocessor.py +243 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/validation.py +143 -0
- synapse_sdk/plugins/categories/upload/actions/upload/__init__.py +19 -0
- synapse_sdk/plugins/categories/upload/actions/upload/action.py +236 -0
- synapse_sdk/plugins/categories/upload/actions/upload/context.py +185 -0
- synapse_sdk/plugins/categories/upload/actions/upload/enums.py +493 -0
- synapse_sdk/plugins/categories/upload/actions/upload/exceptions.py +36 -0
- synapse_sdk/plugins/categories/upload/actions/upload/factory.py +138 -0
- synapse_sdk/plugins/categories/upload/actions/upload/models.py +214 -0
- synapse_sdk/plugins/categories/upload/actions/upload/orchestrator.py +183 -0
- synapse_sdk/plugins/categories/upload/actions/upload/registry.py +113 -0
- synapse_sdk/plugins/categories/upload/actions/upload/run.py +179 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/base.py +107 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/cleanup.py +62 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/collection.py +63 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/generate.py +91 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/initialize.py +82 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/metadata.py +235 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/organize.py +201 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/upload.py +104 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/validate.py +71 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/base.py +82 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/batch.py +39 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/single.py +29 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/flat.py +300 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/recursive.py +287 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/excel.py +174 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/none.py +16 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/upload/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/upload/sync.py +84 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/validation/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/validation/default.py +60 -0
- synapse_sdk/plugins/categories/upload/actions/upload/utils.py +250 -0
- synapse_sdk/plugins/categories/upload/templates/README.md +470 -0
- synapse_sdk/plugins/categories/upload/templates/config.yaml +28 -2
- synapse_sdk/plugins/categories/upload/templates/plugin/__init__.py +310 -0
- synapse_sdk/plugins/categories/upload/templates/plugin/upload.py +82 -20
- synapse_sdk/plugins/models.py +111 -9
- synapse_sdk/plugins/templates/plugin-config-schema.json +7 -0
- synapse_sdk/plugins/templates/schema.json +7 -0
- synapse_sdk/plugins/utils/__init__.py +3 -0
- synapse_sdk/plugins/utils/ray_gcs.py +66 -0
- synapse_sdk/shared/__init__.py +25 -0
- synapse_sdk/utils/converters/dm/__init__.py +42 -41
- synapse_sdk/utils/converters/dm/base.py +137 -0
- synapse_sdk/utils/converters/dm/from_v1.py +208 -562
- synapse_sdk/utils/converters/dm/to_v1.py +258 -304
- synapse_sdk/utils/converters/dm/tools/__init__.py +214 -0
- synapse_sdk/utils/converters/dm/tools/answer.py +95 -0
- synapse_sdk/utils/converters/dm/tools/bounding_box.py +132 -0
- synapse_sdk/utils/converters/dm/tools/bounding_box_3d.py +121 -0
- synapse_sdk/utils/converters/dm/tools/classification.py +75 -0
- synapse_sdk/utils/converters/dm/tools/keypoint.py +117 -0
- synapse_sdk/utils/converters/dm/tools/named_entity.py +111 -0
- synapse_sdk/utils/converters/dm/tools/polygon.py +122 -0
- synapse_sdk/utils/converters/dm/tools/polyline.py +124 -0
- synapse_sdk/utils/converters/dm/tools/prompt.py +94 -0
- synapse_sdk/utils/converters/dm/tools/relation.py +86 -0
- synapse_sdk/utils/converters/dm/tools/segmentation.py +141 -0
- synapse_sdk/utils/converters/dm/tools/segmentation_3d.py +83 -0
- synapse_sdk/utils/converters/dm/types.py +168 -0
- synapse_sdk/utils/converters/dm/utils.py +162 -0
- synapse_sdk/utils/converters/dm_legacy/__init__.py +56 -0
- synapse_sdk/utils/converters/dm_legacy/from_v1.py +627 -0
- synapse_sdk/utils/converters/dm_legacy/to_v1.py +367 -0
- synapse_sdk/utils/file/__init__.py +58 -0
- synapse_sdk/utils/file/archive.py +32 -0
- synapse_sdk/utils/file/checksum.py +56 -0
- synapse_sdk/utils/file/chunking.py +31 -0
- synapse_sdk/utils/file/download.py +385 -0
- synapse_sdk/utils/file/encoding.py +40 -0
- synapse_sdk/utils/file/io.py +22 -0
- synapse_sdk/utils/file/upload.py +165 -0
- synapse_sdk/utils/file/video/__init__.py +29 -0
- synapse_sdk/utils/file/video/transcode.py +307 -0
- synapse_sdk/utils/{file.py → file.py.backup} +77 -0
- synapse_sdk/utils/network.py +272 -0
- synapse_sdk/utils/storage/__init__.py +6 -2
- synapse_sdk/utils/storage/providers/file_system.py +6 -0
- {synapse_sdk-1.0.0b5.dist-info → synapse_sdk-2025.12.3.dist-info}/METADATA +19 -2
- {synapse_sdk-1.0.0b5.dist-info → synapse_sdk-2025.12.3.dist-info}/RECORD +134 -74
- synapse_sdk/devtools/docs/.gitignore +0 -20
- synapse_sdk/devtools/docs/README.md +0 -41
- synapse_sdk/devtools/docs/blog/2019-05-28-first-blog-post.md +0 -12
- synapse_sdk/devtools/docs/blog/2019-05-29-long-blog-post.md +0 -44
- synapse_sdk/devtools/docs/blog/2021-08-01-mdx-blog-post.mdx +0 -24
- synapse_sdk/devtools/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg +0 -0
- synapse_sdk/devtools/docs/blog/2021-08-26-welcome/index.md +0 -29
- synapse_sdk/devtools/docs/blog/authors.yml +0 -25
- synapse_sdk/devtools/docs/blog/tags.yml +0 -19
- synapse_sdk/devtools/docs/docusaurus.config.ts +0 -138
- synapse_sdk/devtools/docs/package-lock.json +0 -17455
- synapse_sdk/devtools/docs/package.json +0 -47
- synapse_sdk/devtools/docs/sidebars.ts +0 -44
- synapse_sdk/devtools/docs/src/components/HomepageFeatures/index.tsx +0 -71
- synapse_sdk/devtools/docs/src/components/HomepageFeatures/styles.module.css +0 -11
- synapse_sdk/devtools/docs/src/css/custom.css +0 -30
- synapse_sdk/devtools/docs/src/pages/index.module.css +0 -23
- synapse_sdk/devtools/docs/src/pages/index.tsx +0 -21
- synapse_sdk/devtools/docs/src/pages/markdown-page.md +0 -7
- synapse_sdk/devtools/docs/static/.nojekyll +0 -0
- synapse_sdk/devtools/docs/static/img/docusaurus-social-card.jpg +0 -0
- synapse_sdk/devtools/docs/static/img/docusaurus.png +0 -0
- synapse_sdk/devtools/docs/static/img/favicon.ico +0 -0
- synapse_sdk/devtools/docs/static/img/logo.png +0 -0
- synapse_sdk/devtools/docs/static/img/undraw_docusaurus_mountain.svg +0 -171
- synapse_sdk/devtools/docs/static/img/undraw_docusaurus_react.svg +0 -170
- synapse_sdk/devtools/docs/static/img/undraw_docusaurus_tree.svg +0 -40
- synapse_sdk/devtools/docs/tsconfig.json +0 -8
- synapse_sdk/plugins/categories/export/actions/export.py +0 -346
- synapse_sdk/plugins/categories/export/enums.py +0 -7
- synapse_sdk/plugins/categories/neural_net/actions/gradio.py +0 -151
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task.py +0 -943
- synapse_sdk/plugins/categories/upload/actions/upload.py +0 -954
- {synapse_sdk-1.0.0b5.dist-info → synapse_sdk-2025.12.3.dist-info}/WHEEL +0 -0
- {synapse_sdk-1.0.0b5.dist-info → synapse_sdk-2025.12.3.dist-info}/entry_points.txt +0 -0
- {synapse_sdk-1.0.0b5.dist-info → synapse_sdk-2025.12.3.dist-info}/licenses/LICENSE +0 -0
- {synapse_sdk-1.0.0b5.dist-info → synapse_sdk-2025.12.3.dist-info}/top_level.txt +0 -0
|
@@ -1,367 +1,321 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
"""
|
|
2
|
+
DM Schema V2 → V1 Converter
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
Created: 2025-12-11
|
|
5
5
|
|
|
6
|
+
V2→V1 conversion:
|
|
7
|
+
- If both annotation_data and annotation_meta exist, complete V1 restoration
|
|
8
|
+
- If only annotation_data exists, convert to V1 using defaults
|
|
9
|
+
"""
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
"""DM v2 to v1 format converter class."""
|
|
11
|
+
from typing import Any
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
from .base import BaseDMConverter
|
|
14
|
+
from .types import (
|
|
15
|
+
MEDIA_TYPE_REVERSE_MAP,
|
|
16
|
+
AnnotationMeta,
|
|
17
|
+
V2ConversionResult,
|
|
18
|
+
)
|
|
12
19
|
|
|
13
|
-
Args:
|
|
14
|
-
new_dm_data (dict): DM v2 format data to be converted
|
|
15
|
-
file_type (str, optional): Type of file being converted (image, video, pcd, text, audio)
|
|
16
|
-
"""
|
|
17
|
-
# Auto-detect file type if not provided
|
|
18
|
-
if file_type is None:
|
|
19
|
-
file_type = self._detect_file_type(new_dm_data)
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
self.annotations = {}
|
|
24
|
-
self.annotations_data = {}
|
|
25
|
-
self.extra = {}
|
|
26
|
-
self.relations = {}
|
|
27
|
-
self.annotation_groups = {}
|
|
21
|
+
class DMV2ToV1Converter(BaseDMConverter):
|
|
22
|
+
"""Converter from DM Schema V2 to V1
|
|
28
23
|
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
V2→V1 conversion:
|
|
25
|
+
- If both annotation_data and annotation_meta exist, complete V1 restoration
|
|
26
|
+
- If only annotation_data exists, convert to V1 using defaults
|
|
31
27
|
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
Example:
|
|
29
|
+
>>> converter = DMV2ToV1Converter()
|
|
30
|
+
>>> # Complete conversion
|
|
31
|
+
>>> v1_data = converter.convert(v2_result)
|
|
32
|
+
>>> # Convert with annotation_data only
|
|
33
|
+
>>> v1_data = converter.convert({"annotation_data": annotation_data})
|
|
34
|
+
"""
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if not data:
|
|
39
|
-
return None
|
|
40
|
-
|
|
41
|
-
# Check for media type keys (plural forms)
|
|
42
|
-
if 'images' in data:
|
|
43
|
-
return 'image'
|
|
44
|
-
elif 'videos' in data:
|
|
45
|
-
return 'video'
|
|
46
|
-
elif 'pcds' in data:
|
|
47
|
-
return 'pcd'
|
|
48
|
-
elif 'texts' in data:
|
|
49
|
-
return 'text'
|
|
50
|
-
elif 'audios' in data:
|
|
51
|
-
return 'audio'
|
|
52
|
-
|
|
53
|
-
return None
|
|
54
|
-
|
|
55
|
-
def convert(self):
|
|
56
|
-
"""Convert DM v2 data to v1 format.
|
|
36
|
+
def _setup_tool_processors(self) -> None:
|
|
37
|
+
"""Register tool processors"""
|
|
38
|
+
from .tools.bounding_box import BoundingBoxProcessor
|
|
57
39
|
|
|
58
|
-
|
|
59
|
-
dict: Converted data in DM v1 format
|
|
60
|
-
"""
|
|
61
|
-
# Reset state
|
|
62
|
-
new_dm_data = self.new_dm_data
|
|
63
|
-
self.annotations = {}
|
|
64
|
-
self.annotations_data = {}
|
|
65
|
-
self.extra = {}
|
|
66
|
-
self.relations = {}
|
|
67
|
-
self.annotation_groups = {}
|
|
68
|
-
|
|
69
|
-
# Process each media type (images, videos, etc.)
|
|
70
|
-
for media_type_plural, media_items in new_dm_data.items():
|
|
71
|
-
if media_type_plural == 'classification':
|
|
72
|
-
continue
|
|
40
|
+
self.register_processor(BoundingBoxProcessor())
|
|
73
41
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
media_id = f'{media_type}_{index}'
|
|
78
|
-
|
|
79
|
-
# Initialize structures for this media
|
|
80
|
-
self.annotations[media_id] = []
|
|
81
|
-
self.annotations_data[media_id] = []
|
|
82
|
-
self.extra[media_id] = {}
|
|
83
|
-
self.relations[media_id] = []
|
|
84
|
-
self.annotation_groups[media_id] = []
|
|
85
|
-
|
|
86
|
-
# Process each tool type in the media item
|
|
87
|
-
for tool_type, tool_data in media_item.items():
|
|
88
|
-
self._process_tool_data(media_id, tool_type, tool_data)
|
|
89
|
-
|
|
90
|
-
# Build final result
|
|
91
|
-
result = {
|
|
92
|
-
'extra': self.extra,
|
|
93
|
-
'relations': self.relations,
|
|
94
|
-
'annotations': self.annotations,
|
|
95
|
-
'annotationsData': self.annotations_data,
|
|
96
|
-
'annotationGroups': self.annotation_groups,
|
|
97
|
-
}
|
|
42
|
+
# polygon to be added later
|
|
43
|
+
try:
|
|
44
|
+
from .tools.polygon import PolygonProcessor
|
|
98
45
|
|
|
99
|
-
|
|
46
|
+
self.register_processor(PolygonProcessor())
|
|
47
|
+
except ImportError:
|
|
48
|
+
pass
|
|
100
49
|
|
|
101
|
-
|
|
102
|
-
|
|
50
|
+
try:
|
|
51
|
+
from .tools.polyline import PolylineProcessor
|
|
103
52
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
tool_data (list): List of annotation data for this tool
|
|
108
|
-
"""
|
|
109
|
-
for annotation in tool_data:
|
|
110
|
-
annotation_id = annotation['id']
|
|
111
|
-
classification = annotation['classification']
|
|
112
|
-
attrs = annotation.get('attrs', [])
|
|
113
|
-
data = annotation.get('data', {})
|
|
114
|
-
|
|
115
|
-
# Create annotation entry
|
|
116
|
-
annotation_entry = {
|
|
117
|
-
'id': annotation_id,
|
|
118
|
-
'tool': tool_type,
|
|
119
|
-
'isLocked': False,
|
|
120
|
-
'isVisible': True,
|
|
121
|
-
'classification': {'class': classification},
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
# Add additional classification attributes from attrs
|
|
125
|
-
for attr in attrs:
|
|
126
|
-
attr_name = attr.get('name')
|
|
127
|
-
attr_value = attr.get('value')
|
|
128
|
-
if attr_name and attr_value is not None:
|
|
129
|
-
annotation_entry['classification'][attr_name] = attr_value
|
|
130
|
-
|
|
131
|
-
# Add special attributes for specific tools
|
|
132
|
-
if tool_type == 'keypoint':
|
|
133
|
-
annotation_entry['shape'] = 'circle'
|
|
134
|
-
|
|
135
|
-
self.annotations[media_id].append(annotation_entry)
|
|
136
|
-
|
|
137
|
-
# Create annotations data entry using tool processor
|
|
138
|
-
processor = self.tool_processors.get(tool_type)
|
|
139
|
-
if processor:
|
|
140
|
-
processor(annotation_id, data, self.annotations_data[media_id])
|
|
141
|
-
else:
|
|
142
|
-
self._handle_unknown_tool(tool_type, annotation_id)
|
|
143
|
-
|
|
144
|
-
def _convert_bounding_box(self, annotation_id, data, annotations_data):
|
|
145
|
-
"""Process bounding box annotation data.
|
|
53
|
+
self.register_processor(PolylineProcessor())
|
|
54
|
+
except ImportError:
|
|
55
|
+
pass
|
|
146
56
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
data (list): Bounding box data [x1, y1, x2, y2]
|
|
150
|
-
annotations_data (list): List to append the processed data
|
|
151
|
-
"""
|
|
152
|
-
if len(data) >= 4:
|
|
153
|
-
x1, y1, width, height = data[:4]
|
|
154
|
-
coordinate = {'x': x1, 'y': y1, 'width': width, 'height': height}
|
|
57
|
+
try:
|
|
58
|
+
from .tools.keypoint import KeypointProcessor
|
|
155
59
|
|
|
156
|
-
|
|
60
|
+
self.register_processor(KeypointProcessor())
|
|
61
|
+
except ImportError:
|
|
62
|
+
pass
|
|
157
63
|
|
|
158
|
-
|
|
159
|
-
|
|
64
|
+
try:
|
|
65
|
+
from .tools.bounding_box_3d import BoundingBox3DProcessor
|
|
160
66
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
annotations_data (list): List to append the processed data
|
|
165
|
-
"""
|
|
166
|
-
entity_data = {'id': annotation_id}
|
|
67
|
+
self.register_processor(BoundingBox3DProcessor())
|
|
68
|
+
except ImportError:
|
|
69
|
+
pass
|
|
167
70
|
|
|
168
|
-
|
|
169
|
-
|
|
71
|
+
try:
|
|
72
|
+
from .tools.segmentation import SegmentationProcessor
|
|
170
73
|
|
|
171
|
-
|
|
172
|
-
|
|
74
|
+
self.register_processor(SegmentationProcessor())
|
|
75
|
+
except ImportError:
|
|
76
|
+
pass
|
|
173
77
|
|
|
174
|
-
|
|
78
|
+
try:
|
|
79
|
+
from .tools.named_entity import NamedEntityProcessor
|
|
175
80
|
|
|
176
|
-
|
|
177
|
-
|
|
81
|
+
self.register_processor(NamedEntityProcessor())
|
|
82
|
+
except ImportError:
|
|
83
|
+
pass
|
|
178
84
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
data (dict): Classification data (usually empty)
|
|
182
|
-
annotations_data (list): List to append the processed data
|
|
183
|
-
"""
|
|
184
|
-
# Classification data is typically empty in v2, so we just add the ID
|
|
185
|
-
annotations_data.append({'id': annotation_id})
|
|
85
|
+
try:
|
|
86
|
+
from .tools.segmentation_3d import Segmentation3DProcessor
|
|
186
87
|
|
|
187
|
-
|
|
188
|
-
|
|
88
|
+
self.register_processor(Segmentation3DProcessor())
|
|
89
|
+
except ImportError:
|
|
90
|
+
pass
|
|
189
91
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
data (list): Polyline data - can be flat [x1, y1, x2, y2, ...] or nested [[x1, y1], [x2, y2], ...]
|
|
193
|
-
annotations_data (list): List to append the processed data
|
|
194
|
-
"""
|
|
195
|
-
coordinates = []
|
|
92
|
+
try:
|
|
93
|
+
from .tools.classification import ClassificationProcessor
|
|
196
94
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
if len(point) >= 2:
|
|
201
|
-
coordinates.append({'x': point[0], 'y': point[1], 'id': self._generate_random_id()})
|
|
202
|
-
else:
|
|
203
|
-
# Flat format: [x1, y1, x2, y2, ...]
|
|
204
|
-
for i in range(0, len(data), 2):
|
|
205
|
-
if i + 1 < len(data):
|
|
206
|
-
coordinates.append({'x': data[i], 'y': data[i + 1], 'id': self._generate_random_id()})
|
|
95
|
+
self.register_processor(ClassificationProcessor())
|
|
96
|
+
except ImportError:
|
|
97
|
+
pass
|
|
207
98
|
|
|
208
|
-
|
|
99
|
+
try:
|
|
100
|
+
from .tools.relation import RelationProcessor
|
|
209
101
|
|
|
210
|
-
|
|
211
|
-
|
|
102
|
+
self.register_processor(RelationProcessor())
|
|
103
|
+
except ImportError:
|
|
104
|
+
pass
|
|
212
105
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
data (list): Keypoint data [x, y]
|
|
216
|
-
annotations_data (list): List to append the processed data
|
|
217
|
-
"""
|
|
218
|
-
if len(data) >= 2:
|
|
219
|
-
coordinate = {'x': data[0], 'y': data[1]}
|
|
106
|
+
try:
|
|
107
|
+
from .tools.prompt import PromptProcessor
|
|
220
108
|
|
|
221
|
-
|
|
109
|
+
self.register_processor(PromptProcessor())
|
|
110
|
+
except ImportError:
|
|
111
|
+
pass
|
|
222
112
|
|
|
223
|
-
|
|
224
|
-
|
|
113
|
+
try:
|
|
114
|
+
from .tools.answer import AnswerProcessor
|
|
225
115
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
annotations_data (list): List to append the processed data
|
|
230
|
-
"""
|
|
231
|
-
annotations_data.append({'id': annotation_id, 'psr': data})
|
|
116
|
+
self.register_processor(AnswerProcessor())
|
|
117
|
+
except ImportError:
|
|
118
|
+
pass
|
|
232
119
|
|
|
233
|
-
def
|
|
234
|
-
|
|
120
|
+
def convert(
|
|
121
|
+
self,
|
|
122
|
+
v2_data: V2ConversionResult | dict[str, Any],
|
|
123
|
+
annotation_meta: AnnotationMeta | None = None,
|
|
124
|
+
) -> dict[str, Any]:
|
|
125
|
+
"""Convert V2 data to V1 format
|
|
235
126
|
|
|
236
127
|
Args:
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
annotations_data (list): List to append the processed data
|
|
240
|
-
"""
|
|
241
|
-
annotation_data = {'id': annotation_id}
|
|
242
|
-
|
|
243
|
-
if isinstance(data, list):
|
|
244
|
-
# Pixel-based segmentation
|
|
245
|
-
annotation_data['pixel_indices'] = data
|
|
246
|
-
elif isinstance(data, dict):
|
|
247
|
-
# Section-based segmentation (video)
|
|
248
|
-
annotation_data['section'] = data
|
|
128
|
+
v2_data: DM Schema V2 format data
|
|
129
|
+
annotation_meta: Optional V1 top-level structure passed separately
|
|
249
130
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
def _convert_video_segmentation(self, annotation_id, data, annotations_data):
|
|
253
|
-
"""Process video segmentation annotation data.
|
|
131
|
+
Returns:
|
|
132
|
+
DM Schema V1 format data
|
|
254
133
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
data (list or dict): Segmentation data (pixel_indices or section)
|
|
258
|
-
annotations_data (list): List to append the processed data
|
|
134
|
+
Raises:
|
|
135
|
+
ValueError: Missing required fields or invalid format
|
|
259
136
|
"""
|
|
260
|
-
|
|
137
|
+
# Extract annotation_data
|
|
138
|
+
if 'annotation_data' in v2_data:
|
|
139
|
+
annotation_data = v2_data['annotation_data']
|
|
140
|
+
# Extract annotation_meta (use from v2_data if present, else use parameter)
|
|
141
|
+
meta = v2_data.get('annotation_meta') or annotation_meta
|
|
142
|
+
else:
|
|
143
|
+
# annotation_data passed directly
|
|
144
|
+
annotation_data = v2_data
|
|
145
|
+
meta = annotation_meta
|
|
261
146
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
elif isinstance(data, dict):
|
|
266
|
-
# Section-based segmentation (video)
|
|
267
|
-
annotation_data['section'] = data
|
|
147
|
+
# Input validation
|
|
148
|
+
if not annotation_data:
|
|
149
|
+
raise ValueError("V2 data requires 'annotation_data'")
|
|
268
150
|
|
|
269
|
-
|
|
151
|
+
# Build V1 data
|
|
152
|
+
return self._merge_data_and_meta(annotation_data, meta)
|
|
270
153
|
|
|
271
|
-
def
|
|
272
|
-
|
|
154
|
+
def _merge_data_and_meta(
|
|
155
|
+
self,
|
|
156
|
+
annotation_data: dict[str, Any],
|
|
157
|
+
annotation_meta: AnnotationMeta | None,
|
|
158
|
+
) -> dict[str, Any]:
|
|
159
|
+
"""Merge annotation_data and annotation_meta to create V1 format
|
|
273
160
|
|
|
274
161
|
Args:
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
annotations_data (list): List to append the processed data
|
|
278
|
-
"""
|
|
279
|
-
annotation_data = {'id': annotation_id}
|
|
162
|
+
annotation_data: V2 common annotation structure
|
|
163
|
+
annotation_meta: V1 top-level structure (restores meta info if present)
|
|
280
164
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
annotation_data['pixel_indices'] = data
|
|
284
|
-
elif isinstance(data, dict):
|
|
285
|
-
# Section-based segmentation
|
|
286
|
-
annotation_data['section'] = data
|
|
287
|
-
|
|
288
|
-
annotations_data.append(annotation_data)
|
|
289
|
-
|
|
290
|
-
def _convert_prompt(self, annotation_id, data, annotations_data):
|
|
291
|
-
"""Process prompt annotation data.
|
|
292
|
-
|
|
293
|
-
Args:
|
|
294
|
-
annotation_id (str): ID of the annotation
|
|
295
|
-
data (dict): Prompt data
|
|
296
|
-
annotations_data (list): List to append the processed data
|
|
165
|
+
Returns:
|
|
166
|
+
Merged V1 format data
|
|
297
167
|
"""
|
|
298
|
-
|
|
168
|
+
annotations: dict[str, list[dict[str, Any]]] = {}
|
|
169
|
+
annotations_data: dict[str, list[dict[str, Any]]] = {}
|
|
299
170
|
|
|
300
|
-
|
|
301
|
-
|
|
171
|
+
# Process by media type
|
|
172
|
+
media_index_by_type: dict[str, int] = {}
|
|
302
173
|
|
|
303
|
-
|
|
174
|
+
for plural_type in ['images', 'videos', 'pcds', 'texts', 'audios', 'prompts']:
|
|
175
|
+
if plural_type not in annotation_data:
|
|
176
|
+
continue
|
|
304
177
|
|
|
305
|
-
|
|
306
|
-
|
|
178
|
+
singular_type = MEDIA_TYPE_REVERSE_MAP.get(plural_type, plural_type.rstrip('s'))
|
|
179
|
+
media_index_by_type[singular_type] = 0
|
|
307
180
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
"""
|
|
313
|
-
annotation_data = {'id': annotation_id}
|
|
314
|
-
|
|
315
|
-
if isinstance(data, dict):
|
|
316
|
-
annotation_data.update(data)
|
|
181
|
+
for media_item in annotation_data[plural_type]:
|
|
182
|
+
# Generate media ID
|
|
183
|
+
media_index_by_type[singular_type] += 1
|
|
184
|
+
media_id = f'{singular_type}_{media_index_by_type[singular_type]}'
|
|
317
185
|
|
|
318
|
-
|
|
186
|
+
# Convert by tool
|
|
187
|
+
ann_list, data_list = self._convert_media_item(media_item, media_id, annotation_meta)
|
|
319
188
|
|
|
320
|
-
|
|
321
|
-
|
|
189
|
+
if ann_list:
|
|
190
|
+
annotations[media_id] = ann_list
|
|
191
|
+
if data_list:
|
|
192
|
+
annotations_data[media_id] = data_list
|
|
322
193
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
coordinates = []
|
|
194
|
+
# Build V1 result
|
|
195
|
+
result: dict[str, Any] = {
|
|
196
|
+
'annotations': annotations,
|
|
197
|
+
'annotationsData': annotations_data,
|
|
198
|
+
}
|
|
329
199
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
200
|
+
# Restore additional fields if annotation_meta exists
|
|
201
|
+
if annotation_meta:
|
|
202
|
+
result['extra'] = annotation_meta.get('extra', {})
|
|
203
|
+
result['relations'] = annotation_meta.get('relations', {})
|
|
204
|
+
result['annotationGroups'] = annotation_meta.get('annotationGroups', {})
|
|
205
|
+
result['assignmentId'] = annotation_meta.get('assignmentId')
|
|
335
206
|
else:
|
|
336
|
-
#
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
207
|
+
# Default values
|
|
208
|
+
result['extra'] = {}
|
|
209
|
+
result['relations'] = {}
|
|
210
|
+
result['annotationGroups'] = {}
|
|
211
|
+
result['assignmentId'] = None
|
|
340
212
|
|
|
341
|
-
|
|
213
|
+
return result
|
|
342
214
|
|
|
343
|
-
def
|
|
344
|
-
|
|
215
|
+
def _convert_media_item(
|
|
216
|
+
self,
|
|
217
|
+
media_item: dict[str, Any],
|
|
218
|
+
media_id: str,
|
|
219
|
+
annotation_meta: AnnotationMeta | None,
|
|
220
|
+
) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
|
|
221
|
+
"""Convert V2 media item to V1 annotations/annotationsData
|
|
345
222
|
|
|
346
223
|
Args:
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
224
|
+
media_item: V2 media item
|
|
225
|
+
media_id: Media ID to generate
|
|
226
|
+
annotation_meta: V1 top-level structure (for meta info restoration)
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
(V1 annotations list, V1 annotationsData list)
|
|
350
230
|
"""
|
|
351
|
-
|
|
231
|
+
annotations: list[dict[str, Any]] = []
|
|
232
|
+
annotations_data: list[dict[str, Any]] = []
|
|
233
|
+
|
|
234
|
+
# Process by tool
|
|
235
|
+
for tool_name, v2_annotations in media_item.items():
|
|
236
|
+
processor = self.get_processor(tool_name)
|
|
237
|
+
if not processor:
|
|
238
|
+
continue
|
|
352
239
|
|
|
353
|
-
|
|
354
|
-
|
|
240
|
+
for v2_ann in v2_annotations:
|
|
241
|
+
# Convert to V1
|
|
242
|
+
v1_ann, v1_data = processor.to_v1(v2_ann)
|
|
243
|
+
|
|
244
|
+
# Restore meta info from annotation_meta
|
|
245
|
+
if annotation_meta:
|
|
246
|
+
v1_ann = self._restore_meta_fields(v1_ann, annotation_meta, v2_ann.get('id', ''), media_id)
|
|
247
|
+
else:
|
|
248
|
+
# Set default values
|
|
249
|
+
v1_ann.setdefault('isLocked', False)
|
|
250
|
+
v1_ann.setdefault('isVisible', True)
|
|
251
|
+
v1_ann.setdefault('isValid', False)
|
|
252
|
+
v1_ann.setdefault('isDrawCompleted', True)
|
|
253
|
+
v1_ann.setdefault('label', [])
|
|
254
|
+
|
|
255
|
+
annotations.append(v1_ann)
|
|
256
|
+
annotations_data.append(v1_data)
|
|
257
|
+
|
|
258
|
+
return annotations, annotations_data
|
|
259
|
+
|
|
260
|
+
def _restore_meta_fields(
|
|
261
|
+
self,
|
|
262
|
+
v1_annotation: dict[str, Any],
|
|
263
|
+
annotation_meta: AnnotationMeta,
|
|
264
|
+
annotation_id: str,
|
|
265
|
+
media_id: str,
|
|
266
|
+
) -> dict[str, Any]:
|
|
267
|
+
"""Restore V1 annotation meta fields from annotation_meta
|
|
355
268
|
|
|
356
269
|
Args:
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
annotations_data.append({'id': annotation_id, 'data': data})
|
|
270
|
+
v1_annotation: Base converted V1 annotation
|
|
271
|
+
annotation_meta: V1 top-level structure
|
|
272
|
+
annotation_id: Annotation ID
|
|
273
|
+
media_id: Media ID
|
|
362
274
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
275
|
+
Returns:
|
|
276
|
+
V1 annotation with restored meta fields
|
|
277
|
+
"""
|
|
278
|
+
# Find annotation in annotation_meta
|
|
279
|
+
meta_annotations = annotation_meta.get('annotations', {})
|
|
280
|
+
|
|
281
|
+
# Try to find by media_id
|
|
282
|
+
source_media_id = None
|
|
283
|
+
for mid in meta_annotations:
|
|
284
|
+
for ann in meta_annotations[mid]:
|
|
285
|
+
if ann.get('id') == annotation_id:
|
|
286
|
+
source_media_id = mid
|
|
287
|
+
break
|
|
288
|
+
if source_media_id:
|
|
289
|
+
break
|
|
290
|
+
|
|
291
|
+
if not source_media_id:
|
|
292
|
+
# Use defaults if not found
|
|
293
|
+
v1_annotation.setdefault('isLocked', False)
|
|
294
|
+
v1_annotation.setdefault('isVisible', True)
|
|
295
|
+
v1_annotation.setdefault('isValid', False)
|
|
296
|
+
v1_annotation.setdefault('isDrawCompleted', True)
|
|
297
|
+
v1_annotation.setdefault('label', [])
|
|
298
|
+
return v1_annotation
|
|
299
|
+
|
|
300
|
+
# Restore meta info from the found annotation
|
|
301
|
+
for meta_ann in meta_annotations[source_media_id]:
|
|
302
|
+
if meta_ann.get('id') == annotation_id:
|
|
303
|
+
# Restore meta fields
|
|
304
|
+
v1_annotation['isLocked'] = meta_ann.get('isLocked', False)
|
|
305
|
+
v1_annotation['isVisible'] = meta_ann.get('isVisible', True)
|
|
306
|
+
v1_annotation['isValid'] = meta_ann.get('isValid', False)
|
|
307
|
+
v1_annotation['isDrawCompleted'] = meta_ann.get('isDrawCompleted', True)
|
|
308
|
+
v1_annotation['label'] = meta_ann.get('label', [])
|
|
309
|
+
|
|
310
|
+
# Merge classification if present in meta
|
|
311
|
+
meta_classification = meta_ann.get('classification')
|
|
312
|
+
if meta_classification:
|
|
313
|
+
# Keep existing class from classification and merge other fields
|
|
314
|
+
current_class = v1_annotation.get('classification', {}).get('class')
|
|
315
|
+
v1_annotation['classification'] = meta_classification.copy()
|
|
316
|
+
if current_class:
|
|
317
|
+
v1_annotation['classification']['class'] = current_class
|
|
318
|
+
|
|
319
|
+
break
|
|
320
|
+
|
|
321
|
+
return v1_annotation
|