synapse-sdk 1.0.0a78__py3-none-any.whl → 1.0.0a80__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of synapse-sdk might be problematic. Click here for more details.

@@ -2,16 +2,25 @@ import json
2
2
  import os
3
3
  import shutil
4
4
  import uuid
5
+ from typing import IO
5
6
 
6
7
 
7
8
  class BaseConverter:
8
9
  """Base class for shared logic between converters."""
9
10
 
10
- def __init__(self, root_dir: str, is_categorized_dataset: bool = False) -> None:
11
+ def __init__(
12
+ self, root_dir: str = None, is_categorized_dataset: bool = False, is_single_conversion: bool = False
13
+ ) -> None:
11
14
  self.root_dir: str = root_dir
12
15
  self.is_categorized_dataset: bool = is_categorized_dataset
16
+ self.is_single_conversion: bool = is_single_conversion
13
17
  self.converted_data = None
14
18
 
19
+ # Set directories if single_conversion is False.
20
+ if not is_single_conversion:
21
+ if not root_dir:
22
+ raise ValueError('root_dir must be specified for conversion')
23
+
15
24
  @staticmethod
16
25
  def ensure_dir(path: str) -> None:
17
26
  """Ensure that the directory exists, creating it if necessary."""
@@ -55,6 +64,24 @@ class BaseConverter:
55
64
 
56
65
  return splits
57
66
 
67
+ def convert_single_file(self, data, original_file: IO, **kwargs):
68
+ """Convert a single data object and corresponding original file.
69
+
70
+ This method should be implemented by subclasses for single file conversion.
71
+ Only available when is_single_conversion=True.
72
+
73
+ Args:
74
+ data: The data object to convert (dict for JSON data, etc.)
75
+ original_file_path: File object for the corresponding original file
76
+ **kwargs: Additional parameters specific to each converter
77
+
78
+ Returns:
79
+ Converted data in the target format
80
+ """
81
+ if not self.is_single_conversion:
82
+ raise RuntimeError('convert_single_file is only available when is_single_conversion=True')
83
+ raise NotImplementedError('Subclasses must implement convert_single_file method')
84
+
58
85
  def _set_directories(self, split=None):
59
86
  """Set `self.json_dir` and `self.original_file_dir` based on the dataset split."""
60
87
  if split:
@@ -106,8 +133,10 @@ class FromDMConverter(BaseConverter):
106
133
  converter.save_to_folder('/my/target/output') # Writes files/folders to output location
107
134
  """
108
135
 
109
- def __init__(self, root_dir: str, is_categorized_dataset: bool = False) -> None:
110
- super().__init__(root_dir, is_categorized_dataset)
136
+ def __init__(
137
+ self, root_dir: str = None, is_categorized_dataset: bool = False, is_single_conversion: bool = False
138
+ ) -> None:
139
+ super().__init__(root_dir, is_categorized_dataset, is_single_conversion)
111
140
  self.version: str = '1.0'
112
141
 
113
142
  def convert(self):
@@ -3,7 +3,7 @@ import json
3
3
  import os
4
4
  import shutil
5
5
  from glob import glob
6
- from typing import Any, Dict
6
+ from typing import Any, Dict, IO
7
7
 
8
8
  from PIL import Image
9
9
  from tqdm import tqdm
@@ -22,14 +22,23 @@ class FromDMToCOCOConverter(FromDMConverter):
22
22
  # 'audio': ['.wav', '.mp3', ...]
23
23
  }
24
24
 
25
- def __init__(self, root_dir, info_dict=None, licenses_list=None, data_type='img', is_categorized_dataset=False):
25
+ def __init__(
26
+ self,
27
+ root_dir=None,
28
+ info_dict=None,
29
+ licenses_list=None,
30
+ data_type='img',
31
+ is_categorized_dataset=False,
32
+ is_single_conversion=False,
33
+ ):
26
34
  """Args:
27
35
  root_dir (str): Root directory containing data.
28
36
  info_dict, licenses_list: COCO metadata.
29
37
  data_type (str): Which data type to use (default: 'img').
30
38
  is_categorized_dataset (bool): Whether to handle train, test, valid splits.
