synapse-sdk 1.0.0a35__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.

Files changed (307) hide show
  1. synapse_sdk/__init__.py +24 -0
  2. synapse_sdk/cli/__init__.py +308 -5
  3. synapse_sdk/cli/alias/utils.py +1 -1
  4. synapse_sdk/cli/code_server.py +687 -0
  5. synapse_sdk/cli/config.py +440 -0
  6. synapse_sdk/cli/devtools.py +90 -0
  7. synapse_sdk/cli/plugin/publish.py +23 -15
  8. synapse_sdk/clients/agent/__init__.py +9 -3
  9. synapse_sdk/clients/agent/container.py +133 -0
  10. synapse_sdk/clients/agent/core.py +19 -0
  11. synapse_sdk/clients/agent/ray.py +298 -9
  12. synapse_sdk/clients/backend/__init__.py +28 -12
  13. synapse_sdk/clients/backend/annotation.py +9 -1
  14. synapse_sdk/clients/backend/core.py +31 -4
  15. synapse_sdk/clients/backend/data_collection.py +186 -0
  16. synapse_sdk/clients/backend/hitl.py +1 -1
  17. synapse_sdk/clients/backend/integration.py +4 -3
  18. synapse_sdk/clients/backend/ml.py +1 -1
  19. synapse_sdk/clients/backend/models.py +35 -1
  20. synapse_sdk/clients/base.py +309 -36
  21. synapse_sdk/clients/ray/serve.py +2 -0
  22. synapse_sdk/devtools/__init__.py +0 -0
  23. synapse_sdk/devtools/config.py +94 -0
  24. synapse_sdk/devtools/docs/.gitignore +20 -0
  25. synapse_sdk/devtools/docs/README.md +41 -0
  26. synapse_sdk/devtools/docs/blog/2019-05-28-first-blog-post.md +12 -0
  27. synapse_sdk/devtools/docs/blog/2019-05-29-long-blog-post.md +44 -0
  28. synapse_sdk/devtools/docs/blog/2021-08-01-mdx-blog-post.mdx +24 -0
  29. synapse_sdk/devtools/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg +0 -0
  30. synapse_sdk/devtools/docs/blog/2021-08-26-welcome/index.md +29 -0
  31. synapse_sdk/devtools/docs/blog/authors.yml +25 -0
  32. synapse_sdk/devtools/docs/blog/tags.yml +19 -0
  33. synapse_sdk/devtools/docs/docs/api/clients/agent.md +43 -0
  34. synapse_sdk/devtools/docs/docs/api/clients/annotation-mixin.md +378 -0
  35. synapse_sdk/devtools/docs/docs/api/clients/backend.md +420 -0
  36. synapse_sdk/devtools/docs/docs/api/clients/base.md +257 -0
  37. synapse_sdk/devtools/docs/docs/api/clients/core-mixin.md +477 -0
  38. synapse_sdk/devtools/docs/docs/api/clients/data-collection-mixin.md +422 -0
  39. synapse_sdk/devtools/docs/docs/api/clients/hitl-mixin.md +554 -0
  40. synapse_sdk/devtools/docs/docs/api/clients/index.md +391 -0
  41. synapse_sdk/devtools/docs/docs/api/clients/integration-mixin.md +571 -0
  42. synapse_sdk/devtools/docs/docs/api/clients/ml-mixin.md +578 -0
  43. synapse_sdk/devtools/docs/docs/api/clients/ray.md +342 -0
  44. synapse_sdk/devtools/docs/docs/api/index.md +52 -0
  45. synapse_sdk/devtools/docs/docs/api/plugins/categories.md +43 -0
  46. synapse_sdk/devtools/docs/docs/api/plugins/models.md +114 -0
  47. synapse_sdk/devtools/docs/docs/api/plugins/utils.md +328 -0
  48. synapse_sdk/devtools/docs/docs/categories.md +0 -0
  49. synapse_sdk/devtools/docs/docs/cli-usage.md +280 -0
  50. synapse_sdk/devtools/docs/docs/concepts/index.md +38 -0
  51. synapse_sdk/devtools/docs/docs/configuration.md +83 -0
  52. synapse_sdk/devtools/docs/docs/contributing.md +306 -0
  53. synapse_sdk/devtools/docs/docs/examples/index.md +29 -0
  54. synapse_sdk/devtools/docs/docs/faq.md +179 -0
  55. synapse_sdk/devtools/docs/docs/features/converters/index.md +455 -0
  56. synapse_sdk/devtools/docs/docs/features/index.md +24 -0
  57. synapse_sdk/devtools/docs/docs/features/utils/file.md +415 -0
  58. synapse_sdk/devtools/docs/docs/features/utils/network.md +378 -0
  59. synapse_sdk/devtools/docs/docs/features/utils/storage.md +57 -0
  60. synapse_sdk/devtools/docs/docs/features/utils/types.md +51 -0
  61. synapse_sdk/devtools/docs/docs/installation.md +94 -0
  62. synapse_sdk/devtools/docs/docs/introduction.md +47 -0
  63. synapse_sdk/devtools/docs/docs/plugins/categories/neural-net-plugins/train-action-overview.md +814 -0
  64. synapse_sdk/devtools/docs/docs/plugins/categories/pre-annotation-plugins/pre-annotation-plugin-overview.md +198 -0
  65. synapse_sdk/devtools/docs/docs/plugins/categories/pre-annotation-plugins/to-task-action-development.md +1645 -0
  66. synapse_sdk/devtools/docs/docs/plugins/categories/pre-annotation-plugins/to-task-overview.md +717 -0
  67. synapse_sdk/devtools/docs/docs/plugins/categories/pre-annotation-plugins/to-task-template-development.md +1380 -0
  68. synapse_sdk/devtools/docs/docs/plugins/categories/upload-plugins/upload-plugin-action.md +948 -0
  69. synapse_sdk/devtools/docs/docs/plugins/categories/upload-plugins/upload-plugin-overview.md +544 -0
  70. synapse_sdk/devtools/docs/docs/plugins/categories/upload-plugins/upload-plugin-template.md +766 -0
  71. synapse_sdk/devtools/docs/docs/plugins/export-plugins.md +1092 -0
  72. synapse_sdk/devtools/docs/docs/plugins/plugins.md +852 -0
  73. synapse_sdk/devtools/docs/docs/quickstart.md +78 -0
  74. synapse_sdk/devtools/docs/docs/troubleshooting.md +519 -0
  75. synapse_sdk/devtools/docs/docs/tutorial-basics/_category_.json +8 -0
  76. synapse_sdk/devtools/docs/docs/tutorial-basics/congratulations.md +23 -0
  77. synapse_sdk/devtools/docs/docs/tutorial-basics/create-a-blog-post.md +34 -0
  78. synapse_sdk/devtools/docs/docs/tutorial-basics/create-a-document.md +57 -0
  79. synapse_sdk/devtools/docs/docs/tutorial-basics/create-a-page.md +43 -0
  80. synapse_sdk/devtools/docs/docs/tutorial-basics/deploy-your-site.md +31 -0
  81. synapse_sdk/devtools/docs/docs/tutorial-basics/markdown-features.mdx +152 -0
  82. synapse_sdk/devtools/docs/docs/tutorial-extras/_category_.json +7 -0
  83. synapse_sdk/devtools/docs/docs/tutorial-extras/img/docsVersionDropdown.png +0 -0
  84. synapse_sdk/devtools/docs/docs/tutorial-extras/img/localeDropdown.png +0 -0
  85. synapse_sdk/devtools/docs/docs/tutorial-extras/manage-docs-versions.md +55 -0
  86. synapse_sdk/devtools/docs/docs/tutorial-extras/translate-your-site.md +88 -0
  87. synapse_sdk/devtools/docs/docusaurus.config.ts +148 -0
  88. synapse_sdk/devtools/docs/i18n/ko/code.json +325 -0
  89. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/agent.md +43 -0
  90. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/annotation-mixin.md +289 -0
  91. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/backend.md +420 -0
  92. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/base.md +257 -0
  93. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/core-mixin.md +417 -0
  94. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/data-collection-mixin.md +356 -0
  95. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/hitl-mixin.md +192 -0
  96. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/index.md +391 -0
  97. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/integration-mixin.md +479 -0
  98. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/ml-mixin.md +284 -0
  99. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/ray.md +342 -0
  100. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/index.md +52 -0
  101. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/plugins/models.md +114 -0
  102. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/categories.md +0 -0
  103. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/cli-usage.md +280 -0
  104. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/concepts/index.md +38 -0
  105. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/configuration.md +83 -0
  106. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/contributing.md +306 -0
  107. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/examples/index.md +29 -0
  108. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/faq.md +179 -0
  109. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/converters/index.md +30 -0
  110. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/index.md +24 -0
  111. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/utils/file.md +415 -0
  112. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/utils/network.md +378 -0
  113. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/utils/storage.md +60 -0
  114. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/utils/types.md +51 -0
  115. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/installation.md +94 -0
  116. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/introduction.md +47 -0
  117. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/neural-net-plugins/train-action-overview.md +815 -0
  118. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/pre-annotation-plugins/pre-annotation-plugin-overview.md +198 -0
  119. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/pre-annotation-plugins/to-task-action-development.md +1645 -0
  120. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/pre-annotation-plugins/to-task-overview.md +717 -0
  121. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/pre-annotation-plugins/to-task-template-development.md +1380 -0
  122. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/upload-plugins/upload-plugin-action.md +948 -0
  123. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/upload-plugins/upload-plugin-overview.md +544 -0
  124. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/upload-plugins/upload-plugin-template.md +766 -0
  125. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/export-plugins.md +1092 -0
  126. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/plugins.md +117 -0
  127. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/quickstart.md +78 -0
  128. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/troubleshooting.md +519 -0
  129. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current.json +34 -0
  130. synapse_sdk/devtools/docs/i18n/ko/docusaurus-theme-classic/footer.json +42 -0
  131. synapse_sdk/devtools/docs/i18n/ko/docusaurus-theme-classic/navbar.json +18 -0
  132. synapse_sdk/devtools/docs/package-lock.json +18784 -0
  133. synapse_sdk/devtools/docs/package.json +48 -0
  134. synapse_sdk/devtools/docs/sidebars.ts +122 -0
  135. synapse_sdk/devtools/docs/src/components/HomepageFeatures/index.tsx +71 -0
  136. synapse_sdk/devtools/docs/src/components/HomepageFeatures/styles.module.css +11 -0
  137. synapse_sdk/devtools/docs/src/css/custom.css +30 -0
  138. synapse_sdk/devtools/docs/src/pages/index.module.css +23 -0
  139. synapse_sdk/devtools/docs/src/pages/index.tsx +21 -0
  140. synapse_sdk/devtools/docs/src/pages/markdown-page.md +7 -0
  141. synapse_sdk/devtools/docs/static/.nojekyll +0 -0
  142. synapse_sdk/devtools/docs/static/img/docusaurus-social-card.jpg +0 -0
  143. synapse_sdk/devtools/docs/static/img/docusaurus.png +0 -0
  144. synapse_sdk/devtools/docs/static/img/favicon.ico +0 -0
  145. synapse_sdk/devtools/docs/static/img/logo.png +0 -0
  146. synapse_sdk/devtools/docs/static/img/undraw_docusaurus_mountain.svg +171 -0
  147. synapse_sdk/devtools/docs/static/img/undraw_docusaurus_react.svg +170 -0
  148. synapse_sdk/devtools/docs/static/img/undraw_docusaurus_tree.svg +40 -0
  149. synapse_sdk/devtools/docs/tsconfig.json +8 -0
  150. synapse_sdk/devtools/server.py +41 -0
  151. synapse_sdk/devtools/streamlit_app/__init__.py +5 -0
  152. synapse_sdk/devtools/streamlit_app/app.py +128 -0
  153. synapse_sdk/devtools/streamlit_app/services/__init__.py +11 -0
  154. synapse_sdk/devtools/streamlit_app/services/job_service.py +233 -0
  155. synapse_sdk/devtools/streamlit_app/services/plugin_service.py +236 -0
  156. synapse_sdk/devtools/streamlit_app/services/serve_service.py +95 -0
  157. synapse_sdk/devtools/streamlit_app/ui/__init__.py +15 -0
  158. synapse_sdk/devtools/streamlit_app/ui/config_tab.py +76 -0
  159. synapse_sdk/devtools/streamlit_app/ui/deployment_tab.py +66 -0
  160. synapse_sdk/devtools/streamlit_app/ui/http_tab.py +125 -0
  161. synapse_sdk/devtools/streamlit_app/ui/jobs_tab.py +573 -0
  162. synapse_sdk/devtools/streamlit_app/ui/serve_tab.py +346 -0
  163. synapse_sdk/devtools/streamlit_app/ui/status_bar.py +118 -0
  164. synapse_sdk/devtools/streamlit_app/utils/__init__.py +40 -0
  165. synapse_sdk/devtools/streamlit_app/utils/json_viewer.py +197 -0
  166. synapse_sdk/devtools/streamlit_app/utils/log_formatter.py +38 -0
  167. synapse_sdk/devtools/streamlit_app/utils/styles.py +241 -0
  168. synapse_sdk/devtools/streamlit_app/utils/ui_components.py +289 -0
  169. synapse_sdk/devtools/streamlit_app.py +10 -0
  170. synapse_sdk/loggers.py +65 -7
  171. synapse_sdk/plugins/README.md +1340 -0
  172. synapse_sdk/plugins/categories/base.py +73 -11
  173. synapse_sdk/plugins/categories/data_validation/actions/validation.py +72 -0
  174. synapse_sdk/plugins/categories/data_validation/templates/plugin/validation.py +33 -5
  175. synapse_sdk/plugins/categories/export/actions/__init__.py +3 -0
  176. synapse_sdk/plugins/categories/export/actions/export/__init__.py +28 -0
  177. synapse_sdk/plugins/categories/export/actions/export/action.py +165 -0
  178. synapse_sdk/plugins/categories/export/actions/export/enums.py +113 -0
  179. synapse_sdk/plugins/categories/export/actions/export/exceptions.py +53 -0
  180. synapse_sdk/plugins/categories/export/actions/export/models.py +74 -0
  181. synapse_sdk/plugins/categories/export/actions/export/run.py +195 -0
  182. synapse_sdk/plugins/categories/export/actions/{export.py → export/utils.py} +47 -82
  183. synapse_sdk/plugins/categories/export/templates/config.yaml +19 -1
  184. synapse_sdk/plugins/categories/export/templates/plugin/__init__.py +390 -0
  185. synapse_sdk/plugins/categories/export/templates/plugin/export.py +153 -129
  186. synapse_sdk/plugins/categories/neural_net/actions/deployment.py +9 -62
  187. synapse_sdk/plugins/categories/neural_net/actions/train.py +1062 -32
  188. synapse_sdk/plugins/categories/neural_net/actions/tune.py +534 -0
  189. synapse_sdk/plugins/categories/neural_net/templates/config.yaml +27 -5
  190. synapse_sdk/plugins/categories/neural_net/templates/plugin/inference.py +26 -10
  191. synapse_sdk/plugins/categories/pre_annotation/actions/__init__.py +4 -0
  192. synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation/__init__.py +3 -0
  193. synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation/action.py +10 -0
  194. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/__init__.py +28 -0
  195. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/action.py +145 -0
  196. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/enums.py +269 -0
  197. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/exceptions.py +14 -0
  198. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/factory.py +76 -0
  199. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/models.py +97 -0
  200. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/orchestrator.py +250 -0
  201. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/run.py +64 -0
  202. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/__init__.py +17 -0
  203. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/annotation.py +287 -0
  204. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/base.py +170 -0
  205. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/extraction.py +83 -0
  206. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/metrics.py +87 -0
  207. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/preprocessor.py +127 -0
  208. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/validation.py +143 -0
  209. synapse_sdk/plugins/categories/pre_annotation/actions/to_task.py +966 -0
  210. synapse_sdk/plugins/categories/pre_annotation/templates/config.yaml +19 -0
  211. synapse_sdk/plugins/categories/pre_annotation/templates/plugin/to_task.py +40 -0
  212. synapse_sdk/plugins/categories/upload/actions/upload/__init__.py +19 -0
  213. synapse_sdk/plugins/categories/upload/actions/upload/action.py +232 -0
  214. synapse_sdk/plugins/categories/upload/actions/upload/context.py +185 -0
  215. synapse_sdk/plugins/categories/upload/actions/upload/enums.py +471 -0
  216. synapse_sdk/plugins/categories/upload/actions/upload/exceptions.py +36 -0
  217. synapse_sdk/plugins/categories/upload/actions/upload/factory.py +138 -0
  218. synapse_sdk/plugins/categories/upload/actions/upload/models.py +203 -0
  219. synapse_sdk/plugins/categories/upload/actions/upload/orchestrator.py +183 -0
  220. synapse_sdk/plugins/categories/upload/actions/upload/registry.py +113 -0
  221. synapse_sdk/plugins/categories/upload/actions/upload/run.py +179 -0
  222. synapse_sdk/plugins/categories/upload/actions/upload/steps/__init__.py +1 -0
  223. synapse_sdk/plugins/categories/upload/actions/upload/steps/base.py +107 -0
  224. synapse_sdk/plugins/categories/upload/actions/upload/steps/cleanup.py +62 -0
  225. synapse_sdk/plugins/categories/upload/actions/upload/steps/collection.py +63 -0
  226. synapse_sdk/plugins/categories/upload/actions/upload/steps/generate.py +84 -0
  227. synapse_sdk/plugins/categories/upload/actions/upload/steps/initialize.py +82 -0
  228. synapse_sdk/plugins/categories/upload/actions/upload/steps/metadata.py +235 -0
  229. synapse_sdk/plugins/categories/upload/actions/upload/steps/organize.py +203 -0
  230. synapse_sdk/plugins/categories/upload/actions/upload/steps/upload.py +97 -0
  231. synapse_sdk/plugins/categories/upload/actions/upload/steps/validate.py +71 -0
  232. synapse_sdk/plugins/categories/upload/actions/upload/strategies/__init__.py +1 -0
  233. synapse_sdk/plugins/categories/upload/actions/upload/strategies/base.py +82 -0
  234. synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/__init__.py +1 -0
  235. synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/batch.py +39 -0
  236. synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/single.py +29 -0
  237. synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/__init__.py +1 -0
  238. synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/flat.py +258 -0
  239. synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/recursive.py +281 -0
  240. synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/__init__.py +1 -0
  241. synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/excel.py +174 -0
  242. synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/none.py +16 -0
  243. synapse_sdk/plugins/categories/upload/actions/upload/strategies/upload/__init__.py +1 -0
  244. synapse_sdk/plugins/categories/upload/actions/upload/strategies/upload/sync.py +84 -0
  245. synapse_sdk/plugins/categories/upload/actions/upload/strategies/validation/__init__.py +1 -0
  246. synapse_sdk/plugins/categories/upload/actions/upload/strategies/validation/default.py +60 -0
  247. synapse_sdk/plugins/categories/upload/actions/upload/utils.py +250 -0
  248. synapse_sdk/plugins/categories/upload/templates/README.md +470 -0
  249. synapse_sdk/plugins/categories/upload/templates/config.yaml +29 -2
  250. synapse_sdk/plugins/categories/upload/templates/plugin/__init__.py +294 -0
  251. synapse_sdk/plugins/categories/upload/templates/plugin/upload.py +88 -30
  252. synapse_sdk/plugins/models.py +122 -16
  253. synapse_sdk/plugins/templates/plugin-config-schema.json +406 -0
  254. synapse_sdk/plugins/templates/schema.json +491 -0
  255. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/requirements.txt +1 -1
  256. synapse_sdk/plugins/utils/__init__.py +46 -0
  257. synapse_sdk/plugins/utils/actions.py +119 -0
  258. synapse_sdk/plugins/utils/config.py +203 -0
  259. synapse_sdk/plugins/{utils.py → utils/legacy.py} +26 -46
  260. synapse_sdk/plugins/utils/ray_gcs.py +66 -0
  261. synapse_sdk/plugins/utils/registry.py +58 -0
  262. synapse_sdk/shared/__init__.py +25 -0
  263. synapse_sdk/shared/enums.py +93 -0
  264. synapse_sdk/utils/converters/__init__.py +240 -0
  265. synapse_sdk/utils/converters/coco/__init__.py +0 -0
  266. synapse_sdk/utils/converters/coco/from_dm.py +322 -0
  267. synapse_sdk/utils/converters/coco/to_dm.py +215 -0
  268. synapse_sdk/utils/converters/dm/__init__.py +56 -0
  269. synapse_sdk/utils/converters/dm/from_v1.py +627 -0
  270. synapse_sdk/utils/converters/dm/to_v1.py +367 -0
  271. synapse_sdk/utils/converters/pascal/__init__.py +0 -0
  272. synapse_sdk/utils/converters/pascal/from_dm.py +244 -0
  273. synapse_sdk/utils/converters/pascal/to_dm.py +214 -0
  274. synapse_sdk/utils/converters/yolo/__init__.py +0 -0
  275. synapse_sdk/utils/converters/yolo/from_dm.py +384 -0
  276. synapse_sdk/utils/converters/yolo/to_dm.py +267 -0
  277. synapse_sdk/utils/dataset.py +46 -0
  278. synapse_sdk/utils/encryption.py +158 -0
  279. synapse_sdk/utils/file/__init__.py +39 -0
  280. synapse_sdk/utils/file/archive.py +32 -0
  281. synapse_sdk/utils/file/checksum.py +56 -0
  282. synapse_sdk/utils/file/chunking.py +31 -0
  283. synapse_sdk/utils/file/download.py +385 -0
  284. synapse_sdk/utils/file/encoding.py +40 -0
  285. synapse_sdk/utils/file/io.py +22 -0
  286. synapse_sdk/utils/file/video/__init__.py +29 -0
  287. synapse_sdk/utils/file/video/transcode.py +307 -0
  288. synapse_sdk/utils/{file.py → file.py.backup} +84 -2
  289. synapse_sdk/utils/http.py +138 -0
  290. synapse_sdk/utils/network.py +293 -0
  291. synapse_sdk/utils/storage/__init__.py +36 -2
  292. synapse_sdk/utils/storage/providers/__init__.py +141 -0
  293. synapse_sdk/utils/storage/providers/file_system.py +134 -0
  294. synapse_sdk/utils/storage/providers/http.py +190 -0
  295. synapse_sdk/utils/storage/providers/s3.py +54 -6
  296. synapse_sdk/utils/storage/providers/sftp.py +31 -0
  297. synapse_sdk/utils/storage/registry.py +6 -0
  298. synapse_sdk-2025.11.7.dist-info/METADATA +122 -0
  299. synapse_sdk-2025.11.7.dist-info/RECORD +386 -0
  300. {synapse_sdk-1.0.0a35.dist-info → synapse_sdk-2025.11.7.dist-info}/WHEEL +1 -1
  301. synapse_sdk/clients/backend/dataset.py +0 -102
  302. synapse_sdk/plugins/categories/upload/actions/upload.py +0 -293
  303. synapse_sdk-1.0.0a35.dist-info/METADATA +0 -47
  304. synapse_sdk-1.0.0a35.dist-info/RECORD +0 -137
  305. {synapse_sdk-1.0.0a35.dist-info → synapse_sdk-2025.11.7.dist-info}/entry_points.txt +0 -0
  306. {synapse_sdk-1.0.0a35.dist-info → synapse_sdk-2025.11.7.dist-info}/licenses/LICENSE +0 -0
  307. {synapse_sdk-1.0.0a35.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