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,58 @@
|
|
|
1
|
+
"""Function to get or create a unique child dataset with a given sample type."""
|
|
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 DatasetCreate, SampleType
|
|
10
|
+
from lightly_studio.resolvers import dataset_resolver
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_or_create_child_dataset(
|
|
14
|
+
session: Session, dataset_id: UUID, sample_type: SampleType
|
|
15
|
+
) -> UUID:
|
|
16
|
+
"""Checks if a unique child dataset with the given sample type exists for the given dataset.
|
|
17
|
+
|
|
18
|
+
If it exists, returns its ID. If not, creates it and then returns its ID.
|
|
19
|
+
If multiple such datasets exist, raises an error.
|
|
20
|
+
|
|
21
|
+
The returned child is a direct child of the given dataset.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
session: The database session.
|
|
25
|
+
dataset_id: The uuid of the dataset to attach to.
|
|
26
|
+
sample_type: The sample type of the child dataset to get or create.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
The uuid of the child dataset.
|
|
30
|
+
|
|
31
|
+
Raises:
|
|
32
|
+
ValueError: If multiple child datasets with the given sample type exist.
|
|
33
|
+
"""
|
|
34
|
+
# Get filtered child datasets.
|
|
35
|
+
dataset = dataset_resolver.get_by_id(session=session, dataset_id=dataset_id)
|
|
36
|
+
if dataset is None:
|
|
37
|
+
raise ValueError(f"Dataset with id {dataset_id} not found.")
|
|
38
|
+
child_datasets = [ds for ds in dataset.children if ds.sample_type == sample_type]
|
|
39
|
+
|
|
40
|
+
# If we have children check if any have the given sample type.
|
|
41
|
+
if len(child_datasets) == 1:
|
|
42
|
+
return child_datasets[0].dataset_id
|
|
43
|
+
if len(child_datasets) > 1:
|
|
44
|
+
raise ValueError(
|
|
45
|
+
f"Multiple child datasets with sample type {sample_type.value} found "
|
|
46
|
+
f"for dataset id {dataset_id}."
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# No child dataset with the given sample type found, create one.
|
|
50
|
+
child_dataset = dataset_resolver.create(
|
|
51
|
+
session=session,
|
|
52
|
+
dataset=DatasetCreate(
|
|
53
|
+
name=f"{dataset.name}__{sample_type.value.lower()}",
|
|
54
|
+
sample_type=sample_type,
|
|
55
|
+
parent_dataset_id=dataset_id,
|
|
56
|
+
),
|
|
57
|
+
)
|
|
58
|
+
return child_dataset.dataset_id
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Retrieve the parent dataset for a given sample ID."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
|
|
7
|
+
from sqlmodel import Session, col, select
|
|
8
|
+
|
|
9
|
+
from lightly_studio.models.dataset import DatasetTable
|
|
10
|
+
from lightly_studio.models.sample import SampleTable
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_parent_dataset_by_sample_id(session: Session, sample_id: UUID) -> DatasetTable | None:
|
|
14
|
+
"""Get parent dataset by sample ID.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
session: Database session
|
|
18
|
+
sample_id: ID of the sample for which to get the parent dataset
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
Returns parent dataset
|
|
22
|
+
"""
|
|
23
|
+
child = session.exec(
|
|
24
|
+
select(DatasetTable).join(SampleTable).where(col(SampleTable.sample_id) == sample_id)
|
|
25
|
+
).one_or_none()
|
|
26
|
+
|
|
27
|
+
return child.parent if child else None
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Retrieve the parent dataset ID for a given dataset ID."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
|
|
7
|
+
from sqlalchemy.orm import aliased
|
|
8
|
+
from sqlmodel import Session, col, select
|
|
9
|
+
|
|
10
|
+
from lightly_studio.models.dataset import DatasetTable
|
|
11
|
+
|
|
12
|
+
ParentDataset = aliased(DatasetTable)
|
|
13
|
+
ChildDataset = aliased(DatasetTable)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_parent_dataset_id(session: Session, dataset_id: UUID) -> DatasetTable | None:
|
|
17
|
+
"""Retrieve the parent dataset for a given dataset ID."""
|
|
18
|
+
return session.exec(
|
|
19
|
+
select(ParentDataset)
|
|
20
|
+
.join(ChildDataset, col(ChildDataset.parent_dataset_id) == col(ParentDataset.dataset_id))
|
|
21
|
+
.where(ChildDataset.dataset_id == dataset_id)
|
|
22
|
+
).one_or_none()
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Implementation of get_root_dataset resolver function."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
|
|
7
|
+
from sqlmodel import Session, col, select
|
|
8
|
+
|
|
9
|
+
from lightly_studio.models.dataset import DatasetTable
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# TODO (Mihnea, 12/2025): Update the dataset_id to be required.
|
|
13
|
+
# The dataset_id is currently optional for backwards compatibility.
|
|
14
|
+
def get_root_dataset(session: Session, dataset_id: UUID | None = None) -> DatasetTable:
|
|
15
|
+
"""Retrieve the root dataset for a given dataset or the first root dataset.
|
|
16
|
+
|
|
17
|
+
If dataset_id is provided, traverses up the hierarchy to find the root ancestor.
|
|
18
|
+
If dataset_id is None, returns the first root dataset (backwards compatibility).
|
|
19
|
+
|
|
20
|
+
A root dataset is defined as a dataset where parent_dataset_id is None.
|
|
21
|
+
The root dataset may or may not have children.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
session: The database session.
|
|
25
|
+
dataset_id: Optional ID of a dataset to find the root for.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
The root dataset.
|
|
29
|
+
|
|
30
|
+
Raises:
|
|
31
|
+
ValueError: If no root dataset is found or dataset_id doesn't exist.
|
|
32
|
+
"""
|
|
33
|
+
if dataset_id is not None:
|
|
34
|
+
# Find the dataset.
|
|
35
|
+
dataset = session.get(DatasetTable, dataset_id)
|
|
36
|
+
if dataset is None:
|
|
37
|
+
raise ValueError(f"Dataset with ID {dataset_id} not found.")
|
|
38
|
+
|
|
39
|
+
# Traverse up the hierarchy until we find the root.
|
|
40
|
+
# TODO (Mihnea, 12/2025): Consider replacing the loop with a recursive CTE,
|
|
41
|
+
# if this becomes a bottleneck.
|
|
42
|
+
while dataset.parent_dataset_id is not None:
|
|
43
|
+
parent = session.get(DatasetTable, dataset.parent_dataset_id)
|
|
44
|
+
if parent is None:
|
|
45
|
+
raise ValueError(
|
|
46
|
+
f"Parent dataset {dataset.parent_dataset_id} not found "
|
|
47
|
+
f"for dataset {dataset.dataset_id}."
|
|
48
|
+
)
|
|
49
|
+
dataset = parent
|
|
50
|
+
|
|
51
|
+
return dataset
|
|
52
|
+
|
|
53
|
+
# Backwards compatibility: return first root dataset
|
|
54
|
+
root_datasets = session.exec(
|
|
55
|
+
select(DatasetTable).where(col(DatasetTable.parent_dataset_id).is_(None))
|
|
56
|
+
).all()
|
|
57
|
+
|
|
58
|
+
if len(root_datasets) == 0:
|
|
59
|
+
raise ValueError("No root dataset found. A root dataset must exist.")
|
|
60
|
+
|
|
61
|
+
return root_datasets[0]
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Handler for database operations related to fetching root datasets with details."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from sqlmodel import Session, col, func, select
|
|
6
|
+
|
|
7
|
+
from lightly_studio.models.dataset import DatasetOverviewView, DatasetTable
|
|
8
|
+
from lightly_studio.models.sample import SampleTable
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_root_datasets_overview(session: Session) -> list[DatasetOverviewView]:
|
|
12
|
+
"""Get root datasets with detailed metadata including sample counts."""
|
|
13
|
+
datasets_query = (
|
|
14
|
+
select( # type: ignore[call-overload]
|
|
15
|
+
DatasetTable.dataset_id,
|
|
16
|
+
DatasetTable.name,
|
|
17
|
+
DatasetTable.sample_type,
|
|
18
|
+
DatasetTable.created_at,
|
|
19
|
+
func.count(col(SampleTable.dataset_id)).label("sample_count"),
|
|
20
|
+
)
|
|
21
|
+
.outerjoin(SampleTable)
|
|
22
|
+
.where(col(DatasetTable.parent_dataset_id).is_(None))
|
|
23
|
+
.group_by(
|
|
24
|
+
DatasetTable.dataset_id,
|
|
25
|
+
DatasetTable.name,
|
|
26
|
+
DatasetTable.sample_type,
|
|
27
|
+
DatasetTable.created_at,
|
|
28
|
+
)
|
|
29
|
+
.order_by(col(DatasetTable.created_at).desc())
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
return [
|
|
33
|
+
DatasetOverviewView(
|
|
34
|
+
dataset_id=row.dataset_id,
|
|
35
|
+
name=row.name,
|
|
36
|
+
sample_type=row.sample_type,
|
|
37
|
+
created_at=row.created_at,
|
|
38
|
+
total_sample_count=row.sample_count,
|
|
39
|
+
)
|
|
40
|
+
for row in session.exec(datasets_query).all()
|
|
41
|
+
]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Implementation of update dataset resolver function."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import datetime, timezone
|
|
6
|
+
from uuid import UUID
|
|
7
|
+
|
|
8
|
+
from sqlmodel import Session
|
|
9
|
+
|
|
10
|
+
from lightly_studio.models.dataset import DatasetCreate, DatasetTable
|
|
11
|
+
from lightly_studio.resolvers.dataset_resolver.get_by_id import get_by_id
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def update(session: Session, dataset_id: UUID, dataset_data: DatasetCreate) -> DatasetTable:
|
|
15
|
+
"""Update an existing dataset."""
|
|
16
|
+
dataset = get_by_id(session=session, dataset_id=dataset_id)
|
|
17
|
+
if not dataset:
|
|
18
|
+
raise ValueError(f"Dataset ID was not found '{dataset_id}'.")
|
|
19
|
+
|
|
20
|
+
dataset.name = dataset_data.name
|
|
21
|
+
dataset.updated_at = datetime.now(timezone.utc)
|
|
22
|
+
|
|
23
|
+
session.commit()
|
|
24
|
+
session.refresh(dataset)
|
|
25
|
+
return dataset
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""Handler for database operations related to embedding models."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
|
|
7
|
+
from sqlmodel import Session, col, select
|
|
8
|
+
|
|
9
|
+
from lightly_studio.models.embedding_model import (
|
|
10
|
+
EmbeddingModelCreate,
|
|
11
|
+
EmbeddingModelTable,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def create(session: Session, embedding_model: EmbeddingModelCreate) -> EmbeddingModelTable:
|
|
16
|
+
"""Create a new EmbeddingModel in the database."""
|
|
17
|
+
db_embedding_model = EmbeddingModelTable.model_validate(embedding_model)
|
|
18
|
+
session.add(db_embedding_model)
|
|
19
|
+
session.commit()
|
|
20
|
+
session.refresh(db_embedding_model)
|
|
21
|
+
return db_embedding_model
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def get_or_create(session: Session, embedding_model: EmbeddingModelCreate) -> EmbeddingModelTable:
|
|
25
|
+
"""Retrieve an existing EmbeddingModel by hash or create a new one if it does not exist."""
|
|
26
|
+
db_model = get_by_model_hash(
|
|
27
|
+
session=session, embedding_model_hash=embedding_model.embedding_model_hash
|
|
28
|
+
)
|
|
29
|
+
if db_model is None:
|
|
30
|
+
return create(session=session, embedding_model=embedding_model)
|
|
31
|
+
|
|
32
|
+
# Validate that the existing model matches the provided data.
|
|
33
|
+
if (
|
|
34
|
+
db_model.name != embedding_model.name
|
|
35
|
+
or db_model.parameter_count_in_mb != embedding_model.parameter_count_in_mb
|
|
36
|
+
or db_model.embedding_dimension != embedding_model.embedding_dimension
|
|
37
|
+
):
|
|
38
|
+
raise ValueError(
|
|
39
|
+
"An embedding model with the same hash but different parameters already exists."
|
|
40
|
+
)
|
|
41
|
+
return db_model
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_all_by_dataset_id(session: Session, dataset_id: UUID) -> list[EmbeddingModelTable]:
|
|
45
|
+
"""Retrieve all embedding models."""
|
|
46
|
+
embedding_models = session.exec(
|
|
47
|
+
select(EmbeddingModelTable)
|
|
48
|
+
.where(EmbeddingModelTable.dataset_id == dataset_id)
|
|
49
|
+
.order_by(col(EmbeddingModelTable.created_at).asc())
|
|
50
|
+
).all()
|
|
51
|
+
return list(embedding_models)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def get_by_id(session: Session, embedding_model_id: UUID) -> EmbeddingModelTable | None:
|
|
55
|
+
"""Retrieve a single embedding model by ID."""
|
|
56
|
+
return session.exec(
|
|
57
|
+
select(EmbeddingModelTable).where(
|
|
58
|
+
EmbeddingModelTable.embedding_model_id == embedding_model_id
|
|
59
|
+
)
|
|
60
|
+
).one_or_none()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def get_by_model_hash(session: Session, embedding_model_hash: str) -> EmbeddingModelTable | None:
|
|
64
|
+
"""Retrieve a single embedding model by hash."""
|
|
65
|
+
return session.exec(
|
|
66
|
+
select(EmbeddingModelTable).where(
|
|
67
|
+
EmbeddingModelTable.embedding_model_hash == embedding_model_hash
|
|
68
|
+
)
|
|
69
|
+
).one_or_none()
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def get_by_name(
|
|
73
|
+
session: Session, dataset_id: UUID, embedding_model_name: str | None
|
|
74
|
+
) -> EmbeddingModelTable:
|
|
75
|
+
"""Helper function to resolve the embedding model name to its ID.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
session: The database session.
|
|
79
|
+
dataset_id: The ID of the dataset.
|
|
80
|
+
embedding_model_name: The name of the embedding model.
|
|
81
|
+
If None, expects the dataset to have exactly one embedding model and
|
|
82
|
+
returns it. Otherwise raises a ValueError.
|
|
83
|
+
If set, expects the dataset to have an embedding model with the given name.
|
|
84
|
+
Otherwise raises a ValueError.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
The embedding model with the given name.
|
|
88
|
+
"""
|
|
89
|
+
embedding_models = get_all_by_dataset_id(
|
|
90
|
+
session=session,
|
|
91
|
+
dataset_id=dataset_id,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
if embedding_model_name is None:
|
|
95
|
+
if len(embedding_models) != 1:
|
|
96
|
+
raise ValueError(
|
|
97
|
+
f"Expected exactly one embedding model, "
|
|
98
|
+
f"but found {len(embedding_models)} with names "
|
|
99
|
+
f"{[model.name for model in embedding_models]}."
|
|
100
|
+
)
|
|
101
|
+
return embedding_models[0]
|
|
102
|
+
|
|
103
|
+
embedding_model_with_name = next(
|
|
104
|
+
(model for model in embedding_models if model.name == embedding_model_name), None
|
|
105
|
+
)
|
|
106
|
+
if embedding_model_with_name is None:
|
|
107
|
+
raise ValueError(f"Embedding model with name `{embedding_model_name}` not found.")
|
|
108
|
+
|
|
109
|
+
return embedding_model_with_name
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def delete(session: Session, embedding_model_id: UUID) -> bool:
|
|
113
|
+
"""Delete an embedding model."""
|
|
114
|
+
embedding_model = get_by_id(session=session, embedding_model_id=embedding_model_id)
|
|
115
|
+
if not embedding_model:
|
|
116
|
+
return False
|
|
117
|
+
|
|
118
|
+
session.delete(embedding_model)
|
|
119
|
+
session.commit()
|
|
120
|
+
return True
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Utility functions for building database queries."""
|
|
2
|
+
# TODO(Michal, 11/2025): Move to image_resolver once DatasetTable.get_samples() is removed.
|
|
3
|
+
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel
|
|
7
|
+
|
|
8
|
+
from lightly_studio.models.image import ImageTable
|
|
9
|
+
from lightly_studio.resolvers.sample_resolver.sample_filter import SampleFilter
|
|
10
|
+
from lightly_studio.type_definitions import QueryType
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class FilterDimensions(BaseModel):
|
|
14
|
+
"""Encapsulates dimension-based filter parameters for querying samples."""
|
|
15
|
+
|
|
16
|
+
min: Optional[int] = None
|
|
17
|
+
max: Optional[int] = None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ImageFilter(BaseModel):
|
|
21
|
+
"""Encapsulates filter parameters for querying samples."""
|
|
22
|
+
|
|
23
|
+
sample_filter: Optional[SampleFilter] = None
|
|
24
|
+
width: Optional[FilterDimensions] = None
|
|
25
|
+
height: Optional[FilterDimensions] = None
|
|
26
|
+
|
|
27
|
+
def apply(self, query: QueryType) -> QueryType:
|
|
28
|
+
"""Apply the filters to the given query."""
|
|
29
|
+
# Apply sample filters to the query.
|
|
30
|
+
if self.sample_filter is not None:
|
|
31
|
+
query = self.sample_filter.apply(query)
|
|
32
|
+
|
|
33
|
+
# Apply dimension-based filters to the query.
|
|
34
|
+
query = self._apply_dimension_filters(query)
|
|
35
|
+
|
|
36
|
+
# Return the modified query.
|
|
37
|
+
return query # noqa: RET504
|
|
38
|
+
|
|
39
|
+
def _apply_dimension_filters(self, query: QueryType) -> QueryType:
|
|
40
|
+
if self.width:
|
|
41
|
+
if self.width.min is not None:
|
|
42
|
+
query = query.where(ImageTable.width >= self.width.min)
|
|
43
|
+
if self.width.max is not None:
|
|
44
|
+
query = query.where(ImageTable.width <= self.width.max)
|
|
45
|
+
if self.height:
|
|
46
|
+
if self.height.min is not None:
|
|
47
|
+
query = query.where(ImageTable.height >= self.height.min)
|
|
48
|
+
if self.height.max is not None:
|
|
49
|
+
query = query.where(ImageTable.height <= self.height.max)
|
|
50
|
+
return query
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Resolvers for database operations."""
|
|
2
|
+
|
|
3
|
+
from lightly_studio.resolvers.image_resolver.create_many import create_many
|
|
4
|
+
from lightly_studio.resolvers.image_resolver.delete import delete
|
|
5
|
+
from lightly_studio.resolvers.image_resolver.filter_new_paths import filter_new_paths
|
|
6
|
+
from lightly_studio.resolvers.image_resolver.get_all_by_dataset_id import get_all_by_dataset_id
|
|
7
|
+
from lightly_studio.resolvers.image_resolver.get_by_id import get_by_id
|
|
8
|
+
from lightly_studio.resolvers.image_resolver.get_dimension_bounds import get_dimension_bounds
|
|
9
|
+
from lightly_studio.resolvers.image_resolver.get_many_by_id import get_many_by_id
|
|
10
|
+
from lightly_studio.resolvers.image_resolver.get_samples_excluding import get_samples_excluding
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"create_many",
|
|
14
|
+
"delete",
|
|
15
|
+
"filter_new_paths",
|
|
16
|
+
"get_all_by_dataset_id",
|
|
17
|
+
"get_by_id",
|
|
18
|
+
"get_dimension_bounds",
|
|
19
|
+
"get_many_by_id",
|
|
20
|
+
"get_samples_excluding",
|
|
21
|
+
]
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Implementation of create functions for images."""
|
|
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.image import ImageCreate, ImageTable
|
|
11
|
+
from lightly_studio.models.sample import SampleCreate
|
|
12
|
+
from lightly_studio.resolvers import dataset_resolver, sample_resolver
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ImageCreateHelper(ImageCreate):
|
|
16
|
+
"""Helper class to create ImageTable with sample_id."""
|
|
17
|
+
|
|
18
|
+
sample_id: UUID
|
|
19
|
+
dataset_id: UUID
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def create_many(session: Session, dataset_id: UUID, samples: list[ImageCreate]) -> list[UUID]:
|
|
23
|
+
"""Create multiple samples in a single database commit.
|
|
24
|
+
|
|
25
|
+
Returns the list of created sample IDs that matches the order of input samples.
|
|
26
|
+
"""
|
|
27
|
+
dataset_resolver.check_dataset_type(
|
|
28
|
+
session=session,
|
|
29
|
+
dataset_id=dataset_id,
|
|
30
|
+
expected_type=SampleType.IMAGE,
|
|
31
|
+
)
|
|
32
|
+
sample_ids = sample_resolver.create_many(
|
|
33
|
+
session=session,
|
|
34
|
+
samples=[SampleCreate(dataset_id=dataset_id) for _ in samples],
|
|
35
|
+
)
|
|
36
|
+
# Bulk create ImageTable entries using the generated sample_ids.
|
|
37
|
+
db_images = [
|
|
38
|
+
ImageTable.model_validate(
|
|
39
|
+
ImageCreateHelper(
|
|
40
|
+
file_name=sample.file_name,
|
|
41
|
+
width=sample.width,
|
|
42
|
+
height=sample.height,
|
|
43
|
+
dataset_id=dataset_id,
|
|
44
|
+
file_path_abs=sample.file_path_abs,
|
|
45
|
+
sample_id=sample_id,
|
|
46
|
+
)
|
|
47
|
+
)
|
|
48
|
+
for sample_id, sample in zip(sample_ids, samples)
|
|
49
|
+
]
|
|
50
|
+
session.bulk_save_objects(db_images)
|
|
51
|
+
session.commit()
|
|
52
|
+
return sample_ids
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Implementation of delete function for images."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
|
|
7
|
+
from sqlmodel import Session
|
|
8
|
+
|
|
9
|
+
from lightly_studio.resolvers import image_resolver
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def delete(session: Session, sample_id: UUID) -> bool:
|
|
13
|
+
"""Delete a sample."""
|
|
14
|
+
sample = image_resolver.get_by_id(session=session, sample_id=sample_id)
|
|
15
|
+
if not sample:
|
|
16
|
+
return False
|
|
17
|
+
|
|
18
|
+
session.delete(sample)
|
|
19
|
+
session.commit()
|
|
20
|
+
return True
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Implementation of filter_new_paths function for images."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from sqlmodel import Session, col, select
|
|
6
|
+
|
|
7
|
+
from lightly_studio.models.image import ImageTable
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def filter_new_paths(session: Session, file_paths_abs: list[str]) -> tuple[list[str], list[str]]:
|
|
11
|
+
"""Return a) file_path_abs that do not already exist in the database and b) those that do."""
|
|
12
|
+
existing_file_paths_abs = set(
|
|
13
|
+
session.exec(
|
|
14
|
+
select(col(ImageTable.file_path_abs)).where(
|
|
15
|
+
col(ImageTable.file_path_abs).in_(file_paths_abs)
|
|
16
|
+
)
|
|
17
|
+
).all()
|
|
18
|
+
)
|
|
19
|
+
file_paths_abs_set = set(file_paths_abs)
|
|
20
|
+
return (
|
|
21
|
+
list(file_paths_abs_set - existing_file_paths_abs), # paths that are not in the DB
|
|
22
|
+
list(file_paths_abs_set & existing_file_paths_abs), # paths that are already in the DB
|
|
23
|
+
)
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""Implementation of get_all_by_dataset_id function for images."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Sequence
|
|
6
|
+
from uuid import UUID
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel
|
|
9
|
+
from sqlalchemy.orm import joinedload, selectinload
|
|
10
|
+
from sqlmodel import Session, col, func, select
|
|
11
|
+
|
|
12
|
+
from lightly_studio.api.routes.api.validators import Paginated
|
|
13
|
+
from lightly_studio.models.annotation.annotation_base import AnnotationBaseTable
|
|
14
|
+
from lightly_studio.models.embedding_model import EmbeddingModelTable
|
|
15
|
+
from lightly_studio.models.image import ImageTable
|
|
16
|
+
from lightly_studio.models.sample import SampleTable
|
|
17
|
+
from lightly_studio.models.sample_embedding import SampleEmbeddingTable
|
|
18
|
+
from lightly_studio.resolvers.image_filter import ImageFilter
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class GetAllSamplesByDatasetIdResult(BaseModel):
|
|
22
|
+
"""Result of getting all samples."""
|
|
23
|
+
|
|
24
|
+
samples: Sequence[ImageTable]
|
|
25
|
+
total_count: int
|
|
26
|
+
next_cursor: int | None = None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_all_by_dataset_id( # noqa: PLR0913
|
|
30
|
+
session: Session,
|
|
31
|
+
dataset_id: UUID,
|
|
32
|
+
pagination: Paginated | None = None,
|
|
33
|
+
filters: ImageFilter | None = None,
|
|
34
|
+
text_embedding: list[float] | None = None,
|
|
35
|
+
sample_ids: list[UUID] | None = None,
|
|
36
|
+
) -> GetAllSamplesByDatasetIdResult:
|
|
37
|
+
"""Retrieve samples for a specific dataset with optional filtering."""
|
|
38
|
+
samples_query = (
|
|
39
|
+
select(ImageTable)
|
|
40
|
+
.options(
|
|
41
|
+
selectinload(ImageTable.sample).options(
|
|
42
|
+
joinedload(SampleTable.tags),
|
|
43
|
+
# Ignore type checker error below as it's a false positive caused by TYPE_CHECKING.
|
|
44
|
+
joinedload(SampleTable.metadata_dict), # type: ignore[arg-type]
|
|
45
|
+
selectinload(SampleTable.captions),
|
|
46
|
+
selectinload(SampleTable.annotations).options(
|
|
47
|
+
joinedload(AnnotationBaseTable.annotation_label),
|
|
48
|
+
joinedload(AnnotationBaseTable.object_detection_details),
|
|
49
|
+
joinedload(AnnotationBaseTable.instance_segmentation_details),
|
|
50
|
+
joinedload(AnnotationBaseTable.semantic_segmentation_details),
|
|
51
|
+
selectinload(AnnotationBaseTable.tags),
|
|
52
|
+
),
|
|
53
|
+
),
|
|
54
|
+
)
|
|
55
|
+
.join(ImageTable.sample)
|
|
56
|
+
.where(SampleTable.dataset_id == dataset_id)
|
|
57
|
+
)
|
|
58
|
+
total_count_query = (
|
|
59
|
+
select(func.count())
|
|
60
|
+
.select_from(ImageTable)
|
|
61
|
+
.join(ImageTable.sample)
|
|
62
|
+
.where(SampleTable.dataset_id == dataset_id)
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
if filters:
|
|
66
|
+
samples_query = filters.apply(samples_query)
|
|
67
|
+
total_count_query = filters.apply(total_count_query)
|
|
68
|
+
|
|
69
|
+
# TODO(Michal, 06/2025): Consider adding sample_ids to the filters.
|
|
70
|
+
if sample_ids:
|
|
71
|
+
samples_query = samples_query.where(col(ImageTable.sample_id).in_(sample_ids))
|
|
72
|
+
total_count_query = total_count_query.where(col(ImageTable.sample_id).in_(sample_ids))
|
|
73
|
+
|
|
74
|
+
if text_embedding:
|
|
75
|
+
# Fetch the first embedding_model_id for the given dataset_id
|
|
76
|
+
embedding_model_id = session.exec(
|
|
77
|
+
select(EmbeddingModelTable.embedding_model_id)
|
|
78
|
+
.where(EmbeddingModelTable.dataset_id == dataset_id)
|
|
79
|
+
.limit(1)
|
|
80
|
+
).first()
|
|
81
|
+
if embedding_model_id:
|
|
82
|
+
# Join with SampleEmbedding table to access embeddings
|
|
83
|
+
samples_query = (
|
|
84
|
+
samples_query.join(
|
|
85
|
+
SampleEmbeddingTable,
|
|
86
|
+
col(ImageTable.sample_id) == col(SampleEmbeddingTable.sample_id),
|
|
87
|
+
)
|
|
88
|
+
.where(SampleEmbeddingTable.embedding_model_id == embedding_model_id)
|
|
89
|
+
.order_by(
|
|
90
|
+
func.list_cosine_distance(
|
|
91
|
+
SampleEmbeddingTable.embedding,
|
|
92
|
+
text_embedding,
|
|
93
|
+
)
|
|
94
|
+
)
|
|
95
|
+
)
|
|
96
|
+
total_count_query = total_count_query.join(
|
|
97
|
+
SampleEmbeddingTable,
|
|
98
|
+
col(ImageTable.sample_id) == col(SampleEmbeddingTable.sample_id),
|
|
99
|
+
).where(SampleEmbeddingTable.embedding_model_id == embedding_model_id)
|
|
100
|
+
else:
|
|
101
|
+
samples_query = samples_query.order_by(col(ImageTable.file_path_abs).asc())
|
|
102
|
+
|
|
103
|
+
# Apply pagination if provided
|
|
104
|
+
if pagination is not None:
|
|
105
|
+
samples_query = samples_query.offset(pagination.offset).limit(pagination.limit)
|
|
106
|
+
|
|
107
|
+
total_count = session.exec(total_count_query).one()
|
|
108
|
+
|
|
109
|
+
next_cursor = None
|
|
110
|
+
if pagination and pagination.offset + pagination.limit < total_count:
|
|
111
|
+
next_cursor = pagination.offset + pagination.limit
|
|
112
|
+
|
|
113
|
+
return GetAllSamplesByDatasetIdResult(
|
|
114
|
+
samples=session.exec(samples_query).all(),
|
|
115
|
+
total_count=total_count,
|
|
116
|
+
next_cursor=next_cursor,
|
|
117
|
+
)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Implementation of get_by_id function for images."""
|
|
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.image import ImageTable
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_by_id(session: Session, sample_id: UUID) -> ImageTable | None:
|
|
13
|
+
"""Retrieve a single sample by ID."""
|
|
14
|
+
return session.exec(select(ImageTable).where(ImageTable.sample_id == sample_id)).one_or_none()
|