lightly-studio 0.4.6__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.
Files changed (356) hide show
  1. lightly_studio/__init__.py +12 -0
  2. lightly_studio/api/__init__.py +0 -0
  3. lightly_studio/api/app.py +131 -0
  4. lightly_studio/api/cache.py +77 -0
  5. lightly_studio/api/db_tables.py +35 -0
  6. lightly_studio/api/features.py +5 -0
  7. lightly_studio/api/routes/api/annotation.py +305 -0
  8. lightly_studio/api/routes/api/annotation_label.py +87 -0
  9. lightly_studio/api/routes/api/annotations/__init__.py +7 -0
  10. lightly_studio/api/routes/api/annotations/create_annotation.py +52 -0
  11. lightly_studio/api/routes/api/caption.py +100 -0
  12. lightly_studio/api/routes/api/classifier.py +384 -0
  13. lightly_studio/api/routes/api/dataset.py +191 -0
  14. lightly_studio/api/routes/api/dataset_tag.py +266 -0
  15. lightly_studio/api/routes/api/embeddings2d.py +90 -0
  16. lightly_studio/api/routes/api/exceptions.py +114 -0
  17. lightly_studio/api/routes/api/export.py +114 -0
  18. lightly_studio/api/routes/api/features.py +17 -0
  19. lightly_studio/api/routes/api/frame.py +241 -0
  20. lightly_studio/api/routes/api/image.py +155 -0
  21. lightly_studio/api/routes/api/metadata.py +161 -0
  22. lightly_studio/api/routes/api/operator.py +75 -0
  23. lightly_studio/api/routes/api/sample.py +103 -0
  24. lightly_studio/api/routes/api/selection.py +87 -0
  25. lightly_studio/api/routes/api/settings.py +41 -0
  26. lightly_studio/api/routes/api/status.py +19 -0
  27. lightly_studio/api/routes/api/text_embedding.py +50 -0
  28. lightly_studio/api/routes/api/validators.py +17 -0
  29. lightly_studio/api/routes/api/video.py +133 -0
  30. lightly_studio/api/routes/healthz.py +13 -0
  31. lightly_studio/api/routes/images.py +104 -0
  32. lightly_studio/api/routes/video_frames_media.py +116 -0
  33. lightly_studio/api/routes/video_media.py +223 -0
  34. lightly_studio/api/routes/webapp.py +51 -0
  35. lightly_studio/api/server.py +94 -0
  36. lightly_studio/core/__init__.py +0 -0
  37. lightly_studio/core/add_samples.py +533 -0
  38. lightly_studio/core/add_videos.py +294 -0
  39. lightly_studio/core/dataset.py +780 -0
  40. lightly_studio/core/dataset_query/__init__.py +14 -0
  41. lightly_studio/core/dataset_query/boolean_expression.py +67 -0
  42. lightly_studio/core/dataset_query/dataset_query.py +317 -0
  43. lightly_studio/core/dataset_query/field.py +113 -0
  44. lightly_studio/core/dataset_query/field_expression.py +79 -0
  45. lightly_studio/core/dataset_query/match_expression.py +23 -0
  46. lightly_studio/core/dataset_query/order_by.py +79 -0
  47. lightly_studio/core/dataset_query/sample_field.py +37 -0
  48. lightly_studio/core/dataset_query/tags_expression.py +46 -0
  49. lightly_studio/core/image_sample.py +36 -0
  50. lightly_studio/core/loading_log.py +56 -0
  51. lightly_studio/core/sample.py +291 -0
  52. lightly_studio/core/start_gui.py +54 -0
  53. lightly_studio/core/video_sample.py +38 -0
  54. lightly_studio/dataset/__init__.py +0 -0
  55. lightly_studio/dataset/edge_embedding_generator.py +155 -0
  56. lightly_studio/dataset/embedding_generator.py +129 -0
  57. lightly_studio/dataset/embedding_manager.py +349 -0
  58. lightly_studio/dataset/env.py +20 -0
  59. lightly_studio/dataset/file_utils.py +49 -0
  60. lightly_studio/dataset/fsspec_lister.py +275 -0
  61. lightly_studio/dataset/mobileclip_embedding_generator.py +158 -0
  62. lightly_studio/dataset/perception_encoder_embedding_generator.py +260 -0
  63. lightly_studio/db_manager.py +166 -0
  64. lightly_studio/dist_lightly_studio_view_app/_app/env.js +1 -0
  65. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/0.GcXvs2l7.css +1 -0
  66. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/12.Dx6SXgAb.css +1 -0
  67. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/17.9X9_k6TP.css +1 -0
  68. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/18.BxiimdIO.css +1 -0
  69. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/2.CkOblLn7.css +1 -0
  70. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/ClassifierSamplesGrid.BJbCDlvs.css +1 -0
  71. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/LightlyLogo.BNjCIww-.png +0 -0
  72. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/OpenSans-Bold.DGvYQtcs.ttf +0 -0
  73. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/OpenSans-Italic-VariableFont_wdth_wght.B4AZ-wl6.ttf +0 -0
  74. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/OpenSans-Medium.DVUZMR_6.ttf +0 -0
  75. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/OpenSans-Regular.DxJTClRG.ttf +0 -0
  76. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/OpenSans-SemiBold.D3TTYgdB.ttf +0 -0
  77. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/OpenSans-VariableFont_wdth_wght.BZBpG5Iz.ttf +0 -0
  78. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/_layout.CefECEWA.css +1 -0
  79. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/_layout.D5tDcjY-.css +1 -0
  80. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/_page.9X9_k6TP.css +1 -0
  81. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/_page.BxiimdIO.css +1 -0
  82. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/_page.Dx6SXgAb.css +1 -0
  83. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/transform._-1mPSEI.css +1 -0
  84. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/0dDyq72A.js +20 -0
  85. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/69_IOA4Y.js +1 -0
  86. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/BK4An2kI.js +1 -0
  87. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/BRmB-kJ9.js +1 -0
  88. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/B_1cpokE.js +1 -0
  89. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/BiqpDEr0.js +1 -0
  90. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/BpLiSKgx.js +1 -0
  91. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/BscxbINH.js +39 -0
  92. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/C1FmrZbK.js +1 -0
  93. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/C80h3dJx.js +1 -0
  94. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/C8mfFM-u.js +2 -0
  95. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CGY1p9L4.js +517 -0
  96. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/COfLknXM.js +1 -0
  97. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CWj6FrbW.js +1 -0
  98. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CYgJF_JY.js +1 -0
  99. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CmLg0ys7.js +1 -0
  100. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CvGjimpO.js +1 -0
  101. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/D3RDXHoj.js +39 -0
  102. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/D4y7iiT3.js +1 -0
  103. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/D9SC3jBb.js +1 -0
  104. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DCuAdx1Q.js +20 -0
  105. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DDBy-_jD.js +1 -0
  106. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DIeogL5L.js +1 -0
  107. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DL9a7v5o.js +1 -0
  108. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DSKECuqX.js +39 -0
  109. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/D_FFv0Oe.js +1 -0
  110. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DiZ5o5vz.js +1 -0
  111. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DkbXUtyG.js +1 -0
  112. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DmK2hulV.js +1 -0
  113. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DqnHaLTj.js +1 -0
  114. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DtWZc_tl.js +1 -0
  115. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DuUalyFS.js +1 -0
  116. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DwIonDAZ.js +1 -0
  117. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/Il-mSPmK.js +1 -0
  118. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/KNLP4aJU.js +1 -0
  119. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/KjYeVjkE.js +1 -0
  120. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/MErlcOXj.js +1 -0
  121. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/VRI4prUD.js +1 -0
  122. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/VYb2dkNs.js +1 -0
  123. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/VqWvU2yF.js +1 -0
  124. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/dHC3otuL.js +1 -0
  125. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/da7Oy_lO.js +1 -0
  126. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/eAy8rZzC.js +2 -0
  127. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/erjNR5MX.js +1 -0
  128. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/f1oG3eFE.js +1 -0
  129. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/rsLi1iKv.js +20 -0
  130. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/rwuuBP9f.js +1 -0
  131. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/xGHZQ1pe.js +3 -0
  132. lightly_studio/dist_lightly_studio_view_app/_app/immutable/entry/app.DrTRUgT3.js +2 -0
  133. lightly_studio/dist_lightly_studio_view_app/_app/immutable/entry/start.BK5EOJl2.js +1 -0
  134. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/0.CIvTuljF.js +4 -0
  135. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/1.UBvSzxdA.js +1 -0
  136. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/10.CQ_tiLJa.js +1 -0
  137. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/11.KqkAcaxW.js +1 -0
  138. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/12.DoYsmxQc.js +1 -0
  139. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/13.571n2LZA.js +1 -0
  140. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/14.DGs689M-.js +1 -0
  141. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/15.CWG1ehzT.js +1 -0
  142. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/16.Dpq6jbSh.js +1 -0
  143. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/17.B5AZbHUU.js +1 -0
  144. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/18.CBga8cnq.js +1 -0
  145. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/2.D2HXgz-8.js +1090 -0
  146. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/3.f4HAg-y3.js +1 -0
  147. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/4.BKF4xuKQ.js +1 -0
  148. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/5.BAE0Pm_f.js +39 -0
  149. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/6.CouWWpzA.js +1 -0
  150. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/7.UBHT0ktp.js +1 -0
  151. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/8.FiYNElcc.js +1 -0
  152. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/9.B3-UaT23.js +1 -0
  153. lightly_studio/dist_lightly_studio_view_app/_app/immutable/workers/clustering.worker-DKqeLtG0.js +2 -0
  154. lightly_studio/dist_lightly_studio_view_app/_app/immutable/workers/search.worker-vNSty3B0.js +1 -0
  155. lightly_studio/dist_lightly_studio_view_app/_app/version.json +1 -0
  156. lightly_studio/dist_lightly_studio_view_app/apple-touch-icon-precomposed.png +0 -0
  157. lightly_studio/dist_lightly_studio_view_app/apple-touch-icon.png +0 -0
  158. lightly_studio/dist_lightly_studio_view_app/favicon.png +0 -0
  159. lightly_studio/dist_lightly_studio_view_app/index.html +45 -0
  160. lightly_studio/errors.py +5 -0
  161. lightly_studio/examples/example.py +25 -0
  162. lightly_studio/examples/example_coco.py +27 -0
  163. lightly_studio/examples/example_coco_caption.py +29 -0
  164. lightly_studio/examples/example_metadata.py +369 -0
  165. lightly_studio/examples/example_operators.py +111 -0
  166. lightly_studio/examples/example_selection.py +28 -0
  167. lightly_studio/examples/example_split_work.py +48 -0
  168. lightly_studio/examples/example_video.py +22 -0
  169. lightly_studio/examples/example_video_annotations.py +157 -0
  170. lightly_studio/examples/example_yolo.py +22 -0
  171. lightly_studio/export/coco_captions.py +69 -0
  172. lightly_studio/export/export_dataset.py +104 -0
  173. lightly_studio/export/lightly_studio_label_input.py +120 -0
  174. lightly_studio/export_schema.py +18 -0
  175. lightly_studio/export_version.py +57 -0
  176. lightly_studio/few_shot_classifier/__init__.py +0 -0
  177. lightly_studio/few_shot_classifier/classifier.py +80 -0
  178. lightly_studio/few_shot_classifier/classifier_manager.py +644 -0
  179. lightly_studio/few_shot_classifier/random_forest_classifier.py +495 -0
  180. lightly_studio/metadata/complex_metadata.py +47 -0
  181. lightly_studio/metadata/compute_similarity.py +84 -0
  182. lightly_studio/metadata/compute_typicality.py +67 -0
  183. lightly_studio/metadata/gps_coordinate.py +41 -0
  184. lightly_studio/metadata/metadata_protocol.py +17 -0
  185. lightly_studio/models/__init__.py +1 -0
  186. lightly_studio/models/annotation/__init__.py +0 -0
  187. lightly_studio/models/annotation/annotation_base.py +303 -0
  188. lightly_studio/models/annotation/instance_segmentation.py +56 -0
  189. lightly_studio/models/annotation/links.py +17 -0
  190. lightly_studio/models/annotation/object_detection.py +47 -0
  191. lightly_studio/models/annotation/semantic_segmentation.py +44 -0
  192. lightly_studio/models/annotation_label.py +47 -0
  193. lightly_studio/models/caption.py +49 -0
  194. lightly_studio/models/classifier.py +20 -0
  195. lightly_studio/models/dataset.py +70 -0
  196. lightly_studio/models/embedding_model.py +30 -0
  197. lightly_studio/models/image.py +96 -0
  198. lightly_studio/models/metadata.py +208 -0
  199. lightly_studio/models/range.py +17 -0
  200. lightly_studio/models/sample.py +154 -0
  201. lightly_studio/models/sample_embedding.py +36 -0
  202. lightly_studio/models/settings.py +69 -0
  203. lightly_studio/models/tag.py +96 -0
  204. lightly_studio/models/two_dim_embedding.py +16 -0
  205. lightly_studio/models/video.py +161 -0
  206. lightly_studio/plugins/__init__.py +0 -0
  207. lightly_studio/plugins/base_operator.py +60 -0
  208. lightly_studio/plugins/operator_registry.py +47 -0
  209. lightly_studio/plugins/parameter.py +70 -0
  210. lightly_studio/py.typed +0 -0
  211. lightly_studio/resolvers/__init__.py +0 -0
  212. lightly_studio/resolvers/annotation_label_resolver/__init__.py +22 -0
  213. lightly_studio/resolvers/annotation_label_resolver/create.py +27 -0
  214. lightly_studio/resolvers/annotation_label_resolver/delete.py +28 -0
  215. lightly_studio/resolvers/annotation_label_resolver/get_all.py +37 -0
  216. lightly_studio/resolvers/annotation_label_resolver/get_by_id.py +24 -0
  217. lightly_studio/resolvers/annotation_label_resolver/get_by_ids.py +25 -0
  218. lightly_studio/resolvers/annotation_label_resolver/get_by_label_name.py +24 -0
  219. lightly_studio/resolvers/annotation_label_resolver/names_by_ids.py +25 -0
  220. lightly_studio/resolvers/annotation_label_resolver/update.py +38 -0
  221. lightly_studio/resolvers/annotation_resolver/__init__.py +40 -0
  222. lightly_studio/resolvers/annotation_resolver/count_annotations_by_dataset.py +129 -0
  223. lightly_studio/resolvers/annotation_resolver/create_many.py +124 -0
  224. lightly_studio/resolvers/annotation_resolver/delete_annotation.py +87 -0
  225. lightly_studio/resolvers/annotation_resolver/delete_annotations.py +60 -0
  226. lightly_studio/resolvers/annotation_resolver/get_all.py +85 -0
  227. lightly_studio/resolvers/annotation_resolver/get_all_with_payload.py +179 -0
  228. lightly_studio/resolvers/annotation_resolver/get_by_id.py +34 -0
  229. lightly_studio/resolvers/annotation_resolver/get_by_id_with_payload.py +130 -0
  230. lightly_studio/resolvers/annotation_resolver/update_annotation_label.py +142 -0
  231. lightly_studio/resolvers/annotation_resolver/update_bounding_box.py +68 -0
  232. lightly_studio/resolvers/annotations/__init__.py +1 -0
  233. lightly_studio/resolvers/annotations/annotations_filter.py +88 -0
  234. lightly_studio/resolvers/caption_resolver.py +129 -0
  235. lightly_studio/resolvers/dataset_resolver/__init__.py +55 -0
  236. lightly_studio/resolvers/dataset_resolver/check_dataset_type.py +29 -0
  237. lightly_studio/resolvers/dataset_resolver/create.py +20 -0
  238. lightly_studio/resolvers/dataset_resolver/delete.py +20 -0
  239. lightly_studio/resolvers/dataset_resolver/export.py +267 -0
  240. lightly_studio/resolvers/dataset_resolver/get_all.py +19 -0
  241. lightly_studio/resolvers/dataset_resolver/get_by_id.py +16 -0
  242. lightly_studio/resolvers/dataset_resolver/get_by_name.py +12 -0
  243. lightly_studio/resolvers/dataset_resolver/get_dataset_details.py +27 -0
  244. lightly_studio/resolvers/dataset_resolver/get_hierarchy.py +31 -0
  245. lightly_studio/resolvers/dataset_resolver/get_or_create_child_dataset.py +58 -0
  246. lightly_studio/resolvers/dataset_resolver/get_parent_dataset_by_sample_id.py +27 -0
  247. lightly_studio/resolvers/dataset_resolver/get_parent_dataset_id.py +22 -0
  248. lightly_studio/resolvers/dataset_resolver/get_root_dataset.py +61 -0
  249. lightly_studio/resolvers/dataset_resolver/get_root_datasets_overview.py +41 -0
  250. lightly_studio/resolvers/dataset_resolver/update.py +25 -0
  251. lightly_studio/resolvers/embedding_model_resolver.py +120 -0
  252. lightly_studio/resolvers/image_filter.py +50 -0
  253. lightly_studio/resolvers/image_resolver/__init__.py +21 -0
  254. lightly_studio/resolvers/image_resolver/create_many.py +52 -0
  255. lightly_studio/resolvers/image_resolver/delete.py +20 -0
  256. lightly_studio/resolvers/image_resolver/filter_new_paths.py +23 -0
  257. lightly_studio/resolvers/image_resolver/get_all_by_dataset_id.py +117 -0
  258. lightly_studio/resolvers/image_resolver/get_by_id.py +14 -0
  259. lightly_studio/resolvers/image_resolver/get_dimension_bounds.py +75 -0
  260. lightly_studio/resolvers/image_resolver/get_many_by_id.py +22 -0
  261. lightly_studio/resolvers/image_resolver/get_samples_excluding.py +43 -0
  262. lightly_studio/resolvers/metadata_resolver/__init__.py +15 -0
  263. lightly_studio/resolvers/metadata_resolver/metadata_filter.py +163 -0
  264. lightly_studio/resolvers/metadata_resolver/sample/__init__.py +21 -0
  265. lightly_studio/resolvers/metadata_resolver/sample/bulk_update_metadata.py +46 -0
  266. lightly_studio/resolvers/metadata_resolver/sample/get_by_sample_id.py +24 -0
  267. lightly_studio/resolvers/metadata_resolver/sample/get_metadata_info.py +104 -0
  268. lightly_studio/resolvers/metadata_resolver/sample/get_value_for_sample.py +27 -0
  269. lightly_studio/resolvers/metadata_resolver/sample/set_value_for_sample.py +53 -0
  270. lightly_studio/resolvers/sample_embedding_resolver.py +132 -0
  271. lightly_studio/resolvers/sample_resolver/__init__.py +17 -0
  272. lightly_studio/resolvers/sample_resolver/count_by_dataset_id.py +16 -0
  273. lightly_studio/resolvers/sample_resolver/create.py +16 -0
  274. lightly_studio/resolvers/sample_resolver/create_many.py +25 -0
  275. lightly_studio/resolvers/sample_resolver/get_by_id.py +14 -0
  276. lightly_studio/resolvers/sample_resolver/get_filtered_samples.py +56 -0
  277. lightly_studio/resolvers/sample_resolver/get_many_by_id.py +22 -0
  278. lightly_studio/resolvers/sample_resolver/sample_filter.py +74 -0
  279. lightly_studio/resolvers/settings_resolver.py +62 -0
  280. lightly_studio/resolvers/tag_resolver.py +299 -0
  281. lightly_studio/resolvers/twodim_embedding_resolver.py +119 -0
  282. lightly_studio/resolvers/video_frame_resolver/__init__.py +23 -0
  283. lightly_studio/resolvers/video_frame_resolver/count_video_frames_annotations.py +83 -0
  284. lightly_studio/resolvers/video_frame_resolver/create_many.py +57 -0
  285. lightly_studio/resolvers/video_frame_resolver/get_all_by_dataset_id.py +63 -0
  286. lightly_studio/resolvers/video_frame_resolver/get_by_id.py +13 -0
  287. lightly_studio/resolvers/video_frame_resolver/get_table_fields_bounds.py +44 -0
  288. lightly_studio/resolvers/video_frame_resolver/video_frame_annotations_counter_filter.py +47 -0
  289. lightly_studio/resolvers/video_frame_resolver/video_frame_filter.py +57 -0
  290. lightly_studio/resolvers/video_resolver/__init__.py +27 -0
  291. lightly_studio/resolvers/video_resolver/count_video_frame_annotations_by_video_dataset.py +86 -0
  292. lightly_studio/resolvers/video_resolver/create_many.py +58 -0
  293. lightly_studio/resolvers/video_resolver/filter_new_paths.py +33 -0
  294. lightly_studio/resolvers/video_resolver/get_all_by_dataset_id.py +181 -0
  295. lightly_studio/resolvers/video_resolver/get_by_id.py +22 -0
  296. lightly_studio/resolvers/video_resolver/get_table_fields_bounds.py +72 -0
  297. lightly_studio/resolvers/video_resolver/get_view_by_id.py +52 -0
  298. lightly_studio/resolvers/video_resolver/video_count_annotations_filter.py +50 -0
  299. lightly_studio/resolvers/video_resolver/video_filter.py +98 -0
  300. lightly_studio/selection/__init__.py +1 -0
  301. lightly_studio/selection/mundig.py +143 -0
  302. lightly_studio/selection/select.py +203 -0
  303. lightly_studio/selection/select_via_db.py +273 -0
  304. lightly_studio/selection/selection_config.py +49 -0
  305. lightly_studio/services/annotations_service/__init__.py +33 -0
  306. lightly_studio/services/annotations_service/create_annotation.py +64 -0
  307. lightly_studio/services/annotations_service/delete_annotation.py +22 -0
  308. lightly_studio/services/annotations_service/get_annotation_by_id.py +31 -0
  309. lightly_studio/services/annotations_service/update_annotation.py +54 -0
  310. lightly_studio/services/annotations_service/update_annotation_bounding_box.py +36 -0
  311. lightly_studio/services/annotations_service/update_annotation_label.py +48 -0
  312. lightly_studio/services/annotations_service/update_annotations.py +29 -0
  313. lightly_studio/setup_logging.py +59 -0
  314. lightly_studio/type_definitions.py +31 -0
  315. lightly_studio/utils/__init__.py +3 -0
  316. lightly_studio/utils/download.py +94 -0
  317. lightly_studio/vendor/__init__.py +1 -0
  318. lightly_studio/vendor/mobileclip/ACKNOWLEDGEMENTS +422 -0
  319. lightly_studio/vendor/mobileclip/LICENSE +31 -0
  320. lightly_studio/vendor/mobileclip/LICENSE_weights_data +50 -0
  321. lightly_studio/vendor/mobileclip/README.md +5 -0
  322. lightly_studio/vendor/mobileclip/__init__.py +96 -0
  323. lightly_studio/vendor/mobileclip/clip.py +77 -0
  324. lightly_studio/vendor/mobileclip/configs/mobileclip_b.json +18 -0
  325. lightly_studio/vendor/mobileclip/configs/mobileclip_s0.json +18 -0
  326. lightly_studio/vendor/mobileclip/configs/mobileclip_s1.json +18 -0
  327. lightly_studio/vendor/mobileclip/configs/mobileclip_s2.json +18 -0
  328. lightly_studio/vendor/mobileclip/image_encoder.py +67 -0
  329. lightly_studio/vendor/mobileclip/logger.py +154 -0
  330. lightly_studio/vendor/mobileclip/models/__init__.py +10 -0
  331. lightly_studio/vendor/mobileclip/models/mci.py +933 -0
  332. lightly_studio/vendor/mobileclip/models/vit.py +433 -0
  333. lightly_studio/vendor/mobileclip/modules/__init__.py +4 -0
  334. lightly_studio/vendor/mobileclip/modules/common/__init__.py +4 -0
  335. lightly_studio/vendor/mobileclip/modules/common/mobileone.py +341 -0
  336. lightly_studio/vendor/mobileclip/modules/common/transformer.py +451 -0
  337. lightly_studio/vendor/mobileclip/modules/image/__init__.py +4 -0
  338. lightly_studio/vendor/mobileclip/modules/image/image_projection.py +113 -0
  339. lightly_studio/vendor/mobileclip/modules/image/replknet.py +188 -0
  340. lightly_studio/vendor/mobileclip/modules/text/__init__.py +4 -0
  341. lightly_studio/vendor/mobileclip/modules/text/repmixer.py +281 -0
  342. lightly_studio/vendor/mobileclip/modules/text/tokenizer.py +38 -0
  343. lightly_studio/vendor/mobileclip/text_encoder.py +245 -0
  344. lightly_studio/vendor/perception_encoder/LICENSE.PE +201 -0
  345. lightly_studio/vendor/perception_encoder/README.md +11 -0
  346. lightly_studio/vendor/perception_encoder/vision_encoder/__init__.py +0 -0
  347. lightly_studio/vendor/perception_encoder/vision_encoder/bpe_simple_vocab_16e6.txt.gz +0 -0
  348. lightly_studio/vendor/perception_encoder/vision_encoder/config.py +205 -0
  349. lightly_studio/vendor/perception_encoder/vision_encoder/config_src.py +264 -0
  350. lightly_studio/vendor/perception_encoder/vision_encoder/pe.py +766 -0
  351. lightly_studio/vendor/perception_encoder/vision_encoder/rope.py +352 -0
  352. lightly_studio/vendor/perception_encoder/vision_encoder/tokenizer.py +347 -0
  353. lightly_studio/vendor/perception_encoder/vision_encoder/transforms.py +36 -0
  354. lightly_studio-0.4.6.dist-info/METADATA +88 -0
  355. lightly_studio-0.4.6.dist-info/RECORD +356 -0
  356. lightly_studio-0.4.6.dist-info/WHEEL +4 -0
