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,75 @@
1
+ """API routes for operators."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+ from uuid import UUID
7
+
8
+ from fastapi import APIRouter, HTTPException
9
+ from pydantic import BaseModel
10
+
11
+ from lightly_studio.api.routes.api.status import HTTP_STATUS_NOT_FOUND
12
+ from lightly_studio.db_manager import SessionDep
13
+ from lightly_studio.plugins.base_operator import OperatorResult
14
+ from lightly_studio.plugins.operator_registry import RegisteredOperatorMetadata, operator_registry
15
+ from lightly_studio.plugins.parameter import BaseParameter
16
+
17
+ operator_router = APIRouter(prefix="/operators", tags=["operators"])
18
+
19
+
20
+ class ExecuteOperatorRequest(BaseModel):
21
+ """Request model for executing an operator."""
22
+
23
+ parameters: dict[str, Any]
24
+
25
+
26
+ @operator_router.get("")
27
+ def get_operators() -> list[RegisteredOperatorMetadata]:
28
+ """Get all registered operators (id, name)."""
29
+ return operator_registry.get_all_metadata()
30
+
31
+
32
+ @operator_router.get("/{operator_id}/parameters")
33
+ def get_operator_parameters(operator_id: str) -> list[BaseParameter]:
34
+ """Get the parameters for a registered operator."""
35
+ operator = operator_registry.get_by_id(operator_id=operator_id)
36
+ if operator is None:
37
+ raise HTTPException(
38
+ status_code=HTTP_STATUS_NOT_FOUND,
39
+ detail=f"Operator '{operator_id}' not found",
40
+ )
41
+ return operator.parameters
42
+
43
+
44
+ @operator_router.post("/datasets/{dataset_id}/{operator_id}/execute", response_model=OperatorResult)
45
+ def execute_operator(
46
+ operator_id: str,
47
+ dataset_id: UUID,
48
+ request: ExecuteOperatorRequest,
49
+ session: SessionDep,
50
+ ) -> OperatorResult:
51
+ """Execute an operator with the provided parameters.
52
+
53
+ Args:
54
+ operator_id: The ID of the operator to execute.
55
+ dataset_id: The ID of the dataset to operate on.
56
+ request: The execution request containing parameters.
57
+ session: Database session.
58
+
59
+ Returns:
60
+ The execution result.
61
+ """
62
+ # Get the operator
63
+ operator = operator_registry.get_by_id(operator_id=operator_id)
64
+ if operator is None:
65
+ raise HTTPException(
66
+ status_code=HTTP_STATUS_NOT_FOUND,
67
+ detail=f"Operator '{operator_id}' not found",
68
+ )
69
+
70
+ # Execute the operator
71
+ return operator.execute(
72
+ session=session,
73
+ dataset_id=dataset_id,
74
+ parameters=request.parameters,
75
+ )
@@ -0,0 +1,103 @@
1
+ """This module contains the API routes for managing samples."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from uuid import UUID
6
+
7
+ from fastapi import APIRouter, Depends, HTTPException, Path
8
+ from pydantic import BaseModel, Field
9
+ from typing_extensions import Annotated
10
+
11
+ from lightly_studio.api.routes.api.status import (
12
+ HTTP_STATUS_CREATED,
13
+ HTTP_STATUS_NOT_FOUND,
14
+ )
15
+ from lightly_studio.api.routes.api.validators import Paginated, PaginatedWithCursor
16
+ from lightly_studio.db_manager import SessionDep
17
+ from lightly_studio.models.sample import SampleViewsWithCount
18
+ from lightly_studio.resolvers import (
19
+ sample_resolver,
20
+ tag_resolver,
21
+ )
22
+ from lightly_studio.resolvers.sample_resolver.get_filtered_samples import SamplesWithCount
23
+ from lightly_studio.resolvers.sample_resolver.sample_filter import SampleFilter
24
+
25
+ sample_router = APIRouter(tags=["sample"])
26
+
27
+
28
+ class ReadSamplesRequest(BaseModel):
29
+ """Request body for reading samples."""
30
+
31
+ filters: SampleFilter = Field(description="Filter parameters for samples")
32
+
33
+
34
+ @sample_router.post("/samples/list", response_model=SampleViewsWithCount)
35
+ def read_samples(
36
+ session: SessionDep,
37
+ body: ReadSamplesRequest,
38
+ pagination: Annotated[PaginatedWithCursor, Depends()],
39
+ ) -> SamplesWithCount:
40
+ """Retrieve a list of samples from the database with optional filtering.
41
+
42
+ Args:
43
+ session: The database session.
44
+ body: Optional request body containing filters.
45
+ pagination: Pagination parameters (cursor and limit).
46
+
47
+ Returns:
48
+ A list of filtered samples.
49
+ """
50
+ if body.filters.dataset_id is None:
51
+ raise ValueError("Dataset ID must be provided in filters.")
52
+ return sample_resolver.get_filtered_samples(
53
+ session=session,
54
+ filters=body.filters,
55
+ pagination=Paginated(offset=pagination.offset, limit=pagination.limit),
56
+ )
57
+
58
+
59
+ @sample_router.post(
60
+ "/datasets/{dataset_id}/samples/{sample_id}/tag/{tag_id}",
61
+ status_code=HTTP_STATUS_CREATED,
62
+ )
63
+ def add_tag_to_sample(
64
+ session: SessionDep,
65
+ sample_id: UUID,
66
+ # TODO(Michal, 10/2025): Remove unused dataset_id.
67
+ dataset_id: Annotated[UUID, Path(title="Dataset Id", description="The ID of the dataset")], # noqa: ARG001
68
+ tag_id: UUID,
69
+ ) -> bool:
70
+ """Add sample to a tag."""
71
+ sample = sample_resolver.get_by_id(session=session, sample_id=sample_id)
72
+ if not sample:
73
+ raise HTTPException(
74
+ status_code=HTTP_STATUS_NOT_FOUND,
75
+ detail=f"Sample {sample_id} not found",
76
+ )
77
+
78
+ if not tag_resolver.add_tag_to_sample(session=session, tag_id=tag_id, sample=sample):
79
+ raise HTTPException(status_code=HTTP_STATUS_NOT_FOUND, detail=f"Tag {tag_id} not found")
80
+
81
+ return True
82
+
83
+
84
+ @sample_router.delete("/datasets/{dataset_id}/samples/{sample_id}/tag/{tag_id}")
85
+ def remove_tag_from_sample(
86
+ session: SessionDep,
87
+ tag_id: UUID,
88
+ # TODO(Michal, 10/2025): Remove unused dataset_id.
89
+ dataset_id: Annotated[UUID, Path(title="Dataset Id", description="The ID of the dataset")], # noqa: ARG001
90
+ sample_id: UUID,
91
+ ) -> bool:
92
+ """Remove sample from a tag."""
93
+ sample = sample_resolver.get_by_id(session=session, sample_id=sample_id)
94
+ if not sample:
95
+ raise HTTPException(
96
+ status_code=HTTP_STATUS_NOT_FOUND,
97
+ detail=f"Sample {sample_id} not found",
98
+ )
99
+
100
+ if not tag_resolver.remove_tag_from_sample(session=session, tag_id=tag_id, sample=sample):
101
+ raise HTTPException(status_code=HTTP_STATUS_NOT_FOUND, detail=f"Tag {tag_id} not found")
102
+
103
+ return True
@@ -0,0 +1,87 @@
1
+ """This module contains the API routes for managing selections."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Union
6
+
7
+ from fastapi import APIRouter, Depends, HTTPException
8
+ from pydantic import BaseModel, Field
9
+ from typing_extensions import Annotated
10
+
11
+ from lightly_studio.api.routes.api.dataset import get_and_validate_dataset_id
12
+ from lightly_studio.db_manager import SessionDep
13
+ from lightly_studio.models.dataset import DatasetTable
14
+ from lightly_studio.resolvers import image_resolver
15
+ from lightly_studio.selection.select_via_db import select_via_database
16
+ from lightly_studio.selection.selection_config import (
17
+ EmbeddingDiversityStrategy,
18
+ MetadataWeightingStrategy,
19
+ SelectionConfig,
20
+ )
21
+
22
+ selection_router = APIRouter()
23
+
24
+ Strategy = Annotated[
25
+ Union[EmbeddingDiversityStrategy, MetadataWeightingStrategy],
26
+ Field(discriminator="strategy_name"),
27
+ ]
28
+
29
+
30
+ class SelectionRequest(BaseModel):
31
+ """Request model for selection."""
32
+
33
+ n_samples_to_select: int = Field(gt=0, description="Number of samples to select")
34
+ selection_result_tag_name: str = Field(min_length=1, description="Name for the result tag")
35
+ strategies: list[Strategy]
36
+
37
+
38
+ @selection_router.post(
39
+ "/datasets/{dataset_id}/selection",
40
+ status_code=204,
41
+ response_model=None,
42
+ )
43
+ def create_combination_selection(
44
+ session: SessionDep,
45
+ dataset: Annotated[
46
+ DatasetTable,
47
+ Depends(get_and_validate_dataset_id),
48
+ ],
49
+ request: SelectionRequest,
50
+ ) -> None:
51
+ """Create a combination selection on the dataset.
52
+
53
+ This endpoint performs combination selection using embeddings and metadata.
54
+ The selected samples are tagged with the specified tag name.
55
+
56
+ Args:
57
+ session: Database session dependency.
58
+ dataset: Dataset to perform selection on.
59
+ request: Selection parameters including sample count and tag name.
60
+
61
+ Returns:
62
+ None (204 No Content on success).
63
+
64
+ Raises:
65
+ HTTPException: 400 if selection fails due to invalid parameters or other errors.
66
+ """
67
+ # Get all samples in dataset as input for selection.
68
+ all_samples_result = image_resolver.get_all_by_dataset_id(
69
+ session=session, dataset_id=dataset.dataset_id
70
+ )
71
+ input_sample_ids = [sample.sample_id for sample in all_samples_result.samples]
72
+ # Validate we have enough samples to select from.
73
+ if len(input_sample_ids) < request.n_samples_to_select:
74
+ raise HTTPException(
75
+ status_code=400,
76
+ detail=f"Dataset has only {len(input_sample_ids)} samples, "
77
+ f"cannot select {request.n_samples_to_select}",
78
+ )
79
+ # Create SelectionConfig with diversity strategy.
80
+ config = SelectionConfig(
81
+ dataset_id=dataset.dataset_id,
82
+ n_samples_to_select=request.n_samples_to_select,
83
+ selection_result_tag_name=request.selection_result_tag_name,
84
+ strategies=request.strategies,
85
+ )
86
+ # Perform selection via database.
87
+ select_via_database(session=session, config=config, input_sample_ids=input_sample_ids)
@@ -0,0 +1,41 @@
1
+ """This module contains the API routes for user settings."""
2
+
3
+ from fastapi import APIRouter
4
+
5
+ from lightly_studio.db_manager import SessionDep
6
+ from lightly_studio.models.settings import SettingView
7
+ from lightly_studio.resolvers import settings_resolver
8
+
9
+ settings_router = APIRouter(tags=["settings"])
10
+
11
+
12
+ @settings_router.get("/settings")
13
+ def get_settings(
14
+ session: SessionDep,
15
+ ) -> SettingView:
16
+ """Get the current settings.
17
+
18
+ Args:
19
+ session: Database session.
20
+
21
+ Returns:
22
+ The current settings.
23
+ """
24
+ return settings_resolver.get_settings(session=session)
25
+
26
+
27
+ @settings_router.post("/settings")
28
+ def set_settings(
29
+ settings: SettingView,
30
+ session: SessionDep,
31
+ ) -> SettingView:
32
+ """Update user settings.
33
+
34
+ Args:
35
+ settings: New settings to apply.
36
+ session: Database session.
37
+
38
+ Returns:
39
+ Updated settings.
40
+ """
41
+ return settings_resolver.set_settings(session=session, settings=settings)
@@ -0,0 +1,19 @@
1
+ """HTTP status codes for use in tests and responses."""
2
+
3
+ HTTP_STATUS_OK = 200
4
+ HTTP_STATUS_CREATED = 201
5
+ HTTP_STATUS_ACCEPTED = 202
6
+ HTTP_STATUS_NO_CONTENT = 204
7
+
8
+ HTTP_STATUS_BAD_REQUEST = 400
9
+ HTTP_STATUS_UNAUTHORIZED = 401
10
+ HTTP_STATUS_FORBIDDEN = 403
11
+ HTTP_STATUS_NOT_FOUND = 404
12
+ HTTP_STATUS_CONFLICT = 409
13
+ HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415
14
+ HTTP_STATUS_UNPROCESSABLE_ENTITY = 422
15
+
16
+ HTTP_STATUS_INTERNAL_SERVER_ERROR = 500
17
+ HTTP_STATUS_NOT_IMPLEMENTED = 501
18
+ HTTP_STATUS_BAD_GATEWAY = 502
19
+ HTTP_STATUS_SERVICE_UNAVAILABLE = 503
@@ -0,0 +1,50 @@
1
+ """This module contains the API routes for managing text embedding."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import List
6
+ from uuid import UUID
7
+
8
+ from fastapi import APIRouter, Depends, HTTPException, Path, Query
9
+ from typing_extensions import Annotated
10
+
11
+ from lightly_studio.api.routes.api.status import (
12
+ HTTP_STATUS_INTERNAL_SERVER_ERROR,
13
+ )
14
+ from lightly_studio.dataset.embedding_manager import (
15
+ EmbeddingManager,
16
+ EmbeddingManagerProvider,
17
+ TextEmbedQuery,
18
+ )
19
+
20
+ text_embedding_router = APIRouter()
21
+ # Define a type alias for the EmbeddingManager dependency
22
+ EmbeddingManagerDep = Annotated[
23
+ EmbeddingManager,
24
+ Depends(lambda: EmbeddingManagerProvider.get_embedding_manager()),
25
+ ]
26
+
27
+
28
+ @text_embedding_router.get("/text_embedding/for_dataset/{dataset_id}", response_model=List[float])
29
+ def embed_text(
30
+ embedding_manager: EmbeddingManagerDep,
31
+ dataset_id: Annotated[UUID, Path(title="The ID of the dataset for which to embed.")],
32
+ query_text: str = Query(..., description="The text to embed."),
33
+ embedding_model_id: Annotated[
34
+ UUID | None,
35
+ Query(..., description="The ID of the embedding model to use."),
36
+ ] = None,
37
+ ) -> list[float]:
38
+ """Retrieve embeddings for the input text."""
39
+ try:
40
+ text_embeddings = embedding_manager.embed_text(
41
+ dataset_id=dataset_id,
42
+ text_query=TextEmbedQuery(text=query_text, embedding_model_id=embedding_model_id),
43
+ )
44
+ except ValueError as exc:
45
+ raise HTTPException(
46
+ status_code=HTTP_STATUS_INTERNAL_SERVER_ERROR,
47
+ detail=f"{exc}",
48
+ ) from None
49
+
50
+ return text_embeddings
@@ -0,0 +1,17 @@
1
+ """General LightlyStudio API models."""
2
+
3
+ from pydantic import BaseModel, Field
4
+
5
+
6
+ class Paginated(BaseModel):
7
+ """Paginated query parameters."""
8
+
9
+ offset: int = Field(0, ge=0, description="Offset for pagination")
10
+ limit: int = Field(100, gt=0, le=100, description="Limit for pagination")
11
+
12
+
13
+ class PaginatedWithCursor(BaseModel):
14
+ """Paginated query parameters."""
15
+
16
+ offset: int = Field(0, ge=0, description="Offset for pagination", alias="cursor")
17
+ limit: int = Field(100, gt=0, le=100, description="Limit for pagination")
@@ -0,0 +1,133 @@
1
+ """API routes for dataset videos."""
2
+
3
+ from typing import List, Optional
4
+ from uuid import UUID
5
+
6
+ from fastapi import APIRouter, Depends, Path
7
+ from pydantic import BaseModel, Field
8
+ from typing_extensions import Annotated
9
+
10
+ from lightly_studio.api.routes.api.validators import Paginated, PaginatedWithCursor
11
+ from lightly_studio.db_manager import SessionDep
12
+ from lightly_studio.models.video import VideoFieldsBoundsView, VideoView, VideoViewsWithCount
13
+ from lightly_studio.resolvers import video_resolver
14
+ from lightly_studio.resolvers.video_resolver.count_video_frame_annotations_by_video_dataset import (
15
+ CountAnnotationsView,
16
+ )
17
+ from lightly_studio.resolvers.video_resolver.video_count_annotations_filter import (
18
+ VideoCountAnnotationsFilter,
19
+ )
20
+ from lightly_studio.resolvers.video_resolver.video_filter import VideoFilter
21
+
22
+ video_router = APIRouter(prefix="/datasets/{dataset_id}/video", tags=["video"])
23
+
24
+
25
+ class VideoFieldsBoundsBody(BaseModel):
26
+ """The body to retrieve the fields bounds."""
27
+
28
+ annotations_frames_labels_id: Optional[List[UUID]] = None
29
+
30
+
31
+ class ReadVideosRequest(BaseModel):
32
+ """Request body for reading videos."""
33
+
34
+ filter: Optional[VideoFilter] = Field(None, description="Filter parameters for videos")
35
+ text_embedding: Optional[List[float]] = Field(None, description="Text embedding to search for")
36
+
37
+
38
+ class ReadVideoCountAnnotationsRequest(BaseModel):
39
+ """Request body for reading video annotations counter."""
40
+
41
+ filter: Optional[VideoCountAnnotationsFilter] = Field(
42
+ None, description="Filter parameters for video annotations counter"
43
+ )
44
+
45
+
46
+ @video_router.post("/annotations/count", response_model=List[CountAnnotationsView])
47
+ def count_video_frame_annotations_by_video_dataset(
48
+ session: SessionDep,
49
+ dataset_id: Annotated[UUID, Path(title="Dataset Id")],
50
+ body: ReadVideoCountAnnotationsRequest,
51
+ ) -> List[CountAnnotationsView]:
52
+ """Retrieve a list of annotations along with total count and filtered count.
53
+
54
+ Args:
55
+ session: The database session.
56
+ dataset_id: The ID of the dataset to retrieve videos for.
57
+ body: The body containing filters.
58
+
59
+ Returns:
60
+ A list of annotations and counters.
61
+ """
62
+ return video_resolver.count_video_frame_annotations_by_video_dataset(
63
+ session=session,
64
+ dataset_id=dataset_id,
65
+ filters=body.filter,
66
+ )
67
+
68
+
69
+ @video_router.post("/", response_model=VideoViewsWithCount)
70
+ def get_all_videos(
71
+ session: SessionDep,
72
+ dataset_id: Annotated[UUID, Path(title="Dataset Id")],
73
+ pagination: Annotated[PaginatedWithCursor, Depends()],
74
+ body: ReadVideosRequest,
75
+ ) -> VideoViewsWithCount:
76
+ """Retrieve a list of all videos for a given dataset ID with pagination.
77
+
78
+ Args:
79
+ session: The database session.
80
+ dataset_id: The ID of the dataset to retrieve videos for.
81
+ pagination: Pagination parameters including offset and limit.
82
+ body: The body containing filters.
83
+
84
+ Returns:
85
+ A list of videos along with the total count.
86
+ """
87
+ return video_resolver.get_all_by_dataset_id(
88
+ session=session,
89
+ dataset_id=dataset_id,
90
+ pagination=Paginated(offset=pagination.offset, limit=pagination.limit),
91
+ filters=body.filter,
92
+ text_embedding=body.text_embedding,
93
+ )
94
+
95
+
96
+ @video_router.get("/{sample_id}", response_model=VideoView)
97
+ def get_video_by_id(
98
+ session: SessionDep,
99
+ sample_id: Annotated[UUID, Path(title="Sample ID")],
100
+ ) -> Optional[VideoView]:
101
+ """Retrieve a video for a given dataset ID by its ID.
102
+
103
+ Args:
104
+ session: The database session.
105
+ sample_id: The ID of the video to retrieve.
106
+
107
+ Returns:
108
+ A video object.
109
+ """
110
+ return video_resolver.get_view_by_id(session=session, sample_id=sample_id)
111
+
112
+
113
+ @video_router.post("/bounds", response_model=Optional[VideoFieldsBoundsView])
114
+ def get_fields_bounds(
115
+ session: SessionDep,
116
+ dataset_id: Annotated[UUID, Path(title="Dataset Id")],
117
+ body: VideoFieldsBoundsBody,
118
+ ) -> Optional[VideoFieldsBoundsView]:
119
+ """Retrieve the fields bounds for a given dataset ID by its ID.
120
+
121
+ Args:
122
+ session: The database session.
123
+ dataset_id: The ID of the dataset to retrieve videos bounds.
124
+ body: The body containg the filters.
125
+
126
+ Returns:
127
+ A video fields bounds object.
128
+ """
129
+ return video_resolver.get_table_fields_bounds(
130
+ dataset_id=dataset_id,
131
+ session=session,
132
+ annotations_frames_labels_id=body.annotations_frames_labels_id,
133
+ )
@@ -0,0 +1,13 @@
1
+ """This module contains the API routes for managing datasets."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from fastapi import APIRouter
6
+
7
+ health_router = APIRouter()
8
+
9
+
10
+ @health_router.get("/healthz", include_in_schema=False)
11
+ def health_check() -> dict[str, str]:
12
+ """Health check endpoint to verify the service is running."""
13
+ return {"status": "healthy"}
@@ -0,0 +1,104 @@
1
+ """Image serving endpoint that supports local files."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from collections.abc import Generator
7
+
8
+ import fsspec
9
+ from fastapi import APIRouter, HTTPException
10
+ from fastapi.responses import StreamingResponse
11
+
12
+ from lightly_studio.api.routes.api import status
13
+ from lightly_studio.db_manager import SessionDep
14
+ from lightly_studio.models import image
15
+
16
+ app_router = APIRouter()
17
+
18
+
19
+ @app_router.get("/sample/{sample_id}")
20
+ async def serve_image_by_sample_id(
21
+ sample_id: str,
22
+ session: SessionDep,
23
+ ) -> StreamingResponse:
24
+ """Serve an image by sample ID.
25
+
26
+ Args:
27
+ sample_id: The ID of the sample.
28
+ session: Database session.
29
+
30
+ Returns:
31
+ StreamingResponse with the image data.
32
+
33
+ Raises:
34
+ HTTPException: If the sample is not found or the file is not accessible.
35
+ """
36
+ # Retrieve the sample from the database.
37
+ sample_record = session.get(image.ImageTable, sample_id)
38
+ if not sample_record:
39
+ raise HTTPException(
40
+ status_code=status.HTTP_STATUS_NOT_FOUND,
41
+ detail=f"Sample not found: {sample_id}",
42
+ )
43
+
44
+ file_path = sample_record.file_path_abs
45
+
46
+ try:
47
+ # Open the file.
48
+ fs, fs_path = fsspec.core.url_to_fs(file_path)
49
+ content = fs.cat_file(fs_path)
50
+
51
+ # Determine content type based on file extension.
52
+ content_type = _get_content_type(file_path)
53
+
54
+ # Create a streaming response.
55
+ def generate() -> Generator[bytes, None, None]:
56
+ yield content
57
+
58
+ return StreamingResponse(
59
+ generate(),
60
+ media_type=content_type,
61
+ headers={
62
+ # Cache for 1 hour
63
+ "Cache-Control": "public, max-age=3600",
64
+ "Content-Length": str(len(content)),
65
+ },
66
+ )
67
+
68
+ except FileNotFoundError as exc:
69
+ raise HTTPException(
70
+ status_code=status.HTTP_STATUS_NOT_FOUND,
71
+ detail=f"File not found: {file_path}",
72
+ ) from exc
73
+ except OSError as exc:
74
+ raise HTTPException(
75
+ status_code=status.HTTP_STATUS_NOT_FOUND,
76
+ detail=f"Error accessing file {file_path}: {exc.strerror}",
77
+ ) from exc
78
+
79
+
80
+ def _get_content_type(file_path: str) -> str:
81
+ """Get the appropriate content type for a file based on its extension.
82
+
83
+ Args:
84
+ file_path: Path to the file.
85
+
86
+ Returns:
87
+ MIME type string.
88
+ """
89
+ ext = os.path.splitext(file_path)[1].lower()
90
+
91
+ content_types = {
92
+ ".png": "image/png",
93
+ ".jpg": "image/jpeg",
94
+ ".jpeg": "image/jpeg",
95
+ ".gif": "image/gif",
96
+ ".webp": "image/webp",
97
+ ".bmp": "image/bmp",
98
+ ".tiff": "image/tiff",
99
+ ".mov": "video/quicktime",
100
+ ".mp4": "video/mp4",
101
+ ".avi": "video/x-msvideo",
102
+ }
103
+
104
+ return content_types.get(ext, "application/octet-stream")