synapse-sdk 1.0.0a11__py3-none-any.whl → 2026.1.1b2__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.
- synapse_sdk/__init__.py +24 -0
- synapse_sdk/cli/__init__.py +9 -8
- synapse_sdk/cli/agent/__init__.py +25 -0
- synapse_sdk/cli/agent/config.py +104 -0
- synapse_sdk/cli/agent/select.py +197 -0
- synapse_sdk/cli/auth.py +104 -0
- synapse_sdk/cli/main.py +1025 -0
- synapse_sdk/cli/plugin/__init__.py +58 -0
- synapse_sdk/cli/plugin/create.py +566 -0
- synapse_sdk/cli/plugin/job.py +196 -0
- synapse_sdk/cli/plugin/publish.py +322 -0
- synapse_sdk/cli/plugin/run.py +131 -0
- synapse_sdk/cli/plugin/test.py +200 -0
- synapse_sdk/clients/README.md +239 -0
- synapse_sdk/clients/__init__.py +5 -0
- synapse_sdk/clients/_template.py +266 -0
- synapse_sdk/clients/agent/__init__.py +84 -29
- synapse_sdk/clients/agent/async_ray.py +289 -0
- synapse_sdk/clients/agent/container.py +83 -0
- synapse_sdk/clients/agent/plugin.py +101 -0
- synapse_sdk/clients/agent/ray.py +296 -39
- synapse_sdk/clients/backend/__init__.py +152 -12
- synapse_sdk/clients/backend/annotation.py +164 -22
- synapse_sdk/clients/backend/core.py +101 -0
- synapse_sdk/clients/backend/data_collection.py +292 -0
- synapse_sdk/clients/backend/hitl.py +87 -0
- synapse_sdk/clients/backend/integration.py +374 -46
- synapse_sdk/clients/backend/ml.py +134 -22
- synapse_sdk/clients/backend/models.py +247 -0
- synapse_sdk/clients/base.py +538 -59
- synapse_sdk/clients/exceptions.py +35 -7
- synapse_sdk/clients/pipeline/__init__.py +5 -0
- synapse_sdk/clients/pipeline/client.py +636 -0
- synapse_sdk/clients/protocols.py +178 -0
- synapse_sdk/clients/utils.py +86 -8
- synapse_sdk/clients/validation.py +58 -0
- synapse_sdk/enums.py +76 -0
- synapse_sdk/exceptions.py +168 -0
- synapse_sdk/integrations/__init__.py +74 -0
- synapse_sdk/integrations/_base.py +119 -0
- synapse_sdk/integrations/_context.py +53 -0
- synapse_sdk/integrations/ultralytics/__init__.py +78 -0
- synapse_sdk/integrations/ultralytics/_callbacks.py +126 -0
- synapse_sdk/integrations/ultralytics/_patches.py +124 -0
- synapse_sdk/loggers.py +476 -95
- synapse_sdk/mcp/MCP.md +69 -0
- synapse_sdk/mcp/__init__.py +48 -0
- synapse_sdk/mcp/__main__.py +6 -0
- synapse_sdk/mcp/config.py +349 -0
- synapse_sdk/mcp/prompts/__init__.py +4 -0
- synapse_sdk/mcp/resources/__init__.py +4 -0
- synapse_sdk/mcp/server.py +1352 -0
- synapse_sdk/mcp/tools/__init__.py +6 -0
- synapse_sdk/plugins/__init__.py +133 -9
- synapse_sdk/plugins/action.py +229 -0
- synapse_sdk/plugins/actions/__init__.py +82 -0
- synapse_sdk/plugins/actions/dataset/__init__.py +37 -0
- synapse_sdk/plugins/actions/dataset/action.py +471 -0
- synapse_sdk/plugins/actions/export/__init__.py +55 -0
- synapse_sdk/plugins/actions/export/action.py +183 -0
- synapse_sdk/plugins/actions/export/context.py +59 -0
- synapse_sdk/plugins/actions/inference/__init__.py +84 -0
- synapse_sdk/plugins/actions/inference/action.py +285 -0
- synapse_sdk/plugins/actions/inference/context.py +81 -0
- synapse_sdk/plugins/actions/inference/deployment.py +322 -0
- synapse_sdk/plugins/actions/inference/serve.py +252 -0
- synapse_sdk/plugins/actions/train/__init__.py +54 -0
- synapse_sdk/plugins/actions/train/action.py +326 -0
- synapse_sdk/plugins/actions/train/context.py +57 -0
- synapse_sdk/plugins/actions/upload/__init__.py +49 -0
- synapse_sdk/plugins/actions/upload/action.py +165 -0
- synapse_sdk/plugins/actions/upload/context.py +61 -0
- synapse_sdk/plugins/config.py +98 -0
- synapse_sdk/plugins/context/__init__.py +109 -0
- synapse_sdk/plugins/context/env.py +113 -0
- synapse_sdk/plugins/datasets/__init__.py +113 -0
- synapse_sdk/plugins/datasets/converters/__init__.py +76 -0
- synapse_sdk/plugins/datasets/converters/base.py +347 -0
- synapse_sdk/plugins/datasets/converters/yolo/__init__.py +9 -0
- synapse_sdk/plugins/datasets/converters/yolo/from_dm.py +468 -0
- synapse_sdk/plugins/datasets/converters/yolo/to_dm.py +381 -0
- synapse_sdk/plugins/datasets/formats/__init__.py +82 -0
- synapse_sdk/plugins/datasets/formats/dm.py +351 -0
- synapse_sdk/plugins/datasets/formats/yolo.py +240 -0
- synapse_sdk/plugins/decorators.py +83 -0
- synapse_sdk/plugins/discovery.py +790 -0
- synapse_sdk/plugins/docs/ACTION_DEV_GUIDE.md +933 -0
- synapse_sdk/plugins/docs/ARCHITECTURE.md +1225 -0
- synapse_sdk/plugins/docs/LOGGING_SYSTEM.md +683 -0
- synapse_sdk/plugins/docs/OVERVIEW.md +531 -0
- synapse_sdk/plugins/docs/PIPELINE_GUIDE.md +145 -0
- synapse_sdk/plugins/docs/README.md +513 -0
- synapse_sdk/plugins/docs/STEP.md +656 -0
- synapse_sdk/plugins/enums.py +70 -10
- synapse_sdk/plugins/errors.py +92 -0
- synapse_sdk/plugins/executors/__init__.py +43 -0
- synapse_sdk/plugins/executors/local.py +99 -0
- synapse_sdk/plugins/executors/ray/__init__.py +18 -0
- synapse_sdk/plugins/executors/ray/base.py +282 -0
- synapse_sdk/plugins/executors/ray/job.py +298 -0
- synapse_sdk/plugins/executors/ray/jobs_api.py +511 -0
- synapse_sdk/plugins/executors/ray/packaging.py +137 -0
- synapse_sdk/plugins/executors/ray/pipeline.py +792 -0
- synapse_sdk/plugins/executors/ray/task.py +257 -0
- synapse_sdk/plugins/models/__init__.py +26 -0
- synapse_sdk/plugins/models/logger.py +173 -0
- synapse_sdk/plugins/models/pipeline.py +25 -0
- synapse_sdk/plugins/pipelines/__init__.py +81 -0
- synapse_sdk/plugins/pipelines/action_pipeline.py +417 -0
- synapse_sdk/plugins/pipelines/context.py +107 -0
- synapse_sdk/plugins/pipelines/display.py +311 -0
- synapse_sdk/plugins/runner.py +114 -0
- synapse_sdk/plugins/schemas/__init__.py +19 -0
- synapse_sdk/plugins/schemas/results.py +152 -0
- synapse_sdk/plugins/steps/__init__.py +63 -0
- synapse_sdk/plugins/steps/base.py +128 -0
- synapse_sdk/plugins/steps/context.py +90 -0
- synapse_sdk/plugins/steps/orchestrator.py +128 -0
- synapse_sdk/plugins/steps/registry.py +103 -0
- synapse_sdk/plugins/steps/utils/__init__.py +20 -0
- synapse_sdk/plugins/steps/utils/logging.py +85 -0
- synapse_sdk/plugins/steps/utils/timing.py +71 -0
- synapse_sdk/plugins/steps/utils/validation.py +68 -0
- synapse_sdk/plugins/templates/__init__.py +50 -0
- synapse_sdk/plugins/templates/base/.gitignore.j2 +26 -0
- synapse_sdk/plugins/templates/base/.synapseignore.j2 +11 -0
- synapse_sdk/plugins/templates/base/README.md.j2 +26 -0
- synapse_sdk/plugins/templates/base/plugin/__init__.py.j2 +1 -0
- synapse_sdk/plugins/templates/base/pyproject.toml.j2 +14 -0
- synapse_sdk/plugins/templates/base/requirements.txt.j2 +1 -0
- synapse_sdk/plugins/templates/custom/plugin/main.py.j2 +18 -0
- synapse_sdk/plugins/templates/data_validation/plugin/validate.py.j2 +32 -0
- synapse_sdk/plugins/templates/export/plugin/export.py.j2 +36 -0
- synapse_sdk/plugins/templates/neural_net/plugin/inference.py.j2 +36 -0
- synapse_sdk/plugins/templates/neural_net/plugin/train.py.j2 +33 -0
- synapse_sdk/plugins/templates/post_annotation/plugin/post_annotate.py.j2 +32 -0
- synapse_sdk/plugins/templates/pre_annotation/plugin/pre_annotate.py.j2 +32 -0
- synapse_sdk/plugins/templates/smart_tool/plugin/auto_label.py.j2 +44 -0
- synapse_sdk/plugins/templates/upload/plugin/upload.py.j2 +35 -0
- synapse_sdk/plugins/testing/__init__.py +25 -0
- synapse_sdk/plugins/testing/sample_actions.py +98 -0
- synapse_sdk/plugins/types.py +206 -0
- synapse_sdk/plugins/upload.py +595 -64
- synapse_sdk/plugins/utils.py +325 -37
- synapse_sdk/shared/__init__.py +25 -0
- synapse_sdk/utils/__init__.py +1 -0
- synapse_sdk/utils/auth.py +74 -0
- synapse_sdk/utils/file/__init__.py +58 -0
- synapse_sdk/utils/file/archive.py +449 -0
- synapse_sdk/utils/file/checksum.py +167 -0
- synapse_sdk/utils/file/download.py +286 -0
- synapse_sdk/utils/file/io.py +129 -0
- synapse_sdk/utils/file/requirements.py +36 -0
- synapse_sdk/utils/network.py +168 -0
- synapse_sdk/utils/storage/__init__.py +238 -0
- synapse_sdk/utils/storage/config.py +188 -0
- synapse_sdk/utils/storage/errors.py +52 -0
- synapse_sdk/utils/storage/providers/__init__.py +13 -0
- synapse_sdk/utils/storage/providers/base.py +76 -0
- synapse_sdk/utils/storage/providers/gcs.py +168 -0
- synapse_sdk/utils/storage/providers/http.py +250 -0
- synapse_sdk/utils/storage/providers/local.py +126 -0
- synapse_sdk/utils/storage/providers/s3.py +177 -0
- synapse_sdk/utils/storage/providers/sftp.py +208 -0
- synapse_sdk/utils/storage/registry.py +125 -0
- synapse_sdk/utils/websocket.py +99 -0
- synapse_sdk-2026.1.1b2.dist-info/METADATA +715 -0
- synapse_sdk-2026.1.1b2.dist-info/RECORD +172 -0
- {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/WHEEL +1 -1
- synapse_sdk-2026.1.1b2.dist-info/licenses/LICENSE +201 -0
- locale/en/LC_MESSAGES/messages.mo +0 -0
- locale/en/LC_MESSAGES/messages.po +0 -39
- locale/ko/LC_MESSAGES/messages.mo +0 -0
- locale/ko/LC_MESSAGES/messages.po +0 -34
- synapse_sdk/cli/create_plugin.py +0 -10
- synapse_sdk/clients/agent/core.py +0 -7
- synapse_sdk/clients/agent/service.py +0 -15
- synapse_sdk/clients/backend/dataset.py +0 -51
- synapse_sdk/clients/ray/__init__.py +0 -6
- synapse_sdk/clients/ray/core.py +0 -22
- synapse_sdk/clients/ray/serve.py +0 -20
- synapse_sdk/i18n.py +0 -35
- synapse_sdk/plugins/categories/__init__.py +0 -0
- synapse_sdk/plugins/categories/base.py +0 -235
- synapse_sdk/plugins/categories/data_validation/__init__.py +0 -0
- synapse_sdk/plugins/categories/data_validation/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/data_validation/actions/validation.py +0 -10
- synapse_sdk/plugins/categories/data_validation/templates/config.yaml +0 -3
- synapse_sdk/plugins/categories/data_validation/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/data_validation/templates/plugin/validation.py +0 -5
- synapse_sdk/plugins/categories/decorators.py +0 -13
- synapse_sdk/plugins/categories/export/__init__.py +0 -0
- synapse_sdk/plugins/categories/export/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/export/actions/export.py +0 -10
- synapse_sdk/plugins/categories/import/__init__.py +0 -0
- synapse_sdk/plugins/categories/import/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/import/actions/import.py +0 -10
- synapse_sdk/plugins/categories/neural_net/__init__.py +0 -0
- synapse_sdk/plugins/categories/neural_net/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/neural_net/actions/deployment.py +0 -45
- synapse_sdk/plugins/categories/neural_net/actions/inference.py +0 -18
- synapse_sdk/plugins/categories/neural_net/actions/test.py +0 -10
- synapse_sdk/plugins/categories/neural_net/actions/train.py +0 -143
- synapse_sdk/plugins/categories/neural_net/templates/config.yaml +0 -12
- synapse_sdk/plugins/categories/neural_net/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/neural_net/templates/plugin/inference.py +0 -4
- synapse_sdk/plugins/categories/neural_net/templates/plugin/test.py +0 -2
- synapse_sdk/plugins/categories/neural_net/templates/plugin/train.py +0 -14
- synapse_sdk/plugins/categories/post_annotation/__init__.py +0 -0
- synapse_sdk/plugins/categories/post_annotation/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/post_annotation/actions/post_annotation.py +0 -10
- synapse_sdk/plugins/categories/post_annotation/templates/config.yaml +0 -3
- synapse_sdk/plugins/categories/post_annotation/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/post_annotation/templates/plugin/post_annotation.py +0 -3
- synapse_sdk/plugins/categories/pre_annotation/__init__.py +0 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation.py +0 -10
- synapse_sdk/plugins/categories/pre_annotation/templates/config.yaml +0 -3
- synapse_sdk/plugins/categories/pre_annotation/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/pre_annotation/templates/plugin/pre_annotation.py +0 -3
- synapse_sdk/plugins/categories/registry.py +0 -16
- synapse_sdk/plugins/categories/smart_tool/__init__.py +0 -0
- synapse_sdk/plugins/categories/smart_tool/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/smart_tool/actions/auto_label.py +0 -37
- synapse_sdk/plugins/categories/smart_tool/templates/config.yaml +0 -7
- synapse_sdk/plugins/categories/smart_tool/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/smart_tool/templates/plugin/auto_label.py +0 -11
- synapse_sdk/plugins/categories/templates.py +0 -32
- synapse_sdk/plugins/cli/__init__.py +0 -21
- synapse_sdk/plugins/cli/publish.py +0 -37
- synapse_sdk/plugins/cli/run.py +0 -67
- synapse_sdk/plugins/exceptions.py +0 -22
- synapse_sdk/plugins/models.py +0 -121
- synapse_sdk/plugins/templates/cookiecutter.json +0 -11
- synapse_sdk/plugins/templates/hooks/post_gen_project.py +0 -3
- synapse_sdk/plugins/templates/hooks/pre_prompt.py +0 -21
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env +0 -24
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env.dist +0 -24
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.gitignore +0 -27
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.pre-commit-config.yaml +0 -7
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/README.md +0 -5
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/config.yaml +0 -6
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/main.py +0 -4
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/plugin/__init__.py +0 -0
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/pyproject.toml +0 -13
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/requirements.txt +0 -1
- synapse_sdk/shared/enums.py +0 -8
- synapse_sdk/utils/debug.py +0 -5
- synapse_sdk/utils/file.py +0 -87
- synapse_sdk/utils/module_loading.py +0 -29
- synapse_sdk/utils/pydantic/__init__.py +0 -0
- synapse_sdk/utils/pydantic/config.py +0 -4
- synapse_sdk/utils/pydantic/errors.py +0 -33
- synapse_sdk/utils/pydantic/validators.py +0 -7
- synapse_sdk/utils/storage.py +0 -91
- synapse_sdk/utils/string.py +0 -11
- synapse_sdk-1.0.0a11.dist-info/LICENSE +0 -21
- synapse_sdk-1.0.0a11.dist-info/METADATA +0 -43
- synapse_sdk-1.0.0a11.dist-info/RECORD +0 -111
- {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/entry_points.txt +0 -0
- {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
"""Base converter classes for dataset format conversions.
|
|
2
|
+
|
|
3
|
+
Provides:
|
|
4
|
+
- BaseConverter: Shared logic for all converters
|
|
5
|
+
- FromDMConverter: Base for DM -> external format conversions
|
|
6
|
+
- ToDMConverter: Base for external format -> DM conversions
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import shutil
|
|
13
|
+
import uuid
|
|
14
|
+
from abc import ABC, abstractmethod
|
|
15
|
+
from enum import StrEnum
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import IO, Any
|
|
18
|
+
|
|
19
|
+
from synapse_sdk.plugins.datasets.formats.dm import DMVersion
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class DatasetFormat(StrEnum):
|
|
23
|
+
"""Supported dataset formats."""
|
|
24
|
+
|
|
25
|
+
DM_V1 = 'dm_v1'
|
|
26
|
+
DM_V2 = 'dm_v2'
|
|
27
|
+
YOLO = 'yolo'
|
|
28
|
+
COCO = 'coco'
|
|
29
|
+
PASCAL = 'pascal'
|
|
30
|
+
|
|
31
|
+
@classmethod
|
|
32
|
+
def from_dm_version(cls, version: DMVersion) -> DatasetFormat:
|
|
33
|
+
"""Get DatasetFormat from DMVersion."""
|
|
34
|
+
if version == DMVersion.V1:
|
|
35
|
+
return cls.DM_V1
|
|
36
|
+
return cls.DM_V2
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class BaseConverter(ABC):
|
|
40
|
+
"""Base class for shared logic between converters.
|
|
41
|
+
|
|
42
|
+
Attributes:
|
|
43
|
+
root_dir: Root directory containing source data.
|
|
44
|
+
is_categorized: Whether dataset has train/valid/test splits.
|
|
45
|
+
is_single_conversion: Whether converting single files (not directories).
|
|
46
|
+
converted_data: Holds converted data after calling convert().
|
|
47
|
+
|
|
48
|
+
Example:
|
|
49
|
+
>>> converter = MyConverter(root_dir='/data/source', is_categorized=True)
|
|
50
|
+
>>> converter.convert()
|
|
51
|
+
>>> converter.save_to_folder('/data/output')
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
IMG_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.bmp', '.webp']
|
|
55
|
+
|
|
56
|
+
def __init__(
|
|
57
|
+
self,
|
|
58
|
+
root_dir: str | Path | None = None,
|
|
59
|
+
is_categorized: bool = False,
|
|
60
|
+
is_single_conversion: bool = False,
|
|
61
|
+
) -> None:
|
|
62
|
+
"""Initialize converter.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
root_dir: Root directory containing data.
|
|
66
|
+
is_categorized: Whether to handle train/valid/test splits.
|
|
67
|
+
is_single_conversion: Whether converting single files only.
|
|
68
|
+
|
|
69
|
+
Raises:
|
|
70
|
+
ValueError: If root_dir not specified for directory conversion.
|
|
71
|
+
"""
|
|
72
|
+
self.root_dir = Path(root_dir) if root_dir else None
|
|
73
|
+
self.is_categorized = is_categorized
|
|
74
|
+
self.is_single_conversion = is_single_conversion
|
|
75
|
+
self.converted_data: Any = None
|
|
76
|
+
|
|
77
|
+
if not is_single_conversion and not root_dir:
|
|
78
|
+
raise ValueError('root_dir must be specified for directory conversion')
|
|
79
|
+
|
|
80
|
+
@staticmethod
|
|
81
|
+
def ensure_dir(path: Path | str) -> Path:
|
|
82
|
+
"""Ensure directory exists, creating if necessary."""
|
|
83
|
+
path = Path(path)
|
|
84
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
85
|
+
return path
|
|
86
|
+
|
|
87
|
+
@staticmethod
|
|
88
|
+
def get_image_size(image_path: Path | str | IO) -> tuple[int, int]:
|
|
89
|
+
"""Get image dimensions (width, height)."""
|
|
90
|
+
from PIL import Image
|
|
91
|
+
|
|
92
|
+
with Image.open(image_path) as img:
|
|
93
|
+
return img.size
|
|
94
|
+
|
|
95
|
+
def find_image_for_label(
|
|
96
|
+
self,
|
|
97
|
+
label_stem: str,
|
|
98
|
+
image_dir: Path,
|
|
99
|
+
) -> Path | None:
|
|
100
|
+
"""Find image file matching a label file stem."""
|
|
101
|
+
for ext in self.IMG_EXTENSIONS:
|
|
102
|
+
img_path = image_dir / f'{label_stem}{ext}'
|
|
103
|
+
if img_path.exists():
|
|
104
|
+
return img_path
|
|
105
|
+
return None
|
|
106
|
+
|
|
107
|
+
def _validate_required_dirs(self, dirs: dict[str, Path]) -> None:
|
|
108
|
+
"""Validate that all required directories exist."""
|
|
109
|
+
for name, path in dirs.items():
|
|
110
|
+
if not path.exists():
|
|
111
|
+
raise FileNotFoundError(f'Required directory "{name}" does not exist: {path}')
|
|
112
|
+
|
|
113
|
+
def _validate_optional_dirs(self, dirs: dict[str, Path]) -> dict[str, Path]:
|
|
114
|
+
"""Validate optional directories, return those that exist."""
|
|
115
|
+
existing = {}
|
|
116
|
+
for name, path in dirs.items():
|
|
117
|
+
if path.exists():
|
|
118
|
+
existing[name] = path
|
|
119
|
+
return existing
|
|
120
|
+
|
|
121
|
+
def _validate_splits(
|
|
122
|
+
self,
|
|
123
|
+
required_splits: list[str],
|
|
124
|
+
optional_splits: list[str] | None = None,
|
|
125
|
+
) -> dict[str, Path]:
|
|
126
|
+
"""Validate required and optional splits in the dataset.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
required_splits: Splits that must exist (e.g., ['train', 'valid']).
|
|
130
|
+
optional_splits: Splits that may exist (e.g., ['test']).
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
Dictionary mapping split names to their paths.
|
|
134
|
+
|
|
135
|
+
Raises:
|
|
136
|
+
FileNotFoundError: If required split is missing.
|
|
137
|
+
"""
|
|
138
|
+
if self.root_dir is None:
|
|
139
|
+
raise ValueError('root_dir must be set')
|
|
140
|
+
|
|
141
|
+
splits: dict[str, Path] = {}
|
|
142
|
+
optional_splits = optional_splits or []
|
|
143
|
+
|
|
144
|
+
if self.is_categorized:
|
|
145
|
+
required_dirs = {split: self.root_dir / split for split in required_splits}
|
|
146
|
+
self._validate_required_dirs(required_dirs)
|
|
147
|
+
splits.update(required_dirs)
|
|
148
|
+
|
|
149
|
+
optional_dirs = {split: self.root_dir / split for split in optional_splits}
|
|
150
|
+
splits.update(self._validate_optional_dirs(optional_dirs))
|
|
151
|
+
else:
|
|
152
|
+
# Non-categorized: expect json/ and original_files/ in root
|
|
153
|
+
required_dirs = {
|
|
154
|
+
'json': self.root_dir / 'json',
|
|
155
|
+
'original_files': self.root_dir / 'original_files',
|
|
156
|
+
}
|
|
157
|
+
self._validate_required_dirs(required_dirs)
|
|
158
|
+
splits['root'] = self.root_dir
|
|
159
|
+
|
|
160
|
+
return splits
|
|
161
|
+
|
|
162
|
+
def _set_directories(self, split: str | None = None) -> None:
|
|
163
|
+
"""Set json_dir and original_file_dir based on split."""
|
|
164
|
+
if self.root_dir is None:
|
|
165
|
+
raise ValueError('root_dir must be set')
|
|
166
|
+
|
|
167
|
+
if split:
|
|
168
|
+
split_dir = self.root_dir / split
|
|
169
|
+
self.json_dir = split_dir / 'json'
|
|
170
|
+
self.original_file_dir = split_dir / 'original_files'
|
|
171
|
+
else:
|
|
172
|
+
self.json_dir = self.root_dir / 'json'
|
|
173
|
+
self.original_file_dir = self.root_dir / 'original_files'
|
|
174
|
+
|
|
175
|
+
@staticmethod
|
|
176
|
+
def _generate_unique_id() -> str:
|
|
177
|
+
"""Generate a unique 10-character ID."""
|
|
178
|
+
return uuid.uuid4().hex[:10]
|
|
179
|
+
|
|
180
|
+
@abstractmethod
|
|
181
|
+
def convert(self) -> Any:
|
|
182
|
+
"""Convert data (in-memory). Must be implemented by subclasses."""
|
|
183
|
+
...
|
|
184
|
+
|
|
185
|
+
@abstractmethod
|
|
186
|
+
def save_to_folder(self, output_dir: str | Path) -> None:
|
|
187
|
+
"""Save converted data to folder. Must be implemented by subclasses."""
|
|
188
|
+
...
|
|
189
|
+
|
|
190
|
+
def convert_single_file(
|
|
191
|
+
self,
|
|
192
|
+
data: Any,
|
|
193
|
+
original_file: IO,
|
|
194
|
+
**kwargs: Any,
|
|
195
|
+
) -> Any:
|
|
196
|
+
"""Convert a single data object and corresponding original file.
|
|
197
|
+
|
|
198
|
+
Only available when is_single_conversion=True.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
data: The data object to convert.
|
|
202
|
+
original_file: File object for the corresponding original file.
|
|
203
|
+
**kwargs: Additional converter-specific parameters.
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
Converted data in target format.
|
|
207
|
+
|
|
208
|
+
Raises:
|
|
209
|
+
RuntimeError: If not in single conversion mode.
|
|
210
|
+
NotImplementedError: If subclass doesn't implement this.
|
|
211
|
+
"""
|
|
212
|
+
if not self.is_single_conversion:
|
|
213
|
+
raise RuntimeError('convert_single_file only available when is_single_conversion=True')
|
|
214
|
+
raise NotImplementedError('Subclasses must implement convert_single_file')
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class FromDMConverter(BaseConverter):
|
|
218
|
+
"""Base class for converting from Datamaker format to external formats.
|
|
219
|
+
|
|
220
|
+
Subclasses convert DM (v1 or v2) -> YOLO, COCO, PASCAL, etc.
|
|
221
|
+
|
|
222
|
+
Expected source structure:
|
|
223
|
+
- Categorized: root_dir/{train,valid,test}/json/*.json + original_files/*
|
|
224
|
+
- Non-categorized: root_dir/json/*.json + original_files/*
|
|
225
|
+
|
|
226
|
+
Example:
|
|
227
|
+
>>> converter = FromDMToYOLOConverter(
|
|
228
|
+
... root_dir='/data/dm_dataset',
|
|
229
|
+
... is_categorized=True,
|
|
230
|
+
... dm_version=DMVersion.V2,
|
|
231
|
+
... )
|
|
232
|
+
>>> result = converter.convert()
|
|
233
|
+
>>> converter.save_to_folder('/data/yolo_output')
|
|
234
|
+
"""
|
|
235
|
+
|
|
236
|
+
source_format: DatasetFormat = DatasetFormat.DM_V2
|
|
237
|
+
target_format: DatasetFormat
|
|
238
|
+
|
|
239
|
+
def __init__(
|
|
240
|
+
self,
|
|
241
|
+
root_dir: str | Path | None = None,
|
|
242
|
+
is_categorized: bool = False,
|
|
243
|
+
is_single_conversion: bool = False,
|
|
244
|
+
dm_version: DMVersion = DMVersion.V2,
|
|
245
|
+
) -> None:
|
|
246
|
+
"""Initialize FromDMConverter.
|
|
247
|
+
|
|
248
|
+
Args:
|
|
249
|
+
root_dir: Root directory containing DM data.
|
|
250
|
+
is_categorized: Whether dataset has train/valid/test splits.
|
|
251
|
+
is_single_conversion: Whether converting single files only.
|
|
252
|
+
dm_version: Datamaker schema version (V1 or V2).
|
|
253
|
+
"""
|
|
254
|
+
super().__init__(root_dir, is_categorized, is_single_conversion)
|
|
255
|
+
self.dm_version = dm_version
|
|
256
|
+
self.source_format = DatasetFormat.from_dm_version(dm_version)
|
|
257
|
+
self.class_names: list[str] = []
|
|
258
|
+
self.class_map: dict[str, int] | None = None
|
|
259
|
+
|
|
260
|
+
def save_to_folder(self, output_dir: str | Path) -> None:
|
|
261
|
+
"""Save converted data to folder.
|
|
262
|
+
|
|
263
|
+
Subclasses should override to implement format-specific saving.
|
|
264
|
+
"""
|
|
265
|
+
output_dir = Path(output_dir)
|
|
266
|
+
self.ensure_dir(output_dir)
|
|
267
|
+
if self.converted_data is None:
|
|
268
|
+
self.converted_data = self.convert()
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
class ToDMConverter(BaseConverter):
|
|
272
|
+
"""Base class for converting external formats to Datamaker format.
|
|
273
|
+
|
|
274
|
+
Subclasses convert YOLO, COCO, PASCAL -> DM (v1 or v2).
|
|
275
|
+
|
|
276
|
+
Output structure:
|
|
277
|
+
- Categorized: output_dir/{train,valid,test}/json/*.json + original_files/*
|
|
278
|
+
- Non-categorized: output_dir/json/*.json + original_files/*
|
|
279
|
+
|
|
280
|
+
Example:
|
|
281
|
+
>>> converter = YOLOToDMConverter(
|
|
282
|
+
... root_dir='/data/yolo_dataset',
|
|
283
|
+
... is_categorized=True,
|
|
284
|
+
... dm_version=DMVersion.V2,
|
|
285
|
+
... )
|
|
286
|
+
>>> result = converter.convert()
|
|
287
|
+
>>> converter.save_to_folder('/data/dm_output')
|
|
288
|
+
"""
|
|
289
|
+
|
|
290
|
+
source_format: DatasetFormat
|
|
291
|
+
target_format: DatasetFormat = DatasetFormat.DM_V2
|
|
292
|
+
|
|
293
|
+
def __init__(
|
|
294
|
+
self,
|
|
295
|
+
root_dir: str | Path | None = None,
|
|
296
|
+
is_categorized: bool = False,
|
|
297
|
+
is_single_conversion: bool = False,
|
|
298
|
+
dm_version: DMVersion = DMVersion.V2,
|
|
299
|
+
) -> None:
|
|
300
|
+
"""Initialize ToDMConverter.
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
root_dir: Root directory containing source data.
|
|
304
|
+
is_categorized: Whether dataset has train/valid/test splits.
|
|
305
|
+
is_single_conversion: Whether converting single files only.
|
|
306
|
+
dm_version: Target Datamaker schema version (V1 or V2).
|
|
307
|
+
"""
|
|
308
|
+
super().__init__(root_dir, is_categorized, is_single_conversion)
|
|
309
|
+
self.dm_version = dm_version
|
|
310
|
+
self.target_format = DatasetFormat.from_dm_version(dm_version)
|
|
311
|
+
|
|
312
|
+
def save_to_folder(self, output_dir: str | Path) -> None:
|
|
313
|
+
"""Save converted DM data to folder."""
|
|
314
|
+
output_dir = Path(output_dir)
|
|
315
|
+
self.ensure_dir(output_dir)
|
|
316
|
+
|
|
317
|
+
if self.converted_data is None:
|
|
318
|
+
self.converted_data = self.convert()
|
|
319
|
+
|
|
320
|
+
if self.is_categorized:
|
|
321
|
+
for split, img_dict in self.converted_data.items():
|
|
322
|
+
split_dir = output_dir / split
|
|
323
|
+
json_dir = self.ensure_dir(split_dir / 'json')
|
|
324
|
+
original_file_dir = self.ensure_dir(split_dir / 'original_files')
|
|
325
|
+
|
|
326
|
+
for img_filename, (dm_json, img_src_path) in img_dict.items():
|
|
327
|
+
json_filename = Path(img_filename).stem + '.json'
|
|
328
|
+
(json_dir / json_filename).write_text(json.dumps(dm_json, indent=2, ensure_ascii=False))
|
|
329
|
+
if img_src_path and Path(img_src_path).exists():
|
|
330
|
+
shutil.copy(img_src_path, original_file_dir / img_filename)
|
|
331
|
+
else:
|
|
332
|
+
json_dir = self.ensure_dir(output_dir / 'json')
|
|
333
|
+
original_file_dir = self.ensure_dir(output_dir / 'original_files')
|
|
334
|
+
|
|
335
|
+
for img_filename, (dm_json, img_src_path) in self.converted_data.items():
|
|
336
|
+
json_filename = Path(img_filename).stem + '.json'
|
|
337
|
+
(json_dir / json_filename).write_text(json.dumps(dm_json, indent=2, ensure_ascii=False))
|
|
338
|
+
if img_src_path and Path(img_src_path).exists():
|
|
339
|
+
shutil.copy(img_src_path, original_file_dir / img_filename)
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
__all__ = [
|
|
343
|
+
'BaseConverter',
|
|
344
|
+
'DatasetFormat',
|
|
345
|
+
'FromDMConverter',
|
|
346
|
+
'ToDMConverter',
|
|
347
|
+
]
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""YOLO format converters."""
|
|
2
|
+
|
|
3
|
+
from synapse_sdk.plugins.datasets.converters.yolo.from_dm import FromDMToYOLOConverter
|
|
4
|
+
from synapse_sdk.plugins.datasets.converters.yolo.to_dm import YOLOToDMConverter
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
'FromDMToYOLOConverter',
|
|
8
|
+
'YOLOToDMConverter',
|
|
9
|
+
]
|