lightly-studio 0.3.1__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.
Potentially problematic release.
This version of lightly-studio might be problematic. Click here for more details.
- lightly_studio/__init__.py +11 -0
- lightly_studio/api/__init__.py +0 -0
- lightly_studio/api/app.py +110 -0
- lightly_studio/api/cache.py +77 -0
- lightly_studio/api/db.py +133 -0
- lightly_studio/api/db_tables.py +32 -0
- lightly_studio/api/features.py +7 -0
- lightly_studio/api/routes/api/annotation.py +233 -0
- lightly_studio/api/routes/api/annotation_label.py +90 -0
- lightly_studio/api/routes/api/annotation_task.py +38 -0
- lightly_studio/api/routes/api/classifier.py +387 -0
- lightly_studio/api/routes/api/dataset.py +182 -0
- lightly_studio/api/routes/api/dataset_tag.py +257 -0
- lightly_studio/api/routes/api/exceptions.py +96 -0
- lightly_studio/api/routes/api/features.py +17 -0
- lightly_studio/api/routes/api/metadata.py +37 -0
- lightly_studio/api/routes/api/metrics.py +80 -0
- lightly_studio/api/routes/api/sample.py +196 -0
- lightly_studio/api/routes/api/settings.py +45 -0
- lightly_studio/api/routes/api/status.py +19 -0
- lightly_studio/api/routes/api/text_embedding.py +48 -0
- lightly_studio/api/routes/api/validators.py +17 -0
- lightly_studio/api/routes/healthz.py +13 -0
- lightly_studio/api/routes/images.py +104 -0
- lightly_studio/api/routes/webapp.py +51 -0
- lightly_studio/api/server.py +82 -0
- lightly_studio/core/__init__.py +0 -0
- lightly_studio/core/dataset.py +523 -0
- lightly_studio/core/sample.py +77 -0
- lightly_studio/core/start_gui.py +15 -0
- lightly_studio/dataset/__init__.py +0 -0
- lightly_studio/dataset/edge_embedding_generator.py +144 -0
- lightly_studio/dataset/embedding_generator.py +91 -0
- lightly_studio/dataset/embedding_manager.py +163 -0
- lightly_studio/dataset/env.py +16 -0
- lightly_studio/dataset/file_utils.py +35 -0
- lightly_studio/dataset/loader.py +622 -0
- lightly_studio/dataset/mobileclip_embedding_generator.py +144 -0
- lightly_studio/dist_lightly_studio_view_app/_app/env.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/0.DenzbfeK.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- +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-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/SelectableSvgGroup.OwPEPQZu.css +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/SelectableSvgGroup.b653GmVf.css +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/_layout.T-zjSUd3.css +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/useFeatureFlags.CV-KWLNP.css +1 -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/B2FVR0s0.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/B90CZVMX.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/B9zumHo5.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/BJXwVxaE.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/Bsi3UGy5.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/Bu7uvVrG.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/Bx1xMsFy.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/BylOuP6i.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/C8I8rFJQ.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CDnpyLsT.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/CcaPhhk3.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CvOmgdoc.js +93 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CxtLVaYz.js +3 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/D5-A_Ffd.js +4 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/D6RI2Zrd.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/D6su9Aln.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/D98V7j6A.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DIRAtgl0.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/DOlTMNyt.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DjUWrjOv.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DjfY96ND.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/H7C68rOM.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/O-EABkf9.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/XO7A28GO.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/hQVEETDE.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/l7KrR96u.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/nAHhluT7.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/r64xT6ao.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/vC4nQVEB.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/x9G_hzyY.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/entry/app.CjnvpsmS.js +2 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/entry/start.0o1H7wM9.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/0.XRq_TUwu.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/1.B4rNYwVp.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/10.DfBwOEhN.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/11.CWG1ehzT.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/12.CwF2_8mP.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/2.CS4muRY-.js +6 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/3.CWHpKonm.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/4.OUWOLQeV.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/5.Dm6t9F5W.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/6.Bw5ck4gK.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/7.CF0EDTR6.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/8.Cw30LEcV.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/9.CPu3CiBc.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 +44 -0
- lightly_studio/examples/example.py +23 -0
- lightly_studio/examples/example_metadata.py +338 -0
- lightly_studio/examples/example_selection.py +39 -0
- lightly_studio/examples/example_split_work.py +67 -0
- lightly_studio/examples/example_v2.py +21 -0
- lightly_studio/export_schema.py +18 -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 +663 -0
- lightly_studio/few_shot_classifier/random_forest_classifier.py +489 -0
- lightly_studio/metadata/complex_metadata.py +47 -0
- lightly_studio/metadata/gps_coordinate.py +41 -0
- lightly_studio/metadata/metadata_protocol.py +17 -0
- lightly_studio/metrics/__init__.py +0 -0
- lightly_studio/metrics/detection/__init__.py +0 -0
- lightly_studio/metrics/detection/map.py +268 -0
- lightly_studio/models/__init__.py +1 -0
- lightly_studio/models/annotation/__init__.py +0 -0
- lightly_studio/models/annotation/annotation_base.py +171 -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/annotation_task.py +28 -0
- lightly_studio/models/classifier.py +20 -0
- lightly_studio/models/dataset.py +84 -0
- lightly_studio/models/embedding_model.py +30 -0
- lightly_studio/models/metadata.py +208 -0
- lightly_studio/models/sample.py +180 -0
- lightly_studio/models/sample_embedding.py +37 -0
- lightly_studio/models/settings.py +60 -0
- lightly_studio/models/tag.py +96 -0
- lightly_studio/py.typed +0 -0
- lightly_studio/resolvers/__init__.py +7 -0
- lightly_studio/resolvers/annotation_label_resolver/__init__.py +21 -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 +22 -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 +33 -0
- lightly_studio/resolvers/annotation_resolver/count_annotations_by_dataset.py +120 -0
- lightly_studio/resolvers/annotation_resolver/create.py +19 -0
- lightly_studio/resolvers/annotation_resolver/create_many.py +96 -0
- lightly_studio/resolvers/annotation_resolver/delete_annotation.py +45 -0
- lightly_studio/resolvers/annotation_resolver/delete_annotations.py +56 -0
- lightly_studio/resolvers/annotation_resolver/get_all.py +74 -0
- lightly_studio/resolvers/annotation_resolver/get_by_id.py +18 -0
- lightly_studio/resolvers/annotation_resolver/update_annotation_label.py +144 -0
- lightly_studio/resolvers/annotation_resolver/update_bounding_box.py +68 -0
- lightly_studio/resolvers/annotation_task_resolver.py +31 -0
- lightly_studio/resolvers/annotations/__init__.py +1 -0
- lightly_studio/resolvers/annotations/annotations_filter.py +89 -0
- lightly_studio/resolvers/dataset_resolver.py +278 -0
- lightly_studio/resolvers/embedding_model_resolver.py +100 -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_set_metadata.py +48 -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 +86 -0
- lightly_studio/resolvers/sample_resolver.py +249 -0
- lightly_studio/resolvers/samples_filter.py +81 -0
- lightly_studio/resolvers/settings_resolver.py +58 -0
- lightly_studio/resolvers/tag_resolver.py +276 -0
- lightly_studio/selection/README.md +6 -0
- lightly_studio/selection/mundig.py +105 -0
- lightly_studio/selection/select.py +96 -0
- lightly_studio/selection/select_via_db.py +93 -0
- lightly_studio/selection/selection_config.py +31 -0
- lightly_studio/services/annotations_service/__init__.py +21 -0
- lightly_studio/services/annotations_service/get_annotation_by_id.py +31 -0
- lightly_studio/services/annotations_service/update_annotation.py +65 -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 +19 -0
- lightly_studio/type_definitions.py +19 -0
- lightly_studio/vendor/ACKNOWLEDGEMENTS +422 -0
- lightly_studio/vendor/LICENSE +31 -0
- lightly_studio/vendor/LICENSE_weights_data +50 -0
- lightly_studio/vendor/README.md +5 -0
- lightly_studio/vendor/__init__.py +1 -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-0.3.1.dist-info/METADATA +520 -0
- lightly_studio-0.3.1.dist-info/RECORD +219 -0
- lightly_studio-0.3.1.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
"""Implementation for calculating Mean Average Precision (MAP)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
from collections import defaultdict
|
|
7
|
+
from collections.abc import Sequence
|
|
8
|
+
from uuid import UUID
|
|
9
|
+
|
|
10
|
+
import torch
|
|
11
|
+
from pydantic import BaseModel
|
|
12
|
+
from torch import Tensor
|
|
13
|
+
from torchmetrics.detection.mean_ap import MeanAveragePrecision
|
|
14
|
+
|
|
15
|
+
from lightly_studio.models.annotation.annotation_base import AnnotationBaseTable
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class DetectionMetricsMAP(BaseModel):
|
|
19
|
+
"""Response for computing the MAP detection metric."""
|
|
20
|
+
|
|
21
|
+
name: str
|
|
22
|
+
map: float
|
|
23
|
+
map_small: float
|
|
24
|
+
map_medium: float
|
|
25
|
+
map_large: float
|
|
26
|
+
mar_1: float
|
|
27
|
+
mar_10: float
|
|
28
|
+
mar_100: float
|
|
29
|
+
mar_small: float
|
|
30
|
+
mar_medium: float
|
|
31
|
+
mar_large: float
|
|
32
|
+
map_50: float
|
|
33
|
+
map_75: float
|
|
34
|
+
map_per_class: dict[str, float] | None = None
|
|
35
|
+
mar_100_per_class: dict[str, float] | None = None
|
|
36
|
+
classes: list[int]
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def value(self) -> float:
|
|
40
|
+
"""Backwards compatibility alias for map."""
|
|
41
|
+
return self.map
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def per_class(self) -> dict[str, float] | None:
|
|
45
|
+
"""Backwards compatibility alias for map_per_class."""
|
|
46
|
+
return self.map_per_class
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def calculate_map_metric( # noqa: C901
|
|
50
|
+
pred_annotations: Sequence[AnnotationBaseTable],
|
|
51
|
+
gt_annotations: Sequence[AnnotationBaseTable],
|
|
52
|
+
) -> DetectionMetricsMAP:
|
|
53
|
+
"""Calculate the Mean Average Precision (MAP) metric."""
|
|
54
|
+
if not gt_annotations or not pred_annotations:
|
|
55
|
+
return DetectionMetricsMAP(
|
|
56
|
+
name="mAP@.5:.95",
|
|
57
|
+
map=0.0,
|
|
58
|
+
map_small=0.0,
|
|
59
|
+
map_medium=0.0,
|
|
60
|
+
map_large=0.0,
|
|
61
|
+
mar_1=0.0,
|
|
62
|
+
mar_10=0.0,
|
|
63
|
+
mar_100=0.0,
|
|
64
|
+
mar_small=0.0,
|
|
65
|
+
mar_medium=0.0,
|
|
66
|
+
mar_large=0.0,
|
|
67
|
+
map_50=-1.0,
|
|
68
|
+
map_75=-1.0,
|
|
69
|
+
map_per_class=None,
|
|
70
|
+
mar_100_per_class=None,
|
|
71
|
+
classes=[],
|
|
72
|
+
)
|
|
73
|
+
uuid_to_int_label: dict[UUID, int] = {}
|
|
74
|
+
|
|
75
|
+
# Map sample_id to annotations.
|
|
76
|
+
sample_id_to_annotations = _group_by_sample_id(
|
|
77
|
+
pred_annotations=pred_annotations,
|
|
78
|
+
gt_annotations=gt_annotations,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# Create the MeanAveragePrecision object.
|
|
82
|
+
map_metric = MeanAveragePrecision(
|
|
83
|
+
box_format="xywh",
|
|
84
|
+
iou_thresholds=None,
|
|
85
|
+
class_metrics=True,
|
|
86
|
+
average="macro",
|
|
87
|
+
backend="faster_coco_eval",
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# Compute MAP updating the object sample by sample.
|
|
91
|
+
for _sample_id, (
|
|
92
|
+
sample_pred_annotations,
|
|
93
|
+
sample_gt_annotations,
|
|
94
|
+
) in sample_id_to_annotations.items():
|
|
95
|
+
# Convert to torchmetrics format.
|
|
96
|
+
prediction_tm = _convert_to_torchmetrics(
|
|
97
|
+
annotations=sample_pred_annotations,
|
|
98
|
+
is_prediction=True,
|
|
99
|
+
uuid_to_int_label_map=uuid_to_int_label,
|
|
100
|
+
)
|
|
101
|
+
ground_truth_tm = _convert_to_torchmetrics(
|
|
102
|
+
annotations=sample_gt_annotations,
|
|
103
|
+
is_prediction=False,
|
|
104
|
+
uuid_to_int_label_map=uuid_to_int_label,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# Update the metric.
|
|
108
|
+
map_metric.update(preds=[prediction_tm], target=[ground_truth_tm])
|
|
109
|
+
|
|
110
|
+
# Compute the final results.
|
|
111
|
+
results = map_metric.compute()
|
|
112
|
+
# Invert label map to recover original UUIDs
|
|
113
|
+
int_to_uuid = {v: k for k, v in uuid_to_int_label.items()}
|
|
114
|
+
|
|
115
|
+
# Helper to convert Tensor to Python list
|
|
116
|
+
def to_list(t: Tensor) -> list[float]:
|
|
117
|
+
if t.dim() == 0:
|
|
118
|
+
return [t.item()]
|
|
119
|
+
return t.tolist()
|
|
120
|
+
|
|
121
|
+
# Scalar metrics
|
|
122
|
+
map_val = results["map"].item()
|
|
123
|
+
map_small = results["map_small"].item()
|
|
124
|
+
map_medium = results["map_medium"].item()
|
|
125
|
+
map_large = results["map_large"].item()
|
|
126
|
+
mar_1 = results["mar_1"].item()
|
|
127
|
+
mar_10 = results["mar_10"].item()
|
|
128
|
+
mar_100 = results["mar_100"].item()
|
|
129
|
+
mar_small = results["mar_small"].item()
|
|
130
|
+
mar_medium = results["mar_medium"].item()
|
|
131
|
+
mar_large = results["mar_large"].item()
|
|
132
|
+
|
|
133
|
+
# IoU-specific metrics
|
|
134
|
+
map_50 = results.get("map_50")
|
|
135
|
+
map_50 = map_50.item() if isinstance(map_50, Tensor) and map_50.dim() == 0 else -1.0
|
|
136
|
+
map_75 = results.get("map_75")
|
|
137
|
+
map_75 = map_75.item() if isinstance(map_75, Tensor) and map_75.dim() == 0 else -1.0
|
|
138
|
+
|
|
139
|
+
# Per-class metrics
|
|
140
|
+
raw_map_pc = results.get("map_per_class")
|
|
141
|
+
per_class_dict: dict[str, float] | None = None
|
|
142
|
+
if raw_map_pc is not None:
|
|
143
|
+
pc_list = to_list(raw_map_pc)
|
|
144
|
+
per_class_dict = {}
|
|
145
|
+
for idx, val in enumerate(pc_list):
|
|
146
|
+
if not math.isnan(val):
|
|
147
|
+
per_class_dict[str(int_to_uuid[idx])] = val
|
|
148
|
+
|
|
149
|
+
# Per-class recall at 100 detections
|
|
150
|
+
raw_mar100_pc = results.get("mar_100_per_class")
|
|
151
|
+
mar_100_pc_dict: dict[str, float] | None = None
|
|
152
|
+
if raw_mar100_pc is not None:
|
|
153
|
+
m100_list = to_list(raw_mar100_pc)
|
|
154
|
+
mar_100_pc_dict = {}
|
|
155
|
+
for idx, val in enumerate(m100_list):
|
|
156
|
+
if not math.isnan(val):
|
|
157
|
+
mar_100_pc_dict[str(int_to_uuid[idx])] = val
|
|
158
|
+
|
|
159
|
+
# Observed classes
|
|
160
|
+
raw_classes = results.get("classes")
|
|
161
|
+
classes_list_output: list[int] = []
|
|
162
|
+
if raw_classes is not None:
|
|
163
|
+
classes_list_output = [int(x) for x in to_list(raw_classes)]
|
|
164
|
+
|
|
165
|
+
return DetectionMetricsMAP(
|
|
166
|
+
name="mAP@.5:.95",
|
|
167
|
+
map=map_val,
|
|
168
|
+
map_small=map_small,
|
|
169
|
+
map_medium=map_medium,
|
|
170
|
+
map_large=map_large,
|
|
171
|
+
mar_1=mar_1,
|
|
172
|
+
mar_10=mar_10,
|
|
173
|
+
mar_100=mar_100,
|
|
174
|
+
mar_small=mar_small,
|
|
175
|
+
mar_medium=mar_medium,
|
|
176
|
+
mar_large=mar_large,
|
|
177
|
+
map_50=map_50,
|
|
178
|
+
map_75=map_75,
|
|
179
|
+
map_per_class=per_class_dict,
|
|
180
|
+
mar_100_per_class=mar_100_pc_dict,
|
|
181
|
+
classes=classes_list_output,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _group_by_sample_id(
|
|
186
|
+
pred_annotations: Sequence[AnnotationBaseTable],
|
|
187
|
+
gt_annotations: Sequence[AnnotationBaseTable],
|
|
188
|
+
) -> dict[
|
|
189
|
+
UUID,
|
|
190
|
+
tuple[list[AnnotationBaseTable], list[AnnotationBaseTable]],
|
|
191
|
+
]:
|
|
192
|
+
"""Group prediction and ground truth annotations by sample_id.
|
|
193
|
+
|
|
194
|
+
Returns a dictionary with sample_id as key and a tuple of
|
|
195
|
+
(list of prediction annotations, list of ground truth annotations) as value.
|
|
196
|
+
"""
|
|
197
|
+
sample_id_to_annotations: defaultdict[
|
|
198
|
+
UUID,
|
|
199
|
+
tuple[list[AnnotationBaseTable], list[AnnotationBaseTable]],
|
|
200
|
+
] = defaultdict(lambda: ([], []))
|
|
201
|
+
for ann in pred_annotations:
|
|
202
|
+
sample_id_to_annotations[ann.sample_id][0].append(ann)
|
|
203
|
+
for ann in gt_annotations:
|
|
204
|
+
sample_id_to_annotations[ann.sample_id][1].append(ann)
|
|
205
|
+
return sample_id_to_annotations
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _convert_to_torchmetrics(
|
|
209
|
+
annotations: Sequence[AnnotationBaseTable],
|
|
210
|
+
is_prediction: bool,
|
|
211
|
+
uuid_to_int_label_map: dict[UUID, int],
|
|
212
|
+
) -> dict[str, Tensor]:
|
|
213
|
+
"""Convert annotations to torchmetrics format.
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
annotations: List of bounding box annotations.
|
|
217
|
+
is_prediction: Whether the annotations are predictions.
|
|
218
|
+
uuid_to_int_label_map: Map from UUID to integer label.
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
Dictionary in torchmetrics format. For predictions the keys are
|
|
222
|
+
`boxes`, `scores`, `labels`. For ground truth they are `boxes`,
|
|
223
|
+
`labels`.
|
|
224
|
+
|
|
225
|
+
"""
|
|
226
|
+
if not annotations:
|
|
227
|
+
empty_boxes = torch.empty((0, 4), dtype=torch.float32)
|
|
228
|
+
empty_labels = torch.empty((0,), dtype=torch.int64)
|
|
229
|
+
if is_prediction:
|
|
230
|
+
return {
|
|
231
|
+
"boxes": empty_boxes,
|
|
232
|
+
"scores": torch.empty((0,), dtype=torch.float32),
|
|
233
|
+
"labels": empty_labels,
|
|
234
|
+
}
|
|
235
|
+
return {"boxes": empty_boxes, "labels": empty_labels}
|
|
236
|
+
|
|
237
|
+
boxes = torch.tensor(
|
|
238
|
+
[
|
|
239
|
+
[
|
|
240
|
+
a.object_detection_details.x,
|
|
241
|
+
a.object_detection_details.y,
|
|
242
|
+
a.object_detection_details.width,
|
|
243
|
+
a.object_detection_details.height,
|
|
244
|
+
]
|
|
245
|
+
for a in annotations
|
|
246
|
+
if a.object_detection_details is not None
|
|
247
|
+
],
|
|
248
|
+
dtype=torch.float32,
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
mapped_labels = []
|
|
252
|
+
# Use the passed-in map instead of a global one
|
|
253
|
+
for uuid_label in (a.annotation_label_id for a in annotations):
|
|
254
|
+
if uuid_label not in uuid_to_int_label_map:
|
|
255
|
+
# Assign the next available integer ID based on the current map size
|
|
256
|
+
uuid_to_int_label_map[uuid_label] = len(uuid_to_int_label_map)
|
|
257
|
+
mapped_labels.append(uuid_to_int_label_map[uuid_label])
|
|
258
|
+
|
|
259
|
+
labels = torch.tensor(mapped_labels, dtype=torch.int64)
|
|
260
|
+
|
|
261
|
+
if is_prediction:
|
|
262
|
+
scores = torch.tensor([a.confidence for a in annotations], dtype=torch.float32)
|
|
263
|
+
return {
|
|
264
|
+
"boxes": boxes,
|
|
265
|
+
"scores": scores,
|
|
266
|
+
"labels": labels,
|
|
267
|
+
}
|
|
268
|
+
return {"boxes": boxes, "labels": labels}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
File without changes
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""This module defines the base annotation model."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
from typing import TYPE_CHECKING, List, Optional
|
|
5
|
+
from uuid import UUID, uuid4
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
from pydantic import Field as PydanticField
|
|
9
|
+
from sqlalchemy.orm import Mapped
|
|
10
|
+
from sqlmodel import Field, Relationship, SQLModel
|
|
11
|
+
|
|
12
|
+
from lightly_studio.models.annotation.instance_segmentation import (
|
|
13
|
+
InstanceSegmentationAnnotationTable,
|
|
14
|
+
InstanceSegmentationAnnotationView,
|
|
15
|
+
)
|
|
16
|
+
from lightly_studio.models.annotation.links import AnnotationTagLinkTable
|
|
17
|
+
from lightly_studio.models.annotation.object_detection import (
|
|
18
|
+
ObjectDetectionAnnotationTable,
|
|
19
|
+
ObjectDetectionAnnotationView,
|
|
20
|
+
)
|
|
21
|
+
from lightly_studio.models.annotation.semantic_segmentation import (
|
|
22
|
+
SemanticSegmentationAnnotationTable,
|
|
23
|
+
SemanticSegmentationAnnotationView,
|
|
24
|
+
)
|
|
25
|
+
from lightly_studio.models.annotation_task import (
|
|
26
|
+
AnnotationTaskTable,
|
|
27
|
+
AnnotationType,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
if TYPE_CHECKING:
|
|
31
|
+
from lightly_studio.models.annotation_label import (
|
|
32
|
+
AnnotationLabelTable,
|
|
33
|
+
)
|
|
34
|
+
from lightly_studio.models.sample import (
|
|
35
|
+
SampleTable,
|
|
36
|
+
)
|
|
37
|
+
from lightly_studio.models.tag import TagTable
|
|
38
|
+
else:
|
|
39
|
+
TagTable = object
|
|
40
|
+
SampleTable = object
|
|
41
|
+
AnnotationLabelTable = object
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class AnnotationBaseTable(SQLModel, table=True):
|
|
45
|
+
"""Base class for all annotation models."""
|
|
46
|
+
|
|
47
|
+
__tablename__ = "annotation_base"
|
|
48
|
+
|
|
49
|
+
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc), index=True)
|
|
50
|
+
|
|
51
|
+
annotation_id: UUID = Field(default_factory=uuid4, primary_key=True)
|
|
52
|
+
annotation_type: AnnotationType
|
|
53
|
+
annotation_label_id: UUID = Field(foreign_key="annotation_labels.annotation_label_id")
|
|
54
|
+
annotation_task_id: UUID = Field(
|
|
55
|
+
foreign_key="annotation_tasks.annotation_task_id",
|
|
56
|
+
)
|
|
57
|
+
confidence: Optional[float] = None
|
|
58
|
+
dataset_id: UUID = Field(foreign_key="datasets.dataset_id")
|
|
59
|
+
sample_id: UUID = Field(foreign_key="samples.sample_id")
|
|
60
|
+
|
|
61
|
+
annotation_label: Mapped["AnnotationLabelTable"] = Relationship(
|
|
62
|
+
sa_relationship_kwargs={"lazy": "select"},
|
|
63
|
+
)
|
|
64
|
+
annotation_task: Mapped["AnnotationTaskTable"] = Relationship(
|
|
65
|
+
sa_relationship_kwargs={"lazy": "select"},
|
|
66
|
+
)
|
|
67
|
+
sample: Mapped[Optional["SampleTable"]] = Relationship(
|
|
68
|
+
sa_relationship_kwargs={"lazy": "select"},
|
|
69
|
+
)
|
|
70
|
+
tags: Mapped[List["TagTable"]] = Relationship(
|
|
71
|
+
back_populates="annotations",
|
|
72
|
+
link_model=AnnotationTagLinkTable,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
""" Details about object detection. """
|
|
76
|
+
object_detection_details: Mapped[Optional["ObjectDetectionAnnotationTable"]] = Relationship(
|
|
77
|
+
back_populates="annotation_base",
|
|
78
|
+
sa_relationship_kwargs={"lazy": "select"},
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
""" Details about instance segmentation. """
|
|
82
|
+
instance_segmentation_details: Mapped[Optional["InstanceSegmentationAnnotationTable"]] = (
|
|
83
|
+
Relationship(
|
|
84
|
+
back_populates="annotation_base",
|
|
85
|
+
sa_relationship_kwargs={"lazy": "select"},
|
|
86
|
+
)
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
""" Details about semantic segmentation. """
|
|
90
|
+
semantic_segmentation_details: Mapped[Optional["SemanticSegmentationAnnotationTable"]] = (
|
|
91
|
+
Relationship(
|
|
92
|
+
back_populates="annotation_base",
|
|
93
|
+
sa_relationship_kwargs={"lazy": "select"},
|
|
94
|
+
)
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class AnnotationCreate(SQLModel):
|
|
99
|
+
"""Input model for creating annotations."""
|
|
100
|
+
|
|
101
|
+
""" Required properties for all annotations. """
|
|
102
|
+
annotation_label_id: UUID
|
|
103
|
+
annotation_type: AnnotationType
|
|
104
|
+
annotation_task_id: UUID
|
|
105
|
+
confidence: Optional[float] = None
|
|
106
|
+
dataset_id: UUID
|
|
107
|
+
sample_id: UUID
|
|
108
|
+
|
|
109
|
+
""" Optional properties for object detection. """
|
|
110
|
+
x: Optional[float] = None
|
|
111
|
+
y: Optional[float] = None
|
|
112
|
+
width: Optional[float] = None
|
|
113
|
+
height: Optional[float] = None
|
|
114
|
+
|
|
115
|
+
""" Optional properties for instance and semantic segmentation. """
|
|
116
|
+
segmentation_mask: Optional[List[int]] = None
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class AnnotationSampleView(SQLModel):
|
|
120
|
+
"""Sample class for annotation view."""
|
|
121
|
+
|
|
122
|
+
file_path_abs: str
|
|
123
|
+
file_name: str
|
|
124
|
+
dataset_id: UUID
|
|
125
|
+
sample_id: UUID
|
|
126
|
+
width: int
|
|
127
|
+
height: int
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class AnnotationView(SQLModel):
|
|
131
|
+
"""Response model for bounding box annotation."""
|
|
132
|
+
|
|
133
|
+
class AnnotationLabel(SQLModel):
|
|
134
|
+
"""Model used when retrieving an annotation label."""
|
|
135
|
+
|
|
136
|
+
annotation_label_name: str
|
|
137
|
+
|
|
138
|
+
sample_id: UUID
|
|
139
|
+
dataset_id: UUID
|
|
140
|
+
annotation_id: UUID
|
|
141
|
+
annotation_type: AnnotationType
|
|
142
|
+
annotation_label: AnnotationLabel
|
|
143
|
+
annotation_task_id: UUID
|
|
144
|
+
confidence: Optional[float] = None
|
|
145
|
+
|
|
146
|
+
object_detection_details: Optional[ObjectDetectionAnnotationView] = None
|
|
147
|
+
instance_segmentation_details: Optional[InstanceSegmentationAnnotationView] = None
|
|
148
|
+
semantic_segmentation_details: Optional[SemanticSegmentationAnnotationView] = None
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class AnnotationWithSampleView(AnnotationView):
|
|
152
|
+
"""Response model for bounding box annotation."""
|
|
153
|
+
|
|
154
|
+
sample: AnnotationSampleView
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class AnnotationViewsWithCount(BaseModel):
|
|
158
|
+
"""Response model for counted annotations."""
|
|
159
|
+
|
|
160
|
+
annotations: List[AnnotationWithSampleView] = PydanticField(..., alias="data")
|
|
161
|
+
total_count: int
|
|
162
|
+
next_cursor: Optional[int] = PydanticField(..., alias="nextCursor")
|
|
163
|
+
|
|
164
|
+
class Config: # noqa: D106
|
|
165
|
+
populate_by_name = True
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class AnnotationDetailsView(AnnotationView):
|
|
169
|
+
"""Representing detailed view of an annotation."""
|
|
170
|
+
|
|
171
|
+
sample: AnnotationSampleView
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""Instance segmentation annotation models.
|
|
2
|
+
|
|
3
|
+
Instance segmentation combines object detection and semantic segmentation,
|
|
4
|
+
identifying objects and providing pixel-level masks for each instance.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import TYPE_CHECKING, List, Optional
|
|
8
|
+
from uuid import UUID, uuid4
|
|
9
|
+
|
|
10
|
+
from sqlalchemy import ARRAY, Column, Integer
|
|
11
|
+
from sqlalchemy.orm import Mapped
|
|
12
|
+
from sqlmodel import Field, Relationship, SQLModel
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from lightly_studio.models.annotation.annotation_base import (
|
|
16
|
+
AnnotationBaseTable,
|
|
17
|
+
)
|
|
18
|
+
else:
|
|
19
|
+
AnnotationBaseTable = object
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class InstanceSegmentationAnnotationTable(SQLModel, table=True):
|
|
23
|
+
"""Database table model for instance segmentation annotations."""
|
|
24
|
+
|
|
25
|
+
__tablename__ = "instance_segmentation_annotations"
|
|
26
|
+
|
|
27
|
+
annotation_id: UUID = Field(
|
|
28
|
+
default_factory=uuid4,
|
|
29
|
+
primary_key=True,
|
|
30
|
+
foreign_key="annotation_base.annotation_id",
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
annotation_base: Mapped["AnnotationBaseTable"] = Relationship(
|
|
34
|
+
back_populates="instance_segmentation_details"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
x: float
|
|
38
|
+
y: float
|
|
39
|
+
width: float
|
|
40
|
+
height: float
|
|
41
|
+
# TODO(Kondrat 06/2025): We need to fix logic in the loader,
|
|
42
|
+
# because it shouldn't be optional.
|
|
43
|
+
# lightly_studio/dataset/loader.py#L148
|
|
44
|
+
segmentation_mask: Optional[List[int]] = Field(
|
|
45
|
+
default=None, sa_column=Column(ARRAY(Integer), nullable=True)
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class InstanceSegmentationAnnotationView(SQLModel):
|
|
50
|
+
"""API response model for instance segmentation annotations."""
|
|
51
|
+
|
|
52
|
+
x: float
|
|
53
|
+
y: float
|
|
54
|
+
width: float
|
|
55
|
+
height: float
|
|
56
|
+
segmentation_mask: Optional[List[int]] = None
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""This module defines the base annotation model."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
from uuid import UUID
|
|
5
|
+
|
|
6
|
+
from sqlmodel import Field, SQLModel
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AnnotationTagLinkTable(SQLModel, table=True):
|
|
10
|
+
"""Model defines the link table between annotations and tags."""
|
|
11
|
+
|
|
12
|
+
annotation_id: Optional[UUID] = Field(
|
|
13
|
+
default=None,
|
|
14
|
+
foreign_key="annotation_base.annotation_id",
|
|
15
|
+
primary_key=True,
|
|
16
|
+
)
|
|
17
|
+
tag_id: Optional[UUID] = Field(default=None, foreign_key="tags.tag_id", primary_key=True)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Object detection annotation models.
|
|
2
|
+
|
|
3
|
+
Object detection identifies and locates objects in images using bounding boxes.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
from uuid import UUID, uuid4
|
|
8
|
+
|
|
9
|
+
from sqlalchemy.orm import Mapped
|
|
10
|
+
from sqlmodel import Field, Relationship, SQLModel
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from lightly_studio.models.annotation.annotation_base import (
|
|
14
|
+
AnnotationBaseTable,
|
|
15
|
+
)
|
|
16
|
+
else:
|
|
17
|
+
AnnotationBaseTable = object
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ObjectDetectionAnnotationTable(SQLModel, table=True):
|
|
21
|
+
"""Database table model for object detection annotations."""
|
|
22
|
+
|
|
23
|
+
__tablename__ = "object_detection_annotations"
|
|
24
|
+
|
|
25
|
+
annotation_id: UUID = Field(
|
|
26
|
+
default_factory=uuid4,
|
|
27
|
+
primary_key=True,
|
|
28
|
+
foreign_key="annotation_base.annotation_id",
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
annotation_base: Mapped["AnnotationBaseTable"] = Relationship(
|
|
32
|
+
back_populates="object_detection_details"
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
x: float
|
|
36
|
+
y: float
|
|
37
|
+
width: float
|
|
38
|
+
height: float
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ObjectDetectionAnnotationView(SQLModel):
|
|
42
|
+
"""API response model for object detection annotations."""
|
|
43
|
+
|
|
44
|
+
x: float
|
|
45
|
+
y: float
|
|
46
|
+
width: float
|
|
47
|
+
height: float
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""This module defines the semantic segmentation annotation model.
|
|
2
|
+
|
|
3
|
+
Semantic segmentation is a computer vision task that assigns a class label to
|
|
4
|
+
each pixel in an image. This module provides the data models for storing and
|
|
5
|
+
managing semantic segmentation annotations.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import TYPE_CHECKING, List
|
|
9
|
+
from uuid import UUID, uuid4
|
|
10
|
+
|
|
11
|
+
from sqlalchemy import ARRAY, Column, Integer
|
|
12
|
+
from sqlalchemy.orm import Mapped
|
|
13
|
+
from sqlmodel import Field, Relationship, SQLModel
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from lightly_studio.models.annotation.annotation_base import (
|
|
17
|
+
AnnotationBaseTable,
|
|
18
|
+
)
|
|
19
|
+
else:
|
|
20
|
+
AnnotationBaseTable = object
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class SemanticSegmentationAnnotationTable(SQLModel, table=True):
|
|
24
|
+
"""Model used to define semantic segmentation annotation table."""
|
|
25
|
+
|
|
26
|
+
__tablename__ = "semantic_segmentation_annotations"
|
|
27
|
+
|
|
28
|
+
annotation_id: UUID = Field(
|
|
29
|
+
default_factory=uuid4,
|
|
30
|
+
primary_key=True,
|
|
31
|
+
foreign_key="annotation_base.annotation_id",
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
segmentation_mask: List[int] = Field(sa_column=Column(ARRAY(Integer), nullable=True))
|
|
35
|
+
|
|
36
|
+
annotation_base: Mapped["AnnotationBaseTable"] = Relationship(
|
|
37
|
+
back_populates="semantic_segmentation_details"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class SemanticSegmentationAnnotationView(SQLModel):
|
|
42
|
+
"""Response model for semantic segmentation annotation."""
|
|
43
|
+
|
|
44
|
+
segmentation_mask: List[int]
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""This module defines the AnnotationLabel model for the application."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
from typing import TYPE_CHECKING, List
|
|
5
|
+
from uuid import UUID, uuid4
|
|
6
|
+
|
|
7
|
+
from sqlalchemy.orm import Mapped
|
|
8
|
+
from sqlmodel import Field, Relationship, SQLModel
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from lightly_studio.models.annotation.annotation_base import (
|
|
12
|
+
AnnotationBaseTable,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
else:
|
|
16
|
+
AnnotationBaseTable = object
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class AnnotationLabelBase(SQLModel):
|
|
20
|
+
"""Base class for the AnnotationLabel model."""
|
|
21
|
+
|
|
22
|
+
annotation_label_name: str
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class AnnotationLabelCreate(AnnotationLabelBase):
|
|
26
|
+
"""Model used when creating an annotation label."""
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class AnnotationLabelView(AnnotationLabelBase):
|
|
30
|
+
"""Model used when retrieving an annotation label."""
|
|
31
|
+
|
|
32
|
+
annotation_label_id: UUID
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class AnnotationLabelTable(AnnotationLabelBase, table=True):
|
|
36
|
+
"""This class defines the AnnotationLabel model."""
|
|
37
|
+
|
|
38
|
+
__tablename__ = "annotation_labels"
|
|
39
|
+
|
|
40
|
+
annotation_label_id: UUID = Field(default_factory=uuid4, primary_key=True)
|
|
41
|
+
created_at: str = Field(
|
|
42
|
+
default_factory=lambda: datetime.now(timezone.utc),
|
|
43
|
+
index=True,
|
|
44
|
+
)
|
|
45
|
+
annotations: Mapped[List["AnnotationBaseTable"]] = Relationship(
|
|
46
|
+
back_populates="annotation_label",
|
|
47
|
+
)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""This module defines the AnnotationTask model."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from uuid import UUID, uuid4
|
|
6
|
+
|
|
7
|
+
from sqlmodel import Field, SQLModel
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AnnotationType(str, Enum):
|
|
11
|
+
"""The type of annotation task."""
|
|
12
|
+
|
|
13
|
+
BBOX = "bbox"
|
|
14
|
+
CLASSIFICATION = "classification"
|
|
15
|
+
SEMANTIC_SEGMENTATION = "semantic_segmentation"
|
|
16
|
+
INSTANCE_SEGMENTATION = "instance_segmentation"
|
|
17
|
+
OBJECT_DETECTION = "object_detection"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class AnnotationTaskTable(SQLModel, table=True):
|
|
21
|
+
"""The annotation task model."""
|
|
22
|
+
|
|
23
|
+
__tablename__ = "annotation_tasks"
|
|
24
|
+
annotation_task_id: UUID = Field(default_factory=uuid4, primary_key=True)
|
|
25
|
+
name: str
|
|
26
|
+
created_at: datetime = Field(default_factory=datetime.utcnow)
|
|
27
|
+
annotation_type: AnnotationType
|
|
28
|
+
is_prediction: bool
|