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,37 @@
|
|
|
1
|
+
"""Fields for querying sample properties in the dataset query system."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from sqlmodel import col
|
|
6
|
+
|
|
7
|
+
from lightly_studio.core.dataset_query.field import (
|
|
8
|
+
DatetimeField,
|
|
9
|
+
NumericalField,
|
|
10
|
+
StringField,
|
|
11
|
+
)
|
|
12
|
+
from lightly_studio.core.dataset_query.tags_expression import TagsAccessor
|
|
13
|
+
from lightly_studio.models.image import ImageTable
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class SampleField:
|
|
17
|
+
"""Providing access to predefined sample fields for queries.
|
|
18
|
+
|
|
19
|
+
It is used for the `query.match(...)` and `query.order_by(...)` methods of the
|
|
20
|
+
`DatasetQuery` class.
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from lightly_studio.core.dataset_query import DatasetQuery, SampleField, OrderByField
|
|
24
|
+
|
|
25
|
+
query = dataset.query()
|
|
26
|
+
query.match(SampleField.tags.contains("cat"))
|
|
27
|
+
query.order_by(OrderByField(SampleField.file_path_abs))
|
|
28
|
+
samples = query.to_list()
|
|
29
|
+
```
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
file_name = StringField(col(ImageTable.file_name))
|
|
33
|
+
width = NumericalField(col(ImageTable.width))
|
|
34
|
+
height = NumericalField(col(ImageTable.height))
|
|
35
|
+
file_path_abs = StringField(col(ImageTable.file_path_abs))
|
|
36
|
+
created_at = DatetimeField(col(ImageTable.created_at))
|
|
37
|
+
tags = TagsAccessor()
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Tag field classes for building dataset queries on sample tags."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
from sqlalchemy import ColumnElement
|
|
8
|
+
from sqlmodel import col
|
|
9
|
+
|
|
10
|
+
from lightly_studio.core.dataset_query.match_expression import MatchExpression
|
|
11
|
+
from lightly_studio.models.sample import SampleTable
|
|
12
|
+
from lightly_studio.models.tag import TagTable
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TagsAccessor:
|
|
16
|
+
"""Provides access to tag operations for query building.
|
|
17
|
+
|
|
18
|
+
This class enables checking tag membership using the contains method:
|
|
19
|
+
SampleField.tags.contains("tag_name") returns a TagsContainsExpression.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def contains(self, tag_name: str) -> TagsContainsExpression:
|
|
23
|
+
"""Check if a tag name is in the sample's tags.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
tag_name: The name of the tag to check for.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
A TagsContainsExpression for building queries.
|
|
30
|
+
"""
|
|
31
|
+
return TagsContainsExpression(tag_name=tag_name)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class TagsContainsExpression(MatchExpression):
|
|
36
|
+
"""Expression for checking if a sample contains a specific tag."""
|
|
37
|
+
|
|
38
|
+
tag_name: str
|
|
39
|
+
|
|
40
|
+
def get(self) -> ColumnElement[bool]:
|
|
41
|
+
"""Get the tag contains expression.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
The SQLAlchemy expression for this field expression.
|
|
45
|
+
"""
|
|
46
|
+
return SampleTable.tags.any(col(TagTable.name) == self.tag_name)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Definition of ImageSample class, representing a dataset image sample."""
|
|
2
|
+
|
|
3
|
+
from sqlmodel import col
|
|
4
|
+
|
|
5
|
+
from lightly_studio.core.sample import DBField, Sample
|
|
6
|
+
from lightly_studio.models.image import ImageTable
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ImageSample(Sample):
|
|
10
|
+
"""Interface to a dataset image sample.
|
|
11
|
+
|
|
12
|
+
Many properties of the sample are directly accessible as attributes of this class.
|
|
13
|
+
```python
|
|
14
|
+
print(f"Sample file name: {sample.file_name}")
|
|
15
|
+
print(f"Sample file path: {sample.file_path_abs}")
|
|
16
|
+
print(f"Sample width: {sample.width}")
|
|
17
|
+
print(f"Sample height: {sample.height}")
|
|
18
|
+
```
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
file_name = DBField(col(ImageTable.file_name))
|
|
22
|
+
width = DBField(col(ImageTable.width))
|
|
23
|
+
height = DBField(col(ImageTable.height))
|
|
24
|
+
file_path_abs = DBField(col(ImageTable.file_path_abs))
|
|
25
|
+
|
|
26
|
+
created_at = DBField(col(ImageTable.created_at))
|
|
27
|
+
updated_at = DBField(col(ImageTable.updated_at))
|
|
28
|
+
|
|
29
|
+
def __init__(self, inner: ImageTable) -> None:
|
|
30
|
+
"""Initialize the Sample.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
inner: The ImageTable SQLAlchemy model instance.
|
|
34
|
+
"""
|
|
35
|
+
self.inner = inner
|
|
36
|
+
super().__init__(sample_table=inner.sample)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""Functions to add samples and their annotations to a dataset in the database."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from uuid import UUID
|
|
8
|
+
|
|
9
|
+
from sqlmodel import Session
|
|
10
|
+
|
|
11
|
+
from lightly_studio.resolvers import (
|
|
12
|
+
sample_resolver,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
# Constants
|
|
18
|
+
MAX_EXAMPLE_PATHS_TO_SHOW = 5
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class LoadingLoggingContext:
|
|
23
|
+
"""Context for the logging while loading data."""
|
|
24
|
+
|
|
25
|
+
n_samples_before_loading: int
|
|
26
|
+
n_samples_to_be_inserted: int = 0
|
|
27
|
+
example_paths_not_inserted: list[str] = field(default_factory=list)
|
|
28
|
+
|
|
29
|
+
def update_example_paths(self, example_paths_not_inserted: list[str]) -> None:
|
|
30
|
+
"""Update the list of example paths that were not inserted."""
|
|
31
|
+
if len(self.example_paths_not_inserted) >= MAX_EXAMPLE_PATHS_TO_SHOW:
|
|
32
|
+
return
|
|
33
|
+
upper_limit = MAX_EXAMPLE_PATHS_TO_SHOW - len(self.example_paths_not_inserted)
|
|
34
|
+
self.example_paths_not_inserted.extend(example_paths_not_inserted[:upper_limit])
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def log_loading_results(
|
|
38
|
+
session: Session, dataset_id: UUID, logging_context: LoadingLoggingContext
|
|
39
|
+
) -> None:
|
|
40
|
+
"""Log the results of loading samples into a dataset.
|
|
41
|
+
|
|
42
|
+
Calculates how many samples were successfully inserted by comparing the
|
|
43
|
+
current sample count with the count before loading. Prints a summary message
|
|
44
|
+
and, if any paths failed to be inserted, prints examples of those paths.
|
|
45
|
+
"""
|
|
46
|
+
n_samples_end = sample_resolver.count_by_dataset_id(session=session, dataset_id=dataset_id)
|
|
47
|
+
n_samples_inserted = n_samples_end - logging_context.n_samples_before_loading
|
|
48
|
+
logger.info(
|
|
49
|
+
f"Added {n_samples_inserted} out of {logging_context.n_samples_to_be_inserted} "
|
|
50
|
+
"new samples to the dataset."
|
|
51
|
+
)
|
|
52
|
+
if logging_context.example_paths_not_inserted:
|
|
53
|
+
logger.warning(
|
|
54
|
+
"Examples of paths that were not added: "
|
|
55
|
+
f"{', '.join(logging_context.example_paths_not_inserted)}"
|
|
56
|
+
)
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
"""Interface for Sample objects."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from abc import ABC
|
|
6
|
+
from collections.abc import Iterable
|
|
7
|
+
from typing import Any, Generic, Protocol, TypeVar, cast
|
|
8
|
+
from uuid import UUID
|
|
9
|
+
|
|
10
|
+
from sqlalchemy.orm import Mapped, object_session
|
|
11
|
+
from sqlmodel import Session
|
|
12
|
+
|
|
13
|
+
from lightly_studio.models.caption import CaptionCreate
|
|
14
|
+
from lightly_studio.models.sample import SampleTable
|
|
15
|
+
from lightly_studio.resolvers import caption_resolver, metadata_resolver, tag_resolver
|
|
16
|
+
|
|
17
|
+
T = TypeVar("T")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class _DBFieldOwner(Protocol):
|
|
21
|
+
inner: Any
|
|
22
|
+
|
|
23
|
+
def get_object_session(self) -> Session: ...
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class DBField(Generic[T]):
|
|
27
|
+
"""Descriptor for a database-backed field.
|
|
28
|
+
|
|
29
|
+
Provides interface to a SQLAlchemy model field. Setting the field
|
|
30
|
+
immediately commits to the database. The owner class must implement
|
|
31
|
+
the inner attribute and the get_object_session() method.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
__slots__ = ("_sqla_descriptor",)
|
|
35
|
+
"""Store the SQLAlchemy descriptor for accessing the field."""
|
|
36
|
+
|
|
37
|
+
def __init__(self, sqla_descriptor: Mapped[T]) -> None:
|
|
38
|
+
"""Initialize the DBField with a SQLAlchemy descriptor."""
|
|
39
|
+
self._sqla_descriptor = sqla_descriptor
|
|
40
|
+
|
|
41
|
+
def __get__(self, obj: _DBFieldOwner | None, owner: type | None = None) -> T:
|
|
42
|
+
"""Get the value of the field from the database."""
|
|
43
|
+
assert obj is not None, "DBField must be accessed via an instance, not the class"
|
|
44
|
+
# Delegate to SQLAlchemy's descriptor.
|
|
45
|
+
value: T = self._sqla_descriptor.__get__(obj.inner, type(obj.inner))
|
|
46
|
+
return value
|
|
47
|
+
|
|
48
|
+
def __set__(self, obj: _DBFieldOwner, value: T) -> None:
|
|
49
|
+
"""Set the value of the field in the database. Commits the session."""
|
|
50
|
+
# Delegate to SQLAlchemy's descriptor.
|
|
51
|
+
self._sqla_descriptor.__set__(obj.inner, value)
|
|
52
|
+
obj.get_object_session().commit()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class Sample(ABC):
|
|
56
|
+
"""Interface to a dataset sample.
|
|
57
|
+
|
|
58
|
+
It is usually returned by a query to the dataset.
|
|
59
|
+
```python
|
|
60
|
+
for sample in dataset:
|
|
61
|
+
...
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Access sample's metadata via the `metadata` property, which
|
|
65
|
+
provides a dictionary-like interface to get and set metadata key-value pairs.
|
|
66
|
+
```python
|
|
67
|
+
some_value = sample.metadata["some_key"]
|
|
68
|
+
sample.metadata["another_key"] = "new_value"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Access sample's tags via the `tags` property.
|
|
72
|
+
```python
|
|
73
|
+
sample.tags = ["tag1", "tag2"] # Replace all tags
|
|
74
|
+
print(f"Current tags: {sample.tags}")
|
|
75
|
+
sample.add_tag("tag_3")
|
|
76
|
+
sample.remove_tag("tag_1")
|
|
77
|
+
```
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
_sample_table: SampleTable
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def sample_id(self) -> UUID:
|
|
84
|
+
"""Sample ID."""
|
|
85
|
+
return self._sample_table.sample_id
|
|
86
|
+
|
|
87
|
+
def __init__(self, sample_table: SampleTable) -> None:
|
|
88
|
+
"""Initialize the Sample.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
sample_table: The SampleTable SQLAlchemy model instance.
|
|
92
|
+
"""
|
|
93
|
+
self._sample_table = sample_table
|
|
94
|
+
self._metadata = SampleMetadata(self)
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def sample_table(self) -> SampleTable:
|
|
98
|
+
"""Returns the SampleTable associated with this Sample."""
|
|
99
|
+
# TODO(lukas 12/2025): This should be later removed, as it exposes private implementation.
|
|
100
|
+
# Remove this once we add a `annotations` property and `embeddings` property.
|
|
101
|
+
return self._sample_table
|
|
102
|
+
|
|
103
|
+
def get_object_session(self) -> Session:
|
|
104
|
+
"""Get the database session for this sample.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
The SQLModel session.
|
|
108
|
+
|
|
109
|
+
Raises:
|
|
110
|
+
RuntimeError: If no active session is found.
|
|
111
|
+
"""
|
|
112
|
+
session = object_session(self._sample_table)
|
|
113
|
+
if session is None:
|
|
114
|
+
raise RuntimeError("No active session found for the sample")
|
|
115
|
+
# Cast from SQLAlchemy Session to SQLModel Session for mypy.
|
|
116
|
+
return cast(Session, session)
|
|
117
|
+
|
|
118
|
+
def add_tag(self, name: str) -> None:
|
|
119
|
+
"""Add a tag to this sample.
|
|
120
|
+
|
|
121
|
+
If the tag doesn't exist, it will be created first.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
name: The name of the tag to add.
|
|
125
|
+
"""
|
|
126
|
+
session = self.get_object_session()
|
|
127
|
+
|
|
128
|
+
# Get or create the tag for this dataset.
|
|
129
|
+
tag = tag_resolver.get_or_create_sample_tag_by_name(
|
|
130
|
+
session=session, dataset_id=self.dataset_id, tag_name=name
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# Add the tag to the sample if not already associated.
|
|
134
|
+
if tag not in self.sample_table.tags:
|
|
135
|
+
tag_resolver.add_tag_to_sample(
|
|
136
|
+
session=session, tag_id=tag.tag_id, sample=self.sample_table
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
def remove_tag(self, name: str) -> None:
|
|
140
|
+
"""Remove a tag from this sample.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
name: The name of the tag to remove.
|
|
144
|
+
"""
|
|
145
|
+
session = self.get_object_session()
|
|
146
|
+
|
|
147
|
+
# Find the tag by name for this dataset.
|
|
148
|
+
existing_tag = tag_resolver.get_by_name(
|
|
149
|
+
session=session, tag_name=name, dataset_id=self.dataset_id
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
# Remove the tag from the sample if it exists and is associated
|
|
153
|
+
if existing_tag is not None and existing_tag in self.sample_table.tags:
|
|
154
|
+
tag_resolver.remove_tag_from_sample(
|
|
155
|
+
session=session, tag_id=existing_tag.tag_id, sample=self.sample_table
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
@property
|
|
159
|
+
def tags(self) -> set[str]:
|
|
160
|
+
"""Get the tag names associated with this sample.
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
A set of tag names as strings.
|
|
164
|
+
"""
|
|
165
|
+
return {tag.name for tag in self.sample_table.tags}
|
|
166
|
+
|
|
167
|
+
@tags.setter
|
|
168
|
+
def tags(self, tags: Iterable[str]) -> None:
|
|
169
|
+
"""Set the tags for this sample, replacing any existing tags.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
tags: Iterable of tag names to associate with this sample.
|
|
173
|
+
"""
|
|
174
|
+
# Get current tag names
|
|
175
|
+
current_tags = self.tags
|
|
176
|
+
new_tags = set(tags)
|
|
177
|
+
|
|
178
|
+
# Remove tags that are no longer needed
|
|
179
|
+
tags_to_remove = current_tags - new_tags
|
|
180
|
+
for tag_name in tags_to_remove:
|
|
181
|
+
self.remove_tag(tag_name)
|
|
182
|
+
|
|
183
|
+
# Add new tags
|
|
184
|
+
tags_to_add = new_tags - current_tags
|
|
185
|
+
for tag_name in tags_to_add:
|
|
186
|
+
self.add_tag(tag_name)
|
|
187
|
+
|
|
188
|
+
@property
|
|
189
|
+
def metadata(self) -> SampleMetadata:
|
|
190
|
+
"""Get dictionary-like access to sample metadata.
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
A dictionary-like object for accessing metadata.
|
|
194
|
+
"""
|
|
195
|
+
return self._metadata
|
|
196
|
+
|
|
197
|
+
@property
|
|
198
|
+
def dataset_id(self) -> UUID:
|
|
199
|
+
"""Get the dataset ID this sample belongs to.
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
The UUID of the dataset.
|
|
203
|
+
"""
|
|
204
|
+
return self.sample_table.dataset_id
|
|
205
|
+
|
|
206
|
+
def add_caption(self, text: str) -> None:
|
|
207
|
+
"""Add a caption to this sample.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
text: The text of the caption to add.
|
|
211
|
+
"""
|
|
212
|
+
session = self.get_object_session()
|
|
213
|
+
caption_resolver.create_many(
|
|
214
|
+
session=session,
|
|
215
|
+
parent_dataset_id=self.dataset_id,
|
|
216
|
+
captions=[
|
|
217
|
+
CaptionCreate(
|
|
218
|
+
parent_sample_id=self.sample_id,
|
|
219
|
+
text=text,
|
|
220
|
+
),
|
|
221
|
+
],
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
@property
|
|
225
|
+
def captions(self) -> list[str]:
|
|
226
|
+
"""Returns the text of all captions."""
|
|
227
|
+
return [caption.text for caption in self.sample_table.captions]
|
|
228
|
+
|
|
229
|
+
@captions.setter
|
|
230
|
+
def captions(self, captions: Iterable[str]) -> None:
|
|
231
|
+
"""Set the captions for this sample, replacing any existing captions.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
captions: Iterable of caption texts to associate with this sample.
|
|
235
|
+
"""
|
|
236
|
+
session = self.get_object_session()
|
|
237
|
+
|
|
238
|
+
# Delete all existing captions for this sample
|
|
239
|
+
caption_sample_ids = [c.sample_id for c in self.sample_table.captions]
|
|
240
|
+
for caption_sample_id in caption_sample_ids:
|
|
241
|
+
caption_resolver.delete_caption(session=session, sample_id=caption_sample_id)
|
|
242
|
+
|
|
243
|
+
# Create new captions from the provided texts
|
|
244
|
+
if captions:
|
|
245
|
+
caption_resolver.create_many(
|
|
246
|
+
session=session,
|
|
247
|
+
parent_dataset_id=self.dataset_id,
|
|
248
|
+
captions=[
|
|
249
|
+
CaptionCreate(parent_sample_id=self.sample_id, text=text) for text in captions
|
|
250
|
+
],
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
class SampleMetadata:
|
|
255
|
+
"""Dictionary-like interface for sample metadata."""
|
|
256
|
+
|
|
257
|
+
def __init__(self, sample: Sample) -> None:
|
|
258
|
+
"""Initialize SampleMetadata.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
sample: The Sample instance this metadata belongs to.
|
|
262
|
+
"""
|
|
263
|
+
self._sample = sample
|
|
264
|
+
|
|
265
|
+
def __getitem__(self, key: str) -> Any:
|
|
266
|
+
"""Get a metadata value by key.
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
key: The metadata key to access.
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
The metadata value for the given key, or None if the key doesn't exist.
|
|
273
|
+
"""
|
|
274
|
+
if self._sample.sample_table.metadata_dict is None:
|
|
275
|
+
return None
|
|
276
|
+
return self._sample.sample_table.metadata_dict.get_value(key)
|
|
277
|
+
|
|
278
|
+
def __setitem__(self, key: str, value: Any) -> None:
|
|
279
|
+
"""Set a metadata key-value pair.
|
|
280
|
+
|
|
281
|
+
Args:
|
|
282
|
+
key: The metadata key.
|
|
283
|
+
value: The metadata value.
|
|
284
|
+
"""
|
|
285
|
+
session = self._sample.get_object_session()
|
|
286
|
+
metadata_resolver.set_value_for_sample(
|
|
287
|
+
session=session,
|
|
288
|
+
sample_id=self._sample.sample_id,
|
|
289
|
+
key=key,
|
|
290
|
+
value=value,
|
|
291
|
+
)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Module to launch the GUI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
from lightly_studio import db_manager
|
|
8
|
+
from lightly_studio.api.server import Server
|
|
9
|
+
from lightly_studio.dataset import env
|
|
10
|
+
from lightly_studio.resolvers import dataset_resolver, sample_resolver
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _validate_has_samples() -> None:
|
|
16
|
+
"""Validate that there are samples in the database before starting GUI.
|
|
17
|
+
|
|
18
|
+
Raises:
|
|
19
|
+
ValueError: If no datasets are found or if no samples exist in any dataset.
|
|
20
|
+
"""
|
|
21
|
+
session = db_manager.persistent_session()
|
|
22
|
+
|
|
23
|
+
# Check if any datasets exist
|
|
24
|
+
datasets = dataset_resolver.get_all(session=session, offset=0, limit=1)
|
|
25
|
+
|
|
26
|
+
if not datasets:
|
|
27
|
+
raise ValueError(
|
|
28
|
+
"No datasets found. Please load a dataset using Dataset class methods "
|
|
29
|
+
"(e.g., add_images_from_path(), add_samples_from_yolo(), etc.) "
|
|
30
|
+
"before starting the GUI."
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# Check if there are any samples in the first dataset
|
|
34
|
+
first_dataset = datasets[0]
|
|
35
|
+
sample_count = sample_resolver.count_by_dataset_id(
|
|
36
|
+
session=session, dataset_id=first_dataset.dataset_id
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
if sample_count == 0:
|
|
40
|
+
raise ValueError(
|
|
41
|
+
"No images have been indexed for the first dataset. "
|
|
42
|
+
"Please ensure your dataset contains valid images and try loading again."
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def start_gui() -> None:
|
|
47
|
+
"""Launch the web interface for the loaded dataset."""
|
|
48
|
+
_validate_has_samples()
|
|
49
|
+
|
|
50
|
+
server = Server(host=env.LIGHTLY_STUDIO_HOST, port=env.LIGHTLY_STUDIO_PORT)
|
|
51
|
+
|
|
52
|
+
logger.info(f"Open the LightlyStudio GUI under: {env.APP_URL}")
|
|
53
|
+
|
|
54
|
+
server.start()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Definition of VideoSample class, representing a dataset video sample."""
|
|
2
|
+
|
|
3
|
+
from sqlmodel import col
|
|
4
|
+
|
|
5
|
+
from lightly_studio.core.sample import DBField, Sample
|
|
6
|
+
from lightly_studio.models.video import VideoTable
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class VideoSample(Sample):
|
|
10
|
+
"""Interface to a dataset video sample.
|
|
11
|
+
|
|
12
|
+
Many properties of the sample are directly accessible as attributes of this class.
|
|
13
|
+
```python
|
|
14
|
+
print(f"Sample file name: {sample.file_name}")
|
|
15
|
+
print(f"Sample file path: {sample.file_path_abs}")
|
|
16
|
+
print(f"Sample width: {sample.width}")
|
|
17
|
+
print(f"Sample height: {sample.height}")
|
|
18
|
+
print(f"Sample duration (seconds): {sample.duration_s}")
|
|
19
|
+
print(f"Sample FPS: {sample.fps}")
|
|
20
|
+
```
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
file_name = DBField(col(VideoTable.file_name))
|
|
24
|
+
width = DBField(col(VideoTable.width))
|
|
25
|
+
height = DBField(col(VideoTable.height))
|
|
26
|
+
file_path_abs = DBField(col(VideoTable.file_path_abs))
|
|
27
|
+
|
|
28
|
+
duration_s = DBField(col(VideoTable.duration_s))
|
|
29
|
+
fps = DBField(col(VideoTable.fps))
|
|
30
|
+
|
|
31
|
+
def __init__(self, inner: VideoTable) -> None:
|
|
32
|
+
"""Initialize the Sample.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
inner: The VideoTable SQLAlchemy model instance.
|
|
36
|
+
"""
|
|
37
|
+
self.inner = inner
|
|
38
|
+
super().__init__(sample_table=inner.sample)
|
|
File without changes
|