39
+ is_single_conversion (bool): Whether to use single file conversion mode.
31
40
  """
32
- super().__init__(root_dir, is_categorized_dataset)
41
+ super().__init__(root_dir, is_categorized_dataset, is_single_conversion)
33
42
  self.data_type = data_type
34
43
  self.info_dict = info_dict or self._default_info()
35
44
  self.licenses_list = licenses_list or self._default_licenses()
@@ -267,3 +276,47 @@ class FromDMToCOCOConverter(FromDMConverter):
267
276
  shutil.copy(src_path, dst_path)
268
277
  else:
269
278
  print(f'[WARNING] Image not found: {src_path}')
279
+
280
+ def convert_single_file(self, data: Dict[str, Any], original_file: IO) -> Dict[str, Any]:
281
+ """Convert a single DM data dict and corresponding image file object to COCO format.
282
+
283
+ Args:
284
+ data: DM format data dictionary (JSON content)
285
+ original_file: File object for the corresponding original image
286
+
287
+ Returns:
288
+ Dictionary containing COCO format data for the single file
289
+ """
290
+ if not self.is_single_conversion:
291
+ raise RuntimeError('convert_single_file is only available when is_single_conversion=True')
292
+
293
+ self.reset_state()
294
+ self.coco_dict = {
295
+ 'info': self.info_dict,
296
+ 'licenses': self.licenses_list,
297
+ 'images': [],
298
+ 'annotations': [],
299
+ 'categories': [],
300
+ }
301
+
302
+ # Process the image file
303
+ with Image.open(original_file) as im:
304
+ width, height = im.size
305
+
306
+ image_info = {
307
+ 'id': self.img_id,
308
+ 'file_name': getattr(original_file, 'name', 'image.jpg'),
309
+ 'width': width,
310
+ 'height': height,
311
+ 'license': self.license_id,
312
+ }
313
+ self.coco_dict['images'].append(image_info)
314
+
315
+ # Process annotations from the first (and only) image in data
316
+ if 'images' in data and len(data['images']) > 0:
317
+ anns = data['images'][0]
318
+ self._process_polylines(anns)
319
+ self._process_bboxes(anns)
320
+ self._process_keypoints(anns)
321
+
322
+ return self.coco_dict
@@ -1,5 +1,6 @@
1
1
  import json
2
2
  import os
3
+ from typing import IO, Any, Dict
3
4
 
4
5
  from synapse_sdk.utils.converters import ToDMConverter
5
6
 
@@ -7,6 +8,9 @@ from synapse_sdk.utils.converters import ToDMConverter
7
8
  class COCOToDMConverter(ToDMConverter):
8
9
  """Convert COCO format annotations to DM (Data Manager) format."""
9
10
 
11
+ def __init__(self, root_dir: str = None, is_categorized_dataset: bool = False, is_single_conversion: bool = False):
12
+ super().__init__(root_dir, is_categorized_dataset, is_single_conversion)
13
+
10
14
  def convert(self):
11
15
  if self.is_categorized_dataset:
12
16
  splits = self._validate_splits(['train', 'valid'], ['test'])
@@ -111,3 +115,101 @@ class COCOToDMConverter(ToDMConverter):
111
115
  dm_json = {'images': [dm_img]}
112
116
  result[img_filename] = (dm_json, img_path)
113
117
  return result
118
+
119
+ def convert_single_file(self, data: Dict[str, Any], original_file: IO) -> Dict[str, Any]:
120
+ """Convert a single COCO annotation data and corresponding image to DM format.
121
+
122
+ Args:
123
+ data: COCO format data dictionary (JSON content)
124
+ original_file: File object for the corresponding original image
125
+
126
+ Returns:
127
+ Dictionary containing DM format data for the single file
128
+ """
129
+ if not self.is_single_conversion:
130
+ raise RuntimeError('convert_single_file is only available when is_single_conversion=True')
131
+
132
+ images = data.get('images', [])
133
+ annotations = data.get('annotations', [])
134
+ categories = data.get('categories', [])
135
+
136
+ if not images:
137
+ raise ValueError('No images found in COCO data')
138
+
139
+ # Get file name from original_file
140
+ img_path = getattr(original_file, 'name', None)
141
+ if not img_path:
142
+ raise ValueError('original_file must have a "name" attribute representing its path or filename.')
143
+ img_basename = os.path.basename(img_path)
144
+
145
+ # Find the matching image info in COCO 'images' section by comparing file name
146
+ # COCO image dicts might use 'file_name', 'filename', or similar
147
+ matched_img = None
148
+ for img in images:
149
+ for key in ['file_name', 'filename', 'name']:
150
+ if key in img and os.path.basename(img[key]) == img_basename:
151
+ matched_img = img
152
+ break
153
+ if matched_img:
154
+ break
155
+
156
+ if not matched_img:
157
+ raise ValueError(f'No matching image found in COCO data for file: {img_basename}')
158
+
159
+ img_id = matched_img['id']
160
+ print('img_id : ', img_id)
161
+ cat_map = {cat['id']: cat for cat in categories}
162
+ anns = [ann for ann in annotations if ann['image_id'] == img_id]
163
+
164
+ dm_img = {
165
+ 'bounding_box': [],
166
+ 'keypoint': [],
167
+ 'relation': [],
168
+ 'group': [],
169
+ }
170
+
171
+ bbox_ids = []
172
+ for ann in anns:
173
+ cat = cat_map.get(ann['category_id'], {})
174
+ if 'bbox' in ann and ann['bbox']:
175
+ bbox_id = self._generate_unique_id()
176
+ bbox_ids.append(bbox_id)
177
+ dm_img['bounding_box'].append({
178
+ 'id': bbox_id,
179
+ 'classification': cat.get('name', str(ann['category_id'])),
180
+ 'attrs': ann.get('attrs', []),
181
+ 'data': list(ann['bbox']),
182
+ })
183
+
184
+ for ann in anns:
185
+ cat = cat_map.get(ann['category_id'], {})
186
+ attrs = ann.get('attrs', [])
187
+ if 'keypoints' in ann and ann['keypoints']:
188
+ kp_names = cat.get('keypoints', [])
189
+ kps = ann['keypoints']
190
+ keypoint_ids = []
191
+ for idx in range(min(len(kps) // 3, len(kp_names))):
192
+ x, y, _ = kps[idx * 3 : idx * 3 + 3]
193
+ kp_id = self._generate_unique_id()
194
+ keypoint_ids.append(kp_id)
195
+ dm_img['keypoint'].append({
196
+ 'id': kp_id,
197
+ 'classification': kp_names[idx] if idx < len(kp_names) else f'keypoint_{idx}',
198
+ 'attrs': attrs,
199
+ 'data': [x, y],
200
+ })
201
+ group_ids = bbox_ids + keypoint_ids
202
+ if group_ids:
203
+ dm_img['group'].append({
204
+ 'id': self._generate_unique_id(),
205
+ 'classification': cat.get('name', str(ann['category_id'])),
206
+ 'attrs': attrs,
207
+ 'data': group_ids,
208
+ })
209
+
210
+ dm_json = {'images': [dm_img]}
211
+ return {
212
+ 'dm_json': dm_json,
213
+ 'image_path': img_path,
214
+ 'image_name': img_basename,
215
+ }
@@ -3,7 +3,7 @@ import os
3
3
  import shutil
4
4
  import xml.etree.ElementTree as ET
5
5
  from glob import glob
6
- from typing import Any, List, Optional
6
+ from typing import Any, Dict, IO, List, Optional
7
7
 
8
8
  from PIL import Image
9
9
 
@@ -15,8 +15,8 @@ class FromDMToPascalConverter(FromDMConverter):
15
15
 
16
16
  IMG_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.bmp']
17
17
 
18
- def __init__(self, root_dir: str, is_categorized_dataset: bool = False):
19
- super().__init__(root_dir, is_categorized_dataset)
18
+ def __init__(self, root_dir: str = None, is_categorized_dataset: bool = False, is_single_conversion: bool = False):
19
+ super().__init__(root_dir, is_categorized_dataset, is_single_conversion)
20
20
  self.class_names = set()
21
21
 
22
22
  def find_image_for_base(self, img_dir: str, base: str) -> Optional[str]:
@@ -175,3 +175,48 @@ class FromDMToPascalConverter(FromDMConverter):
175
175
  for c in sorted(self.class_names):
176
176
  f.write(f'{c}\n')
177
177
  print(f'Pascal VOC data exported to {outdir}')
178
+
179
+ def convert_single_file(self, data: Dict[str, Any], original_file: IO) -> Dict[str, Any]:
180
+ """Convert a single DM data dict and corresponding image file object to Pascal VOC format.
181
+
182
+ Args:
183
+ data: DM format data dictionary (JSON content)
184
+ original_file: File object for the corresponding original image
185
+
186
+ Returns:
187
+ Dictionary containing Pascal VOC format data for the single file
188
+ """
189
+ if not self.is_single_conversion:
190
+ raise RuntimeError('convert_single_file is only available when is_single_conversion=True')
191
+
192
+ # Extract image info from file object
193
+ with Image.open(original_file) as img:
194
+ width, height = img.size
195
+ depth = len(img.getbands())
196
+
197
+ # Get filename from original_file
198
+ img_filename = getattr(original_file, 'name', 'image.jpg')
199
+ if img_filename:
200
+ img_filename = os.path.basename(img_filename)
201
+
202
+ # Process annotations from the first (and only) image in data
203
+ if 'images' in data and len(data['images']) > 0:
204
+ img_ann = data['images'][0]
205
+ objects = self.parse_dm_annotations(img_ann)
206
+ else:
207
+ objects = []
208
+
209
+ # Build Pascal VOC XML
210
+ xml_tree = self.build_pascal_xml(img_filename, (width, height, depth), objects)
211
+ xml_filename = os.path.splitext(img_filename)[0] + '.xml'
212
+
213
+ # Convert XML tree to string for easy viewing
214
+ xml_string = ET.tostring(xml_tree.getroot(), encoding='unicode', xml_declaration=True)
215
+
216
+ return {
217
+ 'xml_tree': xml_tree,
218
+ 'xml_content': xml_string,
219
+ 'xml_filename': xml_filename,
220
+ 'image_filename': img_filename,
221
+ 'class_names': sorted(list(self.class_names)),
222
+ }
@@ -1,6 +1,6 @@
1
1
  import os
2
2
  import xml.etree.ElementTree as ET
3
- from typing import Any, Dict, List, Optional, Tuple
3
+ from typing import Any, Dict, IO, List, Optional, Tuple
4
4
 
5
5
  from PIL import Image
6
6
 
@@ -12,6 +12,9 @@ class PascalToDMConverter(ToDMConverter):
12
12
 
13
13
  IMG_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.bmp']
14
14
 
15
+ def __init__(self, root_dir: str = None, is_categorized_dataset: bool = False, is_single_conversion: bool = False):
16
+ super().__init__(root_dir, is_categorized_dataset, is_single_conversion)
17
+
15
18
  def convert(self):
16
19
  """Convert the Pascal VOC dataset to DM format."""
17
20
  if self.is_categorized_dataset:
@@ -133,3 +136,79 @@ class PascalToDMConverter(ToDMConverter):
133
136
  print(f'[WARNING] Error processing {xml_filename}: {e}, skipping.')
134
137
  continue
135
138
  return result
139
+
140
+ def convert_single_file(self, data: str, original_file: IO) -> Dict[str, Any]:
141
+ """Convert a single Pascal VOC XML data and corresponding image to DM format.
142
+
143
+ Args:
144
+ data: Pascal VOC XML content as string
145
+ original_file: File object for the corresponding original image
146
+
147
+ Returns:
148
+ Dictionary containing DM format data for the single file
149
+ """
150
+ if not self.is_single_conversion:
151
+ raise RuntimeError('convert_single_file is only available when is_single_conversion=True')
152
+
153
+ # Get filename from original_file
154
+ img_path = getattr(original_file, 'name', None)
155
+ if not img_path:
156
+ raise ValueError('original_file must have a "name" attribute representing its path or filename.')
157
+
158
+ img_filename = os.path.basename(img_path)
159
+
160
+ # Parse XML data from string
161
+ try:
162
+ root = ET.fromstring(data)
163
+ except ET.ParseError as e:
164
+ raise ValueError(f'Failed to parse Pascal VOC XML data: {e}')
165
+
166
+ # Extract objects from XML
167
+ objects = []
168
+ for obj in root.findall('object'):
169
+ name_elem = obj.find('name')
170
+ bndbox_elem = obj.find('bndbox')
171
+ if name_elem is None or bndbox_elem is None:
172
+ continue
173
+
174
+ class_name = name_elem.text
175
+ xmin_elem = bndbox_elem.find('xmin')
176
+ ymin_elem = bndbox_elem.find('ymin')
177
+ xmax_elem = bndbox_elem.find('xmax')
178
+ ymax_elem = bndbox_elem.find('ymax')
179
+
180
+ if any(elem is None for elem in [xmin_elem, ymin_elem, xmax_elem, ymax_elem]):
181
+ continue
182
+
183
+ xmin = int(float(xmin_elem.text))
184
+ ymin = int(float(ymin_elem.text))
185
+ xmax = int(float(xmax_elem.text))
186
+ ymax = int(float(ymax_elem.text))
187
+ width = xmax - xmin
188
+ height = ymax - ymin
189
+
190
+ objects.append({'classification': class_name, 'data': [xmin, ymin, width, height]})
191
+
192
+ # Prepare DM annotation structure
193
+ dm_img = {
194
+ 'bounding_box': [],
195
+ 'polygon': [],
196
+ 'keypoint': [],
197
+ 'relation': [],
198
+ 'group': [],
199
+ }
200
+
201
+ for obj in objects:
202
+ dm_img['bounding_box'].append({
203
+ 'id': self._generate_unique_id(),
204
+ 'classification': obj['classification'],
205
+ 'attrs': [],
206
+ 'data': obj['data'],
207
+ })
208
+
209
+ dm_json = {'images': [dm_img]}
210
+ return {
211
+ 'dm_json': dm_json,
212
+ 'image_path': img_path,
213
+ 'image_name': img_filename,
214
+ }
@@ -2,7 +2,7 @@ import json
2
2
  import os
3
3
  import shutil
4
4
  from glob import glob
5
- from typing import Any, Dict, List, Optional, Union
5
+ from typing import IO, Any, Dict, List, Optional, Union
6
6
 
7
7
  from PIL import Image
8
8
 
@@ -14,8 +14,8 @@ class FromDMToYOLOConverter(FromDMConverter):
14
14
 
15
15
  IMG_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.bmp']
16
16
 
17
- def __init__(self, root_dir: str, is_categorized_dataset: bool = False):
18
- super().__init__(root_dir, is_categorized_dataset)
17
+ def __init__(self, root_dir: str = None, is_categorized_dataset: bool = False, is_single_conversion: bool = False):
18
+ super().__init__(root_dir, is_categorized_dataset, is_single_conversion)
19
19
  self.class_names: List[str] = []
20
20
  self.class_map: Dict[str, int] = {}
21
21
  self.dataset_yaml_content: str = ''
@@ -300,3 +300,85 @@ class FromDMToYOLOConverter(FromDMConverter):
300
300
  for c in self.class_names:
301
301
  f.write(f'{c}\n')
302
302
  print(f'YOLO data exported to {output_dir}')
303
+
304
+ def convert_single_file(
305
+ self, data: Dict[str, Any], original_file: IO, class_names: Optional[List[str]] = None
306
+ ) -> Dict[str, Any]:
307
+ """Convert a single DM data dict and corresponding image file object to YOLO format.
308
+
309
+ Args:
310
+ data: DM format data dictionary (JSON content)
311
+ original_file: File object for the corresponding original image
312
+ class_names: Optional list of class names. If not provided, classes will be extracted from data.
313
+
314
+ Returns:
315
+ Dictionary containing YOLO format data for the single file
316
+ """
317
+ if not self.is_single_conversion:
318
+ raise RuntimeError('convert_single_file is only available when is_single_conversion=True')
319
+
320
+ if class_names is None:
321
+ classes = set()
322
+ for img_ann in data['images']:
323
+ for k in ['bounding_box', 'polygon', 'keypoint']:
324
+ if k in img_ann:
325
+ for ann in img_ann[k]:
326
+ classes.add(ann['classification'])
327
+ class_names = sorted(list(classes))
328
+
329
+ class_map = {name: idx for idx, name in enumerate(class_names)}
330
+ # You need to update get_image_size to accept a file object
331
+ width, height = self.get_image_size(original_file)
332
+
333
+ img_ann = data['images'][0]
334
+ label_lines = []
335
+
336
+ # bbox
337
+ if 'bounding_box' in img_ann:
338
+ for box in img_ann['bounding_box']:
339
+ if box['classification'] not in class_map:
340
+ continue
341
+ cidx = class_map[box['classification']]
342
+ x, y, w, h = box['data']
343
+ cx = x + w / 2
344
+ cy = y + h / 2
345
+ cx /= width
346
+ cy /= height
347
+ w /= width
348
+ h /= height
349
+ label_lines.append(f'{cidx} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}')
350
+
351
+ # polygon
352
+ if 'polygon' in img_ann:
353
+ for poly in img_ann['polygon']:
354
+ if poly['classification'] not in class_map:
355
+ continue
356
+ cidx = class_map[poly['classification']]
357
+ poly_str = self.polygon_to_yolo_string(poly['data'], width, height)
358
+ if poly_str:
359
+ label_lines.append(f'{cidx} {poly_str}')
360
+
361
+ # keypoint
362
+ if 'keypoint' in img_ann:
363
+ for kp in img_ann['keypoint']:
364
+ if kp['classification'] not in class_map:
365
+ continue
366
+ cidx = class_map[kp['classification']]
367
+ if 'bounding_box' in kp:
368
+ x, y, w, h = kp['bounding_box']
369
+ cx = x + w / 2
370
+ cy = y + h / 2
371
+ cx /= width
372
+ cy /= height
373
+ w /= width
374
+ h /= height
375
+ else:
376
+ cx, cy, w, h = 0.5, 0.5, 1.0, 1.0
377
+ kp_str = self.keypoints_to_yolo_string(kp['data'], width, height)
378
+ label_lines.append(f'{cidx} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f} {kp_str}')
379
+
380
+ return {
381
+ 'label_lines': label_lines,
382
+ 'class_names': class_names,
383
+ 'class_map': class_map,
384
+ }
@@ -1,5 +1,5 @@
1
1
  import os
2
- from typing import Any, Dict, List, Tuple
2
+ from typing import IO, Any, Dict, List, Tuple
3
3
 
4
4
  import yaml
5
5
  from PIL import Image
@@ -12,6 +12,9 @@ class YOLOToDMConverter(ToDMConverter):
12
12
 
13
13
  IMG_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.bmp']
14
14
 
15
+ def __init__(self, root_dir: str = None, is_categorized_dataset: bool = False, is_single_conversion: bool = False):
16
+ super().__init__(root_dir, is_categorized_dataset, is_single_conversion)
17
+
15
18
  def convert(self):
16
19
  """Convert YOLO dataset to DM format."""
17
20
  if self.is_categorized_dataset:
@@ -183,3 +186,73 @@ class YOLOToDMConverter(ToDMConverter):
183
186
  dm_json = {'images': [dm_img]}
184
187
  result[os.path.basename(img_path)] = (dm_json, img_path)
185
188
  return result
189
+
190
+ def convert_single_file(self, data: List[str], original_file: IO, class_names: List[str]) -> Dict[str, Any]:
191
+ """Convert a single YOLO label data and corresponding image to DM format.
192
+
193
+ Args:
194
+ data: List of YOLO label lines (strings from .txt file content)
195
+ original_file: File object for the corresponding original image
196
+ class_names: List of class names corresponding to indices in the label data
197
+
198
+ Returns:
199
+ Dictionary containing DM format data for the single file
200
+ """
201
+ if not self.is_single_conversion:
202
+ raise RuntimeError('convert_single_file is only available when is_single_conversion=True')
203
+
204
+ img_path = getattr(original_file, 'name', None)
205
+ if not img_path:
206
+ raise ValueError('original_file must have a "name" attribute representing its path or filename.')
207
+
208
+ if hasattr(self, '_get_image_size'):
209
+ img_size = self._get_image_size(original_file)
210
+ else:
211
+ raise AttributeError('Converter missing _get_image_size method for file objects.')
212
+
213
+ # data is already a list of label lines
214
+ label_lines = [line.strip() for line in data if line.strip()]
215
+
216
+ # Prepare DM annotation structure
217
+ dm_img = {
218
+ 'bounding_box': [],
219
+ 'polygon': [],
220
+ 'keypoint': [],
221
+ 'relation': [],
222
+ 'group': [],
223
+ }
224
+
225
+ for line in label_lines:
226
+ ann = self._parse_yolo_line(line, class_names, img_size)
227
+ if ann is None:
228
+ continue
229
+
230
+ if ann['type'] == 'bounding_box':
231
+ dm_img['bounding_box'].append({
232
+ 'id': self._generate_unique_id(),
233
+ 'classification': ann['classification'],
234
+ 'attrs': [],
235
+ 'data': ann['data'],
236
+ })
237
+ elif ann['type'] == 'polygon':
238
+ dm_img['polygon'].append({
239
+ 'id': self._generate_unique_id(),
240
+ 'classification': ann['classification'],
241
+ 'attrs': [],
242
+ 'data': ann['data'],
243
+ })
244
+ elif ann['type'] == 'keypoint':
245
+ dm_img['keypoint'].append({
246
+ 'id': self._generate_unique_id(),
247
+ 'classification': ann['classification'],
248
+ 'attrs': [],
249
+ 'data': ann['data'],
250
+ 'bounding_box': ann['bounding_box'],
251
+ })
252
+
253
+ dm_json = {'images': [dm_img]}
254
+ return {
255
+ 'dm_json': dm_json,
256
+ 'image_path': img_path,
257
+ 'image_name': os.path.basename(img_path),
258
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: synapse-sdk
3
- Version: 1.0.0a78
3
+ Version: 1.0.0a80
4
4
  Summary: synapse sdk
5
5
  Author-email: datamaker <developer@datamaker.io>
6
6
  License: MIT
@@ -196,19 +196,19 @@ synapse_sdk/utils/http.py,sha256=yRxYfru8tMnBVeBK-7S0Ga13yOf8oRHquG5e8K_FWcI,475
196
196
  synapse_sdk/utils/module_loading.py,sha256=chHpU-BZjtYaTBD_q0T7LcKWtqKvYBS4L0lPlKkoMQ8,1020
197
197
  synapse_sdk/utils/network.py,sha256=WI8qn6KlKpHdMi45V57ofKJB8zusJrbQsxT74LwVfsY,1000
198
198
  synapse_sdk/utils/string.py,sha256=rEwuZ9SAaZLcQ8TYiwNKr1h2u4CfnrQx7SUL8NWmChg,216
199
- synapse_sdk/utils/converters/__init__.py,sha256=RysXR5z_hOZBSYuqV0w-MACMY2-HupTqAYdw1nuNW_Q,10053
199
+ synapse_sdk/utils/converters/__init__.py,sha256=xQi_n7xS9BNyDiolsxH2jw1CtD6avxMPj2cHnwvidi8,11311
200
200
  synapse_sdk/utils/converters/coco/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
201
- synapse_sdk/utils/converters/coco/from_dm.py,sha256=78aJ_O2_hmkQQ96nrjHY38roETWfBZB8GRHDn7qKEls,9626
202
- synapse_sdk/utils/converters/coco/to_dm.py,sha256=Ve8LrcKVlzNysam3fidcgP5fdm0_UGbBgSPoj2dT_JA,4906
201
+ synapse_sdk/utils/converters/coco/from_dm.py,sha256=B9zvb8Kph9haYLVIZhzneWiHCImFbuWqAaE7g6Nk0lI,11365
202
+ synapse_sdk/utils/converters/coco/to_dm.py,sha256=YmD_NHKSUL8RZbzWX52FgDaJG0uX4I8f8Omp7ilhSec,9054
203
203
  synapse_sdk/utils/converters/dm/__init__.py,sha256=cHvpr4ljImnqeMvi5sevQiBD-R9ZHBWOpoKUUCiaGHE,3333
204
204
  synapse_sdk/utils/converters/dm/from_v1.py,sha256=wlFwGRD21nTAk0TVwFDxb-2w9Q9eQWJ1MBo-7CQcgKQ,16572
205
205
  synapse_sdk/utils/converters/dm/to_v1.py,sha256=jHeQoopht1lyPUsLK8T0xYK6aHVVYvS6JHiesSaPPk0,9430
206
206
  synapse_sdk/utils/converters/pascal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
207
- synapse_sdk/utils/converters/pascal/from_dm.py,sha256=PvP70jvzCbUKJg49BikgSpc5qb7nvVvfDDFmf1oPJWc,8497
208
- synapse_sdk/utils/converters/pascal/to_dm.py,sha256=RBMlVOdc7ev7jEWj7eyoNoO_1cpdhrJ7LhJqaboxBS4,5751
207
+ synapse_sdk/utils/converters/pascal/from_dm.py,sha256=AKOeQoyeSbSOawf-ya9dLx-pZP_MomNcDaCW5ka5_8Y,10378
208
+ synapse_sdk/utils/converters/pascal/to_dm.py,sha256=JkA_OI_IR1ealZPe2uo4hFBcFyOh_VfeyIY43-R4IBA,8614
209
209
  synapse_sdk/utils/converters/yolo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
210
- synapse_sdk/utils/converters/yolo/from_dm.py,sha256=ppvIt4uceLlkBE7ybc0xhS_C6pK9knso-qWEjeqDMXM,12858
211
- synapse_sdk/utils/converters/yolo/to_dm.py,sha256=2llBYSEMwvvnECcb1nn7p_UXg6Krr3Za5RicVkYRlGI,7531
210
+ synapse_sdk/utils/converters/yolo/from_dm.py,sha256=-JDCQLk4g1_FIVoOwZ1Tcs2kWFkhXRCAPVLKLXz6sLU,16180
211
+ synapse_sdk/utils/converters/yolo/to_dm.py,sha256=UUGTbBNeG5Ao8PSJbizrZRQJfeAMuSDfZs8SGOyr-YU,10464
212
212
  synapse_sdk/utils/pydantic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
213
213
  synapse_sdk/utils/pydantic/config.py,sha256=1vYOcUI35GslfD1rrqhFkNXXJOXt4IDqOPSx9VWGfNE,123
214
214
  synapse_sdk/utils/pydantic/errors.py,sha256=0v0T12eQBr1KrFiEOBu6KMaPK4aPEGEC6etPJGoR5b4,1061
@@ -220,9 +220,9 @@ synapse_sdk/utils/storage/providers/gcp.py,sha256=i2BQCu1Kej1If9SuNr2_lEyTcr5M_n
220
220
  synapse_sdk/utils/storage/providers/http.py,sha256=2DhIulND47JOnS5ZY7MZUex7Su3peAPksGo1Wwg07L4,5828
221
221
  synapse_sdk/utils/storage/providers/s3.py,sha256=ZmqekAvIgcQBdRU-QVJYv1Rlp6VHfXwtbtjTSphua94,2573
222
222
  synapse_sdk/utils/storage/providers/sftp.py,sha256=_8s9hf0JXIO21gvm-JVS00FbLsbtvly4c-ETLRax68A,1426
223
- synapse_sdk-1.0.0a78.dist-info/licenses/LICENSE,sha256=bKzmC5YAg4V1Fhl8OO_tqY8j62hgdncAkN7VrdjmrGk,1101
224
- synapse_sdk-1.0.0a78.dist-info/METADATA,sha256=KL3WRv3GLuhK80ziNNWrs9rWol5amERBImcbwKZQER8,3805
225
- synapse_sdk-1.0.0a78.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
226
- synapse_sdk-1.0.0a78.dist-info/entry_points.txt,sha256=VNptJoGoNJI8yLXfBmhgUefMsmGI0m3-0YoMvrOgbxo,48
227
- synapse_sdk-1.0.0a78.dist-info/top_level.txt,sha256=ytgJMRK1slVOKUpgcw3LEyHHP7S34J6n_gJzdkcSsw8,12
228
- synapse_sdk-1.0.0a78.dist-info/RECORD,,
223
+ synapse_sdk-1.0.0a80.dist-info/licenses/LICENSE,sha256=bKzmC5YAg4V1Fhl8OO_tqY8j62hgdncAkN7VrdjmrGk,1101
224
+ synapse_sdk-1.0.0a80.dist-info/METADATA,sha256=Z9-Wpg7L2jCgXLMrp_BaJ2Hjlg_IQMBCBOBcpLZZBnQ,3805
225
+ synapse_sdk-1.0.0a80.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
226
+ synapse_sdk-1.0.0a80.dist-info/entry_points.txt,sha256=VNptJoGoNJI8yLXfBmhgUefMsmGI0m3-0YoMvrOgbxo,48
227
+ synapse_sdk-1.0.0a80.dist-info/top_level.txt,sha256=ytgJMRK1slVOKUpgcw3LEyHHP7S34J6n_gJzdkcSsw8,12
228
+ synapse_sdk-1.0.0a80.dist-info/RECORD,,