@@ -0,0 +1,533 @@
1
+ """Functions to add samples and their annotations to a dataset in the database."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import logging
7
+ from collections import defaultdict
8
+ from collections.abc import Mapping
9
+ from dataclasses import dataclass
10
+ from pathlib import Path
11
+ from typing import Iterable
12
+ from uuid import UUID
13
+
14
+ import fsspec
15
+ import PIL
16
+ from labelformat.model.binary_mask_segmentation import BinaryMaskSegmentation
17
+ from labelformat.model.bounding_box import BoundingBoxFormat
18
+ from labelformat.model.image import Image
19
+ from labelformat.model.instance_segmentation import (
20
+ ImageInstanceSegmentation,
21
+ InstanceSegmentationInput,
22
+ )
23
+ from labelformat.model.multipolygon import MultiPolygon
24
+ from labelformat.model.object_detection import (
25
+ ImageObjectDetection,
26
+ ObjectDetectionInput,
27
+ )
28
+ from sqlmodel import Session
29
+ from tqdm import tqdm
30
+
31
+ from lightly_studio.core.image_sample import ImageSample
32
+ from lightly_studio.core.loading_log import LoadingLoggingContext, log_loading_results
33
+ from lightly_studio.models.annotation.annotation_base import AnnotationCreate
34
+ from lightly_studio.models.annotation_label import AnnotationLabelCreate
35
+ from lightly_studio.models.caption import CaptionCreate
36
+ from lightly_studio.models.image import ImageCreate
37
+ from lightly_studio.resolvers import (
38
+ annotation_label_resolver,
39
+ annotation_resolver,
40
+ caption_resolver,
41
+ image_resolver,
42
+ sample_resolver,
43
+ tag_resolver,
44
+ )
45
+ from lightly_studio.type_definitions import PathLike
46
+
47
+ logger = logging.getLogger(__name__)
48
+
49
+ # Constants
50
+ SAMPLE_BATCH_SIZE = 32 # Number of samples to process in a single batch
51
+ MAX_EXAMPLE_PATHS_TO_SHOW = 5
52
+
53
+
54
+ @dataclass
55
+ class _AnnotationProcessingContext:
56
+ """Context for processing annotations for a single sample."""
57
+
58
+ dataset_id: UUID
59
+ sample_id: UUID
60
+ label_map: dict[int, UUID]
61
+
62
+
63
+ def load_into_dataset_from_paths(
64
+ session: Session,
65
+ dataset_id: UUID,
66
+ image_paths: Iterable[str],
67
+ ) -> list[UUID]:
68
+ """Load images from file paths into the dataset.
69
+
70
+ Args:
71
+ session: The database session.
72
+ dataset_id: The ID of the dataset to load images into.
73
+ image_paths: An iterable of file paths to the images to load.
74
+
75
+ Returns:
76
+ A list of UUIDs of the created samples.
77
+ """
78
+ samples_to_create: list[ImageCreate] = []
79
+ created_sample_ids: list[UUID] = []
80
+
81
+ logging_context = LoadingLoggingContext(
82
+ n_samples_to_be_inserted=sum(1 for _ in image_paths),
83
+ n_samples_before_loading=sample_resolver.count_by_dataset_id(
84
+ session=session, dataset_id=dataset_id
85
+ ),
86
+ )
87
+
88
+ for image_path in tqdm(
89
+ image_paths,
90
+ desc="Processing images",
91
+ unit=" images",
92
+ ):
93
+ try:
94
+ with fsspec.open(image_path, "rb") as file:
95
+ image = PIL.Image.open(file)
96
+ width, height = image.size
97
+ image.close()
98
+ except (FileNotFoundError, PIL.UnidentifiedImageError, OSError):
99
+ continue
100
+
101
+ sample = ImageCreate(
102
+ file_name=Path(image_path).name,
103
+ file_path_abs=image_path,
104
+ width=width,
105
+ height=height,
106
+ )
107
+ samples_to_create.append(sample)
108
+
109
+ # Process batch when it reaches SAMPLE_BATCH_SIZE
110
+ if len(samples_to_create) >= SAMPLE_BATCH_SIZE:
111
+ created_path_to_id, paths_not_inserted = _create_batch_samples(
112
+ session=session, dataset_id=dataset_id, samples=samples_to_create
113
+ )
114
+ created_sample_ids.extend(created_path_to_id.values())
115
+ logging_context.update_example_paths(paths_not_inserted)
116
+ samples_to_create = []
117
+
118
+ # Handle remaining samples
119
+ if samples_to_create:
120
+ created_path_to_id, paths_not_inserted = _create_batch_samples(
121
+ session=session, dataset_id=dataset_id, samples=samples_to_create
122
+ )
123
+ created_sample_ids.extend(created_path_to_id.values())
124
+ logging_context.update_example_paths(paths_not_inserted)
125
+
126
+ log_loading_results(session=session, dataset_id=dataset_id, logging_context=logging_context)
127
+ return created_sample_ids
128
+
129
+
130
+ def load_into_dataset_from_labelformat(
131
+ session: Session,
132
+ dataset_id: UUID,
133
+ input_labels: ObjectDetectionInput | InstanceSegmentationInput,
134
+ images_path: Path,
135
+ ) -> list[UUID]:
136
+ """Load samples and their annotations from a labelformat input into the dataset.
137
+
138
+ Args:
139
+ session: The database session.
140
+ dataset_id: The ID of the dataset to load samples into.
141
+ input_labels: The labelformat input containing images and annotations.
142
+ images_path: The path to the directory containing the images.
143
+
144
+ Returns:
145
+ A list of UUIDs of the created samples.
146
+ """
147
+ logging_context = LoadingLoggingContext(
148
+ n_samples_to_be_inserted=sum(1 for _ in input_labels.get_labels()),
149
+ n_samples_before_loading=sample_resolver.count_by_dataset_id(
150
+ session=session, dataset_id=dataset_id
151
+ ),
152
+ )
153
+
154
+ # Create label mapping
155
+ label_map = _create_label_map(session=session, input_labels=input_labels)
156
+
157
+ samples_to_create: list[ImageCreate] = []
158
+ created_sample_ids: list[UUID] = []
159
+ path_to_anno_data: dict[str, ImageInstanceSegmentation | ImageObjectDetection] = {}
160
+
161
+ for image_data in tqdm(input_labels.get_labels(), desc="Processing images", unit=" images"):
162
+ image: Image = image_data.image # type: ignore[attr-defined]
163
+
164
+ typed_image_data: ImageInstanceSegmentation | ImageObjectDetection = image_data # type: ignore[assignment]
165
+ sample = ImageCreate(
166
+ file_name=str(image.filename),
167
+ file_path_abs=str(images_path / image.filename),
168
+ width=image.width,
169
+ height=image.height,
170
+ )
171
+ samples_to_create.append(sample)
172
+ path_to_anno_data[sample.file_path_abs] = typed_image_data
173
+
174
+ if len(samples_to_create) >= SAMPLE_BATCH_SIZE:
175
+ created_path_to_id, paths_not_inserted = _create_batch_samples(
176
+ session=session, dataset_id=dataset_id, samples=samples_to_create
177
+ )
178
+ created_sample_ids.extend(created_path_to_id.values())
179
+ logging_context.update_example_paths(paths_not_inserted)
180
+ _process_batch_annotations(
181
+ session=session,
182
+ created_path_to_id=created_path_to_id,
183
+ path_to_anno_data=path_to_anno_data,
184
+ dataset_id=dataset_id,
185
+ label_map=label_map,
186
+ )
187
+ samples_to_create.clear()
188
+ path_to_anno_data.clear()
189
+
190
+ if samples_to_create:
191
+ created_path_to_id, paths_not_inserted = _create_batch_samples(
192
+ session=session, dataset_id=dataset_id, samples=samples_to_create
193
+ )
194
+ created_sample_ids.extend(created_path_to_id.values())
195
+ logging_context.update_example_paths(paths_not_inserted)
196
+ _process_batch_annotations(
197
+ session=session,
198
+ created_path_to_id=created_path_to_id,
199
+ path_to_anno_data=path_to_anno_data,
200
+ dataset_id=dataset_id,
201
+ label_map=label_map,
202
+ )
203
+
204
+ log_loading_results(session=session, dataset_id=dataset_id, logging_context=logging_context)
205
+ return created_sample_ids
206
+
207
+
208
+ def load_into_dataset_from_coco_captions(
209
+ session: Session,
210
+ dataset_id: UUID,
211
+ annotations_json: Path,
212
+ images_path: Path,
213
+ ) -> list[UUID]:
214
+ """Load samples and captions from a COCO captions file into the dataset.
215
+
216
+ Args:
217
+ session: Database session used for resolver operations.
218
+ dataset_id: Identifier of the dataset that receives the samples.
219
+ annotations_json: Path to the COCO captions annotations file.
220
+ images_path: Directory containing the referenced images.
221
+
222
+ Returns:
223
+ The list of newly created sample identifiers.
224
+ """
225
+ with fsspec.open(str(annotations_json), "r") as file:
226
+ coco_payload = json.load(file)
227
+
228
+ images: list[dict[str, object]] = coco_payload.get("images", [])
229
+ annotations: list[dict[str, object]] = coco_payload.get("annotations", [])
230
+
231
+ captions_by_image_id: dict[int, list[str]] = defaultdict(list)
232
+ for annotation in annotations:
233
+ image_id = annotation["image_id"]
234
+ caption = annotation["caption"]
235
+ if not isinstance(image_id, int):
236
+ continue
237
+ if not isinstance(caption, str):
238
+ continue
239
+ caption_text = caption.strip()
240
+ if not caption_text:
241
+ continue
242
+ captions_by_image_id[image_id].append(caption_text)
243
+
244
+ logging_context = LoadingLoggingContext(
245
+ n_samples_to_be_inserted=len(images),
246
+ n_samples_before_loading=sample_resolver.count_by_dataset_id(
247
+ session=session, dataset_id=dataset_id
248
+ ),
249
+ )
250
+
251
+ samples_to_create: list[ImageCreate] = []
252
+ created_sample_ids: list[UUID] = []
253
+ path_to_captions: dict[str, list[str]] = {}
254
+
255
+ for image_info in tqdm(images, desc="Processing images", unit=" images"):
256
+ if isinstance(image_info["id"], int):
257
+ image_id_raw = image_info["id"]
258
+ else:
259
+ continue
260
+ file_name_raw = str(image_info["file_name"])
261
+
262
+ width = image_info["width"] if isinstance(image_info["width"], int) else 0
263
+ height = image_info["height"] if isinstance(image_info["height"], int) else 0
264
+ sample = ImageCreate(
265
+ file_name=file_name_raw,
266
+ file_path_abs=str(images_path / file_name_raw),
267
+ width=width,
268
+ height=height,
269
+ )
270
+ samples_to_create.append(sample)
271
+ path_to_captions[sample.file_path_abs] = captions_by_image_id.get(image_id_raw, [])
272
+
273
+ if len(samples_to_create) >= SAMPLE_BATCH_SIZE:
274
+ created_path_to_id, paths_not_inserted = _create_batch_samples(
275
+ session=session, dataset_id=dataset_id, samples=samples_to_create
276
+ )
277
+ created_sample_ids.extend(created_path_to_id.values())
278
+ logging_context.update_example_paths(paths_not_inserted)
279
+ _process_batch_captions(
280
+ session=session,
281
+ dataset_id=dataset_id,
282
+ created_path_to_id=created_path_to_id,
283
+ path_to_captions=path_to_captions,
284
+ )
285
+ samples_to_create.clear()
286
+ path_to_captions.clear()
287
+
288
+ if samples_to_create:
289
+ created_path_to_id, paths_not_inserted = _create_batch_samples(
290
+ session=session, dataset_id=dataset_id, samples=samples_to_create
291
+ )
292
+ created_sample_ids.extend(created_path_to_id.values())
293
+ logging_context.update_example_paths(paths_not_inserted)
294
+ _process_batch_captions(
295
+ session=session,
296
+ dataset_id=dataset_id,
297
+ created_path_to_id=created_path_to_id,
298
+ path_to_captions=path_to_captions,
299
+ )
300
+
301
+ log_loading_results(session=session, dataset_id=dataset_id, logging_context=logging_context)
302
+ return created_sample_ids
303
+
304
+
305
+ def tag_samples_by_directory(
306
+ session: Session,
307
+ dataset_id: UUID,
308
+ input_path: PathLike,
309
+ sample_ids: list[UUID],
310
+ tag_depth: int,
311
+ ) -> None:
312
+ """Tags samples based on their first-level subdirectory relative to input_path."""
313
+ if tag_depth == 0:
314
+ return
315
+ if tag_depth > 1:
316
+ raise NotImplementedError("tag_depth > 1 is not yet implemented for add_images_from_path.")
317
+
318
+ input_path_abs = Path(input_path).absolute()
319
+
320
+ newly_created_images = image_resolver.get_many_by_id(
321
+ session=session,
322
+ sample_ids=sample_ids,
323
+ )
324
+ newly_created_samples = [ImageSample(inner=image) for image in newly_created_images]
325
+
326
+ logger.info(f"Adding directory tags to {len(sample_ids)} new samples.")
327
+ parent_dir_to_sample_ids: defaultdict[str, list[UUID]] = defaultdict(list)
328
+ for sample in newly_created_samples:
329
+ sample_path_abs = Path(sample.file_path_abs)
330
+ relative_path = sample_path_abs.relative_to(input_path_abs)
331
+
332
+ if len(relative_path.parts) > 1:
333
+ tag_name = relative_path.parts[0]
334
+ if tag_name:
335
+ parent_dir_to_sample_ids[tag_name].append(sample.sample_id)
336
+
337
+ for tag_name, s_ids in parent_dir_to_sample_ids.items():
338
+ tag = tag_resolver.get_or_create_sample_tag_by_name(
339
+ session=session,
340
+ dataset_id=dataset_id,
341
+ tag_name=tag_name,
342
+ )
343
+ tag_resolver.add_sample_ids_to_tag_id(
344
+ session=session,
345
+ tag_id=tag.tag_id,
346
+ sample_ids=s_ids,
347
+ )
348
+ logger.info(f"Created {len(parent_dir_to_sample_ids)} tags from directories.")
349
+
350
+
351
+ def _create_batch_samples(
352
+ session: Session, dataset_id: UUID, samples: list[ImageCreate]
353
+ ) -> tuple[dict[str, UUID], list[str]]:
354
+ """Create the batch samples.
355
+
356
+ Args:
357
+ session: The database session.
358
+ dataset_id: The ID of the dataset to create samples in.
359
+ samples: The samples to create.
360
+
361
+ Returns:
362
+ - A mapping from file paths to the created sample IDs for new samples.
363
+ - A list of file paths that already existed in the database.
364
+ """
365
+ file_path_to_sample = {sample.file_path_abs: sample for sample in samples}
366
+
367
+ # Get the list of new and existing file paths
368
+ file_paths_new, file_paths_exist = image_resolver.filter_new_paths(
369
+ session=session, file_paths_abs=list(file_path_to_sample.keys())
370
+ )
371
+
372
+ # Create only samples with new file paths
373
+ samples_to_create = [file_path_to_sample[file_path_new] for file_path_new in file_paths_new]
374
+ created_sample_ids = image_resolver.create_many(
375
+ session=session, dataset_id=dataset_id, samples=samples_to_create
376
+ )
377
+
378
+ # Create a mapping from file path to sample ID for new samples
379
+ file_path_new_to_sample_id = dict(zip(file_paths_new, created_sample_ids))
380
+ return (file_path_new_to_sample_id, file_paths_exist)
381
+
382
+
383
+ def _create_label_map(
384
+ session: Session,
385
+ input_labels: ObjectDetectionInput | InstanceSegmentationInput,
386
+ ) -> dict[int, UUID]:
387
+ """Create a mapping of category IDs to annotation label IDs."""
388
+ label_map = {}
389
+ for category in tqdm(
390
+ input_labels.get_categories(),
391
+ desc="Processing categories",
392
+ unit=" categories",
393
+ ):
394
+ # Use label if already exists
395
+ label = annotation_label_resolver.get_by_label_name(
396
+ session=session, label_name=category.name
397
+ )
398
+ if label is None:
399
+ # Create new label
400
+ label_create = AnnotationLabelCreate(annotation_label_name=category.name)
401
+ label = annotation_label_resolver.create(session=session, label=label_create)
402
+
403
+ label_map[category.id] = label.annotation_label_id
404
+ return label_map
405
+
406
+
407
+ def _process_object_detection_annotations(
408
+ context: _AnnotationProcessingContext,
409
+ anno_data: ImageObjectDetection,
410
+ ) -> list[AnnotationCreate]:
411
+ """Process object detection annotations for a single image."""
412
+ new_annotations = []
413
+ for obj in anno_data.objects:
414
+ box = obj.box.to_format(BoundingBoxFormat.XYWH)
415
+ x, y, width, height = box
416
+
417
+ new_annotations.append(
418
+ AnnotationCreate(
419
+ dataset_id=context.dataset_id,
420
+ parent_sample_id=context.sample_id,
421
+ annotation_label_id=context.label_map[obj.category.id],
422
+ annotation_type="object_detection",
423
+ x=int(x),
424
+ y=int(y),
425
+ width=int(width),
426
+ height=int(height),
427
+ confidence=obj.confidence,
428
+ )
429
+ )
430
+ return new_annotations
431
+
432
+
433
+ def _process_instance_segmentation_annotations(
434
+ context: _AnnotationProcessingContext,
435
+ anno_data: ImageInstanceSegmentation,
436
+ ) -> list[AnnotationCreate]:
437
+ """Process instance segmentation annotations for a single image."""
438
+ new_annotations = []
439
+ for obj in anno_data.objects:
440
+ segmentation_rle: None | list[int] = None
441
+ if isinstance(obj.segmentation, MultiPolygon):
442
+ box = obj.segmentation.bounding_box().to_format(BoundingBoxFormat.XYWH)
443
+ elif isinstance(obj.segmentation, BinaryMaskSegmentation):
444
+ box = obj.segmentation.bounding_box.to_format(BoundingBoxFormat.XYWH)
445
+ segmentation_rle = obj.segmentation._rle_row_wise # noqa: SLF001
446
+ else:
447
+ raise ValueError(f"Unsupported segmentation type: {type(obj.segmentation)}")
448
+
449
+ x, y, width, height = box
450
+
451
+ new_annotations.append(
452
+ AnnotationCreate(
453
+ dataset_id=context.dataset_id,
454
+ parent_sample_id=context.sample_id,
455
+ annotation_label_id=context.label_map[obj.category.id],
456
+ annotation_type="instance_segmentation",
457
+ x=int(x),
458
+ y=int(y),
459
+ width=int(width),
460
+ height=int(height),
461
+ segmentation_mask=segmentation_rle,
462
+ )
463
+ )
464
+ return new_annotations
465
+
466
+
467
+ def _process_batch_annotations(
468
+ session: Session,
469
+ created_path_to_id: Mapping[str, UUID],
470
+ path_to_anno_data: Mapping[str, ImageInstanceSegmentation | ImageObjectDetection],
471
+ dataset_id: UUID,
472
+ label_map: dict[int, UUID],
473
+ ) -> None:
474
+ """Process annotations for a batch of samples."""
475
+ if len(created_path_to_id) == 0:
476
+ return
477
+
478
+ annotations_to_create: list[AnnotationCreate] = []
479
+
480
+ for sample_path, sample_id in created_path_to_id.items():
481
+ anno_data = path_to_anno_data[sample_path]
482
+
483
+ context = _AnnotationProcessingContext(
484
+ dataset_id=dataset_id,
485
+ sample_id=sample_id,
486
+ label_map=label_map,
487
+ )
488
+
489
+ if isinstance(anno_data, ImageInstanceSegmentation):
490
+ new_annotations = _process_instance_segmentation_annotations(
491
+ context=context, anno_data=anno_data
492
+ )
493
+ elif isinstance(anno_data, ImageObjectDetection):
494
+ new_annotations = _process_object_detection_annotations(
495
+ context=context, anno_data=anno_data
496
+ )
497
+ else:
498
+ raise ValueError(f"Unsupported annotation type: {type(anno_data)}")
499
+
500
+ annotations_to_create.extend(new_annotations)
501
+
502
+ annotation_resolver.create_many(
503
+ session=session, parent_dataset_id=dataset_id, annotations=annotations_to_create
504
+ )
505
+
506
+
507
+ def _process_batch_captions(
508
+ session: Session,
509
+ dataset_id: UUID,
510
+ created_path_to_id: Mapping[str, UUID],
511
+ path_to_captions: Mapping[str, list[str]],
512
+ ) -> None:
513
+ """Process captions for a batch of samples."""
514
+ if len(created_path_to_id) == 0:
515
+ return
516
+
517
+ captions_to_create: list[CaptionCreate] = []
518
+
519
+ for sample_path, sample_id in created_path_to_id.items():
520
+ captions = path_to_captions[sample_path]
521
+ if not captions:
522
+ continue
523
+
524
+ for caption_text in captions:
525
+ caption = CaptionCreate(
526
+ parent_sample_id=sample_id,
527
+ text=caption_text,
528
+ )
529
+ captions_to_create.append(caption)
530
+
531
+ caption_resolver.create_many(
532
+ session=session, parent_dataset_id=dataset_id, captions=captions_to_create
533
+ )