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
synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/export-plugins.md
ADDED
|
@@ -0,0 +1,1092 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: export-plugins
|
|
3
|
+
title: Export 플러그인
|
|
4
|
+
sidebar_position: 2
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Export 플러그인
|
|
8
|
+
|
|
9
|
+
Export 플러그인은 Synapse 플랫폼에서 주석이 달린 데이터, 그라운드 트루스 데이터셋, 할당 및 작업을 내보내기 위한 데이터 내보내기 및 변환 작업을 제공합니다.
|
|
10
|
+
|
|
11
|
+
## 개요
|
|
12
|
+
|
|
13
|
+
**사용 가능한 액션:**
|
|
14
|
+
|
|
15
|
+
- `export` - 다양한 소스(할당, 그라운드 트루스, 작업)에서 사용자 정의 처리와 함께 데이터 내보내기
|
|
16
|
+
|
|
17
|
+
**사용 사례:**
|
|
18
|
+
|
|
19
|
+
- 훈련용 주석 데이터셋 내보내기
|
|
20
|
+
- 그라운드 트루스 데이터를 사용자 정의 형식으로 변환
|
|
21
|
+
- 배포용 데이터 패키지 생성
|
|
22
|
+
- 할당 결과의 배치 처리
|
|
23
|
+
- 외부 도구용 주석 데이터 변환
|
|
24
|
+
|
|
25
|
+
**지원되는 내보내기 대상:**
|
|
26
|
+
|
|
27
|
+
- `assignment` - 주석이 있는 할당 데이터 내보내기
|
|
28
|
+
- `ground_truth` - 그라운드 트루스 데이터셋 버전 내보내기
|
|
29
|
+
- `task` - 관련 주석이 있는 작업 데이터 내보내기
|
|
30
|
+
|
|
31
|
+
## BaseExporter와 Exporter 클래스 구조도
|
|
32
|
+
|
|
33
|
+
다음 다이어그램은 BaseExporter 클래스와 Exporter 클래스 간의 관계와 메서드 구현을 보여줍니다:
|
|
34
|
+
|
|
35
|
+
```mermaid
|
|
36
|
+
classDiagram
|
|
37
|
+
%% Light/Dark mode compatible colors using CSS variables
|
|
38
|
+
classDef baseClass fill:#f0f8ff,stroke:#4169e1,stroke-width:2px,color:#000
|
|
39
|
+
classDef childClass fill:#f0fff0,stroke:#228b22,stroke-width:2px,color:#000
|
|
40
|
+
classDef method fill:#fff8dc,stroke:#daa520,stroke-width:1px,color:#000
|
|
41
|
+
classDef abstractMethod fill:#ffe4e1,stroke:#dc143c,stroke-width:1px,color:#000
|
|
42
|
+
classDef helperMethod fill:#f5f5f5,stroke:#696969,stroke-width:1px,color:#000
|
|
43
|
+
|
|
44
|
+
class BaseExporter {
|
|
45
|
+
%% Core attributes
|
|
46
|
+
+run: object
|
|
47
|
+
+export_items: Generator
|
|
48
|
+
+path_root: Path
|
|
49
|
+
+params: dict
|
|
50
|
+
|
|
51
|
+
%% Main workflow methods
|
|
52
|
+
+export(export_items, results, **kwargs) dict
|
|
53
|
+
+process_data_conversion(export_item) object
|
|
54
|
+
+process_file_saving(...) bool
|
|
55
|
+
+setup_output_directories(path, flag) dict
|
|
56
|
+
|
|
57
|
+
%% Abstract methods (to be implemented)
|
|
58
|
+
+convert_data(data)* object
|
|
59
|
+
+before_convert(data)* object
|
|
60
|
+
+after_convert(data)* object
|
|
61
|
+
|
|
62
|
+
%% File operations
|
|
63
|
+
+save_original_file(result, path, errors) ExportStatus
|
|
64
|
+
+save_as_json(result, path, errors) ExportStatus
|
|
65
|
+
+get_original_file_name(files) str
|
|
66
|
+
|
|
67
|
+
%% Helper methods
|
|
68
|
+
-_create_unique_export_path(name) Path
|
|
69
|
+
-_save_error_list(path, json_errors, file_errors)
|
|
70
|
+
-_process_original_file_saving(...) bool
|
|
71
|
+
-_process_json_file_saving(...) bool
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
class Exporter {
|
|
75
|
+
%% Inherited from BaseExporter
|
|
76
|
+
+export(export_items, results, **kwargs) dict
|
|
77
|
+
|
|
78
|
+
%% Implemented abstract methods
|
|
79
|
+
+convert_data(data) object
|
|
80
|
+
+before_convert(data) object
|
|
81
|
+
+after_convert(data) object
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
%% Inheritance relationship
|
|
85
|
+
BaseExporter <|-- Exporter
|
|
86
|
+
|
|
87
|
+
%% Apply styles
|
|
88
|
+
class BaseExporter baseClass
|
|
89
|
+
class Exporter childClass
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 메서드 실행 플로우
|
|
93
|
+
|
|
94
|
+
Export 작업의 전체 실행 흐름을 보여주는 플로우차트입니다:
|
|
95
|
+
|
|
96
|
+
```mermaid
|
|
97
|
+
flowchart TD
|
|
98
|
+
%% Start
|
|
99
|
+
A[export 메서드 호출] --> B[경로와 메트릭 초기화]
|
|
100
|
+
B --> C[출력 디렉토리 설정]
|
|
101
|
+
C --> D[export_items 순환]
|
|
102
|
+
|
|
103
|
+
%% Data processing pipeline
|
|
104
|
+
D --> E[process_data_conversion]
|
|
105
|
+
E --> F[before_convert]
|
|
106
|
+
F --> G[convert_data]
|
|
107
|
+
G --> H[after_convert]
|
|
108
|
+
|
|
109
|
+
%% File saving pipeline
|
|
110
|
+
H --> I[process_file_saving]
|
|
111
|
+
I --> J{원본 파일 저장?}
|
|
112
|
+
J -->|예| K[_process_original_file_saving]
|
|
113
|
+
J -->|아니오| L[_process_json_file_saving]
|
|
114
|
+
K --> L
|
|
115
|
+
|
|
116
|
+
%% Continue or finish
|
|
117
|
+
L --> M{더 많은 항목?}
|
|
118
|
+
M -->|예| D
|
|
119
|
+
M -->|아니오| N[오류 목록 저장]
|
|
120
|
+
N --> O[내보내기 경로 반환]
|
|
121
|
+
|
|
122
|
+
%% Error handling
|
|
123
|
+
K --> P{원본 파일 실패?}
|
|
124
|
+
P -->|예| Q[다음 항목으로 건너뛰기]
|
|
125
|
+
P -->|아니오| L
|
|
126
|
+
|
|
127
|
+
L --> R{JSON 파일 실패?}
|
|
128
|
+
R -->|예| Q
|
|
129
|
+
R -->|아니오| S[메트릭 업데이트]
|
|
130
|
+
S --> M
|
|
131
|
+
Q --> M
|
|
132
|
+
|
|
133
|
+
%% Styling for light/dark compatibility
|
|
134
|
+
classDef startEnd fill:#e1f5fe,stroke:#01579b,color:#000
|
|
135
|
+
classDef process fill:#f3e5f5,stroke:#4a148c,color:#000
|
|
136
|
+
classDef decision fill:#fff3e0,stroke:#e65100,color:#000
|
|
137
|
+
classDef data fill:#e8f5e8,stroke:#2e7d32,color:#000
|
|
138
|
+
classDef error fill:#ffebee,stroke:#c62828,color:#000
|
|
139
|
+
|
|
140
|
+
class A,O startEnd
|
|
141
|
+
class B,C,E,F,G,H,I,K,L,N,S process
|
|
142
|
+
class J,M,P,R decision
|
|
143
|
+
class D data
|
|
144
|
+
class Q error
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### 주요 관계 및 책임
|
|
148
|
+
|
|
149
|
+
**BaseExporter (추상 기본 클래스)**
|
|
150
|
+
|
|
151
|
+
- **핵심 기능**: 완전한 내보내기 워크플로우 인프라 제공
|
|
152
|
+
- **템플릿 메서드**: `export()` 메서드가 전체 프로세스 조율
|
|
153
|
+
- **훅 메서드**: 커스터마이징을 위한 `convert_data()`, `before_convert()`, `after_convert()`
|
|
154
|
+
- **유틸리티**: 파일 작업, 디렉토리 설정, 오류 처리, 진행률 추적
|
|
155
|
+
|
|
156
|
+
**Exporter (구체적 구현)**
|
|
157
|
+
|
|
158
|
+
- **상속**: `BaseExporter` 확장
|
|
159
|
+
- **최소 구현**: 추상 메서드들의 기본 구현 제공
|
|
160
|
+
- **위임 동작**: 대부분의 메서드가 부모 클래스에 위임
|
|
161
|
+
- **커스터마이징 지점**: 특정 로직을 위해 변환 메서드 오버라이드
|
|
162
|
+
|
|
163
|
+
### 메서드 카테고리
|
|
164
|
+
|
|
165
|
+
- **🔵 핵심 워크플로우**: 주요 내보내기 조율 메서드
|
|
166
|
+
- **🟢 템플릿/훅**: 서브클래스에서 오버라이드하도록 설계된 메서드
|
|
167
|
+
- **🟡 파일 작업**: 구체적인 파일 저장 및 처리 메서드
|
|
168
|
+
- **🔸 헬퍼/유틸리티**: 내부 작업을 위한 프라이빗 메서드
|
|
169
|
+
|
|
170
|
+
이 설계는 **템플릿 메서드 패턴**을 따르며, `BaseExporter.export()`가 알고리즘 골격을 정의하고 서브클래스가 훅 메서드를 통해 특정 단계를 커스터마이징합니다.
|
|
171
|
+
|
|
172
|
+
## 플러그인 설정
|
|
173
|
+
|
|
174
|
+
Export 플러그인 템플릿은 필터링 및 플러그인 검색을 위한 설정 필드를 포함합니다:
|
|
175
|
+
|
|
176
|
+
```yaml
|
|
177
|
+
actions:
|
|
178
|
+
export:
|
|
179
|
+
entrypoint: plugin.export.Exporter
|
|
180
|
+
annotation_types:
|
|
181
|
+
- image
|
|
182
|
+
- video
|
|
183
|
+
- audio
|
|
184
|
+
- text
|
|
185
|
+
- pcd
|
|
186
|
+
- prompt
|
|
187
|
+
|
|
188
|
+
data_types:
|
|
189
|
+
- image
|
|
190
|
+
- video
|
|
191
|
+
- audio
|
|
192
|
+
- text
|
|
193
|
+
- pcd
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### 설정 필드
|
|
197
|
+
|
|
198
|
+
- **data_types**: Export 플러그인 필터링을 위한 지원 데이터 타입 목록 (플러그인 레벨 필터)
|
|
199
|
+
|
|
200
|
+
- 지원 값: `image`, `video`, `audio`, `text`, `pcd`
|
|
201
|
+
- 플랫폼에서 사용자의 데이터 타입에 따라 관련 export 플러그인을 필터링하고 표시하는 데 사용됩니다
|
|
202
|
+
|
|
203
|
+
- **annotation_types**: Export 플러그인 필터링을 위한 어노테이션 타입 목록 (액션 레벨 필터)
|
|
204
|
+
- 지원 값: `image`, `video`, `audio`, `text`, `pcd`, `prompt`
|
|
205
|
+
- 각 액션의 설정 내에서 정의됩니다 (예: `actions.export.annotation_types`)
|
|
206
|
+
- 플랫폼에서 사용자의 주석 타입에 따라 관련 export 플러그인을 필터링하고 표시하는 데 사용됩니다
|
|
207
|
+
- 각 액션마다 다른 어노테이션 타입 요구사항을 가질 수 있습니다
|
|
208
|
+
|
|
209
|
+
**모범 사례**: 플러그인의 실제 기능을 정확히 반영하도록 이 필드들을 커스터마이징하세요. 템플릿에는 모든 일반적인 타입이 예시로 포함되어 있지만, 플러그인이 실제로 지원하는 항목에 맞게 목록을 수정해야 합니다.
|
|
210
|
+
|
|
211
|
+
## BaseExporter 클래스 구조
|
|
212
|
+
|
|
213
|
+
새로운 BaseExporter 클래스는 export 플러그인을 위한 객체지향적 접근 방식을 제공합니다:
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
from synapse_sdk.plugins.categories.export.templates.plugin import BaseExporter
|
|
217
|
+
|
|
218
|
+
class Exporter(BaseExporter):
|
|
219
|
+
"""플러그인 export 액션 인터페이스."""
|
|
220
|
+
|
|
221
|
+
def __init__(self, run, export_items, path_root, **params):
|
|
222
|
+
"""플러그인 export 액션 클래스를 초기화합니다."""
|
|
223
|
+
super().__init__(run, export_items, path_root, **params)
|
|
224
|
+
|
|
225
|
+
def convert_data(self, data):
|
|
226
|
+
"""데이터 변환 로직을 구현하세요."""
|
|
227
|
+
return data
|
|
228
|
+
|
|
229
|
+
def before_convert(self, data):
|
|
230
|
+
"""변환 전 데이터 전처리를 수행합니다."""
|
|
231
|
+
return data
|
|
232
|
+
|
|
233
|
+
def after_convert(self, data):
|
|
234
|
+
"""변환 후 데이터 후처리를 수행합니다."""
|
|
235
|
+
return data
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## BaseExporter의 핵심 기능
|
|
239
|
+
|
|
240
|
+
### 자동 제공 유틸리티
|
|
241
|
+
|
|
242
|
+
- **완전한 export 워크플로우**: `export()` 메서드가 전체 export 프로세스를 관리
|
|
243
|
+
- **데이터 변환 파이프라인**: `process_data_conversion()` 메서드로 before_convert → convert_data → after_convert 처리
|
|
244
|
+
- **파일 저장 관리**: `process_file_saving()` 메서드로 원본 파일과 JSON 파일 저장 처리 (오버라이드 가능)
|
|
245
|
+
- **디렉토리 설정**: `setup_output_directories()` 메서드로 출력 디렉토리 구조 생성 (오버라이드 가능)
|
|
246
|
+
|
|
247
|
+
### 필수 메서드 (서브클래스에서 구현해야 함)
|
|
248
|
+
|
|
249
|
+
- **convert_data()**: export 중 데이터 변환
|
|
250
|
+
|
|
251
|
+
### 선택적 메서드 (서브클래스에서 오버라이드 가능)
|
|
252
|
+
|
|
253
|
+
- **save_original_file()**: export 항목의 원본 파일 저장
|
|
254
|
+
- **save_as_json()**: 데이터를 JSON 파일로 저장
|
|
255
|
+
- **before_convert()**: 변환 전 데이터 전처리
|
|
256
|
+
- **after_convert()**: 변환 후 데이터 후처리
|
|
257
|
+
- **process_file_saving()**: 사용자 정의 파일 저장 로직
|
|
258
|
+
- **additional_file_saving()**: 모든 export 항목 처리 후 추가 파일 저장
|
|
259
|
+
|
|
260
|
+
### 헬퍼 메서드
|
|
261
|
+
|
|
262
|
+
- **\_process_original_file_saving()**: 메트릭과 함께 원본 파일 저장 처리
|
|
263
|
+
- **\_process_json_file_saving()**: 메트릭과 함께 JSON 파일 저장 처리
|
|
264
|
+
|
|
265
|
+
### 자동 제공 유틸리티
|
|
266
|
+
|
|
267
|
+
- `self.run.set_progress()`를 통한 진행률 추적
|
|
268
|
+
- `self.run.log_message()` 및 기타 run 메서드를 통한 로깅
|
|
269
|
+
- run 메서드를 통한 오류 처리 및 메트릭 수집
|
|
270
|
+
|
|
271
|
+
## 추가 파일 저장 (Additional File Saving)
|
|
272
|
+
|
|
273
|
+
`additional_file_saving()` 메서드는 모든 export 항목이 처리된 후에 호출되며, 모든 처리된 항목의 집합적 데이터에 의존하는 파일을 저장하기 위해 설계되었습니다. 다음과 같은 용도로 유용합니다:
|
|
274
|
+
|
|
275
|
+
- 메타데이터 파일 (예: 데이터셋 통계, 클래스 매핑)
|
|
276
|
+
- 설정 파일 (예: YOLO용 dataset.yaml, classes.txt)
|
|
277
|
+
- 요약 파일 (예: export 보고서, 처리 로그)
|
|
278
|
+
- 인덱스 파일 (예: 파일 목록, 디렉토리 구조)
|
|
279
|
+
|
|
280
|
+
### 메서드 시그니처
|
|
281
|
+
|
|
282
|
+
```python
|
|
283
|
+
def additional_file_saving(self, unique_export_path):
|
|
284
|
+
"""모든 export 항목 처리 후 추가 파일 저장.
|
|
285
|
+
|
|
286
|
+
이 메서드는 주 export 루프가 완료된 후 호출되며, 모든 처리된 export 항목의
|
|
287
|
+
집합적 데이터를 기반으로 생성되어야 하는 파일들(예: 메타데이터 파일,
|
|
288
|
+
설정 파일, 요약 파일 등)을 저장하기 위한 것입니다.
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
unique_export_path (str): 추가 파일이 저장될 고유한 export 디렉토리 경로.
|
|
292
|
+
"""
|
|
293
|
+
pass
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### 사용 예시
|
|
297
|
+
|
|
298
|
+
```python
|
|
299
|
+
class YOLOExporter(BaseExporter):
|
|
300
|
+
def __init__(self, run, export_items, path_root, **params):
|
|
301
|
+
super().__init__(run, export_items, path_root, **params)
|
|
302
|
+
self.class_names = set()
|
|
303
|
+
self.dataset_stats = {
|
|
304
|
+
'total_images': 0,
|
|
305
|
+
'total_annotations': 0,
|
|
306
|
+
'class_distribution': {}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
def convert_data(self, data):
|
|
310
|
+
# 변환 중 클래스와 통계 추적
|
|
311
|
+
for annotation in data.get('annotations', []):
|
|
312
|
+
class_name = annotation['class_name']
|
|
313
|
+
self.class_names.add(class_name)
|
|
314
|
+
self.dataset_stats['class_distribution'][class_name] = \
|
|
315
|
+
self.dataset_stats['class_distribution'].get(class_name, 0) + 1
|
|
316
|
+
|
|
317
|
+
self.dataset_stats['total_images'] += 1
|
|
318
|
+
self.dataset_stats['total_annotations'] += len(data.get('annotations', []))
|
|
319
|
+
|
|
320
|
+
return data # ... 나머지 변환 로직
|
|
321
|
+
|
|
322
|
+
def additional_file_saving(self, unique_export_path):
|
|
323
|
+
"""YOLO 설정 및 메타데이터 파일 저장."""
|
|
324
|
+
data_dir = Path(unique_export_path) / 'data'
|
|
325
|
+
data_dir.mkdir(exist_ok=True)
|
|
326
|
+
|
|
327
|
+
# 1. classes.txt 파일 저장
|
|
328
|
+
classes_file = data_dir / 'classes.txt'
|
|
329
|
+
with classes_file.open('w') as f:
|
|
330
|
+
for class_name in sorted(self.class_names):
|
|
331
|
+
f.write(f"{class_name}\n")
|
|
332
|
+
self.run.log_message(f"클래스 파일 저장: {classes_file}")
|
|
333
|
+
|
|
334
|
+
# 2. dataset.yaml 파일 저장
|
|
335
|
+
dataset_config = {
|
|
336
|
+
'path': str(unique_export_path),
|
|
337
|
+
'train': 'images',
|
|
338
|
+
'val': 'images',
|
|
339
|
+
'names': {i: name for i, name in enumerate(sorted(self.class_names))}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
dataset_file = data_dir / 'dataset.yaml'
|
|
343
|
+
with dataset_file.open('w') as f:
|
|
344
|
+
yaml.dump(dataset_config, f, default_flow_style=False)
|
|
345
|
+
self.run.log_message(f"데이터셋 설정 저장: {dataset_file}")
|
|
346
|
+
|
|
347
|
+
# 3. export 통계 저장
|
|
348
|
+
stats_file = data_dir / 'export_stats.json'
|
|
349
|
+
with stats_file.open('w') as f:
|
|
350
|
+
json.dump(self.dataset_stats, f, indent=2)
|
|
351
|
+
self.run.log_message(f"export 통계 저장: {stats_file}")
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### 일반적인 사용 사례
|
|
355
|
+
|
|
356
|
+
#### 1. 데이터셋 설정 파일
|
|
357
|
+
|
|
358
|
+
```python
|
|
359
|
+
def additional_file_saving(self, unique_export_path):
|
|
360
|
+
# 훈련 프레임워크용 데이터셋 설정 생성
|
|
361
|
+
config = {
|
|
362
|
+
'dataset_name': self.params.get('name'),
|
|
363
|
+
'created_at': datetime.now().isoformat(),
|
|
364
|
+
'total_samples': len(self.processed_items),
|
|
365
|
+
'classes': list(self.class_mapping.keys())
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
config_file = Path(unique_export_path) / 'dataset_config.json'
|
|
369
|
+
with config_file.open('w') as f:
|
|
370
|
+
json.dump(config, f, indent=2)
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
#### 2. Export 요약 보고서
|
|
374
|
+
|
|
375
|
+
```python
|
|
376
|
+
def additional_file_saving(self, unique_export_path):
|
|
377
|
+
# export 요약 생성
|
|
378
|
+
summary = {
|
|
379
|
+
'export_info': {
|
|
380
|
+
'plugin_name': self.__class__.__name__,
|
|
381
|
+
'export_time': datetime.now().isoformat(),
|
|
382
|
+
'export_path': str(unique_export_path)
|
|
383
|
+
},
|
|
384
|
+
'statistics': self.get_export_statistics(),
|
|
385
|
+
'errors': self.get_error_summary()
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
summary_file = Path(unique_export_path) / 'export_summary.json'
|
|
389
|
+
with summary_file.open('w') as f:
|
|
390
|
+
json.dump(summary, f, indent=2)
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
#### 3. 인덱스 및 매니페스트 파일
|
|
394
|
+
|
|
395
|
+
```python
|
|
396
|
+
def additional_file_saving(self, unique_export_path):
|
|
397
|
+
# 처리된 항목들에 대한 파일 인덱스 생성
|
|
398
|
+
file_index = []
|
|
399
|
+
for item in self.processed_items:
|
|
400
|
+
file_index.append({
|
|
401
|
+
'original_file': item['original_filename'],
|
|
402
|
+
'json_file': f"{item['stem']}.json",
|
|
403
|
+
'processed_at': item['timestamp']
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
index_file = Path(unique_export_path) / 'file_index.json'
|
|
407
|
+
with index_file.open('w') as f:
|
|
408
|
+
json.dump(file_index, f, indent=2)
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
## 주요 특징
|
|
412
|
+
|
|
413
|
+
- **진행률 추적**: `run.set_progress()`로 내장 진행률 모니터링
|
|
414
|
+
- **오류 처리**: 자동 오류 수집 및 보고
|
|
415
|
+
- **메트릭 로깅**: `run.log_metrics()`로 성공/실패율 추적
|
|
416
|
+
- **파일 관리**: 원본 파일과 처리된 JSON 데이터 모두 처리
|
|
417
|
+
- **로깅**: `run.log_message()` 및 사용자 정의 이벤트로 포괄적인 로깅
|
|
418
|
+
|
|
419
|
+
## 실용적인 예시
|
|
420
|
+
|
|
421
|
+
### YOLO 형식 Exporter with 커스텀 디렉토리 구조
|
|
422
|
+
|
|
423
|
+
다음은 YOLO 형식으로 데이터를 내보내면서 `setup_output_directories`와 `process_file_saving`을 활용하는 완전한 예시입니다:
|
|
424
|
+
|
|
425
|
+
```python
|
|
426
|
+
from synapse_sdk.plugins.categories.export.templates.plugin import BaseExporter
|
|
427
|
+
import os
|
|
428
|
+
import json
|
|
429
|
+
|
|
430
|
+
class YOLOExporter(BaseExporter):
|
|
431
|
+
"""YOLO 형식으로 데이터를 내보내는 플러그인."""
|
|
432
|
+
|
|
433
|
+
def __init__(self, run, export_items, path_root, **params):
|
|
434
|
+
super().__init__(run, export_items, path_root, **params)
|
|
435
|
+
self.class_mapping = {}
|
|
436
|
+
|
|
437
|
+
def setup_output_directories(self, unique_export_path, save_original_file_flag):
|
|
438
|
+
"""YOLO 프로젝트 구조에 맞는 디렉토리 생성."""
|
|
439
|
+
directories = ['images', 'labels', 'data']
|
|
440
|
+
|
|
441
|
+
for directory in directories:
|
|
442
|
+
dir_path = os.path.join(unique_export_path, directory)
|
|
443
|
+
os.makedirs(dir_path, exist_ok=True)
|
|
444
|
+
self.run.log_message(f"YOLO 디렉토리 생성: {dir_path}")
|
|
445
|
+
|
|
446
|
+
return unique_export_path
|
|
447
|
+
|
|
448
|
+
def convert_data(self, data):
|
|
449
|
+
"""주석 데이터를 YOLO 형식으로 변환."""
|
|
450
|
+
converted_annotations = []
|
|
451
|
+
|
|
452
|
+
for annotation in data.get('annotations', []):
|
|
453
|
+
# 바운딩 박스를 YOLO 형식으로 변환
|
|
454
|
+
bbox = annotation['geometry']['bbox']
|
|
455
|
+
image_width = data['image']['width']
|
|
456
|
+
image_height = data['image']['height']
|
|
457
|
+
|
|
458
|
+
# YOLO 형식: center_x, center_y, width, height (정규화)
|
|
459
|
+
center_x = (bbox['x'] + bbox['width'] / 2) / image_width
|
|
460
|
+
center_y = (bbox['y'] + bbox['height'] / 2) / image_height
|
|
461
|
+
width = bbox['width'] / image_width
|
|
462
|
+
height = bbox['height'] / image_height
|
|
463
|
+
|
|
464
|
+
# 클래스 ID 매핑
|
|
465
|
+
class_name = annotation['class_name']
|
|
466
|
+
if class_name not in self.class_mapping:
|
|
467
|
+
self.class_mapping[class_name] = len(self.class_mapping)
|
|
468
|
+
|
|
469
|
+
class_id = self.class_mapping[class_name]
|
|
470
|
+
|
|
471
|
+
converted_annotations.append({
|
|
472
|
+
'class_id': class_id,
|
|
473
|
+
'center_x': center_x,
|
|
474
|
+
'center_y': center_y,
|
|
475
|
+
'width': width,
|
|
476
|
+
'height': height
|
|
477
|
+
})
|
|
478
|
+
|
|
479
|
+
return {
|
|
480
|
+
'yolo_annotations': converted_annotations,
|
|
481
|
+
'class_mapping': self.class_mapping,
|
|
482
|
+
'image_info': data['image']
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
def process_file_saving(
|
|
486
|
+
self,
|
|
487
|
+
final_data,
|
|
488
|
+
unique_export_path,
|
|
489
|
+
save_original_file_flag,
|
|
490
|
+
errors_json_file_list,
|
|
491
|
+
errors_original_file_list,
|
|
492
|
+
original_file_metrics_record,
|
|
493
|
+
data_file_metrics_record,
|
|
494
|
+
current_index,
|
|
495
|
+
):
|
|
496
|
+
"""YOLO 형식으로 파일 저장 처리."""
|
|
497
|
+
try:
|
|
498
|
+
export_item = self.export_items[current_index - 1]
|
|
499
|
+
base_name = os.path.splitext(export_item.original_file.name)[0]
|
|
500
|
+
|
|
501
|
+
# 1. 이미지 파일을 images 폴더에 저장
|
|
502
|
+
if save_original_file_flag:
|
|
503
|
+
images_dir = os.path.join(unique_export_path, 'images')
|
|
504
|
+
image_path = os.path.join(images_dir, export_item.original_file.name)
|
|
505
|
+
import shutil
|
|
506
|
+
shutil.copy2(export_item.original_file.path, image_path)
|
|
507
|
+
self.run.log_message(f"이미지 저장: {image_path}")
|
|
508
|
+
|
|
509
|
+
# 2. YOLO 라벨 파일을 labels 폴더에 저장
|
|
510
|
+
labels_dir = os.path.join(unique_export_path, 'labels')
|
|
511
|
+
label_path = os.path.join(labels_dir, f"{base_name}.txt")
|
|
512
|
+
|
|
513
|
+
with open(label_path, 'w') as f:
|
|
514
|
+
for ann in final_data.get('yolo_annotations', []):
|
|
515
|
+
line = f"{ann['class_id']} {ann['center_x']} {ann['center_y']} {ann['width']} {ann['height']}\n"
|
|
516
|
+
f.write(line)
|
|
517
|
+
|
|
518
|
+
self.run.log_message(f"YOLO 라벨 저장: {label_path}")
|
|
519
|
+
|
|
520
|
+
# 3. 클래스 매핑 파일 저장 (한 번만)
|
|
521
|
+
if current_index == 1: # 첫 번째 파일 처리 시에만
|
|
522
|
+
classes_path = os.path.join(unique_export_path, 'data', 'classes.txt')
|
|
523
|
+
with open(classes_path, 'w') as f:
|
|
524
|
+
for class_name, class_id in sorted(final_data['class_mapping'].items(), key=lambda x: x[1]):
|
|
525
|
+
f.write(f"{class_name}\n")
|
|
526
|
+
self.run.log_message(f"클래스 파일 저장: {classes_path}")
|
|
527
|
+
|
|
528
|
+
return True
|
|
529
|
+
|
|
530
|
+
except Exception as e:
|
|
531
|
+
self.run.log_message(f"파일 저장 중 오류: {str(e)}", level="error")
|
|
532
|
+
errors_json_file_list.append(f"Export item {current_index}: {str(e)}")
|
|
533
|
+
return True # 다른 파일 처리를 계속하기 위해 True 반환
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
이 예시는 BaseExporter의 핵심 확장 포인트인 `setup_output_directories`와 `process_file_saving`을 활용하여:
|
|
537
|
+
|
|
538
|
+
- YOLO 프로젝트 구조 (`images/`, `labels/`, `data/`) 생성
|
|
539
|
+
- 이미지 파일과 YOLO 라벨 파일을 적절한 위치에 저장
|
|
540
|
+
- 클래스 매핑 파일 관리
|
|
541
|
+
- 진행률 추적과 오류 처리
|
|
542
|
+
|
|
543
|
+
이를 보여줍니다.
|
|
544
|
+
|
|
545
|
+
## 빠른 시작 가이드
|
|
546
|
+
|
|
547
|
+
BaseExporter를 사용하여 간단한 플러그인을 만드는 단계별 가이드입니다:
|
|
548
|
+
|
|
549
|
+
### 1단계: 기본 클래스 상속
|
|
550
|
+
|
|
551
|
+
```python
|
|
552
|
+
from synapse_sdk.plugins.categories.export.templates.plugin import BaseExporter
|
|
553
|
+
|
|
554
|
+
class MyExporter(BaseExporter):
|
|
555
|
+
def convert_data(self, data):
|
|
556
|
+
# 필수: 데이터 변환 로직 구현
|
|
557
|
+
return data # 또는 변환된 데이터 반환
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
### 2단계: 필요에 따라 추가 메서드 오버라이드
|
|
561
|
+
|
|
562
|
+
```python
|
|
563
|
+
def before_convert(self, data):
|
|
564
|
+
# 선택적: 변환 전 전처리
|
|
565
|
+
return data
|
|
566
|
+
|
|
567
|
+
def after_convert(self, converted_data):
|
|
568
|
+
# 선택적: 변환 후 후처리
|
|
569
|
+
return converted_data
|
|
570
|
+
|
|
571
|
+
def save_as_json(self, converted_data, output_path):
|
|
572
|
+
# 선택적: 사용자 정의 저장 형식
|
|
573
|
+
# 기본적으로는 JSON 형식으로 저장됨
|
|
574
|
+
pass
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
### 3단계: 플러그인 등록
|
|
578
|
+
|
|
579
|
+
플러그인 디렉토리 구조:
|
|
580
|
+
|
|
581
|
+
```
|
|
582
|
+
my_plugin/
|
|
583
|
+
├── __init__.py
|
|
584
|
+
├── plugin.py # MyExporter 클래스 정의
|
|
585
|
+
└── manifest.yaml # 플러그인 메타데이터
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
## Export 액션 아키텍처
|
|
589
|
+
|
|
590
|
+
데이터 내보내기 처리의 다양한 측면을 위한 특화된 구성 요소를 갖춘 모듈화된 아키텍처로 export 시스템이 리팩터링되었습니다:
|
|
591
|
+
|
|
592
|
+
```mermaid
|
|
593
|
+
classDiagram
|
|
594
|
+
%% Light/Dark mode compatible colors with semi-transparency
|
|
595
|
+
classDef baseClass fill:#e1f5fe80,stroke:#0288d1,stroke-width:2px
|
|
596
|
+
classDef childClass fill:#c8e6c980,stroke:#388e3c,stroke-width:2px
|
|
597
|
+
classDef modelClass fill:#fff9c480,stroke:#f57c00,stroke-width:2px
|
|
598
|
+
classDef utilClass fill:#f5f5f580,stroke:#616161,stroke-width:2px
|
|
599
|
+
classDef enumClass fill:#ffccbc80,stroke:#d32f2f,stroke-width:2px
|
|
600
|
+
|
|
601
|
+
class ExportAction {
|
|
602
|
+
+name: str = "export"
|
|
603
|
+
+category: PluginCategory.EXPORT
|
|
604
|
+
+method: RunMethod.JOB
|
|
605
|
+
+run_class: ExportRun
|
|
606
|
+
+params_model: ExportParams
|
|
607
|
+
+progress_categories: dict
|
|
608
|
+
+metrics_categories: dict
|
|
609
|
+
|
|
610
|
+
+start() dict
|
|
611
|
+
+get_exporter(...) object
|
|
612
|
+
+_get_export_items(target, filter) Generator
|
|
613
|
+
+_create_target_handler(target) object
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
class ExportRun {
|
|
617
|
+
+log_message_with_code(code, args, level) None
|
|
618
|
+
+log_export_event(code, args, level) None
|
|
619
|
+
+export_log_json_file(id, file_info, status) None
|
|
620
|
+
+export_log_original_file(id, file_info, status) None
|
|
621
|
+
+ExportEventLog: BaseModel
|
|
622
|
+
+DataFileLog: BaseModel
|
|
623
|
+
+MetricsRecord: BaseModel
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
class ExportParams {
|
|
627
|
+
+name: str
|
|
628
|
+
+storage: int
|
|
629
|
+
+target: Literal["assignment", "ground_truth", "task"]
|
|
630
|
+
+filter: dict
|
|
631
|
+
+path: str
|
|
632
|
+
+save_original_file: bool = True
|
|
633
|
+
+extra_params: dict = {}
|
|
634
|
+
|
|
635
|
+
+check_storage_exists(value) str
|
|
636
|
+
+validate_target_filter(cls, values) dict
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
class LogCode {
|
|
640
|
+
+EXPORT_STARTED: str
|
|
641
|
+
+ITEMS_DISCOVERED: str
|
|
642
|
+
+CONVERSION_STARTED: str
|
|
643
|
+
+CONVERSION_COMPLETED: str
|
|
644
|
+
+FILE_SAVED: str
|
|
645
|
+
+EXPORT_COMPLETED: str
|
|
646
|
+
+EXPORT_FAILED: str
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
class ExportStatus {
|
|
650
|
+
+SUCCESS: str = "success"
|
|
651
|
+
+FAILED: str = "failed"
|
|
652
|
+
+SKIPPED: str = "skipped"
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
class ExportError {
|
|
656
|
+
+message: str
|
|
657
|
+
+code: str
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
class ExportValidationError {
|
|
661
|
+
+message: str
|
|
662
|
+
+field: str
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
class ExportTargetError {
|
|
666
|
+
+message: str
|
|
667
|
+
+target: str
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
%% Relationships
|
|
671
|
+
ExportAction --> ExportRun : uses
|
|
672
|
+
ExportAction --> ExportParams : validates with
|
|
673
|
+
ExportRun --> LogCode : logs with
|
|
674
|
+
ExportRun --> ExportStatus : tracks status
|
|
675
|
+
ExportAction --> ExportError : may raise
|
|
676
|
+
ExportAction --> ExportValidationError : may raise
|
|
677
|
+
ExportAction --> ExportTargetError : may raise
|
|
678
|
+
|
|
679
|
+
%% Apply styles
|
|
680
|
+
class ExportAction baseClass
|
|
681
|
+
class ExportRun childClass
|
|
682
|
+
class ExportParams modelClass
|
|
683
|
+
class LogCode,ExportStatus enumClass
|
|
684
|
+
class ExportError,ExportValidationError,ExportTargetError utilClass
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
### 모듈화된 구조
|
|
688
|
+
|
|
689
|
+
Export 액션은 깔끔한 모듈화된 구성을 따릅니다:
|
|
690
|
+
|
|
691
|
+
```
|
|
692
|
+
synapse_sdk/plugins/categories/export/actions/export/
|
|
693
|
+
├── __init__.py # 깔끔한 모듈 인터페이스
|
|
694
|
+
├── action.py # ExportAction 클래스
|
|
695
|
+
├── enums.py # ExportStatus, LogCode + LOG_MESSAGES
|
|
696
|
+
├── exceptions.py # Export 특화 예외
|
|
697
|
+
├── models.py # ExportParams 모델
|
|
698
|
+
├── run.py # ExportRun 클래스
|
|
699
|
+
└── utils.py # 대상 핸들러 및 유틸리티
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
**주요 장점:**
|
|
703
|
+
|
|
704
|
+
- **향상된 유지보수성**: 작고 집중된 파일로 이해하고 수정하기 쉬움
|
|
705
|
+
- **코드 일관성**: Export 액션이 이제 upload 액션과 동일한 패턴을 따름
|
|
706
|
+
- **더 나은 구성**: 관련 기능이 논리적으로 그룹화됨
|
|
707
|
+
- **향상된 가독성**: 모듈 전반에 걸친 명확한 관심사 분리
|
|
708
|
+
|
|
709
|
+
## Export 플러그인 생성
|
|
710
|
+
|
|
711
|
+
Export 플러그인은 더 나은 구성과 재사용성을 위해 BaseExporter 클래스 기반 접근 방식을 사용합니다. 커스텀 export 플러그인을 생성하는 방법은 다음과 같습니다:
|
|
712
|
+
|
|
713
|
+
### 1단계: Export 플러그인 템플릿 생성
|
|
714
|
+
|
|
715
|
+
```bash
|
|
716
|
+
synapse plugin create
|
|
717
|
+
# 카테고리로 'export' 선택
|
|
718
|
+
# export 템플릿으로 플러그인이 생성됩니다
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
### 2단계: Export 매개변수 커스터마이징
|
|
722
|
+
|
|
723
|
+
`ExportParams` 모델이 필요한 매개변수를 정의합니다:
|
|
724
|
+
|
|
725
|
+
```python
|
|
726
|
+
from synapse_sdk.plugins.categories.export.actions.export.models import ExportParams
|
|
727
|
+
from pydantic import BaseModel
|
|
728
|
+
from typing import Literal
|
|
729
|
+
|
|
730
|
+
class CustomExportParams(ExportParams):
|
|
731
|
+
# 커스텀 매개변수 추가
|
|
732
|
+
output_format: Literal['json', 'csv', 'xml'] = 'json'
|
|
733
|
+
include_metadata: bool = True
|
|
734
|
+
compression: bool = False
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
### 3단계: 데이터 변환 구현
|
|
738
|
+
|
|
739
|
+
`plugin/export.py`의 `Exporter` 클래스에서 필요한 메서드를 구현합니다:
|
|
740
|
+
|
|
741
|
+
```python
|
|
742
|
+
from datetime import datetime
|
|
743
|
+
from synapse_sdk.plugins.categories.export.templates.plugin import BaseExporter
|
|
744
|
+
|
|
745
|
+
class Exporter(BaseExporter):
|
|
746
|
+
"""COCO 형식 변환을 포함한 커스텀 export 플러그인."""
|
|
747
|
+
|
|
748
|
+
def convert_data(self, data):
|
|
749
|
+
"""주석 데이터를 원하는 형식으로 변환합니다."""
|
|
750
|
+
# 예시: COCO 형식으로 변환
|
|
751
|
+
if data.get('data_type') == 'image_detection':
|
|
752
|
+
return self.convert_to_coco_format(data)
|
|
753
|
+
elif data.get('data_type') == 'image_classification':
|
|
754
|
+
return self.convert_to_classification_format(data)
|
|
755
|
+
return data
|
|
756
|
+
|
|
757
|
+
def before_convert(self, export_item):
|
|
758
|
+
"""변환 전 데이터 전처리."""
|
|
759
|
+
# 검증, 필터링 또는 전처리 추가
|
|
760
|
+
if not export_item.get('data'):
|
|
761
|
+
return None # 빈 항목 건너뛰기
|
|
762
|
+
|
|
763
|
+
# 커스텀 메타데이터 추가
|
|
764
|
+
export_item['processed_at'] = datetime.now().isoformat()
|
|
765
|
+
return export_item
|
|
766
|
+
|
|
767
|
+
def after_convert(self, converted_data):
|
|
768
|
+
"""변환된 데이터 후처리."""
|
|
769
|
+
# 최종 마무리, 검증 또는 형식 지정 추가
|
|
770
|
+
if 'annotations' in converted_data:
|
|
771
|
+
converted_data['annotation_count'] = len(converted_data['annotations'])
|
|
772
|
+
return converted_data
|
|
773
|
+
|
|
774
|
+
def convert_to_coco_format(self, data):
|
|
775
|
+
"""예시: COCO 검출 형식으로 변환."""
|
|
776
|
+
coco_data = {
|
|
777
|
+
"images": [],
|
|
778
|
+
"annotations": [],
|
|
779
|
+
"categories": []
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
# 주석 데이터를 COCO 형식으로 변환
|
|
783
|
+
for annotation in data.get('annotations', []):
|
|
784
|
+
coco_annotation = {
|
|
785
|
+
"id": annotation['id'],
|
|
786
|
+
"image_id": annotation['image_id'],
|
|
787
|
+
"category_id": annotation['category_id'],
|
|
788
|
+
"bbox": annotation['bbox'],
|
|
789
|
+
"area": annotation.get('area', 0),
|
|
790
|
+
"iscrowd": 0
|
|
791
|
+
}
|
|
792
|
+
coco_data["annotations"].append(coco_annotation)
|
|
793
|
+
|
|
794
|
+
return coco_data
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
### 4단계: Export 대상 구성
|
|
798
|
+
|
|
799
|
+
Export 액션은 다양한 데이터 소스를 지원합니다:
|
|
800
|
+
|
|
801
|
+
```python
|
|
802
|
+
# 다양한 대상에 대한 필터 예시
|
|
803
|
+
filters = {
|
|
804
|
+
# 그라운드 트루스 내보내기용
|
|
805
|
+
"ground_truth": {
|
|
806
|
+
"ground_truth_dataset_version": 123,
|
|
807
|
+
"expand": ["data"]
|
|
808
|
+
},
|
|
809
|
+
|
|
810
|
+
# 할당 내보내기용
|
|
811
|
+
"assignment": {
|
|
812
|
+
"project": 456,
|
|
813
|
+
"status": "completed",
|
|
814
|
+
"expand": ["data"]
|
|
815
|
+
},
|
|
816
|
+
|
|
817
|
+
# 작업 내보내기용
|
|
818
|
+
"task": {
|
|
819
|
+
"project": 456,
|
|
820
|
+
"assignment": 789,
|
|
821
|
+
"expand": ["data_unit", "assignment"]
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
```
|
|
825
|
+
|
|
826
|
+
### 5단계: 파일 작업 처리
|
|
827
|
+
|
|
828
|
+
BaseExporter 메서드를 오버라이드하여 파일 저장 및 구성을 커스터마이징합니다:
|
|
829
|
+
|
|
830
|
+
```python
|
|
831
|
+
import json
|
|
832
|
+
from pathlib import Path
|
|
833
|
+
from synapse_sdk.plugins.categories.export.actions.export.enums import ExportStatus
|
|
834
|
+
|
|
835
|
+
class Exporter(BaseExporter):
|
|
836
|
+
"""다중 형식 지원을 포함한 커스텀 export 플러그인."""
|
|
837
|
+
|
|
838
|
+
def save_as_json(self, result, base_path, error_file_list):
|
|
839
|
+
"""다양한 형식으로 커스텀 JSON 저장."""
|
|
840
|
+
file_name = Path(self.get_original_file_name(result['files'])).stem
|
|
841
|
+
|
|
842
|
+
# 매개변수에 따른 출력 형식 선택
|
|
843
|
+
if self.params.get('output_format') == 'csv':
|
|
844
|
+
return self.save_as_csv(result, base_path, error_file_list)
|
|
845
|
+
elif self.params.get('output_format') == 'xml':
|
|
846
|
+
return self.save_as_xml(result, base_path, error_file_list)
|
|
847
|
+
|
|
848
|
+
# 기본 JSON 처리
|
|
849
|
+
json_data = result['data']
|
|
850
|
+
file_info = {'file_name': f'{file_name}.json'}
|
|
851
|
+
|
|
852
|
+
try:
|
|
853
|
+
with (base_path / f'{file_name}.json').open('w', encoding='utf-8') as f:
|
|
854
|
+
json.dump(json_data, f, indent=4, ensure_ascii=False)
|
|
855
|
+
status = ExportStatus.SUCCESS
|
|
856
|
+
except Exception as e:
|
|
857
|
+
error_file_list.append([f'{file_name}.json', str(e)])
|
|
858
|
+
status = ExportStatus.FAILED
|
|
859
|
+
|
|
860
|
+
self.run.export_log_json_file(result['id'], file_info, status)
|
|
861
|
+
return status
|
|
862
|
+
|
|
863
|
+
def setup_output_directories(self, unique_export_path, save_original_file_flag):
|
|
864
|
+
"""커스텀 디렉토리 구조."""
|
|
865
|
+
# 형식별 디렉토리 생성
|
|
866
|
+
output_paths = super().setup_output_directories(unique_export_path, save_original_file_flag)
|
|
867
|
+
|
|
868
|
+
# 출력 형식에 따른 커스텀 디렉토리 추가
|
|
869
|
+
format_dir = unique_export_path / self.params.get('output_format', 'json')
|
|
870
|
+
format_dir.mkdir(parents=True, exist_ok=True)
|
|
871
|
+
output_paths['format_output_path'] = format_dir
|
|
872
|
+
|
|
873
|
+
return output_paths
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
### 6단계: 사용 예시
|
|
877
|
+
|
|
878
|
+
다양한 구성으로 export 플러그인 실행:
|
|
879
|
+
|
|
880
|
+
```bash
|
|
881
|
+
# 그라운드 트루스 데이터 기본 내보내기
|
|
882
|
+
synapse plugin run export '{
|
|
883
|
+
"name": "my_export",
|
|
884
|
+
"storage": 1,
|
|
885
|
+
"target": "ground_truth",
|
|
886
|
+
"filter": {"ground_truth_dataset_version": 123},
|
|
887
|
+
"path": "exports/ground_truth",
|
|
888
|
+
"save_original_file": true
|
|
889
|
+
}' --plugin my-export-plugin
|
|
890
|
+
|
|
891
|
+
# 커스텀 매개변수로 할당 내보내기
|
|
892
|
+
synapse plugin run export '{
|
|
893
|
+
"name": "assignment_export",
|
|
894
|
+
"storage": 1,
|
|
895
|
+
"target": "assignment",
|
|
896
|
+
"filter": {"project": 456, "status": "completed"},
|
|
897
|
+
"path": "exports/assignments",
|
|
898
|
+
"save_original_file": false,
|
|
899
|
+
"extra_params": {
|
|
900
|
+
"output_format": "coco",
|
|
901
|
+
"include_metadata": true
|
|
902
|
+
}
|
|
903
|
+
}' --plugin custom-coco-export
|
|
904
|
+
```
|
|
905
|
+
|
|
906
|
+
## 일반적인 Export 패턴
|
|
907
|
+
|
|
908
|
+
```python
|
|
909
|
+
# 패턴 1: 형식별 변환
|
|
910
|
+
class Exporter(BaseExporter):
|
|
911
|
+
def convert_data(self, data):
|
|
912
|
+
"""YOLO 형식으로 변환."""
|
|
913
|
+
if data.get('task_type') == 'object_detection':
|
|
914
|
+
return self.convert_to_yolo_format(data)
|
|
915
|
+
return data
|
|
916
|
+
|
|
917
|
+
# 패턴 2: 조건부 파일 구성
|
|
918
|
+
class Exporter(BaseExporter):
|
|
919
|
+
def setup_output_directories(self, unique_export_path, save_original_file_flag):
|
|
920
|
+
# 부모 메서드 호출
|
|
921
|
+
output_paths = super().setup_output_directories(unique_export_path, save_original_file_flag)
|
|
922
|
+
|
|
923
|
+
# 카테고리별 별도 폴더 생성
|
|
924
|
+
for category in ['train', 'val', 'test']:
|
|
925
|
+
category_path = unique_export_path / category
|
|
926
|
+
category_path.mkdir(parents=True, exist_ok=True)
|
|
927
|
+
output_paths[f'{category}_path'] = category_path
|
|
928
|
+
|
|
929
|
+
return output_paths
|
|
930
|
+
|
|
931
|
+
# 패턴 3: 검증을 포함한 배치 처리
|
|
932
|
+
class Exporter(BaseExporter):
|
|
933
|
+
def before_convert(self, export_item):
|
|
934
|
+
# 필수 필드 검증
|
|
935
|
+
required_fields = ['data', 'files', 'id']
|
|
936
|
+
for field in required_fields:
|
|
937
|
+
if field not in export_item:
|
|
938
|
+
raise ValueError(f"필수 필드가 누락됨: {field}")
|
|
939
|
+
return export_item
|
|
940
|
+
```
|
|
941
|
+
|
|
942
|
+
## 개발 팁 및 모범 사례
|
|
943
|
+
|
|
944
|
+
### 1. 오류 처리
|
|
945
|
+
|
|
946
|
+
```python
|
|
947
|
+
def convert_data(self, data):
|
|
948
|
+
try:
|
|
949
|
+
# 변환 로직
|
|
950
|
+
result = self.process_annotations(data)
|
|
951
|
+
return result
|
|
952
|
+
except Exception as e:
|
|
953
|
+
self.run.log_message(f"변환 중 오류 발생: {str(e)}", level="error")
|
|
954
|
+
raise # BaseExporter가 오류를 자동으로 처리
|
|
955
|
+
```
|
|
956
|
+
|
|
957
|
+
### 2. 진행률 추적
|
|
958
|
+
|
|
959
|
+
```python
|
|
960
|
+
def convert_data(self, data):
|
|
961
|
+
annotations = data.get('annotations', [])
|
|
962
|
+
total = len(annotations)
|
|
963
|
+
|
|
964
|
+
for i, annotation in enumerate(annotations):
|
|
965
|
+
# 진행률 업데이트 (0-100 사이의 값)
|
|
966
|
+
progress = int((i / total) * 100)
|
|
967
|
+
self.run.set_progress(progress)
|
|
968
|
+
|
|
969
|
+
# 변환 로직...
|
|
970
|
+
|
|
971
|
+
return converted_data
|
|
972
|
+
```
|
|
973
|
+
|
|
974
|
+
### 3. 메트릭 수집
|
|
975
|
+
|
|
976
|
+
```python
|
|
977
|
+
def after_convert(self, converted_data):
|
|
978
|
+
# 유용한 메트릭 수집
|
|
979
|
+
metrics = {
|
|
980
|
+
'total_exported': len(converted_data.get('annotations', [])),
|
|
981
|
+
'processing_time': time.time() - self.start_time,
|
|
982
|
+
'success_rate': self.calculate_success_rate(),
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
self.run.log_metrics(metrics)
|
|
986
|
+
return converted_data
|
|
987
|
+
```
|
|
988
|
+
|
|
989
|
+
### 4. 로깅 활용
|
|
990
|
+
|
|
991
|
+
```python
|
|
992
|
+
def convert_data(self, data):
|
|
993
|
+
self.run.log_message("데이터 변환 시작", level="info")
|
|
994
|
+
|
|
995
|
+
if not data.get('annotations'):
|
|
996
|
+
self.run.log_message("주석 데이터가 없습니다", level="warning")
|
|
997
|
+
return data
|
|
998
|
+
|
|
999
|
+
# 변환 로직...
|
|
1000
|
+
|
|
1001
|
+
self.run.log_message(f"변환 완료: {len(result)} 항목 처리됨", level="success")
|
|
1002
|
+
return result
|
|
1003
|
+
```
|
|
1004
|
+
|
|
1005
|
+
### 5. 매개변수 처리
|
|
1006
|
+
|
|
1007
|
+
```python
|
|
1008
|
+
def __init__(self, run, export_items, path_root, **params):
|
|
1009
|
+
super().__init__(run, export_items, path_root, **params)
|
|
1010
|
+
|
|
1011
|
+
# 사용자 정의 매개변수 처리
|
|
1012
|
+
self.output_format = params.get('output_format', 'json')
|
|
1013
|
+
self.include_metadata = params.get('include_metadata', True)
|
|
1014
|
+
self.compression = params.get('compression', False)
|
|
1015
|
+
```
|
|
1016
|
+
|
|
1017
|
+
## 모범 사례
|
|
1018
|
+
|
|
1019
|
+
### 데이터 처리
|
|
1020
|
+
|
|
1021
|
+
- **메모리 효율성**: 대용량 데이터셋 처리를 위해 제너레이터 사용
|
|
1022
|
+
- **오류 복구**: 개별 항목에 대한 우아한 오류 처리 구현
|
|
1023
|
+
- **진행률 보고**: 장시간 실행되는 내보내기의 진행률을 정기적으로 업데이트
|
|
1024
|
+
- **데이터 검증**: 변환 전 데이터 구조 검증
|
|
1025
|
+
|
|
1026
|
+
```python
|
|
1027
|
+
class Exporter(BaseExporter):
|
|
1028
|
+
def export(self, export_items=None, results=None, **kwargs):
|
|
1029
|
+
"""커스텀 처리를 위한 주 export 메서드 오버라이드."""
|
|
1030
|
+
# 제너레이터를 소비하지 않고 항목 수를 카운트하기 위해 tee 사용
|
|
1031
|
+
items_to_process = export_items if export_items is not None else self.export_items
|
|
1032
|
+
export_items_count, export_items_process = tee(items_to_process)
|
|
1033
|
+
total = sum(1 for _ in export_items_count)
|
|
1034
|
+
|
|
1035
|
+
# 오류 처리가 포함된 커스텀 처리
|
|
1036
|
+
for no, export_item in enumerate(export_items_process, start=1):
|
|
1037
|
+
try:
|
|
1038
|
+
# 내장 데이터 변환 파이프라인 사용
|
|
1039
|
+
processed_item = self.process_data_conversion(export_item)
|
|
1040
|
+
self.run.set_progress(no, total, category='dataset_conversion')
|
|
1041
|
+
except Exception as e:
|
|
1042
|
+
self.run.log_message(f"항목 {no} 처리 중 오류: {str(e)}", "ERROR")
|
|
1043
|
+
continue
|
|
1044
|
+
|
|
1045
|
+
# 표준 처리를 위해 부모의 export 메서드 호출
|
|
1046
|
+
# 또는 자체 완전한 워크플로우 구현
|
|
1047
|
+
return super().export(export_items, results, **kwargs)
|
|
1048
|
+
```
|
|
1049
|
+
|
|
1050
|
+
### 파일 관리
|
|
1051
|
+
|
|
1052
|
+
- **고유 경로**: 타임스탬프나 카운터 접미사로 파일 충돌 방지
|
|
1053
|
+
- **디렉토리 구조**: 출력 파일을 논리적으로 구성
|
|
1054
|
+
- **오류 로깅**: 디버깅을 위해 실패한 파일 추적
|
|
1055
|
+
- **정리**: 완료 시 임시 파일 제거
|
|
1056
|
+
|
|
1057
|
+
```python
|
|
1058
|
+
class Exporter(BaseExporter):
|
|
1059
|
+
def setup_output_directories(self, unique_export_path, save_original_file_flag):
|
|
1060
|
+
"""고유한 export 디렉토리 구조 생성."""
|
|
1061
|
+
# BaseExporter는 이미 _create_unique_export_path를 통해 고유 경로 생성을 처리함
|
|
1062
|
+
# 이 메서드는 내부 디렉토리 구조를 설정함
|
|
1063
|
+
output_paths = super().setup_output_directories(unique_export_path, save_original_file_flag)
|
|
1064
|
+
|
|
1065
|
+
# 필요에 따라 커스텀 서브디렉토리 추가
|
|
1066
|
+
custom_dir = unique_export_path / 'custom_output'
|
|
1067
|
+
custom_dir.mkdir(parents=True, exist_ok=True)
|
|
1068
|
+
output_paths['custom_output_path'] = custom_dir
|
|
1069
|
+
|
|
1070
|
+
return output_paths
|
|
1071
|
+
```
|
|
1072
|
+
|
|
1073
|
+
### 형식 변환
|
|
1074
|
+
|
|
1075
|
+
- **유연한 템플릿**: 여러 데이터 타입과 함께 작동하는 템플릿 설계
|
|
1076
|
+
- **스키마 검증**: 예상 스키마에 대한 출력 검증
|
|
1077
|
+
- **메타데이터 보존**: 변환 중 중요한 메타데이터 유지
|
|
1078
|
+
- **버전 호환성**: 다양한 데이터 스키마 버전 처리
|
|
1079
|
+
|
|
1080
|
+
## 자주 묻는 질문
|
|
1081
|
+
|
|
1082
|
+
**Q: BaseExporter를 사용하지 않고 직접 구현할 수 있나요?**
|
|
1083
|
+
A: 가능하지만 권장하지 않습니다. BaseExporter는 진행률 추적, 오류 처리, 메트릭 수집 등의 기본 기능을 자동으로 제공합니다.
|
|
1084
|
+
|
|
1085
|
+
**Q: 여러 파일 형식으로 동시에 내보낼 수 있나요?**
|
|
1086
|
+
A: `process_file_saving()` 메서드를 오버라이드하여 여러 형식으로 저장할 수 있습니다.
|
|
1087
|
+
|
|
1088
|
+
**Q: 대용량 데이터셋을 처리할 때 메모리 사용량을 최적화하려면?**
|
|
1089
|
+
A: `convert_data()`에서 한 번에 모든 데이터를 로드하지 말고, 스트리밍 방식으로 처리하는 것을 고려해보세요.
|
|
1090
|
+
|
|
1091
|
+
**Q: 진행률이 올바르게 표시되지 않는다면?**
|
|
1092
|
+
A: `self.run.set_progress()`를 적절한 간격으로 호출하고 있는지 확인하세요. 0-100 사이의 정수 값을 사용해야 합니다.
|