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,273 @@
|
|
|
1
|
+
"""Database selection functions for the selection process."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import datetime
|
|
6
|
+
import logging
|
|
7
|
+
from collections import Counter, defaultdict
|
|
8
|
+
from typing import Mapping, Sequence
|
|
9
|
+
from uuid import UUID, uuid4
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
import sqlalchemy
|
|
13
|
+
from numpy.typing import NDArray
|
|
14
|
+
from sqlmodel import Session
|
|
15
|
+
|
|
16
|
+
from lightly_studio.models.tag import TagCreate
|
|
17
|
+
from lightly_studio.resolvers import (
|
|
18
|
+
annotation_label_resolver,
|
|
19
|
+
annotation_resolver,
|
|
20
|
+
embedding_model_resolver,
|
|
21
|
+
metadata_resolver,
|
|
22
|
+
sample_embedding_resolver,
|
|
23
|
+
tag_resolver,
|
|
24
|
+
)
|
|
25
|
+
from lightly_studio.resolvers.annotations.annotations_filter import AnnotationsFilter
|
|
26
|
+
from lightly_studio.selection.mundig import Mundig
|
|
27
|
+
from lightly_studio.selection.selection_config import (
|
|
28
|
+
AnnotationClassBalancingStrategy,
|
|
29
|
+
EmbeddingDiversityStrategy,
|
|
30
|
+
MetadataWeightingStrategy,
|
|
31
|
+
SelectionConfig,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
EPSILON = 1e-6
|
|
35
|
+
|
|
36
|
+
logger = logging.getLogger(__name__)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _aggregate_class_distributions(
|
|
40
|
+
input_sample_ids: Sequence[UUID],
|
|
41
|
+
sample_id_to_annotation_label_ids: Mapping[UUID, list[UUID]],
|
|
42
|
+
target_annotation_ids: list[UUID],
|
|
43
|
+
) -> NDArray[np.float32]:
|
|
44
|
+
"""Aggregates class distributions for a list of samples.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
input_sample_ids:
|
|
48
|
+
A list of sample IDs for which to aggregate the class distributions.
|
|
49
|
+
sample_id_to_annotation_label_ids:
|
|
50
|
+
A dictionary mapping sample IDs to a list of their annotation label IDs.
|
|
51
|
+
target_annotation_ids:
|
|
52
|
+
A list of annotation label IDs that are considered for the distribution.
|
|
53
|
+
The order of these IDs determines the order of the columns in the output.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
A numpy array of shape (n_samples, n_labels) where n_samples is the
|
|
57
|
+
number of input samples and n_labels is the number of target annotation
|
|
58
|
+
labels. Each row in the array represents the class distribution for a
|
|
59
|
+
sample, where the values are the counts of each target annotation label.
|
|
60
|
+
"""
|
|
61
|
+
n_samples = len(input_sample_ids)
|
|
62
|
+
n_labels = len(target_annotation_ids)
|
|
63
|
+
|
|
64
|
+
class_distributions = np.zeros((n_samples, n_labels), dtype=np.float32)
|
|
65
|
+
annotation_id_to_idx = {
|
|
66
|
+
annotation_id: j for j, annotation_id in enumerate(target_annotation_ids)
|
|
67
|
+
}
|
|
68
|
+
for i, sample_id in enumerate(input_sample_ids):
|
|
69
|
+
for annotation_label_id in sample_id_to_annotation_label_ids[sample_id]:
|
|
70
|
+
label_idx = annotation_id_to_idx.get(annotation_label_id)
|
|
71
|
+
if label_idx is not None:
|
|
72
|
+
class_distributions[i, label_idx] += 1
|
|
73
|
+
|
|
74
|
+
return class_distributions
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _process_explicit_target_distribution(
|
|
78
|
+
session: Session,
|
|
79
|
+
target_distribution: dict[str, float],
|
|
80
|
+
annotation_label_ids: Sequence[UUID],
|
|
81
|
+
) -> tuple[dict[UUID, float], set[UUID], float]:
|
|
82
|
+
"""Processes the explicit target distribution.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
session: The SQLAlchemy session.
|
|
86
|
+
target_distribution:
|
|
87
|
+
A dictionary mapping annotation label names to their target proportions.
|
|
88
|
+
annotation_label_ids:
|
|
89
|
+
A sequence of all annotation label IDs to consider for class balancing.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
Tuple of:
|
|
93
|
+
A dictionary mapping annotation label IDs to their effective target proportions.
|
|
94
|
+
The set of unused label IDs
|
|
95
|
+
The target value remaining to 1.0.
|
|
96
|
+
|
|
97
|
+
Raises:
|
|
98
|
+
NotImplementedError: If multiple labels with the same name are found.
|
|
99
|
+
ValueError: If an annotation label name does not exist or if targets sum
|
|
100
|
+
to less than 1.0 and all classes are used.
|
|
101
|
+
"""
|
|
102
|
+
label_id_to_target: dict[UUID, float] = {}
|
|
103
|
+
total_targets = 0.0
|
|
104
|
+
for label_name, target in target_distribution.items():
|
|
105
|
+
try:
|
|
106
|
+
annotation_label = annotation_label_resolver.get_by_label_name(session, label_name)
|
|
107
|
+
except sqlalchemy.exc.MultipleResultsFound as e:
|
|
108
|
+
raise NotImplementedError(
|
|
109
|
+
"Multiple labels with the same name not supported yet."
|
|
110
|
+
) from e
|
|
111
|
+
if annotation_label is None:
|
|
112
|
+
raise ValueError(f"Annotation label with this name does not exist: {label_name}")
|
|
113
|
+
label_id_to_target[annotation_label.annotation_label_id] = target
|
|
114
|
+
total_targets += target
|
|
115
|
+
|
|
116
|
+
all_label_ids = set(annotation_label_ids)
|
|
117
|
+
unused_label_ids = all_label_ids - set(label_id_to_target.keys())
|
|
118
|
+
# `total_targets` can be more or less than 1.0. Both can be ignored, selection will still
|
|
119
|
+
# try correctly to reach the target.
|
|
120
|
+
remaining_ratio = max(1.0 - total_targets, 0.0)
|
|
121
|
+
return label_id_to_target, unused_label_ids, remaining_ratio
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _get_class_balancing_data(
|
|
125
|
+
session: Session,
|
|
126
|
+
strat: AnnotationClassBalancingStrategy,
|
|
127
|
+
annotation_label_ids: Sequence[UUID],
|
|
128
|
+
input_sample_ids: Sequence[UUID],
|
|
129
|
+
sample_id_to_annotation_label_ids: Mapping[UUID, list[UUID]],
|
|
130
|
+
) -> tuple[NDArray[np.float32], list[float]]:
|
|
131
|
+
"""Helper function to get class balancing data."""
|
|
132
|
+
if strat.target_distribution == "uniform":
|
|
133
|
+
target_keys_set = set(annotation_label_ids)
|
|
134
|
+
target_keys = list(target_keys_set)
|
|
135
|
+
target_values = [1.0 / len(target_keys)] * len(target_keys)
|
|
136
|
+
elif strat.target_distribution == "input":
|
|
137
|
+
# Count the number of times each label appears in the input
|
|
138
|
+
input_label_count = Counter(annotation_label_ids)
|
|
139
|
+
target_keys, target_values = (
|
|
140
|
+
list(input_label_count.keys()),
|
|
141
|
+
list(input_label_count.values()),
|
|
142
|
+
)
|
|
143
|
+
elif isinstance(strat.target_distribution, dict):
|
|
144
|
+
label_id_to_target, unused_label_ids, remaining_ratio = (
|
|
145
|
+
_process_explicit_target_distribution(
|
|
146
|
+
session=session,
|
|
147
|
+
target_distribution=strat.target_distribution,
|
|
148
|
+
annotation_label_ids=annotation_label_ids,
|
|
149
|
+
)
|
|
150
|
+
)
|
|
151
|
+
if len(unused_label_ids) >= 1:
|
|
152
|
+
other_uuid = uuid4()
|
|
153
|
+
# Handle the case when not all classes have a target.
|
|
154
|
+
# We replace UUIDs that are present in `unused_label_ids` for `other_uuid` and the
|
|
155
|
+
# target for `other_uuid` is `remaining_ratio`.
|
|
156
|
+
for sample_annotation_label_ids in sample_id_to_annotation_label_ids.values():
|
|
157
|
+
for i, label_id in enumerate(sample_annotation_label_ids):
|
|
158
|
+
if label_id in unused_label_ids:
|
|
159
|
+
sample_annotation_label_ids[i] = other_uuid
|
|
160
|
+
label_id_to_target[other_uuid] = remaining_ratio
|
|
161
|
+
|
|
162
|
+
target_keys, target_values = (
|
|
163
|
+
list(label_id_to_target.keys()),
|
|
164
|
+
list(label_id_to_target.values()),
|
|
165
|
+
)
|
|
166
|
+
else:
|
|
167
|
+
raise ValueError(f"Unknown distribution type: {type(strat.target_distribution)}")
|
|
168
|
+
|
|
169
|
+
class_distributions = _aggregate_class_distributions(
|
|
170
|
+
input_sample_ids=input_sample_ids,
|
|
171
|
+
sample_id_to_annotation_label_ids=sample_id_to_annotation_label_ids,
|
|
172
|
+
target_annotation_ids=target_keys,
|
|
173
|
+
)
|
|
174
|
+
return class_distributions, target_values
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def select_via_database(
|
|
178
|
+
session: Session, config: SelectionConfig, input_sample_ids: list[UUID]
|
|
179
|
+
) -> None:
|
|
180
|
+
"""Run selection using the provided candidate sample ids.
|
|
181
|
+
|
|
182
|
+
First resolves the selection config to concrete database values.
|
|
183
|
+
Then calls Mundig to run the selection with pure values.
|
|
184
|
+
Finally creates a tag for the selected set.
|
|
185
|
+
"""
|
|
186
|
+
# Check if the tag name is already used
|
|
187
|
+
existing_tag = tag_resolver.get_by_name(
|
|
188
|
+
session=session,
|
|
189
|
+
tag_name=config.selection_result_tag_name,
|
|
190
|
+
dataset_id=config.dataset_id,
|
|
191
|
+
)
|
|
192
|
+
if existing_tag:
|
|
193
|
+
msg = (
|
|
194
|
+
f"Tag with name {config.selection_result_tag_name} already exists in the "
|
|
195
|
+
f"dataset {config.dataset_id}. Please use a different tag name."
|
|
196
|
+
)
|
|
197
|
+
raise ValueError(msg)
|
|
198
|
+
|
|
199
|
+
n_samples_to_select = min(config.n_samples_to_select, len(input_sample_ids))
|
|
200
|
+
if n_samples_to_select == 0:
|
|
201
|
+
logger.warning("No samples available for selection.")
|
|
202
|
+
return
|
|
203
|
+
|
|
204
|
+
mundig = Mundig()
|
|
205
|
+
for strat in config.strategies:
|
|
206
|
+
if isinstance(strat, EmbeddingDiversityStrategy):
|
|
207
|
+
embedding_model_id = embedding_model_resolver.get_by_name(
|
|
208
|
+
session=session,
|
|
209
|
+
dataset_id=config.dataset_id,
|
|
210
|
+
embedding_model_name=strat.embedding_model_name,
|
|
211
|
+
).embedding_model_id
|
|
212
|
+
embedding_tables = sample_embedding_resolver.get_by_sample_ids(
|
|
213
|
+
session=session,
|
|
214
|
+
sample_ids=input_sample_ids,
|
|
215
|
+
embedding_model_id=embedding_model_id,
|
|
216
|
+
)
|
|
217
|
+
embeddings = [e.embedding for e in embedding_tables]
|
|
218
|
+
mundig.add_diversity(embeddings=embeddings, strength=strat.strength)
|
|
219
|
+
elif isinstance(strat, MetadataWeightingStrategy):
|
|
220
|
+
key = strat.metadata_key
|
|
221
|
+
weights = []
|
|
222
|
+
for sample_id in input_sample_ids:
|
|
223
|
+
weight = metadata_resolver.get_value_for_sample(session, sample_id, key)
|
|
224
|
+
if not isinstance(weight, (float, int)):
|
|
225
|
+
raise ValueError(
|
|
226
|
+
f"Metadata {key} is not a number, only numbers can be used as weights"
|
|
227
|
+
)
|
|
228
|
+
weights.append(float(weight))
|
|
229
|
+
mundig.add_weighting(weights=weights, strength=strat.strength)
|
|
230
|
+
elif isinstance(strat, AnnotationClassBalancingStrategy):
|
|
231
|
+
annotations = annotation_resolver.get_all(
|
|
232
|
+
session=session,
|
|
233
|
+
filters=AnnotationsFilter(sample_ids=input_sample_ids),
|
|
234
|
+
).annotations
|
|
235
|
+
annotation_label_ids = [a.annotation_label_id for a in annotations]
|
|
236
|
+
sample_id_to_annotation_label_ids = defaultdict(list)
|
|
237
|
+
for annotation in annotations:
|
|
238
|
+
sample_id_to_annotation_label_ids[annotation.parent_sample_id].append(
|
|
239
|
+
annotation.annotation_label_id
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
class_distributions, target_values = _get_class_balancing_data(
|
|
243
|
+
session=session,
|
|
244
|
+
strat=strat,
|
|
245
|
+
annotation_label_ids=annotation_label_ids,
|
|
246
|
+
input_sample_ids=input_sample_ids,
|
|
247
|
+
sample_id_to_annotation_label_ids=sample_id_to_annotation_label_ids,
|
|
248
|
+
)
|
|
249
|
+
mundig.add_class_balancing(
|
|
250
|
+
class_distributions=class_distributions,
|
|
251
|
+
target=target_values,
|
|
252
|
+
strength=strat.strength,
|
|
253
|
+
)
|
|
254
|
+
else:
|
|
255
|
+
raise ValueError(f"Selection strategy of type {type(strat)} is unknown.")
|
|
256
|
+
|
|
257
|
+
selected_indices = mundig.run(n_samples=n_samples_to_select)
|
|
258
|
+
selected_sample_ids = [input_sample_ids[i] for i in selected_indices]
|
|
259
|
+
|
|
260
|
+
datetime_str = datetime.datetime.now(tz=datetime.timezone.utc).isoformat()
|
|
261
|
+
tag_description = f"Selected at {datetime_str} UTC"
|
|
262
|
+
tag = tag_resolver.create(
|
|
263
|
+
session=session,
|
|
264
|
+
tag=TagCreate(
|
|
265
|
+
dataset_id=config.dataset_id,
|
|
266
|
+
name=config.selection_result_tag_name,
|
|
267
|
+
kind="sample",
|
|
268
|
+
description=tag_description,
|
|
269
|
+
),
|
|
270
|
+
)
|
|
271
|
+
tag_resolver.add_sample_ids_to_tag_id(
|
|
272
|
+
session=session, tag_id=tag.tag_id, sample_ids=selected_sample_ids
|
|
273
|
+
)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Pydantic models for the Selection configuration."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Dict, Literal, Sequence
|
|
6
|
+
from uuid import UUID
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel
|
|
9
|
+
|
|
10
|
+
AnnotationsClassName = str
|
|
11
|
+
AnnotationClassToTarget = Dict[AnnotationsClassName, float]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SelectionConfig(BaseModel):
|
|
15
|
+
"""Configuration for the selection process."""
|
|
16
|
+
|
|
17
|
+
dataset_id: UUID
|
|
18
|
+
n_samples_to_select: int
|
|
19
|
+
selection_result_tag_name: str
|
|
20
|
+
strategies: Sequence[SelectionStrategy]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class SelectionStrategy(BaseModel):
|
|
24
|
+
"""Base class for selection strategies."""
|
|
25
|
+
|
|
26
|
+
strength: float = 1.0
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class EmbeddingDiversityStrategy(SelectionStrategy):
|
|
30
|
+
"""Selection strategy based on embedding diversity."""
|
|
31
|
+
|
|
32
|
+
strategy_name: Literal["diversity"] = "diversity"
|
|
33
|
+
embedding_model_name: str | None = None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class MetadataWeightingStrategy(SelectionStrategy):
|
|
37
|
+
"""Selection strategy based on metadata weighting."""
|
|
38
|
+
|
|
39
|
+
strategy_name: Literal["weights"] = "weights"
|
|
40
|
+
metadata_key: str
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class AnnotationClassBalancingStrategy(SelectionStrategy):
|
|
44
|
+
"""Selection strategy based on class balancing."""
|
|
45
|
+
|
|
46
|
+
strategy_name: Literal["balance"] = "balance"
|
|
47
|
+
target_distribution: AnnotationClassToTarget | Literal["uniform"] | Literal["input"]
|
|
48
|
+
# TODO(Lukas 11/2025): Allow specifying the annotation task instead of merging annotations from
|
|
49
|
+
# all tasks.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Services for annotations operations."""
|
|
2
|
+
|
|
3
|
+
from lightly_studio.services.annotations_service.create_annotation import (
|
|
4
|
+
create_annotation,
|
|
5
|
+
)
|
|
6
|
+
from lightly_studio.services.annotations_service.delete_annotation import (
|
|
7
|
+
delete_annotation,
|
|
8
|
+
)
|
|
9
|
+
from lightly_studio.services.annotations_service.get_annotation_by_id import (
|
|
10
|
+
get_annotation_by_id,
|
|
11
|
+
)
|
|
12
|
+
from lightly_studio.services.annotations_service.update_annotation import (
|
|
13
|
+
update_annotation,
|
|
14
|
+
)
|
|
15
|
+
from lightly_studio.services.annotations_service.update_annotation_bounding_box import (
|
|
16
|
+
update_annotation_bounding_box,
|
|
17
|
+
)
|
|
18
|
+
from lightly_studio.services.annotations_service.update_annotation_label import (
|
|
19
|
+
update_annotation_label,
|
|
20
|
+
)
|
|
21
|
+
from lightly_studio.services.annotations_service.update_annotations import (
|
|
22
|
+
update_annotations,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"create_annotation",
|
|
27
|
+
"delete_annotation",
|
|
28
|
+
"get_annotation_by_id",
|
|
29
|
+
"update_annotation",
|
|
30
|
+
"update_annotation_bounding_box",
|
|
31
|
+
"update_annotation_label",
|
|
32
|
+
"update_annotations",
|
|
33
|
+
]
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Create annotation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
from sqlmodel import Session
|
|
9
|
+
|
|
10
|
+
from lightly_studio.models.annotation.annotation_base import (
|
|
11
|
+
AnnotationBaseTable,
|
|
12
|
+
AnnotationCreate,
|
|
13
|
+
AnnotationType,
|
|
14
|
+
)
|
|
15
|
+
from lightly_studio.resolvers import annotation_resolver
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class AnnotationCreateParams(BaseModel):
|
|
19
|
+
"""Input model for create annotation service."""
|
|
20
|
+
|
|
21
|
+
annotation_label_id: UUID
|
|
22
|
+
annotation_type: AnnotationType
|
|
23
|
+
dataset_id: UUID
|
|
24
|
+
parent_sample_id: UUID
|
|
25
|
+
|
|
26
|
+
x: int | None = None
|
|
27
|
+
y: int | None = None
|
|
28
|
+
width: int | None = None
|
|
29
|
+
height: int | None = None
|
|
30
|
+
|
|
31
|
+
segmentation_mask: list[int] | None = None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def create_annotation(session: Session, annotation: AnnotationCreateParams) -> AnnotationBaseTable:
|
|
35
|
+
"""Create a new annotation.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
session: Database session for executing the operation.
|
|
39
|
+
annotation: Annotation data to create.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
The retrieved annotation.
|
|
43
|
+
"""
|
|
44
|
+
annotation_create = AnnotationCreate(
|
|
45
|
+
**annotation.model_dump(),
|
|
46
|
+
)
|
|
47
|
+
new_annotation_ids = annotation_resolver.create_many(
|
|
48
|
+
session=session,
|
|
49
|
+
parent_dataset_id=annotation.dataset_id,
|
|
50
|
+
annotations=[annotation_create],
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
if not new_annotation_ids:
|
|
54
|
+
raise ValueError("Failed to create annotation.")
|
|
55
|
+
|
|
56
|
+
created_annotation = annotation_resolver.get_by_id(
|
|
57
|
+
session=session,
|
|
58
|
+
annotation_id=new_annotation_ids[0],
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
if created_annotation is None:
|
|
62
|
+
raise ValueError(f"Failed to create annotation: {annotation}")
|
|
63
|
+
|
|
64
|
+
return created_annotation
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Delete an annotation by its ID."""
|
|
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 annotation_resolver
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def delete_annotation(session: Session, annotation_id: UUID) -> None:
|
|
13
|
+
"""Delete an annotation by its ID.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
session: Database session for executing the operation.
|
|
17
|
+
annotation_id: ID of the annotation to delete.
|
|
18
|
+
|
|
19
|
+
Raises:
|
|
20
|
+
ValueError: If the annotation with the given ID is not found.
|
|
21
|
+
"""
|
|
22
|
+
annotation_resolver.delete_annotation(session=session, annotation_id=annotation_id)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Get an annotation by its ID."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
|
|
7
|
+
from sqlmodel import Session
|
|
8
|
+
|
|
9
|
+
from lightly_studio.models.annotation.annotation_base import (
|
|
10
|
+
AnnotationBaseTable,
|
|
11
|
+
)
|
|
12
|
+
from lightly_studio.resolvers import (
|
|
13
|
+
annotation_resolver,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_annotation_by_id(session: Session, annotation_id: UUID) -> AnnotationBaseTable:
|
|
18
|
+
"""Retrieve an annotation by its ID.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
session: Database session for executing the operation.
|
|
22
|
+
annotation_id: ID of the annotation to retrieve.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
The retrieved annotation.
|
|
26
|
+
"""
|
|
27
|
+
annotation = annotation_resolver.get_by_id(session=session, annotation_id=annotation_id)
|
|
28
|
+
if not annotation:
|
|
29
|
+
raise ValueError(f"Annotation {annotation_id} not found")
|
|
30
|
+
|
|
31
|
+
return annotation
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""General annotation update service."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
from sqlmodel import Session
|
|
9
|
+
|
|
10
|
+
from lightly_studio.models.annotation.annotation_base import (
|
|
11
|
+
AnnotationBaseTable,
|
|
12
|
+
)
|
|
13
|
+
from lightly_studio.resolvers.annotation_resolver.update_bounding_box import BoundingBoxCoordinates
|
|
14
|
+
from lightly_studio.services import annotations_service
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class AnnotationUpdate(BaseModel):
|
|
18
|
+
"""Model for updating an annotation."""
|
|
19
|
+
|
|
20
|
+
annotation_id: UUID
|
|
21
|
+
dataset_id: UUID
|
|
22
|
+
label_name: str | None = None
|
|
23
|
+
bounding_box: BoundingBoxCoordinates | None = None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def update_annotation(session: Session, annotation_update: AnnotationUpdate) -> AnnotationBaseTable:
|
|
27
|
+
"""Update an annotation.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
session: Database session for executing the operation.
|
|
31
|
+
annotation_update: Object containing updates for the annotation.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
The updated annotation.
|
|
35
|
+
|
|
36
|
+
"""
|
|
37
|
+
result = None
|
|
38
|
+
if annotation_update.label_name is not None:
|
|
39
|
+
result = annotations_service.update_annotation_label(
|
|
40
|
+
session,
|
|
41
|
+
annotation_update.annotation_id,
|
|
42
|
+
annotation_update.label_name,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
if annotation_update.bounding_box is not None:
|
|
46
|
+
result = annotations_service.update_annotation_bounding_box(
|
|
47
|
+
session,
|
|
48
|
+
annotation_update.annotation_id,
|
|
49
|
+
bounding_box=annotation_update.bounding_box,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
if result is None:
|
|
53
|
+
raise ValueError("No updates provided for the annotation.")
|
|
54
|
+
return result
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Update the bounding box of an annotation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
|
|
7
|
+
from sqlmodel import Session
|
|
8
|
+
|
|
9
|
+
from lightly_studio.models.annotation.annotation_base import (
|
|
10
|
+
AnnotationBaseTable,
|
|
11
|
+
)
|
|
12
|
+
from lightly_studio.resolvers import (
|
|
13
|
+
annotation_resolver,
|
|
14
|
+
)
|
|
15
|
+
from lightly_studio.resolvers.annotation_resolver.update_bounding_box import BoundingBoxCoordinates
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def update_annotation_bounding_box(
|
|
19
|
+
session: Session, annotation_id: UUID, bounding_box: BoundingBoxCoordinates
|
|
20
|
+
) -> AnnotationBaseTable:
|
|
21
|
+
"""Update the bounding box of an annotation.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
session: Database session for executing the operation.
|
|
25
|
+
annotation_id: UUID of the annotation to update.
|
|
26
|
+
bounding_box: New bounding box coordinates to assign to the annotation.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
The updated annotation with the new bounding box assigned.
|
|
30
|
+
|
|
31
|
+
"""
|
|
32
|
+
return annotation_resolver.update_bounding_box(
|
|
33
|
+
session,
|
|
34
|
+
annotation_id,
|
|
35
|
+
bounding_box,
|
|
36
|
+
)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Update the label of an annotation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
|
|
7
|
+
from sqlmodel import Session
|
|
8
|
+
|
|
9
|
+
from lightly_studio.models.annotation.annotation_base import (
|
|
10
|
+
AnnotationBaseTable,
|
|
11
|
+
)
|
|
12
|
+
from lightly_studio.models.annotation_label import AnnotationLabelCreate
|
|
13
|
+
from lightly_studio.resolvers import (
|
|
14
|
+
annotation_label_resolver,
|
|
15
|
+
annotation_resolver,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def update_annotation_label(
|
|
20
|
+
session: Session, annotation_id: UUID, label_name: str
|
|
21
|
+
) -> AnnotationBaseTable:
|
|
22
|
+
"""Update the label of an annotation.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
session: Database session for executing the operation.
|
|
26
|
+
annotation_id: UUID of the annotation to update.
|
|
27
|
+
label_name: New label to assign to the annotation.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
The updated annotation with the new label assigned.
|
|
31
|
+
|
|
32
|
+
"""
|
|
33
|
+
annotation_label = annotation_label_resolver.get_by_label_name(
|
|
34
|
+
session,
|
|
35
|
+
label_name,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
if not annotation_label:
|
|
39
|
+
annotation_label = annotation_label_resolver.create(
|
|
40
|
+
session,
|
|
41
|
+
label=AnnotationLabelCreate(annotation_label_name=label_name),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
return annotation_resolver.update_annotation_label(
|
|
45
|
+
session,
|
|
46
|
+
annotation_id,
|
|
47
|
+
annotation_label.annotation_label_id,
|
|
48
|
+
)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""General annotation update service."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from sqlmodel import Session
|
|
6
|
+
|
|
7
|
+
from lightly_studio.models.annotation.annotation_base import (
|
|
8
|
+
AnnotationBaseTable,
|
|
9
|
+
)
|
|
10
|
+
from lightly_studio.services import annotations_service
|
|
11
|
+
from lightly_studio.services.annotations_service.update_annotation import AnnotationUpdate
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def update_annotations(
|
|
15
|
+
session: Session, annotation_updates: list[AnnotationUpdate]
|
|
16
|
+
) -> list[AnnotationBaseTable]:
|
|
17
|
+
"""Update multiple annotations.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
session: Database session for executing the operation.
|
|
21
|
+
annotation_updates: List of objects containing updates for the annotations.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
List of updated annotations.
|
|
25
|
+
"""
|
|
26
|
+
return [
|
|
27
|
+
annotations_service.update_annotation(session, annotation_update)
|
|
28
|
+
for annotation_update in annotation_updates
|
|
29
|
+
]
|