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,129 @@
|
|
|
1
|
+
"""Resolvers for caption."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Sequence
|
|
6
|
+
from uuid import UUID
|
|
7
|
+
|
|
8
|
+
from sqlmodel import Session, col, select
|
|
9
|
+
|
|
10
|
+
from lightly_studio.models.caption import CaptionCreate, CaptionTable
|
|
11
|
+
from lightly_studio.models.dataset import SampleType
|
|
12
|
+
from lightly_studio.models.sample import SampleCreate
|
|
13
|
+
from lightly_studio.resolvers import dataset_resolver, sample_resolver
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class CaptionCreateHelper(CaptionCreate):
|
|
17
|
+
"""Helper class to create CaptionTable with sample_id."""
|
|
18
|
+
|
|
19
|
+
sample_id: UUID
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def create_many(
|
|
23
|
+
session: Session, parent_dataset_id: UUID, captions: Sequence[CaptionCreate]
|
|
24
|
+
) -> list[UUID]:
|
|
25
|
+
"""Create captions for a single dataset in bulk.
|
|
26
|
+
|
|
27
|
+
It is responsibility of the caller to ensure that all parent samples belong to the same
|
|
28
|
+
dataset with ID `parent_dataset_id`. This function does not perform this check for performance
|
|
29
|
+
reasons.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
session: Database session
|
|
33
|
+
parent_dataset_id: UUID of the parent dataset of which the caption dataset is a child
|
|
34
|
+
captions: The captions to create
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
List of created CaptionTable sample_ids
|
|
38
|
+
"""
|
|
39
|
+
if not captions:
|
|
40
|
+
return []
|
|
41
|
+
|
|
42
|
+
caption_dataset_id = dataset_resolver.get_or_create_child_dataset(
|
|
43
|
+
session=session, dataset_id=parent_dataset_id, sample_type=SampleType.CAPTION
|
|
44
|
+
)
|
|
45
|
+
sample_ids = sample_resolver.create_many(
|
|
46
|
+
session=session,
|
|
47
|
+
samples=[SampleCreate(dataset_id=caption_dataset_id) for _ in captions],
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Bulk create CaptionTable entries using the generated sample_ids.
|
|
51
|
+
db_captions = [
|
|
52
|
+
CaptionTable.model_validate(
|
|
53
|
+
CaptionCreateHelper(
|
|
54
|
+
parent_sample_id=sample.parent_sample_id,
|
|
55
|
+
text=sample.text,
|
|
56
|
+
sample_id=sample_id,
|
|
57
|
+
)
|
|
58
|
+
)
|
|
59
|
+
for sample_id, sample in zip(sample_ids, captions)
|
|
60
|
+
]
|
|
61
|
+
session.bulk_save_objects(db_captions)
|
|
62
|
+
session.commit()
|
|
63
|
+
return sample_ids
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def get_by_ids(session: Session, sample_ids: Sequence[UUID]) -> list[CaptionTable]:
|
|
67
|
+
"""Retrieve captions by IDs."""
|
|
68
|
+
results = session.exec(
|
|
69
|
+
select(CaptionTable).where(col(CaptionTable.sample_id).in_(set(sample_ids)))
|
|
70
|
+
).all()
|
|
71
|
+
# Return samples in the same order as the input IDs
|
|
72
|
+
caption_map = {caption.sample_id: caption for caption in results}
|
|
73
|
+
return [caption_map[id_] for id_ in sample_ids if id_ in caption_map]
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def update_text(
|
|
77
|
+
session: Session,
|
|
78
|
+
sample_id: UUID,
|
|
79
|
+
text: str,
|
|
80
|
+
) -> CaptionTable:
|
|
81
|
+
"""Update the text of a caption.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
session: Database session for executing the operation.
|
|
85
|
+
sample_id: UUID of the caption to update.
|
|
86
|
+
text: New text.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
The updated caption with the new text.
|
|
90
|
+
|
|
91
|
+
Raises:
|
|
92
|
+
ValueError: If the caption is not found.
|
|
93
|
+
"""
|
|
94
|
+
captions = get_by_ids(session, [sample_id])
|
|
95
|
+
if not captions:
|
|
96
|
+
raise ValueError(f"Caption with ID {sample_id} not found.")
|
|
97
|
+
|
|
98
|
+
caption = captions[0]
|
|
99
|
+
try:
|
|
100
|
+
caption.text = text
|
|
101
|
+
session.commit()
|
|
102
|
+
session.refresh(caption)
|
|
103
|
+
return caption
|
|
104
|
+
except Exception:
|
|
105
|
+
session.rollback()
|
|
106
|
+
raise
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def delete_caption(
|
|
110
|
+
session: Session,
|
|
111
|
+
sample_id: UUID,
|
|
112
|
+
) -> None:
|
|
113
|
+
"""Delete a caption.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
session: Database session for executing the operation.
|
|
117
|
+
sample_id: UUID of the caption to update.
|
|
118
|
+
|
|
119
|
+
Raises:
|
|
120
|
+
ValueError: If the caption is not found.
|
|
121
|
+
"""
|
|
122
|
+
captions = get_by_ids(session=session, sample_ids=[sample_id])
|
|
123
|
+
if len(captions) == 0:
|
|
124
|
+
raise ValueError(f"Caption with ID {sample_id} not found.")
|
|
125
|
+
|
|
126
|
+
caption = captions[0]
|
|
127
|
+
session.commit()
|
|
128
|
+
session.delete(caption)
|
|
129
|
+
session.commit()
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Resolvers for database operations."""
|
|
2
|
+
|
|
3
|
+
from lightly_studio.resolvers.dataset_resolver.check_dataset_type import (
|
|
4
|
+
check_dataset_type,
|
|
5
|
+
)
|
|
6
|
+
from lightly_studio.resolvers.dataset_resolver.create import create
|
|
7
|
+
from lightly_studio.resolvers.dataset_resolver.delete import delete
|
|
8
|
+
from lightly_studio.resolvers.dataset_resolver.export import (
|
|
9
|
+
export,
|
|
10
|
+
get_filtered_samples_count,
|
|
11
|
+
)
|
|
12
|
+
from lightly_studio.resolvers.dataset_resolver.get_all import get_all
|
|
13
|
+
from lightly_studio.resolvers.dataset_resolver.get_by_id import get_by_id
|
|
14
|
+
from lightly_studio.resolvers.dataset_resolver.get_by_name import get_by_name
|
|
15
|
+
from lightly_studio.resolvers.dataset_resolver.get_dataset_details import (
|
|
16
|
+
get_dataset_details,
|
|
17
|
+
)
|
|
18
|
+
from lightly_studio.resolvers.dataset_resolver.get_hierarchy import (
|
|
19
|
+
get_hierarchy,
|
|
20
|
+
)
|
|
21
|
+
from lightly_studio.resolvers.dataset_resolver.get_or_create_child_dataset import (
|
|
22
|
+
get_or_create_child_dataset,
|
|
23
|
+
)
|
|
24
|
+
from lightly_studio.resolvers.dataset_resolver.get_parent_dataset_by_sample_id import (
|
|
25
|
+
get_parent_dataset_by_sample_id,
|
|
26
|
+
)
|
|
27
|
+
from lightly_studio.resolvers.dataset_resolver.get_parent_dataset_id import (
|
|
28
|
+
get_parent_dataset_id,
|
|
29
|
+
)
|
|
30
|
+
from lightly_studio.resolvers.dataset_resolver.get_root_dataset import (
|
|
31
|
+
get_root_dataset,
|
|
32
|
+
)
|
|
33
|
+
from lightly_studio.resolvers.dataset_resolver.get_root_datasets_overview import (
|
|
34
|
+
get_root_datasets_overview,
|
|
35
|
+
)
|
|
36
|
+
from lightly_studio.resolvers.dataset_resolver.update import update
|
|
37
|
+
|
|
38
|
+
__all__ = [
|
|
39
|
+
"check_dataset_type",
|
|
40
|
+
"create",
|
|
41
|
+
"delete",
|
|
42
|
+
"export",
|
|
43
|
+
"get_all",
|
|
44
|
+
"get_by_id",
|
|
45
|
+
"get_by_name",
|
|
46
|
+
"get_dataset_details",
|
|
47
|
+
"get_filtered_samples_count",
|
|
48
|
+
"get_hierarchy",
|
|
49
|
+
"get_or_create_child_dataset",
|
|
50
|
+
"get_parent_dataset_by_sample_id",
|
|
51
|
+
"get_parent_dataset_id",
|
|
52
|
+
"get_root_dataset",
|
|
53
|
+
"get_root_datasets_overview",
|
|
54
|
+
"update",
|
|
55
|
+
]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Implementation of check_dataset_type resolver function."""
|
|
2
|
+
|
|
3
|
+
from uuid import UUID
|
|
4
|
+
|
|
5
|
+
from sqlmodel import Session
|
|
6
|
+
|
|
7
|
+
from lightly_studio.models.dataset import SampleType
|
|
8
|
+
from lightly_studio.resolvers import dataset_resolver
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def check_dataset_type(session: Session, dataset_id: UUID, expected_type: SampleType) -> None:
|
|
12
|
+
"""Check that the dataset has the expected sample type.
|
|
13
|
+
|
|
14
|
+
Raises a ValueError if the dataset does not have the expected sample type or
|
|
15
|
+
if it does not exist.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
session: The database session.
|
|
19
|
+
dataset_id: The ID of the dataset to check.
|
|
20
|
+
expected_type: The expected sample type.
|
|
21
|
+
"""
|
|
22
|
+
dataset = dataset_resolver.get_by_id(session=session, dataset_id=dataset_id)
|
|
23
|
+
if dataset is None:
|
|
24
|
+
raise ValueError(f"Dataset with id {dataset_id} not found.")
|
|
25
|
+
if dataset.sample_type != expected_type:
|
|
26
|
+
raise ValueError(
|
|
27
|
+
f"Dataset with id {dataset_id} is having sample type "
|
|
28
|
+
f"'{dataset.sample_type.value}', expected '{expected_type.value}'."
|
|
29
|
+
)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Implementation of create dataset resolver function."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from sqlmodel import Session
|
|
6
|
+
|
|
7
|
+
from lightly_studio.models.dataset import DatasetCreate, DatasetTable
|
|
8
|
+
from lightly_studio.resolvers import dataset_resolver
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def create(session: Session, dataset: DatasetCreate) -> DatasetTable:
|
|
12
|
+
"""Create a new dataset in the database."""
|
|
13
|
+
existing = dataset_resolver.get_by_name(session=session, name=dataset.name)
|
|
14
|
+
if existing:
|
|
15
|
+
raise ValueError(f"Dataset with name '{dataset.name}' already exists.")
|
|
16
|
+
db_dataset = DatasetTable.model_validate(dataset)
|
|
17
|
+
session.add(db_dataset)
|
|
18
|
+
session.commit()
|
|
19
|
+
session.refresh(db_dataset)
|
|
20
|
+
return db_dataset
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Implementation of delete dataset resolver function."""
|
|
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 dataset_resolver
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def delete(session: Session, dataset_id: UUID) -> bool:
|
|
13
|
+
"""Delete a dataset."""
|
|
14
|
+
dataset = dataset_resolver.get_by_id(session=session, dataset_id=dataset_id)
|
|
15
|
+
if not dataset:
|
|
16
|
+
return False
|
|
17
|
+
|
|
18
|
+
session.delete(dataset)
|
|
19
|
+
session.commit()
|
|
20
|
+
return True
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
"""Resolver functions for exporting dataset samples based on filters."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, Field, model_validator
|
|
8
|
+
from sqlmodel import Session, and_, col, func, or_, select
|
|
9
|
+
from sqlmodel.sql.expression import SelectOfScalar
|
|
10
|
+
|
|
11
|
+
from lightly_studio.models.annotation.annotation_base import AnnotationBaseTable
|
|
12
|
+
from lightly_studio.models.dataset import SampleType
|
|
13
|
+
from lightly_studio.models.image import ImageTable
|
|
14
|
+
from lightly_studio.models.sample import SampleTable
|
|
15
|
+
from lightly_studio.models.tag import TagTable
|
|
16
|
+
from lightly_studio.resolvers.dataset_resolver.get_hierarchy import get_hierarchy
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ExportFilter(BaseModel):
|
|
20
|
+
"""Export Filter to be used for including or excluding."""
|
|
21
|
+
|
|
22
|
+
tag_ids: list[UUID] | None = Field(default=None, min_length=1, description="List of tag UUIDs")
|
|
23
|
+
sample_ids: list[UUID] | None = Field(
|
|
24
|
+
default=None, min_length=1, description="List of sample UUIDs"
|
|
25
|
+
)
|
|
26
|
+
annotation_ids: list[UUID] | None = Field(
|
|
27
|
+
default=None, min_length=1, description="List of annotation UUIDs"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
@model_validator(mode="after")
|
|
31
|
+
def check_exactly_one(self) -> ExportFilter: # noqa: N804
|
|
32
|
+
"""Ensure that exactly one of the fields is set."""
|
|
33
|
+
count = (
|
|
34
|
+
(self.tag_ids is not None)
|
|
35
|
+
+ (self.sample_ids is not None)
|
|
36
|
+
+ (self.annotation_ids is not None)
|
|
37
|
+
)
|
|
38
|
+
if count != 1:
|
|
39
|
+
raise ValueError("Either tag_ids, sample_ids, or annotation_ids must be set.")
|
|
40
|
+
return self
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# TODO(Michal, 10/2025): Consider moving the export logic to a separate service.
|
|
44
|
+
# This is a legacy code from the initial implementation of the export feature.
|
|
45
|
+
def export(
|
|
46
|
+
session: Session,
|
|
47
|
+
dataset_id: UUID,
|
|
48
|
+
include: ExportFilter | None = None,
|
|
49
|
+
exclude: ExportFilter | None = None,
|
|
50
|
+
) -> list[str]:
|
|
51
|
+
"""Retrieve samples for exporting from a dataset.
|
|
52
|
+
|
|
53
|
+
Only one of include or exclude should be set and not both.
|
|
54
|
+
Furthermore, the include and exclude filter can only have
|
|
55
|
+
one type (tag_ids, sample_ids or annotations_ids) set.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
session: SQLAlchemy session.
|
|
59
|
+
dataset_id: UUID of the dataset.
|
|
60
|
+
include: Filter to include samples.
|
|
61
|
+
exclude: Filter to exclude samples.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
List of file paths
|
|
65
|
+
"""
|
|
66
|
+
# Get all child dataset IDs that could contain annotations
|
|
67
|
+
annotation_dataset_ids = _get_annotation_dataset_ids(session, dataset_id)
|
|
68
|
+
query = _build_export_query(
|
|
69
|
+
dataset_id=dataset_id,
|
|
70
|
+
annotation_dataset_ids=annotation_dataset_ids,
|
|
71
|
+
include=include,
|
|
72
|
+
exclude=exclude,
|
|
73
|
+
)
|
|
74
|
+
result = session.exec(query).all()
|
|
75
|
+
return [sample.file_path_abs for sample in result]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def get_filtered_samples_count(
|
|
79
|
+
session: Session,
|
|
80
|
+
dataset_id: UUID,
|
|
81
|
+
include: ExportFilter | None = None,
|
|
82
|
+
exclude: ExportFilter | None = None,
|
|
83
|
+
) -> int:
|
|
84
|
+
"""Get statistics about the export query.
|
|
85
|
+
|
|
86
|
+
Only one of include or exclude should be set and not both.
|
|
87
|
+
Furthermore, the include and exclude filter can only have
|
|
88
|
+
one type (tag_ids, sample_ids or annotations_ids) set.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
session: SQLAlchemy session.
|
|
92
|
+
dataset_id: UUID of the dataset.
|
|
93
|
+
include: Filter to include samples.
|
|
94
|
+
exclude: Filter to exclude samples.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
Count of files to be exported
|
|
98
|
+
"""
|
|
99
|
+
# Get all child dataset IDs that could contain annotations
|
|
100
|
+
annotation_dataset_ids = _get_annotation_dataset_ids(session, dataset_id)
|
|
101
|
+
query = _build_export_query(
|
|
102
|
+
dataset_id=dataset_id,
|
|
103
|
+
annotation_dataset_ids=annotation_dataset_ids,
|
|
104
|
+
include=include,
|
|
105
|
+
exclude=exclude,
|
|
106
|
+
)
|
|
107
|
+
count_query = select(func.count()).select_from(query.subquery())
|
|
108
|
+
return session.exec(count_query).one() or 0
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _get_annotation_dataset_ids(session: Session, dataset_id: UUID) -> list[UUID]:
|
|
112
|
+
"""Get all child dataset IDs that could contain annotations.
|
|
113
|
+
|
|
114
|
+
This includes the dataset itself and all its child datasets (recursively)
|
|
115
|
+
that have sample_type ANNOTATION.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
session: SQLAlchemy session.
|
|
119
|
+
dataset_id: UUID of the root dataset.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
List of dataset IDs that could contain annotations.
|
|
123
|
+
"""
|
|
124
|
+
hierarchy = get_hierarchy(session, dataset_id)
|
|
125
|
+
return [ds.dataset_id for ds in hierarchy if ds.sample_type == SampleType.ANNOTATION]
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _build_export_query( # noqa: C901
|
|
129
|
+
dataset_id: UUID,
|
|
130
|
+
annotation_dataset_ids: list[UUID],
|
|
131
|
+
include: ExportFilter | None = None,
|
|
132
|
+
exclude: ExportFilter | None = None,
|
|
133
|
+
) -> SelectOfScalar[ImageTable]:
|
|
134
|
+
"""Build the export query based on filters.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
dataset_id: UUID of the dataset.
|
|
138
|
+
annotation_dataset_ids: List of dataset IDs that could contain annotations.
|
|
139
|
+
include: Filter to include samples.
|
|
140
|
+
exclude: Filter to exclude samples.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
SQLModel select query
|
|
144
|
+
"""
|
|
145
|
+
if not include and not exclude:
|
|
146
|
+
raise ValueError("Include or exclude filter is required.")
|
|
147
|
+
if include and exclude:
|
|
148
|
+
raise ValueError("Cannot include and exclude at the same time.")
|
|
149
|
+
|
|
150
|
+
# include tags or sample_ids or annotation_ids from result
|
|
151
|
+
if include:
|
|
152
|
+
if include.tag_ids:
|
|
153
|
+
return (
|
|
154
|
+
select(ImageTable)
|
|
155
|
+
.join(ImageTable.sample)
|
|
156
|
+
.where(SampleTable.dataset_id == dataset_id)
|
|
157
|
+
.where(
|
|
158
|
+
or_(
|
|
159
|
+
# Samples with matching sample tags
|
|
160
|
+
col(SampleTable.tags).any(
|
|
161
|
+
and_(
|
|
162
|
+
TagTable.kind == "sample",
|
|
163
|
+
col(TagTable.tag_id).in_(include.tag_ids),
|
|
164
|
+
)
|
|
165
|
+
),
|
|
166
|
+
# Samples with matching annotation tags
|
|
167
|
+
col(SampleTable.annotations).any(
|
|
168
|
+
col(AnnotationBaseTable.tags).any(
|
|
169
|
+
and_(
|
|
170
|
+
TagTable.kind == "annotation",
|
|
171
|
+
col(TagTable.tag_id).in_(include.tag_ids),
|
|
172
|
+
)
|
|
173
|
+
)
|
|
174
|
+
),
|
|
175
|
+
)
|
|
176
|
+
)
|
|
177
|
+
.order_by(col(ImageTable.created_at).asc())
|
|
178
|
+
.distinct()
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
# get samples by specific sample_ids
|
|
182
|
+
if include.sample_ids:
|
|
183
|
+
return (
|
|
184
|
+
select(ImageTable)
|
|
185
|
+
.join(ImageTable.sample)
|
|
186
|
+
.where(SampleTable.dataset_id == dataset_id)
|
|
187
|
+
.where(col(ImageTable.sample_id).in_(include.sample_ids))
|
|
188
|
+
.order_by(col(ImageTable.created_at).asc())
|
|
189
|
+
.distinct()
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
# get samples by specific annotation_ids
|
|
193
|
+
if include.annotation_ids:
|
|
194
|
+
# Annotations are stored in child datasets, so filter by all annotation dataset IDs
|
|
195
|
+
# Filter by checking if the annotation's sample_id belongs to a sample in
|
|
196
|
+
# annotation_dataset_ids
|
|
197
|
+
annotation_sample_subquery = select(SampleTable.sample_id).where(
|
|
198
|
+
col(SampleTable.dataset_id).in_(annotation_dataset_ids)
|
|
199
|
+
)
|
|
200
|
+
return (
|
|
201
|
+
select(ImageTable)
|
|
202
|
+
.join(ImageTable.sample)
|
|
203
|
+
.join(SampleTable.annotations)
|
|
204
|
+
.where(col(AnnotationBaseTable.sample_id).in_(annotation_sample_subquery))
|
|
205
|
+
.where(col(AnnotationBaseTable.sample_id).in_(include.annotation_ids))
|
|
206
|
+
.order_by(col(ImageTable.created_at).asc())
|
|
207
|
+
.distinct()
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
# exclude tags or sample_ids or annotation_ids from result
|
|
211
|
+
elif exclude:
|
|
212
|
+
if exclude.tag_ids:
|
|
213
|
+
return (
|
|
214
|
+
select(ImageTable)
|
|
215
|
+
.join(ImageTable.sample)
|
|
216
|
+
.where(SampleTable.dataset_id == dataset_id)
|
|
217
|
+
.where(
|
|
218
|
+
and_(
|
|
219
|
+
~col(SampleTable.tags).any(
|
|
220
|
+
and_(
|
|
221
|
+
TagTable.kind == "sample",
|
|
222
|
+
col(TagTable.tag_id).in_(exclude.tag_ids),
|
|
223
|
+
)
|
|
224
|
+
),
|
|
225
|
+
or_(
|
|
226
|
+
~col(SampleTable.annotations).any(),
|
|
227
|
+
~col(SampleTable.annotations).any(
|
|
228
|
+
col(AnnotationBaseTable.tags).any(
|
|
229
|
+
and_(
|
|
230
|
+
TagTable.kind == "annotation",
|
|
231
|
+
col(TagTable.tag_id).in_(exclude.tag_ids),
|
|
232
|
+
)
|
|
233
|
+
)
|
|
234
|
+
),
|
|
235
|
+
),
|
|
236
|
+
)
|
|
237
|
+
)
|
|
238
|
+
.order_by(col(ImageTable.created_at).asc())
|
|
239
|
+
.distinct()
|
|
240
|
+
)
|
|
241
|
+
if exclude.sample_ids:
|
|
242
|
+
return (
|
|
243
|
+
select(ImageTable)
|
|
244
|
+
.join(ImageTable.sample)
|
|
245
|
+
.where(SampleTable.dataset_id == dataset_id)
|
|
246
|
+
.where(col(ImageTable.sample_id).notin_(exclude.sample_ids))
|
|
247
|
+
.order_by(col(ImageTable.created_at).asc())
|
|
248
|
+
.distinct()
|
|
249
|
+
)
|
|
250
|
+
if exclude.annotation_ids:
|
|
251
|
+
return (
|
|
252
|
+
select(ImageTable)
|
|
253
|
+
.join(ImageTable.sample)
|
|
254
|
+
.where(SampleTable.dataset_id == dataset_id)
|
|
255
|
+
.where(
|
|
256
|
+
or_(
|
|
257
|
+
~col(SampleTable.annotations).any(),
|
|
258
|
+
~col(SampleTable.annotations).any(
|
|
259
|
+
col(AnnotationBaseTable.sample_id).in_(exclude.annotation_ids)
|
|
260
|
+
),
|
|
261
|
+
)
|
|
262
|
+
)
|
|
263
|
+
.order_by(col(ImageTable.created_at).asc())
|
|
264
|
+
.distinct()
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
raise ValueError("Invalid include or export filter combination.")
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Implementation of get all datasets resolver function."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from sqlmodel import Session, col, select
|
|
6
|
+
|
|
7
|
+
from lightly_studio.models.dataset import DatasetTable
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# TODO(Michal, 06/2025): Use Paginated struct instead of offset and limit
|
|
11
|
+
def get_all(session: Session, offset: int = 0, limit: int = 100) -> list[DatasetTable]:
|
|
12
|
+
"""Retrieve all datasets with pagination."""
|
|
13
|
+
datasets = session.exec(
|
|
14
|
+
select(DatasetTable)
|
|
15
|
+
.order_by(col(DatasetTable.created_at).asc())
|
|
16
|
+
.offset(offset)
|
|
17
|
+
.limit(limit)
|
|
18
|
+
).all()
|
|
19
|
+
return list(datasets) if datasets else []
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Implementation of get dataset by ID resolver function."""
|
|
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.dataset import DatasetTable
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_by_id(session: Session, dataset_id: UUID) -> DatasetTable | None:
|
|
13
|
+
"""Retrieve a single dataset by ID."""
|
|
14
|
+
return session.exec(
|
|
15
|
+
select(DatasetTable).where(DatasetTable.dataset_id == dataset_id)
|
|
16
|
+
).one_or_none()
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""Implementation of get dataset by name resolver function."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from sqlmodel import Session, select
|
|
6
|
+
|
|
7
|
+
from lightly_studio.models.dataset import DatasetTable
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_by_name(session: Session, name: str) -> DatasetTable | None:
|
|
11
|
+
"""Retrieve a single dataset by name."""
|
|
12
|
+
return session.exec(select(DatasetTable).where(DatasetTable.name == name)).one_or_none()
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Handler for database operations related to datasets."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from sqlmodel import Session, func, select
|
|
6
|
+
|
|
7
|
+
from lightly_studio.models.dataset import DatasetTable, DatasetViewWithCount
|
|
8
|
+
from lightly_studio.models.sample import SampleTable
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_dataset_details(session: Session, dataset: DatasetTable) -> DatasetViewWithCount:
|
|
12
|
+
"""Convert a DatasetTable to DatasetViewWithCount with computed sample count."""
|
|
13
|
+
sample_count = (
|
|
14
|
+
session.exec(
|
|
15
|
+
select(func.count("*")).where(SampleTable.dataset_id == dataset.dataset_id)
|
|
16
|
+
).one()
|
|
17
|
+
or 0
|
|
18
|
+
)
|
|
19
|
+
return DatasetViewWithCount(
|
|
20
|
+
dataset_id=dataset.dataset_id,
|
|
21
|
+
parent_dataset_id=dataset.parent_dataset_id,
|
|
22
|
+
sample_type=dataset.sample_type,
|
|
23
|
+
name=dataset.name,
|
|
24
|
+
created_at=dataset.created_at,
|
|
25
|
+
updated_at=dataset.updated_at,
|
|
26
|
+
total_sample_count=sample_count,
|
|
27
|
+
)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Implementation of get_child_datasets resolver function."""
|
|
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 DatasetTable
|
|
10
|
+
from lightly_studio.resolvers import dataset_resolver
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_hierarchy(session: Session, root_dataset_id: UUID) -> list[DatasetTable]:
|
|
14
|
+
"""Retrieve all child datasets of the given root dataset, including the root itself.
|
|
15
|
+
|
|
16
|
+
The datasets are returned in the depth-first order, starting with the root dataset.
|
|
17
|
+
The relative order of children of any given node is the order in DatasetTable.children.
|
|
18
|
+
"""
|
|
19
|
+
root_dataset = dataset_resolver.get_by_id(session=session, dataset_id=root_dataset_id)
|
|
20
|
+
if root_dataset is None:
|
|
21
|
+
raise ValueError(f"Dataset with id {root_dataset_id} not found.")
|
|
22
|
+
|
|
23
|
+
# Use a stack to perform depth-first traversal of the dataset hierarchy.
|
|
24
|
+
to_process = [root_dataset]
|
|
25
|
+
all_datasets = []
|
|
26
|
+
while to_process:
|
|
27
|
+
current_dataset = to_process.pop()
|
|
28
|
+
all_datasets.append(current_dataset)
|
|
29
|
+
to_process.extend(reversed(current_dataset.children))
|
|
30
|
+
|
|
31
|
+
return all_datasets
|