synapse-sdk 1.0.0a13__py3-none-any.whl → 2025.11.7__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 +310 -5
- synapse_sdk/cli/alias/__init__.py +22 -0
- synapse_sdk/cli/alias/create.py +36 -0
- synapse_sdk/cli/alias/dataclass.py +31 -0
- synapse_sdk/cli/alias/default.py +16 -0
- synapse_sdk/cli/alias/delete.py +15 -0
- synapse_sdk/cli/alias/list.py +19 -0
- synapse_sdk/cli/alias/read.py +15 -0
- synapse_sdk/cli/alias/update.py +17 -0
- synapse_sdk/cli/alias/utils.py +61 -0
- synapse_sdk/cli/code_server.py +687 -0
- synapse_sdk/cli/config.py +440 -0
- synapse_sdk/cli/devtools.py +90 -0
- synapse_sdk/cli/plugin/__init__.py +33 -0
- synapse_sdk/cli/{create_plugin.py → plugin/create.py} +2 -2
- synapse_sdk/cli/plugin/publish.py +45 -0
- synapse_sdk/{plugins/cli → cli/plugin}/run.py +12 -5
- synapse_sdk/clients/agent/__init__.py +9 -3
- synapse_sdk/clients/agent/container.py +133 -0
- synapse_sdk/clients/agent/core.py +19 -0
- synapse_sdk/clients/agent/ray.py +298 -9
- synapse_sdk/clients/backend/__init__.py +41 -12
- synapse_sdk/clients/backend/annotation.py +13 -5
- synapse_sdk/clients/backend/core.py +59 -0
- synapse_sdk/clients/backend/data_collection.py +186 -0
- synapse_sdk/clients/backend/hitl.py +17 -0
- synapse_sdk/clients/backend/integration.py +19 -4
- synapse_sdk/clients/backend/ml.py +10 -7
- synapse_sdk/clients/backend/models.py +78 -0
- synapse_sdk/clients/base.py +381 -34
- synapse_sdk/clients/ray/serve.py +2 -0
- synapse_sdk/clients/validators/collections.py +31 -0
- synapse_sdk/devtools/config.py +94 -0
- synapse_sdk/devtools/docs/.gitignore +20 -0
- synapse_sdk/devtools/docs/README.md +41 -0
- synapse_sdk/devtools/docs/blog/2019-05-28-first-blog-post.md +12 -0
- synapse_sdk/devtools/docs/blog/2019-05-29-long-blog-post.md +44 -0
- synapse_sdk/devtools/docs/blog/2021-08-01-mdx-blog-post.mdx +24 -0
- 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 +29 -0
- synapse_sdk/devtools/docs/blog/authors.yml +25 -0
- synapse_sdk/devtools/docs/blog/tags.yml +19 -0
- synapse_sdk/devtools/docs/docs/api/clients/agent.md +43 -0
- synapse_sdk/devtools/docs/docs/api/clients/annotation-mixin.md +378 -0
- synapse_sdk/devtools/docs/docs/api/clients/backend.md +420 -0
- synapse_sdk/devtools/docs/docs/api/clients/base.md +257 -0
- synapse_sdk/devtools/docs/docs/api/clients/core-mixin.md +477 -0
- synapse_sdk/devtools/docs/docs/api/clients/data-collection-mixin.md +422 -0
- synapse_sdk/devtools/docs/docs/api/clients/hitl-mixin.md +554 -0
- synapse_sdk/devtools/docs/docs/api/clients/index.md +391 -0
- synapse_sdk/devtools/docs/docs/api/clients/integration-mixin.md +571 -0
- synapse_sdk/devtools/docs/docs/api/clients/ml-mixin.md +578 -0
- synapse_sdk/devtools/docs/docs/api/clients/ray.md +342 -0
- synapse_sdk/devtools/docs/docs/api/index.md +52 -0
- synapse_sdk/devtools/docs/docs/api/plugins/categories.md +43 -0
- synapse_sdk/devtools/docs/docs/api/plugins/models.md +114 -0
- synapse_sdk/devtools/docs/docs/api/plugins/utils.md +328 -0
- synapse_sdk/devtools/docs/docs/categories.md +0 -0
- synapse_sdk/devtools/docs/docs/cli-usage.md +280 -0
- synapse_sdk/devtools/docs/docs/concepts/index.md +38 -0
- synapse_sdk/devtools/docs/docs/configuration.md +83 -0
- synapse_sdk/devtools/docs/docs/contributing.md +306 -0
- synapse_sdk/devtools/docs/docs/examples/index.md +29 -0
- synapse_sdk/devtools/docs/docs/faq.md +179 -0
- synapse_sdk/devtools/docs/docs/features/converters/index.md +455 -0
- synapse_sdk/devtools/docs/docs/features/index.md +24 -0
- synapse_sdk/devtools/docs/docs/features/utils/file.md +415 -0
- synapse_sdk/devtools/docs/docs/features/utils/network.md +378 -0
- synapse_sdk/devtools/docs/docs/features/utils/storage.md +57 -0
- synapse_sdk/devtools/docs/docs/features/utils/types.md +51 -0
- synapse_sdk/devtools/docs/docs/installation.md +94 -0
- synapse_sdk/devtools/docs/docs/introduction.md +47 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/neural-net-plugins/train-action-overview.md +814 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/pre-annotation-plugins/pre-annotation-plugin-overview.md +198 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/pre-annotation-plugins/to-task-action-development.md +1645 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/pre-annotation-plugins/to-task-overview.md +717 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/pre-annotation-plugins/to-task-template-development.md +1380 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/upload-plugins/upload-plugin-action.md +948 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/upload-plugins/upload-plugin-overview.md +544 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/upload-plugins/upload-plugin-template.md +766 -0
- synapse_sdk/devtools/docs/docs/plugins/export-plugins.md +1092 -0
- synapse_sdk/devtools/docs/docs/plugins/plugins.md +852 -0
- synapse_sdk/devtools/docs/docs/quickstart.md +78 -0
- synapse_sdk/devtools/docs/docs/troubleshooting.md +519 -0
- synapse_sdk/devtools/docs/docs/tutorial-basics/_category_.json +8 -0
- synapse_sdk/devtools/docs/docs/tutorial-basics/congratulations.md +23 -0
- synapse_sdk/devtools/docs/docs/tutorial-basics/create-a-blog-post.md +34 -0
- synapse_sdk/devtools/docs/docs/tutorial-basics/create-a-document.md +57 -0
- synapse_sdk/devtools/docs/docs/tutorial-basics/create-a-page.md +43 -0
- synapse_sdk/devtools/docs/docs/tutorial-basics/deploy-your-site.md +31 -0
- synapse_sdk/devtools/docs/docs/tutorial-basics/markdown-features.mdx +152 -0
- synapse_sdk/devtools/docs/docs/tutorial-extras/_category_.json +7 -0
- synapse_sdk/devtools/docs/docs/tutorial-extras/img/docsVersionDropdown.png +0 -0
- synapse_sdk/devtools/docs/docs/tutorial-extras/img/localeDropdown.png +0 -0
- synapse_sdk/devtools/docs/docs/tutorial-extras/manage-docs-versions.md +55 -0
- synapse_sdk/devtools/docs/docs/tutorial-extras/translate-your-site.md +88 -0
- synapse_sdk/devtools/docs/docusaurus.config.ts +148 -0
- synapse_sdk/devtools/docs/i18n/ko/code.json +325 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/agent.md +43 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/annotation-mixin.md +289 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/backend.md +420 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/base.md +257 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/core-mixin.md +417 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/data-collection-mixin.md +356 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/hitl-mixin.md +192 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/index.md +391 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/integration-mixin.md +479 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/ml-mixin.md +284 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/ray.md +342 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/index.md +52 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/plugins/models.md +114 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/categories.md +0 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/cli-usage.md +280 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/concepts/index.md +38 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/configuration.md +83 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/contributing.md +306 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/examples/index.md +29 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/faq.md +179 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/converters/index.md +30 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/index.md +24 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/utils/file.md +415 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/utils/network.md +378 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/utils/storage.md +60 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/utils/types.md +51 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/installation.md +94 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/introduction.md +47 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/neural-net-plugins/train-action-overview.md +815 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/pre-annotation-plugins/pre-annotation-plugin-overview.md +198 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/pre-annotation-plugins/to-task-action-development.md +1645 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/pre-annotation-plugins/to-task-overview.md +717 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/pre-annotation-plugins/to-task-template-development.md +1380 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/upload-plugins/upload-plugin-action.md +948 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/upload-plugins/upload-plugin-overview.md +544 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/upload-plugins/upload-plugin-template.md +766 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/export-plugins.md +1092 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/plugins.md +117 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/quickstart.md +78 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/troubleshooting.md +519 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current.json +34 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-theme-classic/footer.json +42 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-theme-classic/navbar.json +18 -0
- synapse_sdk/devtools/docs/package-lock.json +18784 -0
- synapse_sdk/devtools/docs/package.json +48 -0
- synapse_sdk/devtools/docs/sidebars.ts +122 -0
- synapse_sdk/devtools/docs/src/components/HomepageFeatures/index.tsx +71 -0
- synapse_sdk/devtools/docs/src/components/HomepageFeatures/styles.module.css +11 -0
- synapse_sdk/devtools/docs/src/css/custom.css +30 -0
- synapse_sdk/devtools/docs/src/pages/index.module.css +23 -0
- synapse_sdk/devtools/docs/src/pages/index.tsx +21 -0
- synapse_sdk/devtools/docs/src/pages/markdown-page.md +7 -0
- 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 +171 -0
- synapse_sdk/devtools/docs/static/img/undraw_docusaurus_react.svg +170 -0
- synapse_sdk/devtools/docs/static/img/undraw_docusaurus_tree.svg +40 -0
- synapse_sdk/devtools/docs/tsconfig.json +8 -0
- synapse_sdk/devtools/server.py +41 -0
- synapse_sdk/devtools/streamlit_app/__init__.py +5 -0
- synapse_sdk/devtools/streamlit_app/app.py +128 -0
- synapse_sdk/devtools/streamlit_app/services/__init__.py +11 -0
- synapse_sdk/devtools/streamlit_app/services/job_service.py +233 -0
- synapse_sdk/devtools/streamlit_app/services/plugin_service.py +236 -0
- synapse_sdk/devtools/streamlit_app/services/serve_service.py +95 -0
- synapse_sdk/devtools/streamlit_app/ui/__init__.py +15 -0
- synapse_sdk/devtools/streamlit_app/ui/config_tab.py +76 -0
- synapse_sdk/devtools/streamlit_app/ui/deployment_tab.py +66 -0
- synapse_sdk/devtools/streamlit_app/ui/http_tab.py +125 -0
- synapse_sdk/devtools/streamlit_app/ui/jobs_tab.py +573 -0
- synapse_sdk/devtools/streamlit_app/ui/serve_tab.py +346 -0
- synapse_sdk/devtools/streamlit_app/ui/status_bar.py +118 -0
- synapse_sdk/devtools/streamlit_app/utils/__init__.py +40 -0
- synapse_sdk/devtools/streamlit_app/utils/json_viewer.py +197 -0
- synapse_sdk/devtools/streamlit_app/utils/log_formatter.py +38 -0
- synapse_sdk/devtools/streamlit_app/utils/styles.py +241 -0
- synapse_sdk/devtools/streamlit_app/utils/ui_components.py +289 -0
- synapse_sdk/devtools/streamlit_app.py +10 -0
- synapse_sdk/loggers.py +74 -9
- synapse_sdk/plugins/README.md +1340 -0
- synapse_sdk/plugins/__init__.py +0 -13
- synapse_sdk/plugins/categories/base.py +145 -30
- synapse_sdk/plugins/categories/data_validation/actions/validation.py +72 -0
- synapse_sdk/plugins/categories/data_validation/templates/plugin/validation.py +33 -5
- 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 +21 -0
- synapse_sdk/plugins/categories/export/templates/plugin/__init__.py +390 -0
- synapse_sdk/plugins/categories/export/templates/plugin/export.py +160 -0
- synapse_sdk/plugins/categories/neural_net/actions/deployment.py +29 -14
- synapse_sdk/plugins/categories/neural_net/actions/inference.py +13 -1
- synapse_sdk/plugins/categories/neural_net/actions/train.py +1084 -38
- synapse_sdk/plugins/categories/neural_net/actions/tune.py +534 -0
- synapse_sdk/plugins/categories/neural_net/base/__init__.py +0 -0
- synapse_sdk/plugins/categories/neural_net/base/inference.py +37 -0
- synapse_sdk/plugins/categories/neural_net/templates/config.yaml +30 -5
- synapse_sdk/plugins/categories/neural_net/templates/plugin/inference.py +26 -10
- 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/{export/actions/export.py → pre_annotation/actions/pre_annotation/action.py} +4 -4
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/__init__.py +28 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/action.py +145 -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 +97 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/orchestrator.py +250 -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 +287 -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 +87 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/preprocessor.py +127 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/validation.py +143 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task.py +966 -0
- synapse_sdk/plugins/categories/pre_annotation/templates/config.yaml +19 -0
- synapse_sdk/plugins/categories/pre_annotation/templates/plugin/to_task.py +40 -0
- synapse_sdk/plugins/categories/smart_tool/templates/config.yaml +5 -2
- synapse_sdk/plugins/categories/upload/__init__.py +0 -0
- synapse_sdk/plugins/categories/upload/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/upload/actions/upload/__init__.py +19 -0
- synapse_sdk/plugins/categories/upload/actions/upload/action.py +232 -0
- synapse_sdk/plugins/categories/upload/actions/upload/context.py +185 -0
- synapse_sdk/plugins/categories/upload/actions/upload/enums.py +471 -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 +203 -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 +84 -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 +203 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/upload.py +97 -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 +258 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/recursive.py +281 -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 +33 -0
- synapse_sdk/plugins/categories/upload/templates/plugin/__init__.py +294 -0
- synapse_sdk/plugins/categories/upload/templates/plugin/upload.py +102 -0
- synapse_sdk/plugins/enums.py +3 -1
- synapse_sdk/plugins/models.py +140 -16
- synapse_sdk/plugins/templates/plugin-config-schema.json +406 -0
- synapse_sdk/plugins/templates/schema.json +491 -0
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/config.yaml +1 -0
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/requirements.txt +1 -1
- synapse_sdk/plugins/utils/__init__.py +46 -0
- synapse_sdk/plugins/utils/actions.py +119 -0
- synapse_sdk/plugins/utils/config.py +203 -0
- synapse_sdk/plugins/utils/legacy.py +95 -0
- synapse_sdk/plugins/utils/ray_gcs.py +66 -0
- synapse_sdk/plugins/utils/registry.py +58 -0
- synapse_sdk/shared/__init__.py +25 -0
- synapse_sdk/shared/enums.py +93 -0
- synapse_sdk/types.py +19 -0
- synapse_sdk/utils/converters/__init__.py +240 -0
- synapse_sdk/utils/converters/coco/__init__.py +0 -0
- synapse_sdk/utils/converters/coco/from_dm.py +322 -0
- synapse_sdk/utils/converters/coco/to_dm.py +215 -0
- synapse_sdk/utils/converters/dm/__init__.py +56 -0
- synapse_sdk/utils/converters/dm/from_v1.py +627 -0
- synapse_sdk/utils/converters/dm/to_v1.py +367 -0
- synapse_sdk/utils/converters/pascal/__init__.py +0 -0
- synapse_sdk/utils/converters/pascal/from_dm.py +244 -0
- synapse_sdk/utils/converters/pascal/to_dm.py +214 -0
- synapse_sdk/utils/converters/yolo/__init__.py +0 -0
- synapse_sdk/utils/converters/yolo/from_dm.py +384 -0
- synapse_sdk/utils/converters/yolo/to_dm.py +267 -0
- synapse_sdk/utils/dataset.py +46 -0
- synapse_sdk/utils/encryption.py +158 -0
- synapse_sdk/utils/file/__init__.py +39 -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/video/__init__.py +29 -0
- synapse_sdk/utils/file/video/transcode.py +307 -0
- synapse_sdk/utils/file.py.backup +301 -0
- synapse_sdk/utils/http.py +138 -0
- synapse_sdk/utils/network.py +309 -0
- synapse_sdk/utils/storage/__init__.py +72 -0
- synapse_sdk/utils/storage/providers/__init__.py +183 -0
- synapse_sdk/utils/storage/providers/file_system.py +134 -0
- synapse_sdk/utils/storage/providers/gcp.py +13 -0
- synapse_sdk/utils/storage/providers/http.py +190 -0
- synapse_sdk/utils/storage/providers/s3.py +91 -0
- synapse_sdk/utils/storage/providers/sftp.py +47 -0
- synapse_sdk/utils/storage/registry.py +17 -0
- synapse_sdk-2025.11.7.dist-info/METADATA +122 -0
- synapse_sdk-2025.11.7.dist-info/RECORD +386 -0
- {synapse_sdk-1.0.0a13.dist-info → synapse_sdk-2025.11.7.dist-info}/WHEEL +1 -1
- synapse_sdk/clients/backend/dataset.py +0 -51
- synapse_sdk/plugins/categories/import/actions/import.py +0 -10
- synapse_sdk/plugins/cli/__init__.py +0 -21
- synapse_sdk/plugins/cli/publish.py +0 -37
- 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/main.py +0 -4
- synapse_sdk/plugins/utils.py +0 -50
- synapse_sdk/utils/file.py +0 -87
- synapse_sdk/utils/storage.py +0 -91
- synapse_sdk-1.0.0a13.dist-info/METADATA +0 -43
- synapse_sdk-1.0.0a13.dist-info/RECORD +0 -111
- /synapse_sdk/{plugins/categories/import → clients/validators}/__init__.py +0 -0
- /synapse_sdk/{plugins/categories/import/actions → devtools}/__init__.py +0 -0
- {synapse_sdk-1.0.0a13.dist-info → synapse_sdk-2025.11.7.dist-info}/entry_points.txt +0 -0
- {synapse_sdk-1.0.0a13.dist-info → synapse_sdk-2025.11.7.dist-info/licenses}/LICENSE +0 -0
- {synapse_sdk-1.0.0a13.dist-info → synapse_sdk-2025.11.7.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import shutil
|
|
4
|
+
import uuid
|
|
5
|
+
from typing import IO
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BaseConverter:
|
|
9
|
+
"""Base class for shared logic between converters."""
|
|
10
|
+
|
|
11
|
+
def __init__(
|
|
12
|
+
self, root_dir: str = None, is_categorized_dataset: bool = False, is_single_conversion: bool = False
|
|
13
|
+
) -> None:
|
|
14
|
+
self.root_dir: str = root_dir
|
|
15
|
+
self.is_categorized_dataset: bool = is_categorized_dataset
|
|
16
|
+
self.is_single_conversion: bool = is_single_conversion
|
|
17
|
+
self.converted_data = None
|
|
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
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def ensure_dir(path: str) -> None:
|
|
26
|
+
"""Ensure that the directory exists, creating it if necessary."""
|
|
27
|
+
if not os.path.exists(path):
|
|
28
|
+
os.makedirs(path)
|
|
29
|
+
|
|
30
|
+
def _validate_required_dirs(self, dirs):
|
|
31
|
+
"""Validate that all required directories exist."""
|
|
32
|
+
for name, path in dirs.items():
|
|
33
|
+
if not os.path.exists(path):
|
|
34
|
+
raise FileNotFoundError(f'[ERROR] Required directory "{name}" does not exist at {path}')
|
|
35
|
+
|
|
36
|
+
def _validate_optional_dirs(self, dirs):
|
|
37
|
+
"""Validate optional directories and return those that exist."""
|
|
38
|
+
existing_dirs = {}
|
|
39
|
+
for name, path in dirs.items():
|
|
40
|
+
if os.path.exists(path):
|
|
41
|
+
existing_dirs[name] = path
|
|
42
|
+
else:
|
|
43
|
+
print(f'[WARNING] Optional directory "{name}" does not exist. Skipping.')
|
|
44
|
+
return existing_dirs
|
|
45
|
+
|
|
46
|
+
def _validate_splits(self, required_splits, optional_splits=[]):
|
|
47
|
+
"""Validate required and optional splits in the dataset."""
|
|
48
|
+
splits = {}
|
|
49
|
+
|
|
50
|
+
if self.is_categorized_dataset:
|
|
51
|
+
required_dirs = {split: os.path.join(self.root_dir, split) for split in required_splits}
|
|
52
|
+
self._validate_required_dirs(required_dirs)
|
|
53
|
+
splits.update(required_dirs)
|
|
54
|
+
|
|
55
|
+
optional_dirs = {split: os.path.join(self.root_dir, split) for split in optional_splits}
|
|
56
|
+
splits.update(self._validate_optional_dirs(optional_dirs))
|
|
57
|
+
else:
|
|
58
|
+
required_dirs = {
|
|
59
|
+
'json': os.path.join(self.root_dir, 'json'),
|
|
60
|
+
'original_files': os.path.join(self.root_dir, 'original_files'),
|
|
61
|
+
}
|
|
62
|
+
self._validate_required_dirs(required_dirs)
|
|
63
|
+
splits['root'] = self.root_dir
|
|
64
|
+
|
|
65
|
+
return splits
|
|
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
|
+
|
|
85
|
+
def _set_directories(self, split=None):
|
|
86
|
+
"""Set `self.json_dir` and `self.original_file_dir` based on the dataset split."""
|
|
87
|
+
if split:
|
|
88
|
+
split_dir = os.path.join(self.root_dir, split)
|
|
89
|
+
self.json_dir = os.path.join(split_dir, 'json')
|
|
90
|
+
self.original_file_dir = os.path.join(split_dir, 'original_files')
|
|
91
|
+
else:
|
|
92
|
+
self.json_dir = os.path.join(self.root_dir, 'json')
|
|
93
|
+
self.original_file_dir = os.path.join(self.root_dir, 'original_files')
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class FromDMConverter(BaseConverter):
|
|
97
|
+
"""Base class for converting data from DM format to a specific format.
|
|
98
|
+
|
|
99
|
+
Attrs:
|
|
100
|
+
root_dir (str): Root directory containing data.
|
|
101
|
+
is_categorized_dataset (bool): Whether to handle train, test, valid splits.
|
|
102
|
+
version (str): Version of the converter.
|
|
103
|
+
converted_data: Holds the converted data after calling `convert()`.
|
|
104
|
+
|
|
105
|
+
Usage:
|
|
106
|
+
1. Subclass this base class and implement the `convert()` and `save_to_folder()` methods.
|
|
107
|
+
2. Instantiate the converter with the required arguments.
|
|
108
|
+
3. Call `convert()` to perform the in-memory conversion and obtain the result as a dict or list of dicts.
|
|
109
|
+
4. Call `save_to_folder(output_dir)` to save the converted data and optionally copy original files.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
root_dir (str): Path to the root directory containing data.
|
|
113
|
+
- If `is_categorized_dataset=True`, the directory should contain subdirectories for
|
|
114
|
+
`train`, `valid`, and optionally `test`.
|
|
115
|
+
- Each subdirectory should contain `json` and `original_file` folders.
|
|
116
|
+
- `train` and `valid` are required, while `test` is optional.
|
|
117
|
+
is_categorized_dataset (bool): Whether to handle train, test, valid splits.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
- convert(): Returns the converted data as a Python dict or a dictionary with keys for each split.
|
|
121
|
+
- save_to_folder(): Saves the converted data and optionally copies original files
|
|
122
|
+
to the specified output directory.
|
|
123
|
+
|
|
124
|
+
Example usage:
|
|
125
|
+
# Dataset with splits
|
|
126
|
+
converter = MyCustomConverter(root_dir='/path/to/data', is_categorized_dataset=True)
|
|
127
|
+
converted = converter.convert() # Returns a dict with keys for `train`, `valid`, and optionally `test`
|
|
128
|
+
converter.save_to_folder('/my/target/output') # Writes files/folders to output location
|
|
129
|
+
|
|
130
|
+
# Dataset without splits
|
|
131
|
+
converter = MyCustomConverter(root_dir='/path/to/data', is_categorized_dataset=False)
|
|
132
|
+
converted = converter.convert() # Returns a dict or a list, depending on the implementation
|
|
133
|
+
converter.save_to_folder('/my/target/output') # Writes files/folders to output location
|
|
134
|
+
"""
|
|
135
|
+
|
|
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)
|
|
140
|
+
self.version: str = '1.0'
|
|
141
|
+
|
|
142
|
+
def convert(self):
|
|
143
|
+
"""Convert DM format to a specific format.
|
|
144
|
+
|
|
145
|
+
This method should be implemented by subclasses to perform the actual conversion.
|
|
146
|
+
"""
|
|
147
|
+
raise NotImplementedError
|
|
148
|
+
|
|
149
|
+
def save_to_folder(self, output_dir: str) -> None:
|
|
150
|
+
"""Save converted data to the specified folder."""
|
|
151
|
+
self.ensure_dir(output_dir)
|
|
152
|
+
if self.converted_data is None:
|
|
153
|
+
# Automatically call convert() if converted_data is not set
|
|
154
|
+
self.converted_data = self.convert()
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class ToDMConverter(BaseConverter):
|
|
158
|
+
"""Base class for converting data to DM format.
|
|
159
|
+
|
|
160
|
+
Attrs:
|
|
161
|
+
root_dir (str): Root directory containing data.
|
|
162
|
+
is_categorized_dataset (bool): Whether to handle train, test, valid splits.
|
|
163
|
+
converted_data: Holds the converted data after calling `convert()`.
|
|
164
|
+
|
|
165
|
+
Usage:
|
|
166
|
+
1. Subclass this base class and implement the `convert()` method.
|
|
167
|
+
2. Instantiate the converter with the required arguments.
|
|
168
|
+
3. Call `convert()` to perform the in-memory conversion and obtain the result as a dict or list of dicts.
|
|
169
|
+
4. Call `save_to_folder(output_dir)` to save the converted data and optionally copy original files.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
root_dir (str): Path to the root directory containing data.
|
|
173
|
+
- If `is_categorized_dataset=True`, the directory should contain subdirectories for
|
|
174
|
+
`train`, `valid`, and optionally `test`.
|
|
175
|
+
- Each subdirectory should contain `annotations.json` and the corresponding image files.
|
|
176
|
+
- `train` and `valid` are required, while `test` is optional.
|
|
177
|
+
is_categorized_dataset (bool): Whether to handle train, test, valid splits.
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
- convert(): Returns the converted data as a Python dict or a dictionary with keys for each split.
|
|
181
|
+
- save_to_folder(): Saves the converted data and optionally copies original files
|
|
182
|
+
to the specified output directory.
|
|
183
|
+
|
|
184
|
+
Example usage:
|
|
185
|
+
# Dataset with splits
|
|
186
|
+
converter = MyCustomToDMConverter(root_dir='/path/to/data', is_categorized_dataset=True)
|
|
187
|
+
converted = converter.convert() # Returns a dict with keys for `train`, `valid`, and optionally `test`
|
|
188
|
+
converter.save_to_folder('/my/target/output') # Writes files/folders to output location
|
|
189
|
+
|
|
190
|
+
# Dataset without splits
|
|
191
|
+
converter = MyCustomToDMConverter(root_dir='/path/to/data', is_categorized_dataset=False)
|
|
192
|
+
converted = converter.convert() # Returns a dict or a list, depending on the implementation
|
|
193
|
+
converter.save_to_folder('/my/target/output') # Writes files/folders to output location
|
|
194
|
+
"""
|
|
195
|
+
|
|
196
|
+
def convert(self):
|
|
197
|
+
"""Convert data to DM format.
|
|
198
|
+
|
|
199
|
+
This method should be implemented by subclasses to perform the actual conversion.
|
|
200
|
+
"""
|
|
201
|
+
raise NotImplementedError
|
|
202
|
+
|
|
203
|
+
def _generate_unique_id(self):
|
|
204
|
+
"""Generate a unique 10-character UUID."""
|
|
205
|
+
return uuid.uuid4().hex[:10]
|
|
206
|
+
|
|
207
|
+
def save_to_folder(self, output_dir: str) -> None:
|
|
208
|
+
"""Save converted DM schema data to the specified folder."""
|
|
209
|
+
self.ensure_dir(output_dir)
|
|
210
|
+
if self.converted_data is None:
|
|
211
|
+
self.converted_data = self.convert()
|
|
212
|
+
|
|
213
|
+
if self.is_categorized_dataset:
|
|
214
|
+
for split, img_dict in self.converted_data.items():
|
|
215
|
+
split_dir = os.path.join(output_dir, split)
|
|
216
|
+
json_dir = os.path.join(split_dir, 'json')
|
|
217
|
+
original_file_dir = os.path.join(split_dir, 'original_files')
|
|
218
|
+
self.ensure_dir(json_dir)
|
|
219
|
+
self.ensure_dir(original_file_dir)
|
|
220
|
+
for img_filename, (dm_json, img_src_path) in img_dict.items():
|
|
221
|
+
json_filename = os.path.splitext(img_filename)[0] + '.json'
|
|
222
|
+
with open(os.path.join(json_dir, json_filename), 'w', encoding='utf-8') as jf:
|
|
223
|
+
json.dump(dm_json, jf, indent=2, ensure_ascii=False)
|
|
224
|
+
if img_src_path:
|
|
225
|
+
if not os.path.exists(img_src_path):
|
|
226
|
+
raise FileNotFoundError(f'Source file does not exist: {img_src_path}')
|
|
227
|
+
shutil.copy(img_src_path, os.path.join(original_file_dir, img_filename))
|
|
228
|
+
else:
|
|
229
|
+
json_dir = os.path.join(output_dir, 'json')
|
|
230
|
+
original_file_dir = os.path.join(output_dir, 'original_files')
|
|
231
|
+
self.ensure_dir(json_dir)
|
|
232
|
+
self.ensure_dir(original_file_dir)
|
|
233
|
+
for img_filename, (dm_json, img_src_path) in self.converted_data.items():
|
|
234
|
+
json_filename = os.path.splitext(img_filename)[0] + '.json'
|
|
235
|
+
with open(os.path.join(json_dir, json_filename), 'w', encoding='utf-8') as jf:
|
|
236
|
+
json.dump(dm_json, jf, indent=2, ensure_ascii=False)
|
|
237
|
+
if img_src_path and os.path.exists(img_src_path):
|
|
238
|
+
shutil.copy(img_src_path, os.path.join(original_file_dir, img_filename))
|
|
239
|
+
|
|
240
|
+
print(f'[DM] Data exported to {output_dir}')
|
|
File without changes
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
import shutil
|
|
5
|
+
from glob import glob
|
|
6
|
+
from typing import IO, Any, Dict
|
|
7
|
+
|
|
8
|
+
from PIL import Image
|
|
9
|
+
from tqdm import tqdm
|
|
10
|
+
|
|
11
|
+
from synapse_sdk.utils.converters import FromDMConverter
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class FromDMToCOCOConverter(FromDMConverter):
|
|
15
|
+
"""Convert DM (Data Manager) format annotations to COCO format.
|
|
16
|
+
Designed for easy future extensibility to handle various data types.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
SUPPORTED_TYPES = {
|
|
20
|
+
'img': ['.jpg', '.jpeg', '.png', '.bmp', '.tif', '.tiff', '.webp'],
|
|
21
|
+
# 'video': ['.mp4', '.avi', ...],
|
|
22
|
+
# 'audio': ['.wav', '.mp3', ...]
|
|
23
|
+
}
|
|
24
|
+
|
|
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
|
+
):
|
|
34
|
+
"""Args:
|
|
35
|
+
root_dir (str): Root directory containing data.
|
|
36
|
+
info_dict, licenses_list: COCO metadata.
|
|
37
|
+
data_type (str): Which data type to use (default: 'img').
|
|
38
|
+
is_categorized_dataset (bool): Whether to handle train, test, valid splits.
|
|
39
|
+
is_single_conversion (bool): Whether to use single file conversion mode.
|
|
40
|
+
"""
|
|
41
|
+
super().__init__(root_dir, is_categorized_dataset, is_single_conversion)
|
|
42
|
+
self.data_type = data_type
|
|
43
|
+
self.info_dict = info_dict or self._default_info()
|
|
44
|
+
self.licenses_list = licenses_list or self._default_licenses()
|
|
45
|
+
self.reset_state()
|
|
46
|
+
|
|
47
|
+
# --- Helpers & State --- #
|
|
48
|
+
|
|
49
|
+
def reset_state(self):
|
|
50
|
+
self.coco_dict = None
|
|
51
|
+
self.category_name_to_id = {}
|
|
52
|
+
self.annotation_id = 1
|
|
53
|
+
self.img_id = 1
|
|
54
|
+
self.license_id = self.licenses_list[0]['id'] if self.licenses_list else 1
|
|
55
|
+
|
|
56
|
+
def _default_info(self):
|
|
57
|
+
now = datetime.datetime.now()
|
|
58
|
+
return {
|
|
59
|
+
'description': 'Converted from DM format',
|
|
60
|
+
'url': '',
|
|
61
|
+
'version': self.version,
|
|
62
|
+
'year': now.year,
|
|
63
|
+
'contributor': '',
|
|
64
|
+
'date_created': now.strftime('%Y-%m-%d %H:%M:%S'),
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@staticmethod
|
|
68
|
+
def _default_licenses():
|
|
69
|
+
return [{'id': 1, 'name': 'Unknown', 'url': ''}]
|
|
70
|
+
|
|
71
|
+
@staticmethod
|
|
72
|
+
def ensure_dir(path):
|
|
73
|
+
os.makedirs(path, exist_ok=True)
|
|
74
|
+
|
|
75
|
+
# --- File Pairing --- #
|
|
76
|
+
|
|
77
|
+
def _collect_files(self):
|
|
78
|
+
"""Return {basename: file_path} for all supported files in this data type."""
|
|
79
|
+
exts = self.SUPPORTED_TYPES[self.data_type]
|
|
80
|
+
files = {}
|
|
81
|
+
for ext in exts:
|
|
82
|
+
for f in glob(os.path.join(self.original_file_dir, f'*{ext}')):
|
|
83
|
+
base = os.path.splitext(os.path.basename(f))[0]
|
|
84
|
+
files[base] = f
|
|
85
|
+
return files
|
|
86
|
+
|
|
87
|
+
def _find_json_file_pairs(self):
|
|
88
|
+
"""Return list of (json_path, data_path) pairs with matching basenames."""
|
|
89
|
+
if not hasattr(self, 'json_dir') or not self.json_dir:
|
|
90
|
+
self._set_directories()
|
|
91
|
+
|
|
92
|
+
json_files = sorted(glob(os.path.join(self.json_dir, '*.json')))
|
|
93
|
+
file_map = self._collect_files()
|
|
94
|
+
result = []
|
|
95
|
+
for json_file in json_files:
|
|
96
|
+
file_name = os.path.splitext(os.path.basename(json_file))[0]
|
|
97
|
+
if file_name in file_map:
|
|
98
|
+
result.append((json_file, file_map[file_name]))
|
|
99
|
+
return result
|
|
100
|
+
|
|
101
|
+
# --- COCO Info Extraction --- #
|
|
102
|
+
|
|
103
|
+
def _image_info(self, img_path):
|
|
104
|
+
with Image.open(img_path) as im:
|
|
105
|
+
width, height = im.size
|
|
106
|
+
return {
|
|
107
|
+
'id': self.img_id,
|
|
108
|
+
'file_name': os.path.basename(img_path),
|
|
109
|
+
'width': width,
|
|
110
|
+
'height': height,
|
|
111
|
+
'license': self.license_id,
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@staticmethod
|
|
115
|
+
def _poly_to_bbox(poly):
|
|
116
|
+
xs = [p[0] for p in poly]
|
|
117
|
+
ys = [p[1] for p in poly]
|
|
118
|
+
x_min, y_min = min(xs), min(ys)
|
|
119
|
+
x_max, y_max = max(xs), max(ys)
|
|
120
|
+
return [x_min, y_min, x_max - x_min, y_max - y_min]
|
|
121
|
+
|
|
122
|
+
@staticmethod
|
|
123
|
+
def _poly_to_segmentation(poly):
|
|
124
|
+
return [sum(poly, [])]
|
|
125
|
+
|
|
126
|
+
# --- Category Management --- #
|
|
127
|
+
|
|
128
|
+
def _get_or_create_category(self, name, keypoints=None):
|
|
129
|
+
if name not in self.category_name_to_id:
|
|
130
|
+
cid = len(self.category_name_to_id) + 1
|
|
131
|
+
self.category_name_to_id[name] = cid
|
|
132
|
+
cat = {'id': cid, 'name': name, 'supercategory': name}
|
|
133
|
+
if keypoints:
|
|
134
|
+
cat['keypoints'] = keypoints
|
|
135
|
+
cat['skeleton'] = []
|
|
136
|
+
self.coco_dict['categories'].append(cat)
|
|
137
|
+
return self.category_name_to_id[name]
|
|
138
|
+
|
|
139
|
+
# --- Annotation Processing --- #
|
|
140
|
+
|
|
141
|
+
def _process_polylines(self, anns):
|
|
142
|
+
for poly in anns.get('polyline', []):
|
|
143
|
+
cat_id = self._get_or_create_category(poly['classification'])
|
|
144
|
+
seg = self._poly_to_segmentation(poly['data'])
|
|
145
|
+
bbox = self._poly_to_bbox(poly['data'])
|
|
146
|
+
self._add_annotation(cat_id, seg, bbox, area=bbox[2] * bbox[3])
|
|
147
|
+
|
|
148
|
+
def _process_bboxes(self, anns):
|
|
149
|
+
for box in anns.get('bounding_box', []):
|
|
150
|
+
cat_id = self._get_or_create_category(box['classification'])
|
|
151
|
+
bbox = box['data']
|
|
152
|
+
self._add_annotation(cat_id, [], bbox, area=bbox[2] * bbox[3])
|
|
153
|
+
|
|
154
|
+
def _process_keypoints(self, anns):
|
|
155
|
+
if 'keypoint' not in anns:
|
|
156
|
+
return
|
|
157
|
+
keypoints = anns['keypoint']
|
|
158
|
+
cat_id = self._get_or_create_category('keypoints', [kp['classification'] for kp in keypoints])
|
|
159
|
+
kps, xs, ys = [], [], []
|
|
160
|
+
for kp in keypoints:
|
|
161
|
+
x, y = kp['data']
|
|
162
|
+
kps += [x, y, 2]
|
|
163
|
+
xs.append(x)
|
|
164
|
+
ys.append(y)
|
|
165
|
+
bbox = [min(xs), min(ys), max(xs) - min(xs), max(ys) - min(ys)]
|
|
166
|
+
self.coco_dict['annotations'].append({
|
|
167
|
+
'id': self.annotation_id,
|
|
168
|
+
'image_id': self.img_id,
|
|
169
|
+
'category_id': cat_id,
|
|
170
|
+
'keypoints': kps,
|
|
171
|
+
'num_keypoints': len(keypoints),
|
|
172
|
+
'bbox': bbox,
|
|
173
|
+
'area': 0,
|
|
174
|
+
'iscrowd': 0,
|
|
175
|
+
})
|
|
176
|
+
self.annotation_id += 1
|
|
177
|
+
|
|
178
|
+
def _add_annotation(self, cat_id, segmentation, bbox, area):
|
|
179
|
+
self.coco_dict['annotations'].append({
|
|
180
|
+
'id': self.annotation_id,
|
|
181
|
+
'image_id': self.img_id,
|
|
182
|
+
'category_id': cat_id,
|
|
183
|
+
'segmentation': segmentation,
|
|
184
|
+
'bbox': bbox,
|
|
185
|
+
'iscrowd': 0,
|
|
186
|
+
'area': area,
|
|
187
|
+
})
|
|
188
|
+
self.annotation_id += 1
|
|
189
|
+
|
|
190
|
+
# --- Main Conversion Logic --- #
|
|
191
|
+
|
|
192
|
+
def convert(self) -> Dict[str, Any]:
|
|
193
|
+
"""Convert DM format to COCO format, supporting dataset splits."""
|
|
194
|
+
if self.is_categorized_dataset:
|
|
195
|
+
required_splits = ['train', 'valid']
|
|
196
|
+
optional_splits = ['test']
|
|
197
|
+
split_dirs = self._validate_splits(required_splits, optional_splits)
|
|
198
|
+
|
|
199
|
+
result = {}
|
|
200
|
+
for split in split_dirs.keys():
|
|
201
|
+
self._set_directories(split)
|
|
202
|
+
self.reset_state()
|
|
203
|
+
result[split] = self._convert_single_split()
|
|
204
|
+
|
|
205
|
+
return result
|
|
206
|
+
else:
|
|
207
|
+
self._set_directories()
|
|
208
|
+
return self._convert_single_split()
|
|
209
|
+
|
|
210
|
+
def _convert_single_split(self) -> Dict[str, Any]:
|
|
211
|
+
"""Convert a single dataset split."""
|
|
212
|
+
self.reset_state()
|
|
213
|
+
self.coco_dict = {
|
|
214
|
+
'info': self.info_dict,
|
|
215
|
+
'licenses': self.licenses_list,
|
|
216
|
+
'images': [],
|
|
217
|
+
'annotations': [],
|
|
218
|
+
'categories': [],
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
pairs = self._find_json_file_pairs()
|
|
222
|
+
if not pairs:
|
|
223
|
+
raise FileNotFoundError(
|
|
224
|
+
f'No matching JSON-{self.data_type} pairs found in {self.json_dir} and {self.original_file_dir}'
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
for json_path, data_path in tqdm(pairs, desc=f'Converting to COCO ({self.data_type})'):
|
|
228
|
+
try:
|
|
229
|
+
with open(json_path, encoding='utf-8') as jf:
|
|
230
|
+
data = json.load(jf)
|
|
231
|
+
|
|
232
|
+
self.coco_dict['images'].append(self._image_info(data_path))
|
|
233
|
+
anns = data.get('images', [{}])[0]
|
|
234
|
+
|
|
235
|
+
self._process_polylines(anns)
|
|
236
|
+
self._process_bboxes(anns)
|
|
237
|
+
self._process_keypoints(anns)
|
|
238
|
+
self.img_id += 1
|
|
239
|
+
|
|
240
|
+
except Exception as e:
|
|
241
|
+
print(f'[ERROR] {json_path}: {e}')
|
|
242
|
+
continue
|
|
243
|
+
|
|
244
|
+
return self.coco_dict
|
|
245
|
+
|
|
246
|
+
def save_to_folder(self, output_dir):
|
|
247
|
+
"""Save the converted COCO data and original files to the specified folder."""
|
|
248
|
+
super().save_to_folder(output_dir)
|
|
249
|
+
|
|
250
|
+
if self.is_categorized_dataset:
|
|
251
|
+
for split, coco_data in self.converted_data.items():
|
|
252
|
+
split_output_dir = os.path.join(output_dir, split)
|
|
253
|
+
self._save_annotations_and_images(
|
|
254
|
+
coco_data, split_output_dir, os.path.join(self.root_dir, split, 'original_files')
|
|
255
|
+
)
|
|
256
|
+
else:
|
|
257
|
+
self._save_annotations_and_images(
|
|
258
|
+
self.converted_data, output_dir, os.path.join(self.root_dir, 'original_files')
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
def _save_annotations_and_images(self, coco_data, output_dir, original_file_dir):
|
|
262
|
+
"""Helper method to save annotations and copy original images."""
|
|
263
|
+
self.ensure_dir(output_dir)
|
|
264
|
+
|
|
265
|
+
# Save annotations
|
|
266
|
+
json_path = os.path.join(output_dir, 'annotations.json')
|
|
267
|
+
with open(json_path, 'w', encoding='utf-8') as f:
|
|
268
|
+
json.dump(coco_data, f, indent=4, ensure_ascii=False)
|
|
269
|
+
print(f'COCO annotations saved to {json_path}')
|
|
270
|
+
|
|
271
|
+
# Copy original images
|
|
272
|
+
for image in coco_data['images']:
|
|
273
|
+
src_path = os.path.join(original_file_dir, image['file_name'])
|
|
274
|
+
dst_path = os.path.join(output_dir, image['file_name'])
|
|
275
|
+
if os.path.exists(src_path):
|
|
276
|
+
shutil.copy(src_path, dst_path)
|
|
277
|
+
else:
|
|
278
|
+
print(f'[WARNING] Image not found: {src_path}')
|
|
279
|
+
|
|
280
|
+
def convert_single_file(self, data: Dict[str, Any], original_file: IO, file_name: str) -> 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': file_name,
|
|
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
|