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,179 @@
1
+ """Get all annotations with payload resolver."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+ from uuid import UUID
7
+
8
+ from sqlalchemy.orm import aliased, joinedload, load_only
9
+ from sqlmodel import Session, col, func, select
10
+ from sqlmodel.sql.expression import Select
11
+
12
+ from lightly_studio.api.routes.api.validators import Paginated
13
+ from lightly_studio.models.annotation.annotation_base import (
14
+ AnnotationBaseTable,
15
+ AnnotationWithPayloadAndCountView,
16
+ AnnotationWithPayloadView,
17
+ ImageAnnotationView,
18
+ SampleAnnotationView,
19
+ VideoAnnotationView,
20
+ VideoFrameAnnotationView,
21
+ )
22
+ from lightly_studio.models.dataset import SampleType
23
+ from lightly_studio.models.image import ImageTable
24
+ from lightly_studio.models.sample import SampleTable
25
+ from lightly_studio.models.video import VideoFrameTable, VideoTable
26
+ from lightly_studio.resolvers import dataset_resolver
27
+ from lightly_studio.resolvers.annotations.annotations_filter import (
28
+ AnnotationsFilter,
29
+ )
30
+
31
+
32
+ def get_all_with_payload(
33
+ session: Session,
34
+ dataset_id: UUID,
35
+ pagination: Paginated | None = None,
36
+ filters: AnnotationsFilter | None = None,
37
+ ) -> AnnotationWithPayloadAndCountView:
38
+ """Get all annotations with payload from the database.
39
+
40
+ Args:
41
+ session: Database session
42
+ pagination: Optional pagination parameters
43
+ filters: Optional filters to apply to the query
44
+ dataset_id: ID of the dataset to get annotations for
45
+
46
+ Returns:
47
+ List of annotations matching the filters with payload
48
+ """
49
+ parent_dataset = dataset_resolver.get_parent_dataset_id(session=session, dataset_id=dataset_id)
50
+
51
+ if parent_dataset is None:
52
+ raise ValueError(f"Dataset with id {dataset_id} does not have a parent dataset.")
53
+
54
+ sample_type = parent_dataset.sample_type
55
+
56
+ base_query = _build_base_query(sample_type=sample_type)
57
+
58
+ if filters:
59
+ base_query = filters.apply(base_query)
60
+
61
+ annotations_query = base_query.order_by(
62
+ *_extra_order_by(sample_type=sample_type),
63
+ col(AnnotationBaseTable.created_at).asc(),
64
+ col(AnnotationBaseTable.sample_id).asc(),
65
+ )
66
+
67
+ total_count_query = select(func.count()).select_from(base_query.subquery())
68
+ total_count = session.exec(total_count_query).one()
69
+
70
+ if pagination is not None:
71
+ annotations_query = annotations_query.offset(pagination.offset).limit(pagination.limit)
72
+
73
+ next_cursor = None
74
+ if pagination and pagination.offset + pagination.limit < total_count:
75
+ next_cursor = pagination.offset + pagination.limit
76
+
77
+ rows = session.exec(annotations_query).all()
78
+
79
+ return AnnotationWithPayloadAndCountView(
80
+ total_count=total_count,
81
+ next_cursor=next_cursor,
82
+ annotations=[
83
+ AnnotationWithPayloadView(
84
+ parent_sample_type=sample_type,
85
+ annotation=annotation,
86
+ parent_sample_data=_serialize_annotation_payload(payload),
87
+ )
88
+ for annotation, payload in rows
89
+ ],
90
+ )
91
+
92
+
93
+ def _build_base_query(
94
+ sample_type: SampleType,
95
+ ) -> Select[tuple[AnnotationBaseTable, Any]]:
96
+ if sample_type == SampleType.IMAGE:
97
+ # this alias is needed to avoid name clashes in joins
98
+ SampleFromImage = aliased(SampleTable) # noqa: N806
99
+
100
+ return (
101
+ select(AnnotationBaseTable, ImageTable)
102
+ .join(
103
+ ImageTable,
104
+ col(ImageTable.sample_id) == col(AnnotationBaseTable.parent_sample_id),
105
+ )
106
+ .join(SampleFromImage, col(SampleFromImage.sample_id) == col(ImageTable.sample_id))
107
+ .options(
108
+ load_only(
109
+ ImageTable.file_path_abs, # type: ignore[arg-type]
110
+ ImageTable.sample_id, # type: ignore[arg-type]
111
+ ImageTable.height, # type: ignore[arg-type]
112
+ ImageTable.width, # type: ignore[arg-type]
113
+ ),
114
+ joinedload(ImageTable.sample).load_only(
115
+ SampleTable.dataset_id, # type: ignore[arg-type]
116
+ ),
117
+ )
118
+ )
119
+
120
+ if sample_type in (SampleType.VIDEO_FRAME, SampleType.VIDEO):
121
+ return (
122
+ select(AnnotationBaseTable, VideoFrameTable)
123
+ .join(
124
+ VideoFrameTable,
125
+ col(VideoFrameTable.sample_id) == col(AnnotationBaseTable.parent_sample_id),
126
+ )
127
+ .join(VideoFrameTable.video)
128
+ .options(
129
+ load_only(VideoFrameTable.sample_id), # type: ignore[arg-type]
130
+ joinedload(VideoFrameTable.video).load_only(
131
+ VideoTable.height, # type: ignore[arg-type]
132
+ VideoTable.width, # type: ignore[arg-type]
133
+ VideoTable.file_path_abs, # type: ignore[arg-type]
134
+ ),
135
+ )
136
+ )
137
+
138
+ raise NotImplementedError(f"Unsupported sample type: {sample_type}")
139
+
140
+
141
+ def _extra_order_by(sample_type: SampleType) -> list[Any]:
142
+ """Return extra order by clauses for the query."""
143
+ if sample_type == SampleType.IMAGE:
144
+ return [
145
+ col(ImageTable.file_path_abs).asc(),
146
+ ]
147
+
148
+ if sample_type in (SampleType.VIDEO_FRAME, SampleType.VIDEO):
149
+ return [
150
+ col(VideoTable.file_path_abs).asc(),
151
+ ]
152
+
153
+ return []
154
+
155
+
156
+ def _serialize_annotation_payload(
157
+ payload: ImageTable | VideoFrameTable,
158
+ ) -> ImageAnnotationView | VideoFrameAnnotationView:
159
+ """Serialize annotation based on sample type."""
160
+ if isinstance(payload, ImageTable):
161
+ return ImageAnnotationView(
162
+ height=payload.height,
163
+ width=payload.width,
164
+ file_path_abs=payload.file_path_abs,
165
+ sample_id=payload.sample_id,
166
+ sample=SampleAnnotationView(dataset_id=payload.sample.dataset_id),
167
+ )
168
+
169
+ if isinstance(payload, VideoFrameTable):
170
+ return VideoFrameAnnotationView(
171
+ sample_id=payload.sample_id,
172
+ video=VideoAnnotationView(
173
+ width=payload.video.width,
174
+ height=payload.video.height,
175
+ file_path_abs=payload.video.file_path_abs,
176
+ ),
177
+ )
178
+
179
+ raise NotImplementedError("Unsupported sample type")
@@ -0,0 +1,34 @@
1
+ """Handler for database operations related to annotations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Sequence
6
+ from uuid import UUID
7
+
8
+ from sqlmodel import Session, col, select
9
+
10
+ from lightly_studio.models.annotation.annotation_base import (
11
+ AnnotationBaseTable,
12
+ )
13
+
14
+
15
+ def get_by_id(session: Session, annotation_id: UUID) -> AnnotationBaseTable | None:
16
+ """Retrieve a single annotation by ID."""
17
+ return session.exec(
18
+ select(AnnotationBaseTable).where(AnnotationBaseTable.sample_id == annotation_id)
19
+ ).one_or_none()
20
+
21
+
22
+ def get_by_ids(session: Session, annotation_ids: Sequence[UUID]) -> Sequence[AnnotationBaseTable]:
23
+ """Retrieve multiple annotations by their IDs.
24
+
25
+ Args:
26
+ session: The database session to use for the query.
27
+ annotation_ids: A list of annotation IDs to retrieve.
28
+
29
+ Returns:
30
+ A list of annotations matching the provided IDs.
31
+ """
32
+ return session.exec(
33
+ select(AnnotationBaseTable).where(col(AnnotationBaseTable.sample_id).in_(annotation_ids))
34
+ ).all()
@@ -0,0 +1,130 @@
1
+ """Get annotation by id with payload resolver."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from uuid import UUID
6
+
7
+ from sqlalchemy.orm import aliased, joinedload, load_only
8
+ from sqlmodel import Session, col, select
9
+
10
+ from lightly_studio.models.annotation.annotation_base import (
11
+ AnnotationBaseTable,
12
+ AnnotationDetailsWithPayloadView,
13
+ ImageAnnotationDetailsView,
14
+ VideoFrameAnnotationDetailsView,
15
+ )
16
+ from lightly_studio.models.dataset import SampleType
17
+ from lightly_studio.models.image import ImageTable
18
+ from lightly_studio.models.sample import SampleTable
19
+ from lightly_studio.models.video import VideoFrameTable, VideoTable
20
+ from lightly_studio.resolvers import dataset_resolver
21
+
22
+
23
+ def get_by_id_with_payload(
24
+ session: Session,
25
+ sample_id: UUID,
26
+ ) -> AnnotationDetailsWithPayloadView | None:
27
+ """Get annotation by its id with payload from the database.
28
+
29
+ Args:
30
+ session: Database session
31
+ sample_id: ID of the sample to get annotations for
32
+
33
+ Returns:
34
+ Returns annotations with payload
35
+ """
36
+ parent_dataset = dataset_resolver.get_parent_dataset_by_sample_id(
37
+ session=session, sample_id=sample_id
38
+ )
39
+
40
+ if parent_dataset is None:
41
+ raise ValueError(f"Sample with id {sample_id} does not have a parent dataset.")
42
+
43
+ parent_sample_type = parent_dataset.sample_type
44
+
45
+ if parent_sample_type in SampleType.VIDEO_FRAME:
46
+ return _get_video_frame_annotation_by_id(
47
+ session=session, sample_id=sample_id, parent_sample_type=parent_sample_type
48
+ )
49
+ if parent_sample_type == SampleType.IMAGE:
50
+ return _get_image_annotation_by_id(session=session, sample_id=sample_id)
51
+
52
+ raise NotImplementedError("Unsupported sample type")
53
+
54
+
55
+ SampleFromImage = aliased(SampleTable)
56
+
57
+
58
+ def _get_image_annotation_by_id(
59
+ session: Session, sample_id: UUID
60
+ ) -> AnnotationDetailsWithPayloadView | None:
61
+ base_query = (
62
+ select(AnnotationBaseTable, ImageTable)
63
+ .join(
64
+ ImageTable,
65
+ col(ImageTable.sample_id) == col(AnnotationBaseTable.parent_sample_id),
66
+ )
67
+ .join(SampleFromImage, col(SampleFromImage.sample_id) == col(ImageTable.sample_id))
68
+ .options(
69
+ load_only(
70
+ ImageTable.file_path_abs, # type: ignore[arg-type]
71
+ ImageTable.file_name, # type: ignore[arg-type]
72
+ ImageTable.sample_id, # type: ignore[arg-type]
73
+ ImageTable.height, # type: ignore[arg-type]
74
+ ImageTable.width, # type: ignore[arg-type]
75
+ ),
76
+ )
77
+ .where(col(AnnotationBaseTable.sample_id) == sample_id)
78
+ )
79
+
80
+ row = session.exec(base_query).one_or_none()
81
+
82
+ if row is None:
83
+ return None
84
+
85
+ (annotation, payload) = row
86
+
87
+ return AnnotationDetailsWithPayloadView(
88
+ parent_sample_type=SampleType.IMAGE,
89
+ annotation=annotation,
90
+ parent_sample_data=ImageAnnotationDetailsView.from_image_table(payload),
91
+ )
92
+
93
+
94
+ def _get_video_frame_annotation_by_id(
95
+ session: Session, sample_id: UUID, parent_sample_type: SampleType
96
+ ) -> AnnotationDetailsWithPayloadView | None:
97
+ base_query = (
98
+ select(AnnotationBaseTable, VideoFrameTable)
99
+ .join(
100
+ VideoFrameTable,
101
+ col(VideoFrameTable.sample_id) == col(AnnotationBaseTable.parent_sample_id),
102
+ )
103
+ .join(VideoFrameTable.video)
104
+ .options(
105
+ load_only(
106
+ VideoFrameTable.sample_id, # type: ignore[arg-type]
107
+ VideoFrameTable.frame_number, # type: ignore[arg-type]
108
+ VideoFrameTable.frame_timestamp_s, # type: ignore[arg-type]
109
+ ),
110
+ joinedload(VideoFrameTable.video).load_only(
111
+ VideoTable.height, # type: ignore[arg-type]
112
+ VideoTable.width, # type: ignore[arg-type]
113
+ VideoTable.file_path_abs, # type: ignore[arg-type]
114
+ ),
115
+ )
116
+ .where(col(AnnotationBaseTable.sample_id) == sample_id)
117
+ )
118
+
119
+ row = session.exec(base_query).one_or_none()
120
+
121
+ if row is None:
122
+ return None
123
+
124
+ (annotation, payload) = row
125
+
126
+ return AnnotationDetailsWithPayloadView(
127
+ parent_sample_type=parent_sample_type,
128
+ annotation=annotation,
129
+ parent_sample_data=VideoFrameAnnotationDetailsView.from_video_frame_table(payload),
130
+ )
@@ -0,0 +1,142 @@
1
+ """Module for handling the update of annotation labels in the database."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TypeVar
6
+ from uuid import UUID
7
+
8
+ from sqlmodel import Session, SQLModel
9
+
10
+ from lightly_studio.models.annotation.annotation_base import (
11
+ AnnotationBaseTable,
12
+ )
13
+ from lightly_studio.models.annotation.instance_segmentation import (
14
+ InstanceSegmentationAnnotationTable,
15
+ )
16
+ from lightly_studio.models.annotation.links import AnnotationTagLinkTable
17
+ from lightly_studio.models.annotation.object_detection import ObjectDetectionAnnotationTable
18
+ from lightly_studio.models.annotation.semantic_segmentation import (
19
+ SemanticSegmentationAnnotationTable,
20
+ )
21
+ from lightly_studio.resolvers import (
22
+ annotation_resolver,
23
+ )
24
+
25
+ T = TypeVar("T", bound=SQLModel)
26
+
27
+
28
+ def update_annotation_label(
29
+ session: Session, annotation_id: UUID, annotation_label_id: UUID
30
+ ) -> AnnotationBaseTable:
31
+ """Update the label of an annotation.
32
+
33
+ Args:
34
+ session: Database session for executing the operation.
35
+ annotation_id: UUID of the annotation to update.
36
+ annotation_label_id: UUID of the new label to assign to the annotation.
37
+
38
+ Returns:
39
+ The updated annotation with the new label assigned.
40
+
41
+ Raises:
42
+ ValueError: If the annotation is not found.
43
+ """
44
+ annotation = annotation_resolver.get_by_id(session, annotation_id)
45
+ if not annotation:
46
+ raise ValueError(f"Annotation with ID {annotation_id} not found.")
47
+
48
+ # DuckDB has no "looking ahead" functionality for referenced tables.
49
+ # We need to work around this by deleting the existing and re-inserting
50
+ # Check https://duckdb.org/docs/stable/sql/indexes.html#over-eager-constraint-checking-in-foreign-keys
51
+
52
+ # DuckDB has no "looking ahead" functionality for referenced tables and neither does it support cascading updates. # noqa: E501
53
+ # Herefore we need to delete and re-insert the affected rows.
54
+ # more information can be found in the DuckDB documentation https://duckdb.org/docs/stable/sql/statements/create_table.html.
55
+ try:
56
+ # copy content
57
+ annotation_copy = annotation.model_copy(update={"annotation_label_id": annotation_label_id})
58
+
59
+ annotation_type = annotation_copy.annotation_type
60
+
61
+ annotation_tags = [
62
+ AnnotationTagLinkTable(
63
+ annotation_sample_id=annotation_copy.sample_id,
64
+ tag_id=tag.tag_id,
65
+ )
66
+ for tag in annotation.tags
67
+ ]
68
+
69
+ # we need to create a new annotation details before committing
70
+ # because copy will be gone with the commit
71
+ instance_segmentation = (
72
+ InstanceSegmentationAnnotationTable(
73
+ sample_id=annotation_copy.sample_id,
74
+ segmentation_mask=annotation_copy.instance_segmentation_details.segmentation_mask,
75
+ x=annotation_copy.instance_segmentation_details.x,
76
+ y=annotation_copy.instance_segmentation_details.y,
77
+ width=annotation_copy.instance_segmentation_details.width,
78
+ height=annotation_copy.instance_segmentation_details.height,
79
+ )
80
+ if annotation_type == "instance_segmentation"
81
+ and annotation_copy.instance_segmentation_details
82
+ else None
83
+ )
84
+
85
+ object_detection = (
86
+ ObjectDetectionAnnotationTable(
87
+ sample_id=annotation_copy.sample_id,
88
+ x=annotation_copy.object_detection_details.x,
89
+ y=annotation_copy.object_detection_details.y,
90
+ width=annotation_copy.object_detection_details.width,
91
+ height=annotation_copy.object_detection_details.height,
92
+ )
93
+ if annotation_type == "object_detection" and annotation_copy.object_detection_details
94
+ else None
95
+ )
96
+
97
+ semantic_segmentation = (
98
+ SemanticSegmentationAnnotationTable(
99
+ sample_id=annotation_copy.sample_id,
100
+ segmentation_mask=annotation_copy.semantic_segmentation_details.segmentation_mask,
101
+ )
102
+ if annotation_type == "semantic_segmentation"
103
+ and annotation_copy.semantic_segmentation_details
104
+ else None
105
+ )
106
+
107
+ # delete
108
+ annotation_resolver.delete_annotation(session, annotation.sample_id, delete_sample=False)
109
+
110
+ new_annotation = AnnotationBaseTable(
111
+ sample_id=annotation_copy.sample_id,
112
+ annotation_label_id=annotation_copy.annotation_label_id,
113
+ annotation_type=annotation_copy.annotation_type,
114
+ confidence=annotation_copy.confidence,
115
+ created_at=annotation_copy.created_at,
116
+ parent_sample_id=annotation_copy.parent_sample_id,
117
+ )
118
+
119
+ session.add(new_annotation)
120
+
121
+ if instance_segmentation:
122
+ session.add(instance_segmentation)
123
+
124
+ if object_detection:
125
+ session.add(object_detection)
126
+
127
+ if semantic_segmentation:
128
+ session.add(semantic_segmentation)
129
+
130
+ if annotation_tags:
131
+ session.add_all(annotation_tags)
132
+
133
+ session.commit()
134
+ session.flush()
135
+
136
+ return annotation_copy
137
+ except Exception:
138
+ # Explicit rollback to be safe, then re-raise the original error.
139
+ session.rollback()
140
+ raise
141
+
142
+ return annotation
@@ -0,0 +1,68 @@
1
+ """Module for handling the update of annotation bounding box coordinates in the database."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from uuid import UUID
7
+
8
+ from sqlmodel import Session
9
+
10
+ from lightly_studio.models.annotation.annotation_base import AnnotationBaseTable
11
+ from lightly_studio.resolvers import annotation_resolver
12
+
13
+
14
+ @dataclass
15
+ class BoundingBoxCoordinates:
16
+ """Represents bounding box coordinates."""
17
+
18
+ x: int
19
+ y: int
20
+ width: int
21
+ height: int
22
+
23
+
24
+ def update_bounding_box(
25
+ session: Session,
26
+ annotation_id: UUID,
27
+ coordinates: BoundingBoxCoordinates,
28
+ ) -> AnnotationBaseTable:
29
+ """Update the bounding box coordinates of an annotation.
30
+
31
+ Args:
32
+ session: Database session for executing the operation.
33
+ annotation_id: UUID of the annotation to update.
34
+ coordinates: New bounding box coordinates.
35
+
36
+ Returns:
37
+ The updated annotation with the new bounding box coordinates.
38
+
39
+ Raises:
40
+ ValueError: If the annotation is not found.
41
+ """
42
+ annotation = annotation_resolver.get_by_id(session, annotation_id)
43
+ if not annotation:
44
+ raise ValueError(f"Annotation with ID {annotation_id} not found.")
45
+
46
+ try:
47
+ if annotation.object_detection_details:
48
+ annotation.object_detection_details.x = coordinates.x
49
+ annotation.object_detection_details.y = coordinates.y
50
+ annotation.object_detection_details.width = coordinates.width
51
+ annotation.object_detection_details.height = coordinates.height
52
+ session.add(annotation.object_detection_details)
53
+
54
+ elif annotation.instance_segmentation_details:
55
+ annotation.instance_segmentation_details.x = coordinates.x
56
+ annotation.instance_segmentation_details.y = coordinates.y
57
+ annotation.instance_segmentation_details.width = coordinates.width
58
+ annotation.instance_segmentation_details.height = coordinates.height
59
+ session.add(annotation.instance_segmentation_details)
60
+ else:
61
+ raise ValueError("Annotation type does not support bounding boxes.")
62
+
63
+ session.commit()
64
+ session.refresh(annotation)
65
+ return annotation
66
+ except Exception:
67
+ session.rollback()
68
+ raise
@@ -0,0 +1,88 @@
1
+ """Filtering functionality for annotations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from uuid import UUID
6
+
7
+ from pydantic import BaseModel, Field
8
+ from sqlmodel import col
9
+
10
+ from lightly_studio.models.annotation.annotation_base import AnnotationBaseTable, AnnotationType
11
+ from lightly_studio.models.sample import SampleTable
12
+ from lightly_studio.models.tag import TagTable
13
+ from lightly_studio.type_definitions import QueryType
14
+
15
+
16
+ class AnnotationsFilter(BaseModel):
17
+ """Handles filtering for annotation queries."""
18
+
19
+ annotation_types: list[AnnotationType] | None = Field(
20
+ default=None,
21
+ description="Types of annotation to filter (e.g., 'object_detection')",
22
+ )
23
+ dataset_ids: list[UUID] | None = Field(default=None, description="List of dataset UUIDs")
24
+ annotation_label_ids: list[UUID] | None = Field(
25
+ default=None, description="List of annotation label UUIDs"
26
+ )
27
+ annotation_tag_ids: list[UUID] | None = Field(default=None, description="List of tag UUIDs")
28
+ sample_tag_ids: list[UUID] | None = Field(
29
+ default=None,
30
+ description="List of sample tag UUIDs to filter annotations by",
31
+ )
32
+ sample_ids: list[UUID] | None = Field(
33
+ default=None, description="List of sample UUIDs to filter annotations by"
34
+ )
35
+
36
+ def apply(
37
+ self,
38
+ query: QueryType,
39
+ ) -> QueryType:
40
+ """Apply filters to an annotation query.
41
+
42
+ Args:
43
+ query: The base query to apply filters to
44
+ annotation_table: The SQLModel table class for the annotation type
45
+
46
+ Returns:
47
+ The query with filters applied
48
+ """
49
+ # Filter by dataset
50
+ if self.dataset_ids:
51
+ query = query.join(AnnotationBaseTable.sample).where(
52
+ col(SampleTable.dataset_id).in_(self.dataset_ids)
53
+ )
54
+
55
+ # Filter by annotation label
56
+ if self.annotation_label_ids:
57
+ query = query.where(
58
+ col(AnnotationBaseTable.annotation_label_id).in_(self.annotation_label_ids)
59
+ )
60
+
61
+ # Filter by annotation tags
62
+ if self.annotation_tag_ids:
63
+ query = (
64
+ query.join(AnnotationBaseTable.tags)
65
+ .where(
66
+ AnnotationBaseTable.tags.any(col(TagTable.tag_id).in_(self.annotation_tag_ids))
67
+ )
68
+ .distinct()
69
+ )
70
+
71
+ # Filter by sample tags
72
+ if self.sample_tag_ids:
73
+ query = (
74
+ query.join(AnnotationBaseTable.parent_sample)
75
+ .join(SampleTable.tags)
76
+ .where(SampleTable.tags.any(col(TagTable.tag_id).in_(self.sample_tag_ids)))
77
+ .distinct()
78
+ )
79
+
80
+ # Filter by sample ids
81
+ if self.sample_ids:
82
+ query = query.where(col(AnnotationBaseTable.parent_sample_id).in_(self.sample_ids))
83
+
84
+ # Filter by annotation type
85
+ if self.annotation_types:
86
+ query = query.where(col(AnnotationBaseTable.annotation_type).in_(self.annotation_types))
87
+
88
+ return query