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.
- lightly_studio/__init__.py +12 -0
- lightly_studio/api/__init__.py +0 -0
- lightly_studio/api/app.py +131 -0
- lightly_studio/api/cache.py +77 -0
- lightly_studio/api/db_tables.py +35 -0
- lightly_studio/api/features.py +5 -0
- lightly_studio/api/routes/api/annotation.py +305 -0
- lightly_studio/api/routes/api/annotation_label.py +87 -0
- lightly_studio/api/routes/api/annotations/__init__.py +7 -0
- lightly_studio/api/routes/api/annotations/create_annotation.py +52 -0
- lightly_studio/api/routes/api/caption.py +100 -0
- lightly_studio/api/routes/api/classifier.py +384 -0
- lightly_studio/api/routes/api/dataset.py +191 -0
- lightly_studio/api/routes/api/dataset_tag.py +266 -0
- lightly_studio/api/routes/api/embeddings2d.py +90 -0
- lightly_studio/api/routes/api/exceptions.py +114 -0
- lightly_studio/api/routes/api/export.py +114 -0
- lightly_studio/api/routes/api/features.py +17 -0
- lightly_studio/api/routes/api/frame.py +241 -0
- lightly_studio/api/routes/api/image.py +155 -0
- lightly_studio/api/routes/api/metadata.py +161 -0
- lightly_studio/api/routes/api/operator.py +75 -0
- lightly_studio/api/routes/api/sample.py +103 -0
- lightly_studio/api/routes/api/selection.py +87 -0
- lightly_studio/api/routes/api/settings.py +41 -0
- lightly_studio/api/routes/api/status.py +19 -0
- lightly_studio/api/routes/api/text_embedding.py +50 -0
- lightly_studio/api/routes/api/validators.py +17 -0
- lightly_studio/api/routes/api/video.py +133 -0
- lightly_studio/api/routes/healthz.py +13 -0
- lightly_studio/api/routes/images.py +104 -0
- lightly_studio/api/routes/video_frames_media.py +116 -0
- lightly_studio/api/routes/video_media.py +223 -0
- lightly_studio/api/routes/webapp.py +51 -0
- lightly_studio/api/server.py +94 -0
- lightly_studio/core/__init__.py +0 -0
- lightly_studio/core/add_samples.py +533 -0
- lightly_studio/core/add_videos.py +294 -0
- lightly_studio/core/dataset.py +780 -0
- lightly_studio/core/dataset_query/__init__.py +14 -0
- lightly_studio/core/dataset_query/boolean_expression.py +67 -0
- lightly_studio/core/dataset_query/dataset_query.py +317 -0
- lightly_studio/core/dataset_query/field.py +113 -0
- lightly_studio/core/dataset_query/field_expression.py +79 -0
- lightly_studio/core/dataset_query/match_expression.py +23 -0
- lightly_studio/core/dataset_query/order_by.py +79 -0
- lightly_studio/core/dataset_query/sample_field.py +37 -0
- lightly_studio/core/dataset_query/tags_expression.py +46 -0
- lightly_studio/core/image_sample.py +36 -0
- lightly_studio/core/loading_log.py +56 -0
- lightly_studio/core/sample.py +291 -0
- lightly_studio/core/start_gui.py +54 -0
- lightly_studio/core/video_sample.py +38 -0
- lightly_studio/dataset/__init__.py +0 -0
- lightly_studio/dataset/edge_embedding_generator.py +155 -0
- lightly_studio/dataset/embedding_generator.py +129 -0
- lightly_studio/dataset/embedding_manager.py +349 -0
- lightly_studio/dataset/env.py +20 -0
- lightly_studio/dataset/file_utils.py +49 -0
- lightly_studio/dataset/fsspec_lister.py +275 -0
- lightly_studio/dataset/mobileclip_embedding_generator.py +158 -0
- lightly_studio/dataset/perception_encoder_embedding_generator.py +260 -0
- lightly_studio/db_manager.py +166 -0
- lightly_studio/dist_lightly_studio_view_app/_app/env.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/0.GcXvs2l7.css +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/12.Dx6SXgAb.css +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/17.9X9_k6TP.css +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/18.BxiimdIO.css +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/2.CkOblLn7.css +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/ClassifierSamplesGrid.BJbCDlvs.css +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/LightlyLogo.BNjCIww-.png +0 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/OpenSans-Bold.DGvYQtcs.ttf +0 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/OpenSans-Italic-VariableFont_wdth_wght.B4AZ-wl6.ttf +0 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/OpenSans-Medium.DVUZMR_6.ttf +0 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/OpenSans-Regular.DxJTClRG.ttf +0 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/OpenSans-SemiBold.D3TTYgdB.ttf +0 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/OpenSans-VariableFont_wdth_wght.BZBpG5Iz.ttf +0 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/_layout.CefECEWA.css +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/_layout.D5tDcjY-.css +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/_page.9X9_k6TP.css +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/_page.BxiimdIO.css +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/_page.Dx6SXgAb.css +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/transform._-1mPSEI.css +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/0dDyq72A.js +20 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/69_IOA4Y.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/BK4An2kI.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/BRmB-kJ9.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/B_1cpokE.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/BiqpDEr0.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/BpLiSKgx.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/BscxbINH.js +39 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/C1FmrZbK.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/C80h3dJx.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/C8mfFM-u.js +2 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CGY1p9L4.js +517 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/COfLknXM.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CWj6FrbW.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CYgJF_JY.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CmLg0ys7.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CvGjimpO.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/D3RDXHoj.js +39 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/D4y7iiT3.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/D9SC3jBb.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DCuAdx1Q.js +20 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DDBy-_jD.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DIeogL5L.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DL9a7v5o.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DSKECuqX.js +39 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/D_FFv0Oe.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DiZ5o5vz.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DkbXUtyG.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DmK2hulV.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DqnHaLTj.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DtWZc_tl.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DuUalyFS.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DwIonDAZ.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/Il-mSPmK.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/KNLP4aJU.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/KjYeVjkE.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/MErlcOXj.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/VRI4prUD.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/VYb2dkNs.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/VqWvU2yF.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/dHC3otuL.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/da7Oy_lO.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/eAy8rZzC.js +2 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/erjNR5MX.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/f1oG3eFE.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/rsLi1iKv.js +20 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/rwuuBP9f.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/xGHZQ1pe.js +3 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/entry/app.DrTRUgT3.js +2 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/entry/start.BK5EOJl2.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/0.CIvTuljF.js +4 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/1.UBvSzxdA.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/10.CQ_tiLJa.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/11.KqkAcaxW.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/12.DoYsmxQc.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/13.571n2LZA.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/14.DGs689M-.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/15.CWG1ehzT.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/16.Dpq6jbSh.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/17.B5AZbHUU.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/18.CBga8cnq.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/2.D2HXgz-8.js +1090 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/3.f4HAg-y3.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/4.BKF4xuKQ.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/5.BAE0Pm_f.js +39 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/6.CouWWpzA.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/7.UBHT0ktp.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/8.FiYNElcc.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/9.B3-UaT23.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/workers/clustering.worker-DKqeLtG0.js +2 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/workers/search.worker-vNSty3B0.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/version.json +1 -0
- lightly_studio/dist_lightly_studio_view_app/apple-touch-icon-precomposed.png +0 -0
- lightly_studio/dist_lightly_studio_view_app/apple-touch-icon.png +0 -0
- lightly_studio/dist_lightly_studio_view_app/favicon.png +0 -0
- lightly_studio/dist_lightly_studio_view_app/index.html +45 -0
- lightly_studio/errors.py +5 -0
- lightly_studio/examples/example.py +25 -0
- lightly_studio/examples/example_coco.py +27 -0
- lightly_studio/examples/example_coco_caption.py +29 -0
- lightly_studio/examples/example_metadata.py +369 -0
- lightly_studio/examples/example_operators.py +111 -0
- lightly_studio/examples/example_selection.py +28 -0
- lightly_studio/examples/example_split_work.py +48 -0
- lightly_studio/examples/example_video.py +22 -0
- lightly_studio/examples/example_video_annotations.py +157 -0
- lightly_studio/examples/example_yolo.py +22 -0
- lightly_studio/export/coco_captions.py +69 -0
- lightly_studio/export/export_dataset.py +104 -0
- lightly_studio/export/lightly_studio_label_input.py +120 -0
- lightly_studio/export_schema.py +18 -0
- lightly_studio/export_version.py +57 -0
- lightly_studio/few_shot_classifier/__init__.py +0 -0
- lightly_studio/few_shot_classifier/classifier.py +80 -0
- lightly_studio/few_shot_classifier/classifier_manager.py +644 -0
- lightly_studio/few_shot_classifier/random_forest_classifier.py +495 -0
- lightly_studio/metadata/complex_metadata.py +47 -0
- lightly_studio/metadata/compute_similarity.py +84 -0
- lightly_studio/metadata/compute_typicality.py +67 -0
- lightly_studio/metadata/gps_coordinate.py +41 -0
- lightly_studio/metadata/metadata_protocol.py +17 -0
- lightly_studio/models/__init__.py +1 -0
- lightly_studio/models/annotation/__init__.py +0 -0
- lightly_studio/models/annotation/annotation_base.py +303 -0
- lightly_studio/models/annotation/instance_segmentation.py +56 -0
- lightly_studio/models/annotation/links.py +17 -0
- lightly_studio/models/annotation/object_detection.py +47 -0
- lightly_studio/models/annotation/semantic_segmentation.py +44 -0
- lightly_studio/models/annotation_label.py +47 -0
- lightly_studio/models/caption.py +49 -0
- lightly_studio/models/classifier.py +20 -0
- lightly_studio/models/dataset.py +70 -0
- lightly_studio/models/embedding_model.py +30 -0
- lightly_studio/models/image.py +96 -0
- lightly_studio/models/metadata.py +208 -0
- lightly_studio/models/range.py +17 -0
- lightly_studio/models/sample.py +154 -0
- lightly_studio/models/sample_embedding.py +36 -0
- lightly_studio/models/settings.py +69 -0
- lightly_studio/models/tag.py +96 -0
- lightly_studio/models/two_dim_embedding.py +16 -0
- lightly_studio/models/video.py +161 -0
- lightly_studio/plugins/__init__.py +0 -0
- lightly_studio/plugins/base_operator.py +60 -0
- lightly_studio/plugins/operator_registry.py +47 -0
- lightly_studio/plugins/parameter.py +70 -0
- lightly_studio/py.typed +0 -0
- lightly_studio/resolvers/__init__.py +0 -0
- lightly_studio/resolvers/annotation_label_resolver/__init__.py +22 -0
- lightly_studio/resolvers/annotation_label_resolver/create.py +27 -0
- lightly_studio/resolvers/annotation_label_resolver/delete.py +28 -0
- lightly_studio/resolvers/annotation_label_resolver/get_all.py +37 -0
- lightly_studio/resolvers/annotation_label_resolver/get_by_id.py +24 -0
- lightly_studio/resolvers/annotation_label_resolver/get_by_ids.py +25 -0
- lightly_studio/resolvers/annotation_label_resolver/get_by_label_name.py +24 -0
- lightly_studio/resolvers/annotation_label_resolver/names_by_ids.py +25 -0
- lightly_studio/resolvers/annotation_label_resolver/update.py +38 -0
- lightly_studio/resolvers/annotation_resolver/__init__.py +40 -0
- lightly_studio/resolvers/annotation_resolver/count_annotations_by_dataset.py +129 -0
- lightly_studio/resolvers/annotation_resolver/create_many.py +124 -0
- lightly_studio/resolvers/annotation_resolver/delete_annotation.py +87 -0
- lightly_studio/resolvers/annotation_resolver/delete_annotations.py +60 -0
- lightly_studio/resolvers/annotation_resolver/get_all.py +85 -0
- lightly_studio/resolvers/annotation_resolver/get_all_with_payload.py +179 -0
- lightly_studio/resolvers/annotation_resolver/get_by_id.py +34 -0
- lightly_studio/resolvers/annotation_resolver/get_by_id_with_payload.py +130 -0
- lightly_studio/resolvers/annotation_resolver/update_annotation_label.py +142 -0
- lightly_studio/resolvers/annotation_resolver/update_bounding_box.py +68 -0
- lightly_studio/resolvers/annotations/__init__.py +1 -0
- lightly_studio/resolvers/annotations/annotations_filter.py +88 -0
- lightly_studio/resolvers/caption_resolver.py +129 -0
- lightly_studio/resolvers/dataset_resolver/__init__.py +55 -0
- lightly_studio/resolvers/dataset_resolver/check_dataset_type.py +29 -0
- lightly_studio/resolvers/dataset_resolver/create.py +20 -0
- lightly_studio/resolvers/dataset_resolver/delete.py +20 -0
- lightly_studio/resolvers/dataset_resolver/export.py +267 -0
- lightly_studio/resolvers/dataset_resolver/get_all.py +19 -0
- lightly_studio/resolvers/dataset_resolver/get_by_id.py +16 -0
- lightly_studio/resolvers/dataset_resolver/get_by_name.py +12 -0
- lightly_studio/resolvers/dataset_resolver/get_dataset_details.py +27 -0
- lightly_studio/resolvers/dataset_resolver/get_hierarchy.py +31 -0
- lightly_studio/resolvers/dataset_resolver/get_or_create_child_dataset.py +58 -0
- lightly_studio/resolvers/dataset_resolver/get_parent_dataset_by_sample_id.py +27 -0
- lightly_studio/resolvers/dataset_resolver/get_parent_dataset_id.py +22 -0
- lightly_studio/resolvers/dataset_resolver/get_root_dataset.py +61 -0
- lightly_studio/resolvers/dataset_resolver/get_root_datasets_overview.py +41 -0
- lightly_studio/resolvers/dataset_resolver/update.py +25 -0
- lightly_studio/resolvers/embedding_model_resolver.py +120 -0
- lightly_studio/resolvers/image_filter.py +50 -0
- lightly_studio/resolvers/image_resolver/__init__.py +21 -0
- lightly_studio/resolvers/image_resolver/create_many.py +52 -0
- lightly_studio/resolvers/image_resolver/delete.py +20 -0
- lightly_studio/resolvers/image_resolver/filter_new_paths.py +23 -0
- lightly_studio/resolvers/image_resolver/get_all_by_dataset_id.py +117 -0
- lightly_studio/resolvers/image_resolver/get_by_id.py +14 -0
- lightly_studio/resolvers/image_resolver/get_dimension_bounds.py +75 -0
- lightly_studio/resolvers/image_resolver/get_many_by_id.py +22 -0
- lightly_studio/resolvers/image_resolver/get_samples_excluding.py +43 -0
- lightly_studio/resolvers/metadata_resolver/__init__.py +15 -0
- lightly_studio/resolvers/metadata_resolver/metadata_filter.py +163 -0
- lightly_studio/resolvers/metadata_resolver/sample/__init__.py +21 -0
- lightly_studio/resolvers/metadata_resolver/sample/bulk_update_metadata.py +46 -0
- lightly_studio/resolvers/metadata_resolver/sample/get_by_sample_id.py +24 -0
- lightly_studio/resolvers/metadata_resolver/sample/get_metadata_info.py +104 -0
- lightly_studio/resolvers/metadata_resolver/sample/get_value_for_sample.py +27 -0
- lightly_studio/resolvers/metadata_resolver/sample/set_value_for_sample.py +53 -0
- lightly_studio/resolvers/sample_embedding_resolver.py +132 -0
- lightly_studio/resolvers/sample_resolver/__init__.py +17 -0
- lightly_studio/resolvers/sample_resolver/count_by_dataset_id.py +16 -0
- lightly_studio/resolvers/sample_resolver/create.py +16 -0
- lightly_studio/resolvers/sample_resolver/create_many.py +25 -0
- lightly_studio/resolvers/sample_resolver/get_by_id.py +14 -0
- lightly_studio/resolvers/sample_resolver/get_filtered_samples.py +56 -0
- lightly_studio/resolvers/sample_resolver/get_many_by_id.py +22 -0
- lightly_studio/resolvers/sample_resolver/sample_filter.py +74 -0
- lightly_studio/resolvers/settings_resolver.py +62 -0
- lightly_studio/resolvers/tag_resolver.py +299 -0
- lightly_studio/resolvers/twodim_embedding_resolver.py +119 -0
- lightly_studio/resolvers/video_frame_resolver/__init__.py +23 -0
- lightly_studio/resolvers/video_frame_resolver/count_video_frames_annotations.py +83 -0
- lightly_studio/resolvers/video_frame_resolver/create_many.py +57 -0
- lightly_studio/resolvers/video_frame_resolver/get_all_by_dataset_id.py +63 -0
- lightly_studio/resolvers/video_frame_resolver/get_by_id.py +13 -0
- lightly_studio/resolvers/video_frame_resolver/get_table_fields_bounds.py +44 -0
- lightly_studio/resolvers/video_frame_resolver/video_frame_annotations_counter_filter.py +47 -0
- lightly_studio/resolvers/video_frame_resolver/video_frame_filter.py +57 -0
- lightly_studio/resolvers/video_resolver/__init__.py +27 -0
- lightly_studio/resolvers/video_resolver/count_video_frame_annotations_by_video_dataset.py +86 -0
- lightly_studio/resolvers/video_resolver/create_many.py +58 -0
- lightly_studio/resolvers/video_resolver/filter_new_paths.py +33 -0
- lightly_studio/resolvers/video_resolver/get_all_by_dataset_id.py +181 -0
- lightly_studio/resolvers/video_resolver/get_by_id.py +22 -0
- lightly_studio/resolvers/video_resolver/get_table_fields_bounds.py +72 -0
- lightly_studio/resolvers/video_resolver/get_view_by_id.py +52 -0
- lightly_studio/resolvers/video_resolver/video_count_annotations_filter.py +50 -0
- lightly_studio/resolvers/video_resolver/video_filter.py +98 -0
- lightly_studio/selection/__init__.py +1 -0
- lightly_studio/selection/mundig.py +143 -0
- lightly_studio/selection/select.py +203 -0
- lightly_studio/selection/select_via_db.py +273 -0
- lightly_studio/selection/selection_config.py +49 -0
- lightly_studio/services/annotations_service/__init__.py +33 -0
- lightly_studio/services/annotations_service/create_annotation.py +64 -0
- lightly_studio/services/annotations_service/delete_annotation.py +22 -0
- lightly_studio/services/annotations_service/get_annotation_by_id.py +31 -0
- lightly_studio/services/annotations_service/update_annotation.py +54 -0
- lightly_studio/services/annotations_service/update_annotation_bounding_box.py +36 -0
- lightly_studio/services/annotations_service/update_annotation_label.py +48 -0
- lightly_studio/services/annotations_service/update_annotations.py +29 -0
- lightly_studio/setup_logging.py +59 -0
- lightly_studio/type_definitions.py +31 -0
- lightly_studio/utils/__init__.py +3 -0
- lightly_studio/utils/download.py +94 -0
- lightly_studio/vendor/__init__.py +1 -0
- lightly_studio/vendor/mobileclip/ACKNOWLEDGEMENTS +422 -0
- lightly_studio/vendor/mobileclip/LICENSE +31 -0
- lightly_studio/vendor/mobileclip/LICENSE_weights_data +50 -0
- lightly_studio/vendor/mobileclip/README.md +5 -0
- lightly_studio/vendor/mobileclip/__init__.py +96 -0
- lightly_studio/vendor/mobileclip/clip.py +77 -0
- lightly_studio/vendor/mobileclip/configs/mobileclip_b.json +18 -0
- lightly_studio/vendor/mobileclip/configs/mobileclip_s0.json +18 -0
- lightly_studio/vendor/mobileclip/configs/mobileclip_s1.json +18 -0
- lightly_studio/vendor/mobileclip/configs/mobileclip_s2.json +18 -0
- lightly_studio/vendor/mobileclip/image_encoder.py +67 -0
- lightly_studio/vendor/mobileclip/logger.py +154 -0
- lightly_studio/vendor/mobileclip/models/__init__.py +10 -0
- lightly_studio/vendor/mobileclip/models/mci.py +933 -0
- lightly_studio/vendor/mobileclip/models/vit.py +433 -0
- lightly_studio/vendor/mobileclip/modules/__init__.py +4 -0
- lightly_studio/vendor/mobileclip/modules/common/__init__.py +4 -0
- lightly_studio/vendor/mobileclip/modules/common/mobileone.py +341 -0
- lightly_studio/vendor/mobileclip/modules/common/transformer.py +451 -0
- lightly_studio/vendor/mobileclip/modules/image/__init__.py +4 -0
- lightly_studio/vendor/mobileclip/modules/image/image_projection.py +113 -0
- lightly_studio/vendor/mobileclip/modules/image/replknet.py +188 -0
- lightly_studio/vendor/mobileclip/modules/text/__init__.py +4 -0
- lightly_studio/vendor/mobileclip/modules/text/repmixer.py +281 -0
- lightly_studio/vendor/mobileclip/modules/text/tokenizer.py +38 -0
- lightly_studio/vendor/mobileclip/text_encoder.py +245 -0
- lightly_studio/vendor/perception_encoder/LICENSE.PE +201 -0
- lightly_studio/vendor/perception_encoder/README.md +11 -0
- lightly_studio/vendor/perception_encoder/vision_encoder/__init__.py +0 -0
- lightly_studio/vendor/perception_encoder/vision_encoder/bpe_simple_vocab_16e6.txt.gz +0 -0
- lightly_studio/vendor/perception_encoder/vision_encoder/config.py +205 -0
- lightly_studio/vendor/perception_encoder/vision_encoder/config_src.py +264 -0
- lightly_studio/vendor/perception_encoder/vision_encoder/pe.py +766 -0
- lightly_studio/vendor/perception_encoder/vision_encoder/rope.py +352 -0
- lightly_studio/vendor/perception_encoder/vision_encoder/tokenizer.py +347 -0
- lightly_studio/vendor/perception_encoder/vision_encoder/transforms.py +36 -0
- lightly_studio-0.4.6.dist-info/METADATA +88 -0
- lightly_studio-0.4.6.dist-info/RECORD +356 -0
- lightly_studio-0.4.6.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Retrieve the minimum and maximum values of each video frame table field."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
from uuid import UUID
|
|
7
|
+
|
|
8
|
+
from sqlalchemy import Select, select
|
|
9
|
+
from sqlmodel import Session, col, func
|
|
10
|
+
|
|
11
|
+
from lightly_studio.models.range import IntRange
|
|
12
|
+
from lightly_studio.models.sample import SampleTable
|
|
13
|
+
from lightly_studio.models.video import (
|
|
14
|
+
VideoFrameFieldsBoundsView,
|
|
15
|
+
VideoFrameTable,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_table_fields_bounds(
|
|
20
|
+
session: Session,
|
|
21
|
+
dataset_id: UUID,
|
|
22
|
+
) -> VideoFrameFieldsBoundsView | None:
|
|
23
|
+
"""Find the minimum and maximum values (bounds) of the video frames fields.
|
|
24
|
+
|
|
25
|
+
This includes frame number.
|
|
26
|
+
"""
|
|
27
|
+
query: Select[tuple[Any, ...]] = (
|
|
28
|
+
select(
|
|
29
|
+
func.min(VideoFrameTable.frame_number).label("min_frame_number"),
|
|
30
|
+
func.max(VideoFrameTable.frame_number).label("max_frame_number"),
|
|
31
|
+
)
|
|
32
|
+
.join(SampleTable)
|
|
33
|
+
.where(col(SampleTable.dataset_id) == dataset_id)
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
result = session.execute(query).mappings().one()
|
|
37
|
+
|
|
38
|
+
## If the min_frame_number is empty, then no data is found.
|
|
39
|
+
if result["min_frame_number"] is None:
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
return VideoFrameFieldsBoundsView(
|
|
43
|
+
frame_number=IntRange(min=result["min_frame_number"], max=result["max_frame_number"]),
|
|
44
|
+
)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Utility functions for building database queries."""
|
|
2
|
+
|
|
3
|
+
from typing import List, Optional
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
from sqlmodel import col, select
|
|
7
|
+
|
|
8
|
+
from lightly_studio.models.annotation.annotation_base import AnnotationBaseTable
|
|
9
|
+
from lightly_studio.models.annotation_label import AnnotationLabelTable
|
|
10
|
+
from lightly_studio.models.video import VideoFrameTable
|
|
11
|
+
from lightly_studio.resolvers.video_frame_resolver.video_frame_filter import VideoFrameFilter
|
|
12
|
+
from lightly_studio.type_definitions import QueryType
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class VideoFrameAnnotationsCounterFilter(BaseModel):
|
|
16
|
+
"""Encapsulates filter parameters for querying video frame annotations counter."""
|
|
17
|
+
|
|
18
|
+
video_filter: Optional[VideoFrameFilter] = None
|
|
19
|
+
annotations_labels: Optional[List[str]] = None
|
|
20
|
+
|
|
21
|
+
def apply(self, query: QueryType) -> QueryType:
|
|
22
|
+
"""Apply the filters to the given query."""
|
|
23
|
+
query = self._apply_annotations_label(query)
|
|
24
|
+
|
|
25
|
+
if self.video_filter:
|
|
26
|
+
query = self.video_filter.apply(query)
|
|
27
|
+
|
|
28
|
+
return query
|
|
29
|
+
|
|
30
|
+
def _apply_annotations_label(self, query: QueryType) -> QueryType:
|
|
31
|
+
if not self.annotations_labels:
|
|
32
|
+
return query
|
|
33
|
+
|
|
34
|
+
frame_filtered_video_ids_subquery = (
|
|
35
|
+
select(VideoFrameTable.sample_id)
|
|
36
|
+
.join(
|
|
37
|
+
AnnotationBaseTable,
|
|
38
|
+
col(AnnotationBaseTable.parent_sample_id) == VideoFrameTable.sample_id,
|
|
39
|
+
)
|
|
40
|
+
.join(AnnotationBaseTable.annotation_label)
|
|
41
|
+
.where(
|
|
42
|
+
col(AnnotationLabelTable.annotation_label_name).in_(self.annotations_labels or [])
|
|
43
|
+
)
|
|
44
|
+
.distinct()
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
return query.where(col(VideoFrameTable.sample_id).in_(frame_filtered_video_ids_subquery))
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Utility functions for building database queries."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
from uuid import UUID
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel
|
|
7
|
+
from sqlmodel import col
|
|
8
|
+
|
|
9
|
+
from lightly_studio.models.video import VideoFrameTable, VideoTable
|
|
10
|
+
from lightly_studio.resolvers.image_filter import FilterDimensions
|
|
11
|
+
from lightly_studio.resolvers.sample_resolver.sample_filter import SampleFilter
|
|
12
|
+
from lightly_studio.type_definitions import QueryType
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class VideoFrameFilter(BaseModel):
|
|
16
|
+
"""Encapsulates filter parameters for querying video frames."""
|
|
17
|
+
|
|
18
|
+
frame_number: Optional[FilterDimensions] = None
|
|
19
|
+
video_id: Optional[UUID] = None
|
|
20
|
+
sample_filter: Optional[SampleFilter] = None
|
|
21
|
+
|
|
22
|
+
def apply(self, query: QueryType) -> QueryType:
|
|
23
|
+
"""Apply the filters to the given query."""
|
|
24
|
+
query = self._apply_frame_number_filters(query)
|
|
25
|
+
query = self._apply_video_id(query)
|
|
26
|
+
|
|
27
|
+
if self.sample_filter:
|
|
28
|
+
query = self.sample_filter.apply(query)
|
|
29
|
+
|
|
30
|
+
return query
|
|
31
|
+
|
|
32
|
+
def _apply_video_id(self, query: QueryType) -> QueryType:
|
|
33
|
+
if self.video_id:
|
|
34
|
+
return query.where(col(VideoTable.sample_id) == self.video_id)
|
|
35
|
+
|
|
36
|
+
return query
|
|
37
|
+
|
|
38
|
+
def _apply_frame_number_filters(self, query: QueryType) -> QueryType:
|
|
39
|
+
min_frame_number = (
|
|
40
|
+
self.frame_number.min
|
|
41
|
+
if self.frame_number and self.frame_number.min is not None
|
|
42
|
+
else None
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
max_frame_number = (
|
|
46
|
+
self.frame_number.max
|
|
47
|
+
if self.frame_number and self.frame_number.max is not None
|
|
48
|
+
else None
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
if min_frame_number is not None:
|
|
52
|
+
query = query.where(col(VideoFrameTable.frame_number) >= min_frame_number)
|
|
53
|
+
|
|
54
|
+
if max_frame_number is not None:
|
|
55
|
+
query = query.where(col(VideoFrameTable.frame_number) <= max_frame_number)
|
|
56
|
+
|
|
57
|
+
return query
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Resolvers for video database operations."""
|
|
2
|
+
|
|
3
|
+
from lightly_studio.resolvers.video_resolver.count_video_frame_annotations_by_video_dataset import (
|
|
4
|
+
count_video_frame_annotations_by_video_dataset,
|
|
5
|
+
)
|
|
6
|
+
from lightly_studio.resolvers.video_resolver.create_many import create_many
|
|
7
|
+
from lightly_studio.resolvers.video_resolver.filter_new_paths import filter_new_paths
|
|
8
|
+
from lightly_studio.resolvers.video_resolver.get_all_by_dataset_id import (
|
|
9
|
+
get_all_by_dataset_id,
|
|
10
|
+
get_all_by_dataset_id_with_frames,
|
|
11
|
+
)
|
|
12
|
+
from lightly_studio.resolvers.video_resolver.get_by_id import get_by_id
|
|
13
|
+
from lightly_studio.resolvers.video_resolver.get_table_fields_bounds import (
|
|
14
|
+
get_table_fields_bounds,
|
|
15
|
+
)
|
|
16
|
+
from lightly_studio.resolvers.video_resolver.get_view_by_id import get_view_by_id
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"count_video_frame_annotations_by_video_dataset",
|
|
20
|
+
"create_many",
|
|
21
|
+
"filter_new_paths",
|
|
22
|
+
"get_all_by_dataset_id",
|
|
23
|
+
"get_all_by_dataset_id_with_frames",
|
|
24
|
+
"get_by_id",
|
|
25
|
+
"get_table_fields_bounds",
|
|
26
|
+
"get_view_by_id",
|
|
27
|
+
]
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Count video frame annotations by video dataset."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, List, Optional, Tuple
|
|
4
|
+
from uuid import UUID
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel
|
|
7
|
+
from sqlmodel import Session, asc, col, func, select
|
|
8
|
+
from sqlmodel.sql.expression import Select
|
|
9
|
+
|
|
10
|
+
from lightly_studio.models.annotation.annotation_base import AnnotationBaseTable
|
|
11
|
+
from lightly_studio.models.annotation_label import AnnotationLabelTable
|
|
12
|
+
from lightly_studio.models.sample import SampleTable
|
|
13
|
+
from lightly_studio.models.video import VideoFrameTable, VideoTable
|
|
14
|
+
from lightly_studio.resolvers.video_resolver.video_count_annotations_filter import (
|
|
15
|
+
VideoCountAnnotationsFilter,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CountAnnotationsView(BaseModel):
|
|
20
|
+
"""Count annotations view."""
|
|
21
|
+
|
|
22
|
+
label_name: str
|
|
23
|
+
total_count: int
|
|
24
|
+
current_count: int
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def count_video_frame_annotations_by_video_dataset(
|
|
28
|
+
session: Session, dataset_id: UUID, filters: Optional[VideoCountAnnotationsFilter] = None
|
|
29
|
+
) -> List[CountAnnotationsView]:
|
|
30
|
+
"""Count the annotations by video frames."""
|
|
31
|
+
unfiltered_query = (
|
|
32
|
+
_build_base_query(dataset_id=dataset_id, count_column_name="total")
|
|
33
|
+
.group_by(col(AnnotationBaseTable.annotation_label_id))
|
|
34
|
+
.subquery("unfiltered")
|
|
35
|
+
)
|
|
36
|
+
filtered_query = _build_base_query(dataset_id=dataset_id, count_column_name="filtered_count")
|
|
37
|
+
|
|
38
|
+
if filters:
|
|
39
|
+
filtered_query = filters.apply(filtered_query)
|
|
40
|
+
|
|
41
|
+
filtered_subquery = filtered_query.group_by(
|
|
42
|
+
col(AnnotationBaseTable.annotation_label_id)
|
|
43
|
+
).subquery("filtered")
|
|
44
|
+
|
|
45
|
+
final_query: Select[Any] = (
|
|
46
|
+
select(
|
|
47
|
+
col(AnnotationLabelTable.annotation_label_name).label("label"),
|
|
48
|
+
col(unfiltered_query.c.total).label("total"),
|
|
49
|
+
func.coalesce(filtered_subquery.c.filtered_count, 0).label("filtered_count"),
|
|
50
|
+
)
|
|
51
|
+
.select_from(AnnotationLabelTable)
|
|
52
|
+
.join(
|
|
53
|
+
unfiltered_query,
|
|
54
|
+
unfiltered_query.c.label_id == col(AnnotationLabelTable.annotation_label_id),
|
|
55
|
+
)
|
|
56
|
+
.outerjoin(
|
|
57
|
+
filtered_subquery,
|
|
58
|
+
filtered_subquery.c.label_id == col(AnnotationLabelTable.annotation_label_id),
|
|
59
|
+
)
|
|
60
|
+
.order_by(asc(AnnotationLabelTable.annotation_label_name))
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
rows = session.execute(final_query).mappings().all()
|
|
64
|
+
return [
|
|
65
|
+
CountAnnotationsView(
|
|
66
|
+
label_name=row["label"], total_count=row["total"], current_count=row["filtered_count"]
|
|
67
|
+
)
|
|
68
|
+
for row in rows
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _build_base_query(dataset_id: UUID, count_column_name: str) -> Select[Tuple[Any, int]]:
|
|
73
|
+
return (
|
|
74
|
+
select(
|
|
75
|
+
col(AnnotationBaseTable.annotation_label_id).label("label_id"),
|
|
76
|
+
func.count(func.distinct(VideoTable.sample_id)).label(count_column_name),
|
|
77
|
+
)
|
|
78
|
+
.select_from(AnnotationBaseTable)
|
|
79
|
+
.join(
|
|
80
|
+
VideoFrameTable,
|
|
81
|
+
col(VideoFrameTable.sample_id) == col(AnnotationBaseTable.parent_sample_id),
|
|
82
|
+
)
|
|
83
|
+
.join(SampleTable, col(SampleTable.sample_id) == col(VideoFrameTable.parent_sample_id))
|
|
84
|
+
.join(VideoTable, col(VideoTable.sample_id) == col(SampleTable.sample_id))
|
|
85
|
+
.where(col(SampleTable.dataset_id) == dataset_id)
|
|
86
|
+
)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Implementation of create functions for videos."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
|
|
7
|
+
from sqlmodel import Session
|
|
8
|
+
|
|
9
|
+
from lightly_studio.models.dataset import SampleType
|
|
10
|
+
from lightly_studio.models.sample import SampleCreate
|
|
11
|
+
from lightly_studio.models.video import VideoCreate, VideoTable
|
|
12
|
+
from lightly_studio.resolvers import dataset_resolver, sample_resolver
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class VideoCreateHelper(VideoCreate):
|
|
16
|
+
"""Helper class to create VideoTable with sample_id."""
|
|
17
|
+
|
|
18
|
+
sample_id: UUID
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def create_many(session: Session, dataset_id: UUID, samples: list[VideoCreate]) -> list[UUID]:
|
|
22
|
+
"""Create multiple video samples in a single database commit.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
session: The database session.
|
|
26
|
+
dataset_id: The uuid of the dataset to attach to.
|
|
27
|
+
samples: The videos to create in the database.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
List of IDs of VideoTable entries that got added to the database.
|
|
31
|
+
"""
|
|
32
|
+
dataset_resolver.check_dataset_type(
|
|
33
|
+
session=session,
|
|
34
|
+
dataset_id=dataset_id,
|
|
35
|
+
expected_type=SampleType.VIDEO,
|
|
36
|
+
)
|
|
37
|
+
sample_ids = sample_resolver.create_many(
|
|
38
|
+
session=session,
|
|
39
|
+
samples=[SampleCreate(dataset_id=dataset_id) for _ in samples],
|
|
40
|
+
)
|
|
41
|
+
# Bulk create VideoTable entries using the generated sample_ids.
|
|
42
|
+
db_videos = [
|
|
43
|
+
VideoTable.model_validate(
|
|
44
|
+
VideoCreateHelper(
|
|
45
|
+
file_name=sample.file_name,
|
|
46
|
+
width=sample.width,
|
|
47
|
+
height=sample.height,
|
|
48
|
+
duration_s=sample.duration_s,
|
|
49
|
+
fps=sample.fps,
|
|
50
|
+
file_path_abs=sample.file_path_abs,
|
|
51
|
+
sample_id=sample_id,
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
for sample_id, sample in zip(sample_ids, samples)
|
|
55
|
+
]
|
|
56
|
+
session.bulk_save_objects(db_videos)
|
|
57
|
+
session.commit()
|
|
58
|
+
return sample_ids
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Implementation of filter_new_paths function for videos."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from sqlmodel import Session, col, select
|
|
6
|
+
|
|
7
|
+
from lightly_studio.models.video import VideoTable
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# TODO(Horatiu, 11/2025): Add dataset_id parameter to support multiple datasets.
|
|
11
|
+
def filter_new_paths(session: Session, file_paths_abs: list[str]) -> tuple[list[str], list[str]]:
|
|
12
|
+
"""Filter the file_paths into existing in DB and non existing in DB.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
session: The database session.
|
|
16
|
+
file_paths_abs: The file paths to filter.
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
file_paths_abs that don't exist in the database,
|
|
20
|
+
file_paths_abs that exist in the database
|
|
21
|
+
"""
|
|
22
|
+
existing_file_paths_abs = set(
|
|
23
|
+
session.exec(
|
|
24
|
+
select(col(VideoTable.file_path_abs)).where(
|
|
25
|
+
col(VideoTable.file_path_abs).in_(file_paths_abs)
|
|
26
|
+
)
|
|
27
|
+
).all()
|
|
28
|
+
)
|
|
29
|
+
file_paths_abs_set = set(file_paths_abs)
|
|
30
|
+
return (
|
|
31
|
+
list(file_paths_abs_set - existing_file_paths_abs), # paths that are not in the DB
|
|
32
|
+
list(file_paths_abs_set & existing_file_paths_abs), # paths that are in the DB
|
|
33
|
+
)
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"""Implementation of get_all_by_dataset_id function for videos."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Sequence
|
|
6
|
+
from uuid import UUID
|
|
7
|
+
|
|
8
|
+
from sqlalchemy import and_
|
|
9
|
+
from sqlalchemy.orm import joinedload, selectinload
|
|
10
|
+
from sqlmodel import Session, col, func, select
|
|
11
|
+
|
|
12
|
+
from lightly_studio.api.routes.api.frame import build_frame_view
|
|
13
|
+
from lightly_studio.api.routes.api.validators import Paginated
|
|
14
|
+
from lightly_studio.models.embedding_model import EmbeddingModelTable
|
|
15
|
+
from lightly_studio.models.sample import SampleTable, SampleView
|
|
16
|
+
from lightly_studio.models.sample_embedding import SampleEmbeddingTable
|
|
17
|
+
from lightly_studio.models.video import (
|
|
18
|
+
VideoFrameTable,
|
|
19
|
+
VideoTable,
|
|
20
|
+
VideoView,
|
|
21
|
+
VideoViewsWithCount,
|
|
22
|
+
)
|
|
23
|
+
from lightly_studio.resolvers.video_resolver.video_filter import VideoFilter
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_all_by_dataset_id( # noqa: PLR0913
|
|
27
|
+
session: Session,
|
|
28
|
+
dataset_id: UUID,
|
|
29
|
+
pagination: Paginated | None = None,
|
|
30
|
+
sample_ids: list[UUID] | None = None,
|
|
31
|
+
filters: VideoFilter | None = None,
|
|
32
|
+
text_embedding: list[float] | None = None,
|
|
33
|
+
) -> VideoViewsWithCount:
|
|
34
|
+
"""Retrieve samples for a specific dataset with optional filtering."""
|
|
35
|
+
# Subquery to find the minimum frame_number for each video
|
|
36
|
+
min_frame_subquery = (
|
|
37
|
+
select(
|
|
38
|
+
VideoFrameTable.parent_sample_id,
|
|
39
|
+
func.min(col(VideoFrameTable.frame_number)).label("min_frame_number"),
|
|
40
|
+
)
|
|
41
|
+
.group_by(col(VideoFrameTable.parent_sample_id))
|
|
42
|
+
.subquery()
|
|
43
|
+
)
|
|
44
|
+
# TODO(Horatiu, 11/2025): Check if it is possible to optimize this query.
|
|
45
|
+
# Query to get videos with their first frame (frame with min frame_number)
|
|
46
|
+
# First join the subquery to VideoTable, then join VideoFrameTable
|
|
47
|
+
samples_query = (
|
|
48
|
+
select(VideoTable, VideoFrameTable)
|
|
49
|
+
.join(VideoTable.sample)
|
|
50
|
+
.outerjoin(
|
|
51
|
+
min_frame_subquery,
|
|
52
|
+
min_frame_subquery.c.parent_sample_id == VideoTable.sample_id,
|
|
53
|
+
)
|
|
54
|
+
.outerjoin(
|
|
55
|
+
VideoFrameTable,
|
|
56
|
+
and_(
|
|
57
|
+
col(VideoFrameTable.parent_sample_id) == col(VideoTable.sample_id),
|
|
58
|
+
col(VideoFrameTable.frame_number) == min_frame_subquery.c.min_frame_number,
|
|
59
|
+
),
|
|
60
|
+
)
|
|
61
|
+
.where(SampleTable.dataset_id == dataset_id)
|
|
62
|
+
.options(
|
|
63
|
+
selectinload(VideoFrameTable.sample).options(
|
|
64
|
+
joinedload(SampleTable.tags),
|
|
65
|
+
# Ignore type checker error - false positive from TYPE_CHECKING.
|
|
66
|
+
joinedload(SampleTable.metadata_dict), # type: ignore[arg-type]
|
|
67
|
+
selectinload(SampleTable.captions),
|
|
68
|
+
),
|
|
69
|
+
selectinload(VideoTable.sample).options(
|
|
70
|
+
joinedload(SampleTable.tags),
|
|
71
|
+
# Ignore type checker error - false positive from TYPE_CHECKING.
|
|
72
|
+
joinedload(SampleTable.metadata_dict), # type: ignore[arg-type]
|
|
73
|
+
selectinload(SampleTable.captions),
|
|
74
|
+
),
|
|
75
|
+
)
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
total_count_query = (
|
|
79
|
+
select(func.count())
|
|
80
|
+
.select_from(VideoTable)
|
|
81
|
+
.join(VideoTable.sample)
|
|
82
|
+
.where(SampleTable.dataset_id == dataset_id)
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
if text_embedding:
|
|
86
|
+
# Fetch the first embedding_model_id for the given dataset_id
|
|
87
|
+
embedding_model_id = session.exec(
|
|
88
|
+
select(EmbeddingModelTable.embedding_model_id)
|
|
89
|
+
.where(EmbeddingModelTable.dataset_id == dataset_id)
|
|
90
|
+
.limit(1)
|
|
91
|
+
).first()
|
|
92
|
+
|
|
93
|
+
if text_embedding and embedding_model_id:
|
|
94
|
+
# Join with SampleEmbedding table to access embeddings
|
|
95
|
+
samples_query = (
|
|
96
|
+
samples_query.join(
|
|
97
|
+
SampleEmbeddingTable,
|
|
98
|
+
col(VideoTable.sample_id) == col(SampleEmbeddingTable.sample_id),
|
|
99
|
+
)
|
|
100
|
+
.where(SampleEmbeddingTable.embedding_model_id == embedding_model_id)
|
|
101
|
+
.order_by(
|
|
102
|
+
func.list_cosine_distance(
|
|
103
|
+
SampleEmbeddingTable.embedding,
|
|
104
|
+
text_embedding,
|
|
105
|
+
)
|
|
106
|
+
)
|
|
107
|
+
)
|
|
108
|
+
total_count_query = total_count_query.join(
|
|
109
|
+
SampleEmbeddingTable,
|
|
110
|
+
col(VideoTable.sample_id) == col(SampleEmbeddingTable.sample_id),
|
|
111
|
+
).where(SampleEmbeddingTable.embedding_model_id == embedding_model_id)
|
|
112
|
+
else:
|
|
113
|
+
samples_query = samples_query.order_by(col(VideoTable.file_path_abs).asc())
|
|
114
|
+
|
|
115
|
+
if sample_ids:
|
|
116
|
+
samples_query = samples_query.where(col(VideoTable.sample_id).in_(sample_ids))
|
|
117
|
+
total_count_query = total_count_query.where(col(VideoTable.sample_id).in_(sample_ids))
|
|
118
|
+
|
|
119
|
+
if filters:
|
|
120
|
+
samples_query = filters.apply(samples_query)
|
|
121
|
+
total_count_query = filters.apply(total_count_query)
|
|
122
|
+
|
|
123
|
+
samples_query = samples_query.order_by(col(VideoTable.file_path_abs).asc())
|
|
124
|
+
|
|
125
|
+
# Apply pagination if provided
|
|
126
|
+
if pagination is not None:
|
|
127
|
+
samples_query = samples_query.offset(pagination.offset).limit(pagination.limit)
|
|
128
|
+
|
|
129
|
+
total_count = session.exec(total_count_query).one()
|
|
130
|
+
|
|
131
|
+
next_cursor = None
|
|
132
|
+
if pagination and pagination.offset + pagination.limit < total_count:
|
|
133
|
+
next_cursor = pagination.offset + pagination.limit
|
|
134
|
+
|
|
135
|
+
# Fetch videos with their first frames and convert to VideoView
|
|
136
|
+
results = session.exec(samples_query).all()
|
|
137
|
+
video_views = [
|
|
138
|
+
convert_video_table_to_view(video=video, first_frame=first_frame)
|
|
139
|
+
for video, first_frame in results
|
|
140
|
+
]
|
|
141
|
+
|
|
142
|
+
return VideoViewsWithCount(
|
|
143
|
+
samples=video_views,
|
|
144
|
+
total_count=total_count,
|
|
145
|
+
next_cursor=next_cursor,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
# TODO(Horatiu, 11/2025): This should be deleted when we have proper way of getting all frames for
|
|
150
|
+
# a video.
|
|
151
|
+
def get_all_by_dataset_id_with_frames(
|
|
152
|
+
session: Session,
|
|
153
|
+
dataset_id: UUID,
|
|
154
|
+
) -> Sequence[VideoTable]:
|
|
155
|
+
"""Retrieve video table with all the samples."""
|
|
156
|
+
samples_query = (
|
|
157
|
+
select(VideoTable).join(VideoTable.sample).where(SampleTable.dataset_id == dataset_id)
|
|
158
|
+
)
|
|
159
|
+
samples_query = samples_query.order_by(col(VideoTable.file_path_abs).asc())
|
|
160
|
+
return session.exec(samples_query).all()
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def convert_video_table_to_view(
|
|
164
|
+
video: VideoTable, first_frame: VideoFrameTable | None
|
|
165
|
+
) -> VideoView:
|
|
166
|
+
"""Convert VideoTable to VideoView with only the first frame."""
|
|
167
|
+
first_frame_view = None
|
|
168
|
+
if first_frame:
|
|
169
|
+
first_frame_view = build_frame_view(first_frame)
|
|
170
|
+
|
|
171
|
+
return VideoView(
|
|
172
|
+
width=video.width,
|
|
173
|
+
height=video.height,
|
|
174
|
+
duration_s=video.duration_s,
|
|
175
|
+
fps=video.fps,
|
|
176
|
+
file_name=video.file_name,
|
|
177
|
+
file_path_abs=video.file_path_abs,
|
|
178
|
+
sample_id=video.sample_id,
|
|
179
|
+
sample=SampleView.model_validate(video.sample),
|
|
180
|
+
frame=first_frame_view,
|
|
181
|
+
)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Find a video by its id."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
|
|
7
|
+
from sqlmodel import Session, select
|
|
8
|
+
|
|
9
|
+
from lightly_studio.models.video import VideoTable
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_by_id(session: Session, sample_id: UUID) -> VideoTable | None:
|
|
13
|
+
"""Retrieve a single video sample by ID.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
session: The database session.
|
|
17
|
+
sample_id: The ID of the video to retrieve.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
A VideoTable object or none.
|
|
21
|
+
"""
|
|
22
|
+
return session.exec(select(VideoTable).where(VideoTable.sample_id == sample_id)).one()
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Retrieve the minimum and maximum values of each video table field."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
from uuid import UUID
|
|
7
|
+
|
|
8
|
+
from sqlalchemy import Select, select
|
|
9
|
+
from sqlmodel import Session, col, func
|
|
10
|
+
|
|
11
|
+
from lightly_studio.models.annotation.annotation_base import AnnotationBaseTable
|
|
12
|
+
from lightly_studio.models.range import FloatRange, IntRange
|
|
13
|
+
from lightly_studio.models.sample import SampleTable
|
|
14
|
+
from lightly_studio.models.video import (
|
|
15
|
+
VideoFieldsBoundsView,
|
|
16
|
+
VideoFrameTable,
|
|
17
|
+
VideoTable,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_table_fields_bounds(
|
|
22
|
+
session: Session, dataset_id: UUID, annotations_frames_labels_id: list[UUID] | None = None
|
|
23
|
+
) -> VideoFieldsBoundsView | None:
|
|
24
|
+
"""Find the minimum and maximum values (bounds) of the video fields.
|
|
25
|
+
|
|
26
|
+
This includes fps, width, height, and duration. If annotation label
|
|
27
|
+
filters are provided, only videos containing those annotations are
|
|
28
|
+
considered.
|
|
29
|
+
"""
|
|
30
|
+
query: Select[tuple[Any, ...]] = select(
|
|
31
|
+
func.min(VideoTable.width).label("min_width"),
|
|
32
|
+
func.max(VideoTable.width).label("max_width"),
|
|
33
|
+
func.min(VideoTable.height).label("min_height"),
|
|
34
|
+
func.max(VideoTable.height).label("max_height"),
|
|
35
|
+
func.min(VideoTable.duration_s).label("min_duration_s"),
|
|
36
|
+
func.max(VideoTable.duration_s).label("max_duration_s"),
|
|
37
|
+
func.min(VideoTable.fps).label("min_fps"),
|
|
38
|
+
func.max(VideoTable.fps).label("max_fps"),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
query = query.join(SampleTable)
|
|
42
|
+
|
|
43
|
+
if annotations_frames_labels_id:
|
|
44
|
+
annotation_video_ids_subquery = (
|
|
45
|
+
select(col(VideoTable.sample_id))
|
|
46
|
+
.select_from(VideoTable)
|
|
47
|
+
.join(VideoFrameTable)
|
|
48
|
+
.join(SampleTable, col(SampleTable.sample_id) == col(VideoFrameTable.sample_id))
|
|
49
|
+
.join(
|
|
50
|
+
AnnotationBaseTable,
|
|
51
|
+
col(AnnotationBaseTable.parent_sample_id) == col(SampleTable.sample_id),
|
|
52
|
+
)
|
|
53
|
+
.where(col(AnnotationBaseTable.annotation_label_id).in_(annotations_frames_labels_id))
|
|
54
|
+
.distinct()
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
query = query.where(col(VideoTable.sample_id).in_(annotation_video_ids_subquery))
|
|
58
|
+
|
|
59
|
+
query = query.where(col(SampleTable.dataset_id) == dataset_id)
|
|
60
|
+
|
|
61
|
+
result = session.execute(query).mappings().one()
|
|
62
|
+
|
|
63
|
+
## If the min_width is empty, then no data is found.
|
|
64
|
+
if result["min_width"] is None:
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
return VideoFieldsBoundsView(
|
|
68
|
+
width=IntRange(min=result["min_width"], max=result["max_width"]),
|
|
69
|
+
height=IntRange(min=result["min_height"], max=result["max_height"]),
|
|
70
|
+
duration_s=FloatRange(min=result["min_duration_s"], max=result["max_duration_s"]),
|
|
71
|
+
fps=FloatRange(min=result["min_fps"], max=result["max_fps"]),
|
|
72
|
+
)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Find a video view by its id."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
|
|
7
|
+
from sqlalchemy import and_
|
|
8
|
+
from sqlmodel import Session, col, func, select
|
|
9
|
+
|
|
10
|
+
from lightly_studio.models.video import VideoFrameTable, VideoTable, VideoView
|
|
11
|
+
from lightly_studio.resolvers.video_resolver.get_all_by_dataset_id import (
|
|
12
|
+
convert_video_table_to_view,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_view_by_id(session: Session, sample_id: UUID) -> VideoView | None:
|
|
17
|
+
"""Retrieve a video view by its ID.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
session: The database session.
|
|
21
|
+
sample_id: The ID of the video to retrieve.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
A VideoView object or none.
|
|
25
|
+
"""
|
|
26
|
+
min_frame_subquery = (
|
|
27
|
+
select(
|
|
28
|
+
VideoFrameTable.parent_sample_id,
|
|
29
|
+
func.min(VideoFrameTable.frame_number).label("min_frame_number"),
|
|
30
|
+
)
|
|
31
|
+
.group_by(col(VideoFrameTable.parent_sample_id))
|
|
32
|
+
.subquery()
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
query = (
|
|
36
|
+
select(VideoTable, VideoFrameTable)
|
|
37
|
+
.outerjoin(
|
|
38
|
+
min_frame_subquery,
|
|
39
|
+
min_frame_subquery.c.parent_sample_id == VideoTable.sample_id,
|
|
40
|
+
)
|
|
41
|
+
.outerjoin(
|
|
42
|
+
VideoFrameTable,
|
|
43
|
+
and_(
|
|
44
|
+
col(VideoFrameTable.parent_sample_id) == col(VideoTable.sample_id),
|
|
45
|
+
col(VideoFrameTable.frame_number) == min_frame_subquery.c.min_frame_number,
|
|
46
|
+
),
|
|
47
|
+
)
|
|
48
|
+
.where(VideoTable.sample_id == sample_id)
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
video, first_frame = session.exec(query).one()
|
|
52
|
+
return convert_video_table_to_view(video, first_frame)
|