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,273 @@
1
+ """Database selection functions for the selection process."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import datetime
6
+ import logging
7
+ from collections import Counter, defaultdict
8
+ from typing import Mapping, Sequence
9
+ from uuid import UUID, uuid4
10
+
11
+ import numpy as np
12
+ import sqlalchemy
13
+ from numpy.typing import NDArray
14
+ from sqlmodel import Session
15
+
16
+ from lightly_studio.models.tag import TagCreate
17
+ from lightly_studio.resolvers import (
18
+ annotation_label_resolver,
19
+ annotation_resolver,
20
+ embedding_model_resolver,
21
+ metadata_resolver,
22
+ sample_embedding_resolver,
23
+ tag_resolver,
24
+ )
25
+ from lightly_studio.resolvers.annotations.annotations_filter import AnnotationsFilter
26
+ from lightly_studio.selection.mundig import Mundig
27
+ from lightly_studio.selection.selection_config import (
28
+ AnnotationClassBalancingStrategy,
29
+ EmbeddingDiversityStrategy,
30
+ MetadataWeightingStrategy,
31
+ SelectionConfig,
32
+ )
33
+
34
+ EPSILON = 1e-6
35
+
36
+ logger = logging.getLogger(__name__)
37
+
38
+
39
+ def _aggregate_class_distributions(
40
+ input_sample_ids: Sequence[UUID],
41
+ sample_id_to_annotation_label_ids: Mapping[UUID, list[UUID]],
42
+ target_annotation_ids: list[UUID],
43
+ ) -> NDArray[np.float32]:
44
+ """Aggregates class distributions for a list of samples.
45
+
46
+ Args:
47
+ input_sample_ids:
48
+ A list of sample IDs for which to aggregate the class distributions.
49
+ sample_id_to_annotation_label_ids:
50
+ A dictionary mapping sample IDs to a list of their annotation label IDs.
51
+ target_annotation_ids:
52
+ A list of annotation label IDs that are considered for the distribution.
53
+ The order of these IDs determines the order of the columns in the output.
54
+
55
+ Returns:
56
+ A numpy array of shape (n_samples, n_labels) where n_samples is the
57
+ number of input samples and n_labels is the number of target annotation
58
+ labels. Each row in the array represents the class distribution for a
59
+ sample, where the values are the counts of each target annotation label.
60
+ """
61
+ n_samples = len(input_sample_ids)
62
+ n_labels = len(target_annotation_ids)
63
+
64
+ class_distributions = np.zeros((n_samples, n_labels), dtype=np.float32)
65
+ annotation_id_to_idx = {
66
+ annotation_id: j for j, annotation_id in enumerate(target_annotation_ids)
67
+ }
68
+ for i, sample_id in enumerate(input_sample_ids):
69
+ for annotation_label_id in sample_id_to_annotation_label_ids[sample_id]:
70
+ label_idx = annotation_id_to_idx.get(annotation_label_id)
71
+ if label_idx is not None:
72
+ class_distributions[i, label_idx] += 1
73
+
74
+ return class_distributions
75
+
76
+
77
+ def _process_explicit_target_distribution(
78
+ session: Session,
79
+ target_distribution: dict[str, float],
80
+ annotation_label_ids: Sequence[UUID],
81
+ ) -> tuple[dict[UUID, float], set[UUID], float]:
82
+ """Processes the explicit target distribution.
83
+
84
+ Args:
85
+ session: The SQLAlchemy session.
86
+ target_distribution:
87
+ A dictionary mapping annotation label names to their target proportions.
88
+ annotation_label_ids:
89
+ A sequence of all annotation label IDs to consider for class balancing.
90
+
91
+ Returns:
92
+ Tuple of:
93
+ A dictionary mapping annotation label IDs to their effective target proportions.
94
+ The set of unused label IDs
95
+ The target value remaining to 1.0.
96
+
97
+ Raises:
98
+ NotImplementedError: If multiple labels with the same name are found.
99
+ ValueError: If an annotation label name does not exist or if targets sum
100
+ to less than 1.0 and all classes are used.
101
+ """
102
+ label_id_to_target: dict[UUID, float] = {}
103
+ total_targets = 0.0
104
+ for label_name, target in target_distribution.items():
105
+ try:
106
+ annotation_label = annotation_label_resolver.get_by_label_name(session, label_name)
107
+ except sqlalchemy.exc.MultipleResultsFound as e:
108
+ raise NotImplementedError(
109
+ "Multiple labels with the same name not supported yet."
110
+ ) from e
111
+ if annotation_label is None:
112
+ raise ValueError(f"Annotation label with this name does not exist: {label_name}")
113
+ label_id_to_target[annotation_label.annotation_label_id] = target
114
+ total_targets += target
115
+
116
+ all_label_ids = set(annotation_label_ids)
117
+ unused_label_ids = all_label_ids - set(label_id_to_target.keys())
118
+ # `total_targets` can be more or less than 1.0. Both can be ignored, selection will still
119
+ # try correctly to reach the target.
120
+ remaining_ratio = max(1.0 - total_targets, 0.0)
121
+ return label_id_to_target, unused_label_ids, remaining_ratio
122
+
123
+
124
+ def _get_class_balancing_data(
125
+ session: Session,
126
+ strat: AnnotationClassBalancingStrategy,
127
+ annotation_label_ids: Sequence[UUID],
128
+ input_sample_ids: Sequence[UUID],
129
+ sample_id_to_annotation_label_ids: Mapping[UUID, list[UUID]],
130
+ ) -> tuple[NDArray[np.float32], list[float]]:
131
+ """Helper function to get class balancing data."""
132
+ if strat.target_distribution == "uniform":
133
+ target_keys_set = set(annotation_label_ids)
134
+ target_keys = list(target_keys_set)
135
+ target_values = [1.0 / len(target_keys)] * len(target_keys)
136
+ elif strat.target_distribution == "input":
137
+ # Count the number of times each label appears in the input
138
+ input_label_count = Counter(annotation_label_ids)
139
+ target_keys, target_values = (
140
+ list(input_label_count.keys()),
141
+ list(input_label_count.values()),
142
+ )
143
+ elif isinstance(strat.target_distribution, dict):
144
+ label_id_to_target, unused_label_ids, remaining_ratio = (
145
+ _process_explicit_target_distribution(
146
+ session=session,
147
+ target_distribution=strat.target_distribution,
148
+ annotation_label_ids=annotation_label_ids,
149
+ )
150
+ )
151
+ if len(unused_label_ids) >= 1:
152
+ other_uuid = uuid4()
153
+ # Handle the case when not all classes have a target.
154
+ # We replace UUIDs that are present in `unused_label_ids` for `other_uuid` and the
155
+ # target for `other_uuid` is `remaining_ratio`.
156
+ for sample_annotation_label_ids in sample_id_to_annotation_label_ids.values():
157
+ for i, label_id in enumerate(sample_annotation_label_ids):
158
+ if label_id in unused_label_ids:
159
+ sample_annotation_label_ids[i] = other_uuid
160
+ label_id_to_target[other_uuid] = remaining_ratio
161
+
162
+ target_keys, target_values = (
163
+ list(label_id_to_target.keys()),
164
+ list(label_id_to_target.values()),
165
+ )
166
+ else:
167
+ raise ValueError(f"Unknown distribution type: {type(strat.target_distribution)}")
168
+
169
+ class_distributions = _aggregate_class_distributions(
170
+ input_sample_ids=input_sample_ids,
171
+ sample_id_to_annotation_label_ids=sample_id_to_annotation_label_ids,
172
+ target_annotation_ids=target_keys,
173
+ )
174
+ return class_distributions, target_values
175
+
176
+
177
+ def select_via_database(
178
+ session: Session, config: SelectionConfig, input_sample_ids: list[UUID]
179
+ ) -> None:
180
+ """Run selection using the provided candidate sample ids.
181
+
182
+ First resolves the selection config to concrete database values.
183
+ Then calls Mundig to run the selection with pure values.
184
+ Finally creates a tag for the selected set.
185
+ """
186
+ # Check if the tag name is already used
187
+ existing_tag = tag_resolver.get_by_name(
188
+ session=session,
189
+ tag_name=config.selection_result_tag_name,
190
+ dataset_id=config.dataset_id,
191
+ )
192
+ if existing_tag:
193
+ msg = (
194
+ f"Tag with name {config.selection_result_tag_name} already exists in the "
195
+ f"dataset {config.dataset_id}. Please use a different tag name."
196
+ )
197
+ raise ValueError(msg)
198
+
199
+ n_samples_to_select = min(config.n_samples_to_select, len(input_sample_ids))
200
+ if n_samples_to_select == 0:
201
+ logger.warning("No samples available for selection.")
202
+ return
203
+
204
+ mundig = Mundig()
205
+ for strat in config.strategies:
206
+ if isinstance(strat, EmbeddingDiversityStrategy):
207
+ embedding_model_id = embedding_model_resolver.get_by_name(
208
+ session=session,
209
+ dataset_id=config.dataset_id,
210
+ embedding_model_name=strat.embedding_model_name,
211
+ ).embedding_model_id
212
+ embedding_tables = sample_embedding_resolver.get_by_sample_ids(
213
+ session=session,
214
+ sample_ids=input_sample_ids,
215
+ embedding_model_id=embedding_model_id,
216
+ )
217
+ embeddings = [e.embedding for e in embedding_tables]
218
+ mundig.add_diversity(embeddings=embeddings, strength=strat.strength)
219
+ elif isinstance(strat, MetadataWeightingStrategy):
220
+ key = strat.metadata_key
221
+ weights = []
222
+ for sample_id in input_sample_ids:
223
+ weight = metadata_resolver.get_value_for_sample(session, sample_id, key)
224
+ if not isinstance(weight, (float, int)):
225
+ raise ValueError(
226
+ f"Metadata {key} is not a number, only numbers can be used as weights"
227
+ )
228
+ weights.append(float(weight))
229
+ mundig.add_weighting(weights=weights, strength=strat.strength)
230
+ elif isinstance(strat, AnnotationClassBalancingStrategy):
231
+ annotations = annotation_resolver.get_all(
232
+ session=session,
233
+ filters=AnnotationsFilter(sample_ids=input_sample_ids),
234
+ ).annotations
235
+ annotation_label_ids = [a.annotation_label_id for a in annotations]
236
+ sample_id_to_annotation_label_ids = defaultdict(list)
237
+ for annotation in annotations:
238
+ sample_id_to_annotation_label_ids[annotation.parent_sample_id].append(
239
+ annotation.annotation_label_id
240
+ )
241
+
242
+ class_distributions, target_values = _get_class_balancing_data(
243
+ session=session,
244
+ strat=strat,
245
+ annotation_label_ids=annotation_label_ids,
246
+ input_sample_ids=input_sample_ids,
247
+ sample_id_to_annotation_label_ids=sample_id_to_annotation_label_ids,
248
+ )
249
+ mundig.add_class_balancing(
250
+ class_distributions=class_distributions,
251
+ target=target_values,
252
+ strength=strat.strength,
253
+ )
254
+ else:
255
+ raise ValueError(f"Selection strategy of type {type(strat)} is unknown.")
256
+
257
+ selected_indices = mundig.run(n_samples=n_samples_to_select)
258
+ selected_sample_ids = [input_sample_ids[i] for i in selected_indices]
259
+
260
+ datetime_str = datetime.datetime.now(tz=datetime.timezone.utc).isoformat()
261
+ tag_description = f"Selected at {datetime_str} UTC"
262
+ tag = tag_resolver.create(
263
+ session=session,
264
+ tag=TagCreate(
265
+ dataset_id=config.dataset_id,
266
+ name=config.selection_result_tag_name,
267
+ kind="sample",
268
+ description=tag_description,
269
+ ),
270
+ )
271
+ tag_resolver.add_sample_ids_to_tag_id(
272
+ session=session, tag_id=tag.tag_id, sample_ids=selected_sample_ids
273
+ )
@@ -0,0 +1,49 @@
1
+ """Pydantic models for the Selection configuration."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Dict, Literal, Sequence
6
+ from uuid import UUID
7
+
8
+ from pydantic import BaseModel
9
+
10
+ AnnotationsClassName = str
11
+ AnnotationClassToTarget = Dict[AnnotationsClassName, float]
12
+
13
+
14
+ class SelectionConfig(BaseModel):
15
+ """Configuration for the selection process."""
16
+
17
+ dataset_id: UUID
18
+ n_samples_to_select: int
19
+ selection_result_tag_name: str
20
+ strategies: Sequence[SelectionStrategy]
21
+
22
+
23
+ class SelectionStrategy(BaseModel):
24
+ """Base class for selection strategies."""
25
+
26
+ strength: float = 1.0
27
+
28
+
29
+ class EmbeddingDiversityStrategy(SelectionStrategy):
30
+ """Selection strategy based on embedding diversity."""
31
+
32
+ strategy_name: Literal["diversity"] = "diversity"
33
+ embedding_model_name: str | None = None
34
+
35
+
36
+ class MetadataWeightingStrategy(SelectionStrategy):
37
+ """Selection strategy based on metadata weighting."""
38
+
39
+ strategy_name: Literal["weights"] = "weights"
40
+ metadata_key: str
41
+
42
+
43
+ class AnnotationClassBalancingStrategy(SelectionStrategy):
44
+ """Selection strategy based on class balancing."""
45
+
46
+ strategy_name: Literal["balance"] = "balance"
47
+ target_distribution: AnnotationClassToTarget | Literal["uniform"] | Literal["input"]
48
+ # TODO(Lukas 11/2025): Allow specifying the annotation task instead of merging annotations from
49
+ # all tasks.
@@ -0,0 +1,33 @@
1
+ """Services for annotations operations."""
2
+
3
+ from lightly_studio.services.annotations_service.create_annotation import (
4
+ create_annotation,
5
+ )
6
+ from lightly_studio.services.annotations_service.delete_annotation import (
7
+ delete_annotation,
8
+ )
9
+ from lightly_studio.services.annotations_service.get_annotation_by_id import (
10
+ get_annotation_by_id,
11
+ )
12
+ from lightly_studio.services.annotations_service.update_annotation import (
13
+ update_annotation,
14
+ )
15
+ from lightly_studio.services.annotations_service.update_annotation_bounding_box import (
16
+ update_annotation_bounding_box,
17
+ )
18
+ from lightly_studio.services.annotations_service.update_annotation_label import (
19
+ update_annotation_label,
20
+ )
21
+ from lightly_studio.services.annotations_service.update_annotations import (
22
+ update_annotations,
23
+ )
24
+
25
+ __all__ = [
26
+ "create_annotation",
27
+ "delete_annotation",
28
+ "get_annotation_by_id",
29
+ "update_annotation",
30
+ "update_annotation_bounding_box",
31
+ "update_annotation_label",
32
+ "update_annotations",
33
+ ]
@@ -0,0 +1,64 @@
1
+ """Create annotation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from uuid import UUID
6
+
7
+ from pydantic import BaseModel
8
+ from sqlmodel import Session
9
+
10
+ from lightly_studio.models.annotation.annotation_base import (
11
+ AnnotationBaseTable,
12
+ AnnotationCreate,
13
+ AnnotationType,
14
+ )
15
+ from lightly_studio.resolvers import annotation_resolver
16
+
17
+
18
+ class AnnotationCreateParams(BaseModel):
19
+ """Input model for create annotation service."""
20
+
21
+ annotation_label_id: UUID
22
+ annotation_type: AnnotationType
23
+ dataset_id: UUID
24
+ parent_sample_id: UUID
25
+
26
+ x: int | None = None
27
+ y: int | None = None
28
+ width: int | None = None
29
+ height: int | None = None
30
+
31
+ segmentation_mask: list[int] | None = None
32
+
33
+
34
+ def create_annotation(session: Session, annotation: AnnotationCreateParams) -> AnnotationBaseTable:
35
+ """Create a new annotation.
36
+
37
+ Args:
38
+ session: Database session for executing the operation.
39
+ annotation: Annotation data to create.
40
+
41
+ Returns:
42
+ The retrieved annotation.
43
+ """
44
+ annotation_create = AnnotationCreate(
45
+ **annotation.model_dump(),
46
+ )
47
+ new_annotation_ids = annotation_resolver.create_many(
48
+ session=session,
49
+ parent_dataset_id=annotation.dataset_id,
50
+ annotations=[annotation_create],
51
+ )
52
+
53
+ if not new_annotation_ids:
54
+ raise ValueError("Failed to create annotation.")
55
+
56
+ created_annotation = annotation_resolver.get_by_id(
57
+ session=session,
58
+ annotation_id=new_annotation_ids[0],
59
+ )
60
+
61
+ if created_annotation is None:
62
+ raise ValueError(f"Failed to create annotation: {annotation}")
63
+
64
+ return created_annotation
@@ -0,0 +1,22 @@
1
+ """Delete an annotation by its ID."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from uuid import UUID
6
+
7
+ from sqlmodel import Session
8
+
9
+ from lightly_studio.resolvers import annotation_resolver
10
+
11
+
12
+ def delete_annotation(session: Session, annotation_id: UUID) -> None:
13
+ """Delete an annotation by its ID.
14
+
15
+ Args:
16
+ session: Database session for executing the operation.
17
+ annotation_id: ID of the annotation to delete.
18
+
19
+ Raises:
20
+ ValueError: If the annotation with the given ID is not found.
21
+ """
22
+ annotation_resolver.delete_annotation(session=session, annotation_id=annotation_id)
@@ -0,0 +1,31 @@
1
+ """Get an annotation by its ID."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from uuid import UUID
6
+
7
+ from sqlmodel import Session
8
+
9
+ from lightly_studio.models.annotation.annotation_base import (
10
+ AnnotationBaseTable,
11
+ )
12
+ from lightly_studio.resolvers import (
13
+ annotation_resolver,
14
+ )
15
+
16
+
17
+ def get_annotation_by_id(session: Session, annotation_id: UUID) -> AnnotationBaseTable:
18
+ """Retrieve an annotation by its ID.
19
+
20
+ Args:
21
+ session: Database session for executing the operation.
22
+ annotation_id: ID of the annotation to retrieve.
23
+
24
+ Returns:
25
+ The retrieved annotation.
26
+ """
27
+ annotation = annotation_resolver.get_by_id(session=session, annotation_id=annotation_id)
28
+ if not annotation:
29
+ raise ValueError(f"Annotation {annotation_id} not found")
30
+
31
+ return annotation
@@ -0,0 +1,54 @@
1
+ """General annotation update service."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from uuid import UUID
6
+
7
+ from pydantic import BaseModel
8
+ from sqlmodel import Session
9
+
10
+ from lightly_studio.models.annotation.annotation_base import (
11
+ AnnotationBaseTable,
12
+ )
13
+ from lightly_studio.resolvers.annotation_resolver.update_bounding_box import BoundingBoxCoordinates
14
+ from lightly_studio.services import annotations_service
15
+
16
+
17
+ class AnnotationUpdate(BaseModel):
18
+ """Model for updating an annotation."""
19
+
20
+ annotation_id: UUID
21
+ dataset_id: UUID
22
+ label_name: str | None = None
23
+ bounding_box: BoundingBoxCoordinates | None = None
24
+
25
+
26
+ def update_annotation(session: Session, annotation_update: AnnotationUpdate) -> AnnotationBaseTable:
27
+ """Update an annotation.
28
+
29
+ Args:
30
+ session: Database session for executing the operation.
31
+ annotation_update: Object containing updates for the annotation.
32
+
33
+ Returns:
34
+ The updated annotation.
35
+
36
+ """
37
+ result = None
38
+ if annotation_update.label_name is not None:
39
+ result = annotations_service.update_annotation_label(
40
+ session,
41
+ annotation_update.annotation_id,
42
+ annotation_update.label_name,
43
+ )
44
+
45
+ if annotation_update.bounding_box is not None:
46
+ result = annotations_service.update_annotation_bounding_box(
47
+ session,
48
+ annotation_update.annotation_id,
49
+ bounding_box=annotation_update.bounding_box,
50
+ )
51
+
52
+ if result is None:
53
+ raise ValueError("No updates provided for the annotation.")
54
+ return result
@@ -0,0 +1,36 @@
1
+ """Update the bounding box of an annotation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from uuid import UUID
6
+
7
+ from sqlmodel import Session
8
+
9
+ from lightly_studio.models.annotation.annotation_base import (
10
+ AnnotationBaseTable,
11
+ )
12
+ from lightly_studio.resolvers import (
13
+ annotation_resolver,
14
+ )
15
+ from lightly_studio.resolvers.annotation_resolver.update_bounding_box import BoundingBoxCoordinates
16
+
17
+
18
+ def update_annotation_bounding_box(
19
+ session: Session, annotation_id: UUID, bounding_box: BoundingBoxCoordinates
20
+ ) -> AnnotationBaseTable:
21
+ """Update the bounding box of an annotation.
22
+
23
+ Args:
24
+ session: Database session for executing the operation.
25
+ annotation_id: UUID of the annotation to update.
26
+ bounding_box: New bounding box coordinates to assign to the annotation.
27
+
28
+ Returns:
29
+ The updated annotation with the new bounding box assigned.
30
+
31
+ """
32
+ return annotation_resolver.update_bounding_box(
33
+ session,
34
+ annotation_id,
35
+ bounding_box,
36
+ )
@@ -0,0 +1,48 @@
1
+ """Update the label of an annotation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from uuid import UUID
6
+
7
+ from sqlmodel import Session
8
+
9
+ from lightly_studio.models.annotation.annotation_base import (
10
+ AnnotationBaseTable,
11
+ )
12
+ from lightly_studio.models.annotation_label import AnnotationLabelCreate
13
+ from lightly_studio.resolvers import (
14
+ annotation_label_resolver,
15
+ annotation_resolver,
16
+ )
17
+
18
+
19
+ def update_annotation_label(
20
+ session: Session, annotation_id: UUID, label_name: str
21
+ ) -> AnnotationBaseTable:
22
+ """Update the label of an annotation.
23
+
24
+ Args:
25
+ session: Database session for executing the operation.
26
+ annotation_id: UUID of the annotation to update.
27
+ label_name: New label to assign to the annotation.
28
+
29
+ Returns:
30
+ The updated annotation with the new label assigned.
31
+
32
+ """
33
+ annotation_label = annotation_label_resolver.get_by_label_name(
34
+ session,
35
+ label_name,
36
+ )
37
+
38
+ if not annotation_label:
39
+ annotation_label = annotation_label_resolver.create(
40
+ session,
41
+ label=AnnotationLabelCreate(annotation_label_name=label_name),
42
+ )
43
+
44
+ return annotation_resolver.update_annotation_label(
45
+ session,
46
+ annotation_id,
47
+ annotation_label.annotation_label_id,
48
+ )
@@ -0,0 +1,29 @@
1
+ """General annotation update service."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from sqlmodel import Session
6
+
7
+ from lightly_studio.models.annotation.annotation_base import (
8
+ AnnotationBaseTable,
9
+ )
10
+ from lightly_studio.services import annotations_service
11
+ from lightly_studio.services.annotations_service.update_annotation import AnnotationUpdate
12
+
13
+
14
+ def update_annotations(
15
+ session: Session, annotation_updates: list[AnnotationUpdate]
16
+ ) -> list[AnnotationBaseTable]:
17
+ """Update multiple annotations.
18
+
19
+ Args:
20
+ session: Database session for executing the operation.
21
+ annotation_updates: List of objects containing updates for the annotations.
22
+
23
+ Returns:
24
+ List of updated annotations.
25
+ """
26
+ return [
27
+ annotations_service.update_annotation(session, annotation_update)
28
+ for annotation_update in annotation_updates
29
+ ]