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,299 @@
|
|
|
1
|
+
"""Handler for database operations related to tags."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import datetime, timezone
|
|
6
|
+
from uuid import UUID
|
|
7
|
+
|
|
8
|
+
import sqlmodel
|
|
9
|
+
from sqlmodel import Session, col, select
|
|
10
|
+
|
|
11
|
+
from lightly_studio.models.annotation.annotation_base import AnnotationBaseTable
|
|
12
|
+
from lightly_studio.models.annotation.links import AnnotationTagLinkTable
|
|
13
|
+
from lightly_studio.models.sample import SampleTable, SampleTagLinkTable
|
|
14
|
+
from lightly_studio.models.tag import TagCreate, TagTable, TagUpdate
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def create(session: Session, tag: TagCreate) -> TagTable:
|
|
18
|
+
"""Create a new tag in the database."""
|
|
19
|
+
db_tag = TagTable.model_validate(tag)
|
|
20
|
+
session.add(db_tag)
|
|
21
|
+
session.commit()
|
|
22
|
+
session.refresh(db_tag)
|
|
23
|
+
return db_tag
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# TODO(Michal, 06/2025): Use Paginated struct instead of offset/limit.
|
|
27
|
+
def get_all_by_dataset_id(
|
|
28
|
+
session: Session, dataset_id: UUID, offset: int = 0, limit: int | None = None
|
|
29
|
+
) -> list[TagTable]:
|
|
30
|
+
"""Retrieve all tags with pagination."""
|
|
31
|
+
query = (
|
|
32
|
+
select(TagTable)
|
|
33
|
+
.where(TagTable.dataset_id == dataset_id)
|
|
34
|
+
.order_by(col(TagTable.created_at).asc(), col(TagTable.tag_id).asc())
|
|
35
|
+
.offset(offset)
|
|
36
|
+
)
|
|
37
|
+
if limit is not None:
|
|
38
|
+
query = query.limit(limit)
|
|
39
|
+
tags = session.exec(query).all()
|
|
40
|
+
return list(tags) if tags else []
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_by_id(session: Session, tag_id: UUID) -> TagTable | None:
|
|
44
|
+
"""Retrieve a single tag by ID."""
|
|
45
|
+
return session.exec(select(TagTable).where(TagTable.tag_id == tag_id)).one_or_none()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_by_name(session: Session, tag_name: str, dataset_id: UUID | None) -> TagTable | None:
|
|
49
|
+
"""Retrieve a single tag by ID."""
|
|
50
|
+
if dataset_id:
|
|
51
|
+
return session.exec(
|
|
52
|
+
select(TagTable)
|
|
53
|
+
.where(TagTable.dataset_id == dataset_id)
|
|
54
|
+
.where(TagTable.name == tag_name)
|
|
55
|
+
).one_or_none()
|
|
56
|
+
return session.exec(select(TagTable).where(TagTable.name == tag_name)).one_or_none()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def update(session: Session, tag_id: UUID, tag_data: TagUpdate) -> TagTable | None:
|
|
60
|
+
"""Update an existing tag."""
|
|
61
|
+
tag = get_by_id(session=session, tag_id=tag_id)
|
|
62
|
+
if not tag:
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
# due to duckdb/OLAP optimisations, update operations effecting unique
|
|
66
|
+
# constraints (e.g colums) will lead to a unique constraint violation.
|
|
67
|
+
# This is due to a update is implemented as delete+insert. The error
|
|
68
|
+
# happens only within the same session.
|
|
69
|
+
# To fix it, we can delete, commit + insert a new tag.
|
|
70
|
+
# https://duckdb.org/docs/sql/indexes#over-eager-unique-constraint-checking
|
|
71
|
+
session.delete(tag)
|
|
72
|
+
session.commit()
|
|
73
|
+
|
|
74
|
+
# create clone of tag with updated values
|
|
75
|
+
tag_updated = TagTable.model_validate(tag)
|
|
76
|
+
tag_updated.name = tag_data.name
|
|
77
|
+
tag_updated.description = tag_data.description
|
|
78
|
+
tag_updated.updated_at = datetime.now(timezone.utc)
|
|
79
|
+
|
|
80
|
+
session.add(tag_updated)
|
|
81
|
+
session.commit()
|
|
82
|
+
session.refresh(tag_updated)
|
|
83
|
+
return tag_updated
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def delete(session: Session, tag_id: UUID) -> bool:
|
|
87
|
+
"""Delete a tag."""
|
|
88
|
+
tag = get_by_id(session=session, tag_id=tag_id)
|
|
89
|
+
if not tag:
|
|
90
|
+
return False
|
|
91
|
+
|
|
92
|
+
session.delete(tag)
|
|
93
|
+
session.commit()
|
|
94
|
+
return True
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def add_tag_to_sample(
|
|
98
|
+
session: Session,
|
|
99
|
+
tag_id: UUID,
|
|
100
|
+
sample: SampleTable,
|
|
101
|
+
) -> SampleTable | None:
|
|
102
|
+
"""Add a tag to a sample."""
|
|
103
|
+
tag = get_by_id(session=session, tag_id=tag_id)
|
|
104
|
+
if not tag or not tag.tag_id:
|
|
105
|
+
return None
|
|
106
|
+
if tag.kind != "sample":
|
|
107
|
+
raise ValueError(f"Tag {tag_id} is not of kind 'sample'")
|
|
108
|
+
|
|
109
|
+
sample.tags.append(tag)
|
|
110
|
+
session.add(sample)
|
|
111
|
+
session.commit()
|
|
112
|
+
session.refresh(sample)
|
|
113
|
+
return sample
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def remove_tag_from_sample(
|
|
117
|
+
session: Session,
|
|
118
|
+
tag_id: UUID,
|
|
119
|
+
sample: SampleTable,
|
|
120
|
+
) -> SampleTable | None:
|
|
121
|
+
"""Remove a tag from a sample."""
|
|
122
|
+
tag = get_by_id(session=session, tag_id=tag_id)
|
|
123
|
+
if not tag or not tag.tag_id:
|
|
124
|
+
return None
|
|
125
|
+
if tag.kind != "sample":
|
|
126
|
+
raise ValueError(f"Tag {tag_id} is not of kind 'sample'")
|
|
127
|
+
|
|
128
|
+
sample.tags.remove(tag)
|
|
129
|
+
session.add(sample)
|
|
130
|
+
session.commit()
|
|
131
|
+
session.refresh(sample)
|
|
132
|
+
return sample
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def add_tag_to_annotation(
|
|
136
|
+
session: Session,
|
|
137
|
+
tag_id: UUID,
|
|
138
|
+
annotation: AnnotationBaseTable,
|
|
139
|
+
) -> AnnotationBaseTable | None:
|
|
140
|
+
"""Add a tag to a annotation."""
|
|
141
|
+
tag = get_by_id(session=session, tag_id=tag_id)
|
|
142
|
+
if not tag or not tag.tag_id:
|
|
143
|
+
return None
|
|
144
|
+
if tag.kind != "annotation":
|
|
145
|
+
raise ValueError(f"Tag {tag_id} is not of kind 'annotation'")
|
|
146
|
+
|
|
147
|
+
annotation.tags.append(tag)
|
|
148
|
+
session.add(annotation)
|
|
149
|
+
session.commit()
|
|
150
|
+
session.refresh(annotation)
|
|
151
|
+
return annotation
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def assign_tag_to_annotation(
|
|
155
|
+
session: Session,
|
|
156
|
+
tag: TagTable,
|
|
157
|
+
annotation: AnnotationBaseTable,
|
|
158
|
+
) -> AnnotationBaseTable:
|
|
159
|
+
"""Add a tag to a annotation."""
|
|
160
|
+
annotation.tags.append(tag)
|
|
161
|
+
session.add(annotation)
|
|
162
|
+
session.commit()
|
|
163
|
+
session.refresh(annotation)
|
|
164
|
+
return annotation
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def remove_tag_from_annotation(
|
|
168
|
+
session: Session,
|
|
169
|
+
tag_id: UUID,
|
|
170
|
+
annotation: AnnotationBaseTable,
|
|
171
|
+
) -> AnnotationBaseTable | None:
|
|
172
|
+
"""Remove a tag from a annotation."""
|
|
173
|
+
tag = get_by_id(session=session, tag_id=tag_id)
|
|
174
|
+
if not tag or not tag.tag_id:
|
|
175
|
+
return None
|
|
176
|
+
if tag.kind != "annotation":
|
|
177
|
+
raise ValueError(f"Tag {tag_id} is not of kind 'annotation'")
|
|
178
|
+
|
|
179
|
+
annotation.tags.remove(tag)
|
|
180
|
+
session.add(annotation)
|
|
181
|
+
session.commit()
|
|
182
|
+
session.refresh(annotation)
|
|
183
|
+
return annotation
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def add_sample_ids_to_tag_id(
|
|
187
|
+
session: Session,
|
|
188
|
+
tag_id: UUID,
|
|
189
|
+
sample_ids: list[UUID],
|
|
190
|
+
) -> TagTable | None:
|
|
191
|
+
"""Add a list of sample_ids to a tag."""
|
|
192
|
+
tag = get_by_id(session=session, tag_id=tag_id)
|
|
193
|
+
if not tag or not tag.tag_id:
|
|
194
|
+
return None
|
|
195
|
+
if tag.kind != "sample":
|
|
196
|
+
raise ValueError(f"Tag {tag_id} is not of kind 'sample'")
|
|
197
|
+
|
|
198
|
+
for sample_id in sample_ids:
|
|
199
|
+
session.merge(SampleTagLinkTable(sample_id=sample_id, tag_id=tag_id))
|
|
200
|
+
|
|
201
|
+
session.commit()
|
|
202
|
+
session.refresh(tag)
|
|
203
|
+
return tag
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def remove_sample_ids_from_tag_id(
|
|
207
|
+
session: Session,
|
|
208
|
+
tag_id: UUID,
|
|
209
|
+
sample_ids: list[UUID],
|
|
210
|
+
) -> TagTable | None:
|
|
211
|
+
"""Remove a list of sample_ids to a tag."""
|
|
212
|
+
tag = get_by_id(session=session, tag_id=tag_id)
|
|
213
|
+
if not tag or not tag.tag_id:
|
|
214
|
+
return None
|
|
215
|
+
if tag.kind != "sample":
|
|
216
|
+
raise ValueError(f"Tag {tag_id} is not of kind 'sample'")
|
|
217
|
+
|
|
218
|
+
session.exec( # type:ignore[call-overload]
|
|
219
|
+
sqlmodel.delete(SampleTagLinkTable).where(
|
|
220
|
+
col(SampleTagLinkTable.tag_id) == tag_id,
|
|
221
|
+
col(SampleTagLinkTable.sample_id).in_(sample_ids),
|
|
222
|
+
)
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
session.commit()
|
|
226
|
+
session.refresh(tag)
|
|
227
|
+
return tag
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def add_annotation_ids_to_tag_id(
|
|
231
|
+
session: Session,
|
|
232
|
+
tag_id: UUID,
|
|
233
|
+
annotation_ids: list[UUID],
|
|
234
|
+
) -> TagTable | None:
|
|
235
|
+
"""Add a list of annotation_ids to a tag."""
|
|
236
|
+
tag = get_by_id(session=session, tag_id=tag_id)
|
|
237
|
+
if not tag or not tag.tag_id:
|
|
238
|
+
return None
|
|
239
|
+
if tag.kind != "annotation":
|
|
240
|
+
raise ValueError(f"Tag {tag_id} is not of kind 'annotation'")
|
|
241
|
+
|
|
242
|
+
for annotation_id in annotation_ids:
|
|
243
|
+
session.merge(
|
|
244
|
+
AnnotationTagLinkTable(
|
|
245
|
+
tag_id=tag_id,
|
|
246
|
+
annotation_sample_id=annotation_id,
|
|
247
|
+
)
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
session.commit()
|
|
251
|
+
session.refresh(tag)
|
|
252
|
+
return tag
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def remove_annotation_ids_from_tag_id(
|
|
256
|
+
session: Session,
|
|
257
|
+
tag_id: UUID,
|
|
258
|
+
annotation_ids: list[UUID],
|
|
259
|
+
) -> TagTable | None:
|
|
260
|
+
"""Remove a list of things to a tag."""
|
|
261
|
+
tag = get_by_id(session=session, tag_id=tag_id)
|
|
262
|
+
if not tag or not tag.tag_id:
|
|
263
|
+
return None
|
|
264
|
+
if tag.kind != "annotation":
|
|
265
|
+
raise ValueError(f"Tag {tag_id} is not of kind 'annotation'")
|
|
266
|
+
|
|
267
|
+
session.exec( # type:ignore[call-overload]
|
|
268
|
+
sqlmodel.delete(AnnotationTagLinkTable).where(
|
|
269
|
+
col(AnnotationTagLinkTable.tag_id) == tag_id,
|
|
270
|
+
col(AnnotationTagLinkTable.annotation_sample_id).in_(annotation_ids),
|
|
271
|
+
)
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
session.commit()
|
|
275
|
+
session.refresh(tag)
|
|
276
|
+
return tag
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def get_or_create_sample_tag_by_name(
|
|
280
|
+
session: Session,
|
|
281
|
+
dataset_id: UUID,
|
|
282
|
+
tag_name: str,
|
|
283
|
+
) -> TagTable:
|
|
284
|
+
"""Get an existing sample tag by name or create a new one if it doesn't exist.
|
|
285
|
+
|
|
286
|
+
Args:
|
|
287
|
+
session: Database session for executing queries.
|
|
288
|
+
dataset_id: The dataset ID to search/create the tag for.
|
|
289
|
+
tag_name: Name of the tag to get or create.
|
|
290
|
+
|
|
291
|
+
Returns:
|
|
292
|
+
The existing or newly created sample tag.
|
|
293
|
+
"""
|
|
294
|
+
existing_tag = get_by_name(session=session, tag_name=tag_name, dataset_id=dataset_id)
|
|
295
|
+
if existing_tag:
|
|
296
|
+
return existing_tag
|
|
297
|
+
|
|
298
|
+
new_tag = TagCreate(name=tag_name, dataset_id=dataset_id, kind="sample")
|
|
299
|
+
return create(session=session, tag=new_tag)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""Handler for getting cached 2D embeddings from high-dimensional embeddings."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
from lightly_mundig import TwoDimEmbedding # type: ignore[import-untyped]
|
|
9
|
+
from numpy.typing import NDArray
|
|
10
|
+
from sqlmodel import Session, col, select
|
|
11
|
+
|
|
12
|
+
from lightly_studio.dataset.env import LIGHTLY_STUDIO_LICENSE_KEY
|
|
13
|
+
from lightly_studio.models.embedding_model import EmbeddingModelTable
|
|
14
|
+
from lightly_studio.models.sample import SampleTable
|
|
15
|
+
from lightly_studio.models.two_dim_embedding import TwoDimEmbeddingTable
|
|
16
|
+
from lightly_studio.resolvers import sample_embedding_resolver
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_twodim_embeddings(
|
|
20
|
+
session: Session,
|
|
21
|
+
dataset_id: UUID,
|
|
22
|
+
embedding_model_id: UUID,
|
|
23
|
+
) -> tuple[NDArray[np.float32], NDArray[np.float32], list[UUID]]:
|
|
24
|
+
"""Return cached 2D embeddings together with their sample identifiers.
|
|
25
|
+
|
|
26
|
+
Uses a cache to avoid recomputing the 2D embeddings. The cache key combines the sorted
|
|
27
|
+
sample identifiers with a deterministic 64-bit hash over the stored high-dimensional
|
|
28
|
+
embeddings.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
session: Database session.
|
|
32
|
+
dataset_id: Dataset identifier.
|
|
33
|
+
embedding_model_id: Embedding model identifier.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Tuple of (x coordinates, y coordinates, ordered sample IDs).
|
|
37
|
+
"""
|
|
38
|
+
embedding_model = session.get(EmbeddingModelTable, embedding_model_id)
|
|
39
|
+
if embedding_model is None:
|
|
40
|
+
raise ValueError(f"Embedding model {embedding_model_id} not found.")
|
|
41
|
+
|
|
42
|
+
# Define a fixed order of sample IDs for the cache key.
|
|
43
|
+
sample_ids_ordered = list(
|
|
44
|
+
session.exec(
|
|
45
|
+
select(SampleTable.sample_id)
|
|
46
|
+
.where(SampleTable.dataset_id == dataset_id)
|
|
47
|
+
.order_by(col(SampleTable.sample_id).asc())
|
|
48
|
+
).all()
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# Check if we have a cached 2D embedding for the given samples and embedding model.
|
|
52
|
+
# The order is defined by sample_ids_ordered.
|
|
53
|
+
cache_key, sample_ids_of_samples_with_embeddings = (
|
|
54
|
+
sample_embedding_resolver.get_hash_by_sample_ids(
|
|
55
|
+
session=session,
|
|
56
|
+
sample_ids_ordered=sample_ids_ordered,
|
|
57
|
+
embedding_model_id=embedding_model_id,
|
|
58
|
+
)
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
if not sample_ids_of_samples_with_embeddings:
|
|
62
|
+
empty = np.array([], dtype=np.float32)
|
|
63
|
+
return empty, empty, []
|
|
64
|
+
|
|
65
|
+
# If there is a cached entry, return it.
|
|
66
|
+
cached = session.get(TwoDimEmbeddingTable, cache_key)
|
|
67
|
+
if cached is not None:
|
|
68
|
+
x_values = np.array(cached.x, dtype=np.float32)
|
|
69
|
+
y_values = np.array(cached.y, dtype=np.float32)
|
|
70
|
+
return x_values, y_values, sample_ids_of_samples_with_embeddings
|
|
71
|
+
|
|
72
|
+
# No cached entry found - load the high-dimensional embeddings.
|
|
73
|
+
# The order is defined by sample_ids_of_samples_with_embeddings.
|
|
74
|
+
sample_embeddings = sample_embedding_resolver.get_by_sample_ids(
|
|
75
|
+
session=session,
|
|
76
|
+
sample_ids=sample_ids_of_samples_with_embeddings,
|
|
77
|
+
embedding_model_id=embedding_model_id,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# If there are no embeddings, return empty arrays.
|
|
81
|
+
if not sample_embeddings:
|
|
82
|
+
empty = np.array([], dtype=np.float32)
|
|
83
|
+
return empty, empty, []
|
|
84
|
+
|
|
85
|
+
# Compute the 2D embedding from the high-dimensional embeddings.
|
|
86
|
+
# The order is now defined by sample_embeddings. They are the ordered subset of the
|
|
87
|
+
# sample_ids_of_samples_with_embeddings that have embeddings.
|
|
88
|
+
sample_ids_of_samples_with_embeddings = [embedding.sample_id for embedding in sample_embeddings]
|
|
89
|
+
embedding_values = [embedding.embedding for embedding in sample_embeddings]
|
|
90
|
+
planar_embeddings = _calculate_2d_embeddings(embedding_values)
|
|
91
|
+
embeddings_2d = np.asarray(planar_embeddings, dtype=np.float32)
|
|
92
|
+
x_values, y_values = embeddings_2d[:, 0], embeddings_2d[:, 1]
|
|
93
|
+
|
|
94
|
+
# Write the computed 2D embeddings to the cache.
|
|
95
|
+
cache_entry = TwoDimEmbeddingTable(hash=cache_key, x=list(x_values), y=list(y_values))
|
|
96
|
+
session.add(cache_entry)
|
|
97
|
+
session.commit()
|
|
98
|
+
|
|
99
|
+
return x_values, y_values, sample_ids_of_samples_with_embeddings
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _calculate_2d_embeddings(embedding_values: list[list[float]]) -> list[tuple[float, float]]:
|
|
103
|
+
n_samples = len(embedding_values)
|
|
104
|
+
# For 0, 1 or 2 samples we hard-code deterministic coordinates.
|
|
105
|
+
if n_samples == 0:
|
|
106
|
+
return []
|
|
107
|
+
if n_samples == 1:
|
|
108
|
+
return [(0.0, 0.0)]
|
|
109
|
+
if n_samples == 2: # noqa: PLR2004
|
|
110
|
+
return [(0.0, 0.0), (1.0, 1.0)]
|
|
111
|
+
|
|
112
|
+
license_key = LIGHTLY_STUDIO_LICENSE_KEY
|
|
113
|
+
if license_key is None:
|
|
114
|
+
raise ValueError(
|
|
115
|
+
"LIGHTLY_STUDIO_LICENSE_KEY environment variable is not set. "
|
|
116
|
+
"Please set it to your LightlyStudio license key."
|
|
117
|
+
)
|
|
118
|
+
embedding_calculator = TwoDimEmbedding(embedding_values, license_key)
|
|
119
|
+
return embedding_calculator.calculate_2d_embedding() # type: ignore[no-any-return]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Resolvers for video_frame database operations."""
|
|
2
|
+
|
|
3
|
+
from lightly_studio.resolvers.video_frame_resolver.count_video_frames_annotations import (
|
|
4
|
+
count_video_frames_annotations,
|
|
5
|
+
)
|
|
6
|
+
from lightly_studio.resolvers.video_frame_resolver.create_many import create_many
|
|
7
|
+
from lightly_studio.resolvers.video_frame_resolver.get_all_by_dataset_id import (
|
|
8
|
+
get_all_by_dataset_id,
|
|
9
|
+
)
|
|
10
|
+
from lightly_studio.resolvers.video_frame_resolver.get_by_id import (
|
|
11
|
+
get_by_id,
|
|
12
|
+
)
|
|
13
|
+
from lightly_studio.resolvers.video_frame_resolver.get_table_fields_bounds import (
|
|
14
|
+
get_table_fields_bounds,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"count_video_frames_annotations",
|
|
19
|
+
"create_many",
|
|
20
|
+
"get_all_by_dataset_id",
|
|
21
|
+
"get_by_id",
|
|
22
|
+
"get_table_fields_bounds",
|
|
23
|
+
]
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""Count video frames annotations."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, List, Optional, Tuple
|
|
4
|
+
from uuid import UUID
|
|
5
|
+
|
|
6
|
+
from sqlmodel import Session, asc, col, func, select
|
|
7
|
+
from sqlmodel.sql.expression import Select
|
|
8
|
+
|
|
9
|
+
from lightly_studio.models.annotation.annotation_base import AnnotationBaseTable
|
|
10
|
+
from lightly_studio.models.annotation_label import AnnotationLabelTable
|
|
11
|
+
from lightly_studio.models.sample import SampleTable
|
|
12
|
+
from lightly_studio.models.video import VideoFrameTable
|
|
13
|
+
from lightly_studio.resolvers.video_frame_resolver.video_frame_annotations_counter_filter import (
|
|
14
|
+
VideoFrameAnnotationsCounterFilter,
|
|
15
|
+
)
|
|
16
|
+
from lightly_studio.resolvers.video_resolver.count_video_frame_annotations_by_video_dataset import (
|
|
17
|
+
CountAnnotationsView,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def count_video_frames_annotations(
|
|
22
|
+
session: Session, dataset_id: UUID, filters: Optional[VideoFrameAnnotationsCounterFilter] = None
|
|
23
|
+
) -> List[CountAnnotationsView]:
|
|
24
|
+
"""Count the annotations by video frames."""
|
|
25
|
+
unfiltered_query = (
|
|
26
|
+
_build_base_query(dataset_id=dataset_id, count_column_name="total")
|
|
27
|
+
.group_by(col(AnnotationBaseTable.annotation_label_id))
|
|
28
|
+
.subquery("unfiltered")
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
filtered_query = _build_base_query(dataset_id=dataset_id, count_column_name="filtered_count")
|
|
32
|
+
|
|
33
|
+
if filters:
|
|
34
|
+
filtered_query = filters.apply(filtered_query)
|
|
35
|
+
|
|
36
|
+
filtered_subquery = filtered_query.group_by(
|
|
37
|
+
col(AnnotationBaseTable.annotation_label_id)
|
|
38
|
+
).subquery("filtered")
|
|
39
|
+
|
|
40
|
+
final_query: Select[Any] = (
|
|
41
|
+
select(
|
|
42
|
+
col(AnnotationLabelTable.annotation_label_name).label("label"),
|
|
43
|
+
col(unfiltered_query.c.total).label("total"),
|
|
44
|
+
func.coalesce(filtered_subquery.c.filtered_count, 0).label("filtered_count"),
|
|
45
|
+
)
|
|
46
|
+
.select_from(AnnotationLabelTable)
|
|
47
|
+
.join(
|
|
48
|
+
unfiltered_query,
|
|
49
|
+
unfiltered_query.c.label_id == col(AnnotationLabelTable.annotation_label_id),
|
|
50
|
+
)
|
|
51
|
+
.outerjoin(
|
|
52
|
+
filtered_subquery,
|
|
53
|
+
filtered_subquery.c.label_id == col(AnnotationLabelTable.annotation_label_id),
|
|
54
|
+
)
|
|
55
|
+
.order_by(asc(AnnotationLabelTable.annotation_label_name))
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
rows = session.execute(final_query).mappings().all()
|
|
59
|
+
|
|
60
|
+
return [
|
|
61
|
+
CountAnnotationsView(
|
|
62
|
+
label_name=row["label"],
|
|
63
|
+
total_count=row["total"],
|
|
64
|
+
current_count=row["filtered_count"],
|
|
65
|
+
)
|
|
66
|
+
for row in rows
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _build_base_query(dataset_id: UUID, count_column_name: str) -> Select[Tuple[Any, int]]:
|
|
71
|
+
return (
|
|
72
|
+
select(
|
|
73
|
+
col(AnnotationBaseTable.annotation_label_id).label("label_id"),
|
|
74
|
+
func.count(col(AnnotationBaseTable.annotation_label_id)).label(count_column_name),
|
|
75
|
+
)
|
|
76
|
+
.select_from(AnnotationBaseTable)
|
|
77
|
+
.join(
|
|
78
|
+
VideoFrameTable,
|
|
79
|
+
col(VideoFrameTable.sample_id) == col(AnnotationBaseTable.parent_sample_id),
|
|
80
|
+
)
|
|
81
|
+
.join(SampleTable, col(VideoFrameTable.parent_sample_id) == col(SampleTable.sample_id))
|
|
82
|
+
.where(col(SampleTable.dataset_id) == dataset_id)
|
|
83
|
+
)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Implementation of create functions for video_frames."""
|
|
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 VideoFrameCreate, VideoFrameTable
|
|
12
|
+
from lightly_studio.resolvers import dataset_resolver, sample_resolver
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class VideoFrameCreateHelper(VideoFrameCreate):
|
|
16
|
+
"""Helper class to create VideoFrameTable with sample_id."""
|
|
17
|
+
|
|
18
|
+
sample_id: UUID
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def create_many(session: Session, dataset_id: UUID, samples: list[VideoFrameCreate]) -> list[UUID]:
|
|
22
|
+
"""Create multiple video_frame 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 video_frames to create in the database.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
List of UUIDs of VideoFrameTable 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_FRAME,
|
|
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 VideoFrameTable entries using the generated sample_ids.
|
|
42
|
+
db_video_frames = [
|
|
43
|
+
VideoFrameTable.model_validate(
|
|
44
|
+
VideoFrameCreateHelper(
|
|
45
|
+
frame_number=sample.frame_number,
|
|
46
|
+
frame_timestamp_s=sample.frame_timestamp_s,
|
|
47
|
+
frame_timestamp_pts=sample.frame_timestamp_pts,
|
|
48
|
+
parent_sample_id=sample.parent_sample_id,
|
|
49
|
+
sample_id=sample_id,
|
|
50
|
+
rotation_deg=sample.rotation_deg,
|
|
51
|
+
)
|
|
52
|
+
)
|
|
53
|
+
for sample_id, sample in zip(sample_ids, samples)
|
|
54
|
+
]
|
|
55
|
+
session.bulk_save_objects(db_video_frames)
|
|
56
|
+
session.commit()
|
|
57
|
+
return sample_ids
|
|
@@ -0,0 +1,63 @@
|
|
|
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 typing import Any
|
|
7
|
+
from uuid import UUID
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel
|
|
10
|
+
from sqlmodel import Session, col, func, select
|
|
11
|
+
|
|
12
|
+
from lightly_studio.api.routes.api.validators import Paginated
|
|
13
|
+
from lightly_studio.models.sample import SampleTable
|
|
14
|
+
from lightly_studio.models.video import VideoFrameTable, VideoTable
|
|
15
|
+
from lightly_studio.resolvers.video_frame_resolver.video_frame_filter import VideoFrameFilter
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class VideoFramesWithCount(BaseModel):
|
|
19
|
+
"""Result of getting all samples."""
|
|
20
|
+
|
|
21
|
+
samples: Sequence[VideoFrameTable]
|
|
22
|
+
total_count: int
|
|
23
|
+
next_cursor: int | None = None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_all_by_dataset_id(
|
|
27
|
+
session: Session,
|
|
28
|
+
dataset_id: UUID,
|
|
29
|
+
pagination: Paginated | None = None,
|
|
30
|
+
video_frame_filter: VideoFrameFilter | None = None,
|
|
31
|
+
) -> VideoFramesWithCount:
|
|
32
|
+
"""Retrieve video frame samples for a specific dataset with optional filtering."""
|
|
33
|
+
filters: list[Any] = [SampleTable.dataset_id == dataset_id]
|
|
34
|
+
|
|
35
|
+
base_query = (
|
|
36
|
+
select(VideoFrameTable)
|
|
37
|
+
.join(VideoFrameTable.sample)
|
|
38
|
+
.join(VideoFrameTable.video)
|
|
39
|
+
.where(*filters)
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
if video_frame_filter:
|
|
43
|
+
base_query = video_frame_filter.apply(base_query)
|
|
44
|
+
|
|
45
|
+
samples_query = base_query.order_by(
|
|
46
|
+
col(VideoTable.file_path_abs).asc(), col(VideoFrameTable.frame_number).asc()
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# Apply pagination if provided
|
|
50
|
+
if pagination is not None:
|
|
51
|
+
samples_query = samples_query.offset(pagination.offset).limit(pagination.limit)
|
|
52
|
+
|
|
53
|
+
total_count_query = select(func.count()).select_from(base_query.subquery())
|
|
54
|
+
total_count = session.exec(total_count_query).one()
|
|
55
|
+
next_cursor = None
|
|
56
|
+
if pagination and pagination.offset + pagination.limit < total_count:
|
|
57
|
+
next_cursor = pagination.offset + pagination.limit
|
|
58
|
+
|
|
59
|
+
return VideoFramesWithCount(
|
|
60
|
+
samples=session.exec(samples_query).all(),
|
|
61
|
+
total_count=total_count,
|
|
62
|
+
next_cursor=next_cursor,
|
|
63
|
+
)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Retrieve the video frame by ID resolver implementation."""
|
|
2
|
+
|
|
3
|
+
from uuid import UUID
|
|
4
|
+
|
|
5
|
+
from sqlmodel import Session, select
|
|
6
|
+
|
|
7
|
+
from lightly_studio.models.video import VideoFrameTable
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_by_id(session: Session, sample_id: UUID) -> VideoFrameTable:
|
|
11
|
+
"""Retrieve a single video frame by ID within a dataset."""
|
|
12
|
+
query = select(VideoFrameTable).where(VideoFrameTable.sample_id == sample_id)
|
|
13
|
+
return session.exec(query).one()
|