lightly-studio 0.3.2__py3-none-any.whl → 0.3.3__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 +1 -1
- lightly_studio/api/app.py +6 -4
- lightly_studio/api/db_tables.py +0 -3
- lightly_studio/api/routes/api/annotation.py +26 -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/dataset.py +3 -5
- lightly_studio/api/routes/api/embeddings2d.py +104 -0
- lightly_studio/api/routes/api/export.py +73 -0
- lightly_studio/api/routes/api/selection.py +87 -0
- lightly_studio/core/add_samples.py +0 -9
- lightly_studio/core/dataset.py +32 -48
- lightly_studio/core/dataset_query/dataset_query.py +5 -0
- lightly_studio/dataset/env.py +4 -0
- lightly_studio/dataset/file_utils.py +13 -2
- lightly_studio/dataset/loader.py +0 -54
- lightly_studio/dataset/mobileclip_embedding_generator.py +3 -2
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/0.CA_CXIBb.css +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/_layout.DS78jgNY.css +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/{SelectableSvgGroup.BNTuXSAe.css → index.BVs_sZj9.css} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/{SelectableSvgGroup.BBm0IWdq.css → transform.D487hwJk.css} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/6t3IJ0vQ.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{gLNdjSzu.js → 8NsknIT2.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{Cur71c3O.js → BND_-4Kp.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{DRZO-E-T.js → BdfTHw61.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{BqBqV92V.js → BfHVnyNT.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/BjkP1AHA.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/BuuNVL9G.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{7YNGEs1C.js → BzKGpnl4.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{C0JiMuYn.js → CCx7Ho51.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{DcGCxgpH.js → CH6P3X75.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CR2upx_Q.js +4 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CWPZrTTJ.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{Ccq4ZD0B.js → Cs1XmhiF.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{OH7-C_mc.js → CwPowJfP.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CxFKfZ9T.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/Cxevwdid.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{C98Hk3r5.js → D4whDBUi.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/D6r9vr07.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{DqUGznj_.js → DA6bFLPR.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{2O287xak.js → DEgUu98i.js} +2 -2
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{KpAtIldw.js → DGTPl6Gk.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{Cs31G8Qn.js → DKGxBSlK.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{D8GZDMNN.js → DQXoLcsF.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DQe_kdRt.js +92 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DcY4jgG3.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{Crk-jcvV.js → RmD8FzRo.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/V-MnMC1X.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{Df3aMO5B.js → keKYsoph.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/entry/{app.BI-EA5gL.js → app.BVr6DYqP.js} +2 -2
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/entry/start.u7zsVvqp.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/0.Da2agmdd.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/{1._I9GR805.js → 1.B11tVRJV.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/{10.J2RBFrSr.js → 10.l30Zud4h.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/{12.Cmqj25a-.js → 12.CgKPGcAP.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/2.C8HLK8mj.js +857 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/{3.w9g4AcAx.js → 3.CLvg3QcJ.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/{4.BBI8KwnD.js → 4.BQhDtXUI.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/5.-6XqWX5G.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/{6.CrbkRPam.js → 6.uBV1Lhat.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/{7.FomEdhD6.js → 7.BXsgoQZh.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/8.BkbcnUs8.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/{9.CajIG5ce.js → 9.Bkrv-Vww.js} +1 -1
- 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 -1
- lightly_studio/dist_lightly_studio_view_app/index.html +14 -14
- lightly_studio/export/export_dataset.py +65 -0
- lightly_studio/export/lightly_studio_label_input.py +120 -0
- lightly_studio/few_shot_classifier/classifier_manager.py +5 -26
- lightly_studio/metadata/compute_typicality.py +67 -0
- lightly_studio/models/annotation/annotation_base.py +11 -12
- lightly_studio/resolvers/annotation_label_resolver/__init__.py +2 -1
- lightly_studio/resolvers/annotation_label_resolver/get_all.py +15 -0
- lightly_studio/resolvers/annotation_resolver/__init__.py +2 -3
- lightly_studio/resolvers/annotation_resolver/create_many.py +3 -3
- lightly_studio/resolvers/annotation_resolver/delete_annotation.py +1 -1
- lightly_studio/resolvers/annotation_resolver/delete_annotations.py +7 -3
- lightly_studio/resolvers/annotation_resolver/get_by_id.py +19 -1
- lightly_studio/resolvers/annotation_resolver/update_annotation_label.py +0 -1
- lightly_studio/resolvers/annotations/annotations_filter.py +1 -11
- lightly_studio/selection/mundig.py +7 -10
- lightly_studio/selection/selection_config.py +4 -1
- lightly_studio/services/annotations_service/__init__.py +8 -0
- lightly_studio/services/annotations_service/create_annotation.py +63 -0
- lightly_studio/services/annotations_service/delete_annotation.py +22 -0
- {lightly_studio-0.3.2.dist-info → lightly_studio-0.3.3.dist-info}/METADATA +152 -27
- {lightly_studio-0.3.2.dist-info → lightly_studio-0.3.3.dist-info}/RECORD +89 -85
- lightly_studio/api/routes/api/annotation_task.py +0 -37
- lightly_studio/api/routes/api/metrics.py +0 -76
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/0.DenzbfeK.css +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/_layout.T-zjSUd3.css +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/BBoGk9hq.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/BRnH9v23.js +0 -92
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/Bg1Y5eUZ.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CG0dMCJi.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/Cpy-nab_.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CsKrY2zA.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CzgC3GFB.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DFRh-Spp.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DkR_EZ_B.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/M1Q1F7bw.js +0 -4
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/i0ZZ4z06.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/entry/start.CcsRl3cZ.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/0.BbO4Zc3r.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/2.C45iKJHA.js +0 -6
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/5.huHuxdiF.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/8.Cb_ADSLk.js +0 -1
- lightly_studio/metrics/__init__.py +0 -0
- lightly_studio/metrics/detection/__init__.py +0 -0
- lightly_studio/metrics/detection/map.py +0 -268
- lightly_studio/models/annotation_task.py +0 -28
- lightly_studio/resolvers/annotation_resolver/create.py +0 -19
- lightly_studio/resolvers/annotation_task_resolver.py +0 -31
- {lightly_studio-0.3.2.dist-info → lightly_studio-0.3.3.dist-info}/WHEEL +0 -0
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
from lightly_studio.resolvers.annotation_resolver.count_annotations_by_dataset import (
|
|
4
4
|
count_annotations_by_dataset,
|
|
5
5
|
)
|
|
6
|
-
from lightly_studio.resolvers.annotation_resolver.create import create
|
|
7
6
|
from lightly_studio.resolvers.annotation_resolver.create_many import create_many
|
|
8
7
|
from lightly_studio.resolvers.annotation_resolver.delete_annotation import (
|
|
9
8
|
delete_annotation,
|
|
@@ -12,7 +11,7 @@ from lightly_studio.resolvers.annotation_resolver.delete_annotations import (
|
|
|
12
11
|
delete_annotations,
|
|
13
12
|
)
|
|
14
13
|
from lightly_studio.resolvers.annotation_resolver.get_all import get_all
|
|
15
|
-
from lightly_studio.resolvers.annotation_resolver.get_by_id import get_by_id
|
|
14
|
+
from lightly_studio.resolvers.annotation_resolver.get_by_id import get_by_id, get_by_ids
|
|
16
15
|
from lightly_studio.resolvers.annotation_resolver.update_annotation_label import (
|
|
17
16
|
update_annotation_label,
|
|
18
17
|
)
|
|
@@ -22,12 +21,12 @@ from lightly_studio.resolvers.annotation_resolver.update_bounding_box import (
|
|
|
22
21
|
|
|
23
22
|
__all__ = [
|
|
24
23
|
"count_annotations_by_dataset",
|
|
25
|
-
"create",
|
|
26
24
|
"create_many",
|
|
27
25
|
"delete_annotation",
|
|
28
26
|
"delete_annotations",
|
|
29
27
|
"get_all",
|
|
30
28
|
"get_by_id",
|
|
29
|
+
"get_by_ids",
|
|
31
30
|
"update_annotation_label",
|
|
32
31
|
"update_bounding_box",
|
|
33
32
|
]
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from collections.abc import Sequence
|
|
6
|
+
from uuid import UUID
|
|
6
7
|
|
|
7
8
|
from sqlmodel import Session
|
|
8
9
|
|
|
@@ -24,7 +25,7 @@ from lightly_studio.models.annotation.semantic_segmentation import (
|
|
|
24
25
|
def create_many(
|
|
25
26
|
session: Session,
|
|
26
27
|
annotations: list[AnnotationCreate],
|
|
27
|
-
) -> Sequence[
|
|
28
|
+
) -> Sequence[UUID]:
|
|
28
29
|
"""Create many annotations with object detection details in bulk."""
|
|
29
30
|
# Step 1: Create all base annotations
|
|
30
31
|
base_annotations = []
|
|
@@ -37,7 +38,6 @@ def create_many(
|
|
|
37
38
|
db_base_annotation = AnnotationBaseTable(
|
|
38
39
|
annotation_label_id=annotation_create.annotation_label_id,
|
|
39
40
|
annotation_type=annotation_create.annotation_type,
|
|
40
|
-
annotation_task_id=annotation_create.annotation_task_id,
|
|
41
41
|
confidence=annotation_create.confidence,
|
|
42
42
|
dataset_id=annotation_create.dataset_id,
|
|
43
43
|
sample_id=annotation_create.sample_id,
|
|
@@ -93,4 +93,4 @@ def create_many(
|
|
|
93
93
|
# Commit everything
|
|
94
94
|
session.commit()
|
|
95
95
|
|
|
96
|
-
return base_annotations
|
|
96
|
+
return [annotation.annotation_id for annotation in base_annotations]
|
|
@@ -26,7 +26,7 @@ def delete_annotation(
|
|
|
26
26
|
annotation_id=annotation_id,
|
|
27
27
|
)
|
|
28
28
|
if not annotation:
|
|
29
|
-
|
|
29
|
+
raise ValueError(f"Annotation {annotation_id} not found")
|
|
30
30
|
if annotation.object_detection_details:
|
|
31
31
|
session.delete(annotation.object_detection_details)
|
|
32
32
|
if annotation.instance_segmentation_details:
|
|
@@ -18,14 +18,12 @@ from lightly_studio.resolvers.annotations.annotations_filter import (
|
|
|
18
18
|
|
|
19
19
|
def delete_annotations(
|
|
20
20
|
session: Session,
|
|
21
|
-
annotation_task_ids: list[UUID] | None,
|
|
22
21
|
annotation_label_ids: list[UUID] | None,
|
|
23
22
|
) -> None:
|
|
24
23
|
"""Delete all annotations and their tag links using filters.
|
|
25
24
|
|
|
26
25
|
Args:
|
|
27
26
|
session: Database session.
|
|
28
|
-
annotation_task_ids: List of annotation task IDs to filter by.
|
|
29
27
|
annotation_label_ids: List of annotation label IDs to filter by.
|
|
30
28
|
"""
|
|
31
29
|
# Find annotation_ids to delete
|
|
@@ -33,9 +31,15 @@ def delete_annotations(
|
|
|
33
31
|
session,
|
|
34
32
|
filters=AnnotationsFilter(
|
|
35
33
|
annotation_label_ids=annotation_label_ids,
|
|
36
|
-
annotation_task_ids=annotation_task_ids,
|
|
37
34
|
),
|
|
38
35
|
).annotations
|
|
36
|
+
for annotation in annotations:
|
|
37
|
+
if annotation.object_detection_details:
|
|
38
|
+
session.delete(annotation.object_detection_details)
|
|
39
|
+
if annotation.instance_segmentation_details:
|
|
40
|
+
session.delete(annotation.instance_segmentation_details)
|
|
41
|
+
if annotation.semantic_segmentation_details:
|
|
42
|
+
session.delete(annotation.semantic_segmentation_details)
|
|
39
43
|
annotation_ids = [annotation.annotation_id for annotation in annotations]
|
|
40
44
|
# TODO(Horatiu, 06/2025): Check if there is a way to delete the links
|
|
41
45
|
# automatically using SQLModel/SQLAlchemy.
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from collections.abc import Sequence
|
|
5
6
|
from uuid import UUID
|
|
6
7
|
|
|
7
|
-
from sqlmodel import Session, select
|
|
8
|
+
from sqlmodel import Session, col, select
|
|
8
9
|
|
|
9
10
|
from lightly_studio.models.annotation.annotation_base import (
|
|
10
11
|
AnnotationBaseTable,
|
|
@@ -16,3 +17,20 @@ def get_by_id(session: Session, annotation_id: UUID) -> AnnotationBaseTable | No
|
|
|
16
17
|
return session.exec(
|
|
17
18
|
select(AnnotationBaseTable).where(AnnotationBaseTable.annotation_id == annotation_id)
|
|
18
19
|
).one_or_none()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_by_ids(session: Session, annotation_ids: Sequence[UUID]) -> Sequence[AnnotationBaseTable]:
|
|
23
|
+
"""Retrieve multiple annotations by their IDs.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
session: The database session to use for the query.
|
|
27
|
+
annotation_ids: A list of annotation IDs to retrieve.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
A list of annotations matching the provided IDs.
|
|
31
|
+
"""
|
|
32
|
+
return session.exec(
|
|
33
|
+
select(AnnotationBaseTable).where(
|
|
34
|
+
col(AnnotationBaseTable.annotation_id).in_(annotation_ids)
|
|
35
|
+
)
|
|
36
|
+
).all()
|
|
@@ -111,7 +111,6 @@ def update_annotation_label(
|
|
|
111
111
|
annotation_id=annotation_copy.annotation_id,
|
|
112
112
|
annotation_label_id=annotation_copy.annotation_label_id,
|
|
113
113
|
annotation_type=annotation_copy.annotation_type,
|
|
114
|
-
annotation_task_id=annotation_copy.annotation_task_id,
|
|
115
114
|
confidence=annotation_copy.confidence,
|
|
116
115
|
created_at=annotation_copy.created_at,
|
|
117
116
|
dataset_id=annotation_copy.dataset_id,
|
|
@@ -7,8 +7,7 @@ from uuid import UUID
|
|
|
7
7
|
from pydantic import BaseModel, Field
|
|
8
8
|
from sqlmodel import col
|
|
9
9
|
|
|
10
|
-
from lightly_studio.models.annotation.annotation_base import AnnotationBaseTable
|
|
11
|
-
from lightly_studio.models.annotation_task import AnnotationType
|
|
10
|
+
from lightly_studio.models.annotation.annotation_base import AnnotationBaseTable, AnnotationType
|
|
12
11
|
from lightly_studio.models.sample import SampleTable
|
|
13
12
|
from lightly_studio.models.tag import TagTable
|
|
14
13
|
from lightly_studio.type_definitions import QueryType
|
|
@@ -30,9 +29,6 @@ class AnnotationsFilter(BaseModel):
|
|
|
30
29
|
default=None,
|
|
31
30
|
description="List of sample tag UUIDs to filter annotations by",
|
|
32
31
|
)
|
|
33
|
-
annotation_task_ids: list[UUID] | None = Field(
|
|
34
|
-
default=None, description="List of annotation task UUIDs"
|
|
35
|
-
)
|
|
36
32
|
|
|
37
33
|
def apply(
|
|
38
34
|
self,
|
|
@@ -51,12 +47,6 @@ class AnnotationsFilter(BaseModel):
|
|
|
51
47
|
if self.dataset_ids:
|
|
52
48
|
query = query.where(col(AnnotationBaseTable.dataset_id).in_(self.dataset_ids))
|
|
53
49
|
|
|
54
|
-
# Filter by annotation task
|
|
55
|
-
if self.annotation_task_ids:
|
|
56
|
-
query = query.where(
|
|
57
|
-
col(AnnotationBaseTable.annotation_task_id).in_(self.annotation_task_ids)
|
|
58
|
-
)
|
|
59
|
-
|
|
60
50
|
# Filter by annotation label
|
|
61
51
|
if self.annotation_label_ids:
|
|
62
52
|
query = query.where(
|
|
@@ -10,29 +10,26 @@ from typing import Iterable
|
|
|
10
10
|
# Or remove the type ignore once typing stubs were added manually.
|
|
11
11
|
import lightly_mundig # type: ignore[import-untyped]
|
|
12
12
|
import numpy as np
|
|
13
|
-
|
|
13
|
+
|
|
14
|
+
from lightly_studio.dataset.env import LIGHTLY_STUDIO_LICENSE_KEY
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
class Mundig:
|
|
17
|
-
"""Python
|
|
18
|
+
"""Python interface for the Mundig selection algorithm.
|
|
18
19
|
|
|
19
20
|
This class provides a Python interface to the lightly_mundig Rust library
|
|
20
|
-
for sample selection.
|
|
21
|
+
for sample selection. It allows combining different selection strategies
|
|
22
|
+
such as diversity and weighting.
|
|
21
23
|
"""
|
|
22
24
|
|
|
23
25
|
def __init__(self) -> None:
|
|
24
26
|
"""Initialize the Mundig selection interface."""
|
|
25
|
-
|
|
26
|
-
env = Env()
|
|
27
|
-
env.read_env()
|
|
28
|
-
license_key = env.str("LIGHTLY_STUDIO_LICENSE_KEY", default=None)
|
|
29
|
-
if license_key is None:
|
|
27
|
+
if LIGHTLY_STUDIO_LICENSE_KEY is None:
|
|
30
28
|
raise ValueError(
|
|
31
29
|
"LIGHTLY_STUDIO_LICENSE_KEY environment variable is not set. "
|
|
32
30
|
"Please set it to your LightlyStudio license key."
|
|
33
31
|
)
|
|
34
|
-
|
|
35
|
-
self.mundig = lightly_mundig.Selection(token=license_key)
|
|
32
|
+
self.mundig = lightly_mundig.Selection(token=LIGHTLY_STUDIO_LICENSE_KEY)
|
|
36
33
|
|
|
37
34
|
self.n_input_samples: int | None = None
|
|
38
35
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from typing import Literal, Sequence
|
|
5
6
|
from uuid import UUID
|
|
6
7
|
|
|
7
8
|
from pydantic import BaseModel
|
|
@@ -13,7 +14,7 @@ class SelectionConfig(BaseModel):
|
|
|
13
14
|
dataset_id: UUID
|
|
14
15
|
n_samples_to_select: int
|
|
15
16
|
selection_result_tag_name: str
|
|
16
|
-
strategies:
|
|
17
|
+
strategies: Sequence[SelectionStrategy]
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
class SelectionStrategy(BaseModel):
|
|
@@ -25,10 +26,12 @@ class SelectionStrategy(BaseModel):
|
|
|
25
26
|
class EmbeddingDiversityStrategy(SelectionStrategy):
|
|
26
27
|
"""Selection strategy based on embedding diversity."""
|
|
27
28
|
|
|
29
|
+
strategy_name: Literal["diversity"] = "diversity"
|
|
28
30
|
embedding_model_name: str | None
|
|
29
31
|
|
|
30
32
|
|
|
31
33
|
class MetadataWeightingStrategy(SelectionStrategy):
|
|
32
34
|
"""Selection strategy based on metadata weighting."""
|
|
33
35
|
|
|
36
|
+
strategy_name: Literal["weights"] = "weights"
|
|
34
37
|
metadata_key: str
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
"""Services for annotations operations."""
|
|
2
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
|
+
)
|
|
3
9
|
from lightly_studio.services.annotations_service.get_annotation_by_id import (
|
|
4
10
|
get_annotation_by_id,
|
|
5
11
|
)
|
|
@@ -17,6 +23,8 @@ from lightly_studio.services.annotations_service.update_annotations import (
|
|
|
17
23
|
)
|
|
18
24
|
|
|
19
25
|
__all__ = [
|
|
26
|
+
"create_annotation",
|
|
27
|
+
"delete_annotation",
|
|
20
28
|
"get_annotation_by_id",
|
|
21
29
|
"update_annotation",
|
|
22
30
|
"update_annotation_bounding_box",
|
|
@@ -0,0 +1,63 @@
|
|
|
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
|
+
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
|
+
annotations=[annotation_create],
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
if not new_annotation_ids:
|
|
53
|
+
raise ValueError("Failed to create annotation.")
|
|
54
|
+
|
|
55
|
+
created_annotation = annotation_resolver.get_by_id(
|
|
56
|
+
session=session,
|
|
57
|
+
annotation_id=new_annotation_ids[0],
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
if created_annotation is None:
|
|
61
|
+
raise ValueError(f"Failed to create annotation: {annotation}")
|
|
62
|
+
|
|
63
|
+
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)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lightly-studio
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: LightlyStudio is a lightweight, fast, and easy-to-use data exploration tool for data scientists and engineers.
|
|
5
5
|
Classifier: Operating System :: MacOS :: MacOS X
|
|
6
6
|
Classifier: Operating System :: Microsoft :: Windows
|
|
@@ -20,16 +20,23 @@ Requires-Dist: fastapi>=0.115.5
|
|
|
20
20
|
Requires-Dist: faster-coco-eval>=1.6.5
|
|
21
21
|
Requires-Dist: fsspec>=2023.1.0
|
|
22
22
|
Requires-Dist: labelformat>=0.1.7
|
|
23
|
-
Requires-Dist: lightly-mundig==0.1.
|
|
23
|
+
Requires-Dist: lightly-mundig==0.1.4
|
|
24
24
|
Requires-Dist: open-clip-torch>=2.20.0
|
|
25
|
+
Requires-Dist: pyarrow>=17.0.0
|
|
25
26
|
Requires-Dist: python-multipart>=0.0.20
|
|
26
27
|
Requires-Dist: scikit-learn==1.3.2
|
|
27
28
|
Requires-Dist: sqlmodel>=0.0.22
|
|
28
|
-
Requires-Dist: torchmetrics>=1.5.2
|
|
29
29
|
Requires-Dist: tqdm>=4.65.0
|
|
30
30
|
Requires-Dist: typing-extensions>=4.12.2
|
|
31
31
|
Requires-Dist: uvicorn>=0.32.1
|
|
32
32
|
Requires-Dist: xxhash>=3.5.0
|
|
33
|
+
Provides-Extra: cloud-storage
|
|
34
|
+
Requires-Dist: adlfs>=2023.1.0; extra == 'cloud-storage'
|
|
35
|
+
Requires-Dist: gcsfs>=2023.1.0; extra == 'cloud-storage'
|
|
36
|
+
Requires-Dist: s3fs>=2023.1.0; extra == 'cloud-storage'
|
|
37
|
+
Provides-Extra: lightly-edge
|
|
38
|
+
Requires-Dist: lightly-edge-sdk>=1.0.1b2; extra == 'lightly-edge'
|
|
39
|
+
Requires-Dist: opencv-python; extra == 'lightly-edge'
|
|
33
40
|
Description-Content-Type: text/markdown
|
|
34
41
|
|
|
35
42
|
<div align="center">
|
|
@@ -113,42 +120,47 @@ python -m venv venv
|
|
|
113
120
|
.\venv\Scripts\activate
|
|
114
121
|
|
|
115
122
|
# 2. Install LightlyStudio
|
|
116
|
-
pip install
|
|
123
|
+
pip install lightly-studio
|
|
117
124
|
```
|
|
118
125
|
|
|
119
126
|
## **Quickstart**
|
|
120
127
|
|
|
121
|
-
Download
|
|
122
|
-
|
|
123
|
-
### YOLO Object Detection
|
|
124
|
-
|
|
125
|
-
To run an example using a yolo dataset, clone the example repository and run the example script from below:
|
|
128
|
+
Download example datasets by cloning the example repository:
|
|
126
129
|
|
|
127
130
|
```shell
|
|
128
131
|
git clone https://github.com/lightly-ai/dataset_examples dataset_examples
|
|
129
132
|
```
|
|
130
133
|
|
|
131
|
-
|
|
134
|
+
### YOLO Object Detection
|
|
135
|
+
|
|
136
|
+
To run an example using a YOLO dataset, create a file named `example_yolo.py` with the
|
|
137
|
+
following contents in the same directory that contains the `dataset_examples/` folder:
|
|
132
138
|
|
|
133
139
|
```python
|
|
134
|
-
|
|
140
|
+
# example_yolo.py
|
|
135
141
|
|
|
142
|
+
from pathlib import Path
|
|
136
143
|
import lightly_studio as ls
|
|
137
144
|
|
|
138
|
-
data_yaml_path = Path(__file__).resolve().parent / "data.yaml"
|
|
139
|
-
|
|
140
145
|
# Create a dataset and add the samples from the yolo format
|
|
141
146
|
dataset = ls.Dataset.create()
|
|
142
147
|
dataset.add_samples_from_yolo(
|
|
143
|
-
data_yaml=
|
|
148
|
+
data_yaml="dataset_examples/road_signs_yolo/data.yaml",
|
|
144
149
|
input_split="test",
|
|
145
150
|
)
|
|
146
151
|
|
|
147
152
|
# Start the UI application on the port 8001.
|
|
148
153
|
ls.start_gui()
|
|
154
|
+
```
|
|
149
155
|
|
|
156
|
+
Run the script:
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
python example_yolo.py
|
|
150
160
|
```
|
|
151
161
|
|
|
162
|
+
When you are done, stop the app by pressing Ctrl+C in the terminal.
|
|
163
|
+
|
|
152
164
|
<details>
|
|
153
165
|
<summary>The YOLO format details:</summary>
|
|
154
166
|
|
|
@@ -183,26 +195,21 @@ Where coordinates are normalized between 0 and 1.
|
|
|
183
195
|
|
|
184
196
|
### COCO Instance Segmentation
|
|
185
197
|
|
|
186
|
-
To run an instance segmentation example using a COCO dataset,
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
git clone https://github.com/lightly-ai/dataset_examples dataset_examples
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
**`example_coco.py` script to explore the dataset:**
|
|
198
|
+
To run an instance segmentation example using a COCO dataset, create a file named
|
|
199
|
+
`example_coco.py` with the following contents in the same directory that contains
|
|
200
|
+
the `dataset_examples/` folder:
|
|
193
201
|
|
|
194
202
|
```python
|
|
195
|
-
|
|
203
|
+
# example_coco.py
|
|
196
204
|
|
|
205
|
+
from pathlib import Path
|
|
197
206
|
import lightly_studio as ls
|
|
198
207
|
|
|
199
|
-
current_dir = Path(__file__).resolve().parent
|
|
200
|
-
|
|
201
208
|
# Create a dataset and add the samples from the coco format
|
|
202
209
|
dataset = ls.Dataset.create()
|
|
203
210
|
dataset.add_samples_from_coco(
|
|
204
|
-
annotations_json=
|
|
205
|
-
images_path=
|
|
211
|
+
annotations_json="dataset_examples/coco_subset_128_images/instances_train2017.json",
|
|
212
|
+
images_path="dataset_examples/coco_subset_128_images/images",
|
|
206
213
|
annotation_type=ls.AnnotationType.INSTANCE_SEGMENTATION,
|
|
207
214
|
)
|
|
208
215
|
|
|
@@ -210,6 +217,14 @@ dataset.add_samples_from_coco(
|
|
|
210
217
|
ls.start_gui()
|
|
211
218
|
```
|
|
212
219
|
|
|
220
|
+
Run the script:
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
python example_coco.py
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
When you are done, stop the app by pressing Ctrl+C in the terminal.
|
|
227
|
+
|
|
213
228
|
<details>
|
|
214
229
|
<summary>The COCO format details:</summary>
|
|
215
230
|
|
|
@@ -253,6 +268,53 @@ dataset.add_samples_from_path(path="/path/to/image_dataset")
|
|
|
253
268
|
ls.start_gui()
|
|
254
269
|
```
|
|
255
270
|
|
|
271
|
+
#### ☁️ Cloud Storage Support
|
|
272
|
+
|
|
273
|
+
#### Installation with Cloud Storage Support
|
|
274
|
+
|
|
275
|
+
```shell
|
|
276
|
+
pip install lightly-studio[cloud-storage]
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
#### Example: Loading Dataset from Cloud Storage
|
|
280
|
+
|
|
281
|
+
```python
|
|
282
|
+
import lightly_studio as ls
|
|
283
|
+
|
|
284
|
+
dataset = ls.Dataset.create()
|
|
285
|
+
|
|
286
|
+
# Load dataset from S3
|
|
287
|
+
dataset.add_samples_from_path(path="s3://my-bucket/path/to/images/")
|
|
288
|
+
|
|
289
|
+
# You can use glob pattern in the file path
|
|
290
|
+
dataset.add_samples_from_path(path="s3://my-bucket/path/to/images/**/*.jpg") # matches all .jpg files recursively
|
|
291
|
+
|
|
292
|
+
# Load dataset from gcs
|
|
293
|
+
dataset.add_samples_from_path(path="gs://path/to/images/")
|
|
294
|
+
|
|
295
|
+
ls.start_gui()
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
**Note**: Currently, cloud storage support is limited to loading images only. Annotation files (YOLO labels, COCO JSON files, etc.) cannot be loaded directly from cloud storage paths.
|
|
299
|
+
|
|
300
|
+
#### Authentication
|
|
301
|
+
|
|
302
|
+
**Important**: Cloud storage authentication must be configured before running LightlyStudio. The application relies on your existing cloud storage credentials and will not prompt for authentication.
|
|
303
|
+
|
|
304
|
+
#### AWS S3
|
|
305
|
+
|
|
306
|
+
You can use either of the following two options:
|
|
307
|
+
|
|
308
|
+
- **Set environment variables manually**: Set `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` (LightlyStudio uses `s3fs` under the hood to connect to S3)
|
|
309
|
+
- **Authenticate using AWS CLI**: Run `aws configure` (this will automatically set the environment variables that LightlyStudio can access)
|
|
310
|
+
|
|
311
|
+
#### Google Cloud Storage
|
|
312
|
+
|
|
313
|
+
You can use either of the following two options:
|
|
314
|
+
|
|
315
|
+
- **Set environment variable manually**: Set `GOOGLE_APPLICATION_CREDENTIALS` pointing to your service account key file (LightlyStudio uses `gcsfs` under the hood to connect to GCS)
|
|
316
|
+
- **Authenticate using gcloud CLI**: Run `gcloud auth application-default login` (this will automatically set the environment variables that LightlyStudio can access)
|
|
317
|
+
|
|
256
318
|
#### Load Images With Annotations
|
|
257
319
|
|
|
258
320
|
The `Dataset` currently supports:
|
|
@@ -564,6 +626,16 @@ In some use cases, one might want to assign a tag to the samples that are the re
|
|
|
564
626
|
query.add_tag("tag_name")
|
|
565
627
|
```
|
|
566
628
|
|
|
629
|
+
#### Export Samples
|
|
630
|
+
|
|
631
|
+
Currently, exporting to the COCO object detection format is supported. The following example shows how to export the samples in the query to a COCO JSON file:
|
|
632
|
+
|
|
633
|
+
```py
|
|
634
|
+
from pathlib import Path
|
|
635
|
+
|
|
636
|
+
query.export().to_coco_object_detections(Path("coco_export.json"))
|
|
637
|
+
```
|
|
638
|
+
|
|
567
639
|
### Examples
|
|
568
640
|
|
|
569
641
|
#### Add Custom Metadata
|
|
@@ -635,7 +707,9 @@ import os
|
|
|
635
707
|
os.environ["LIGHTLY_STUDIO_LICENSE_KEY"] = "license_key_here"
|
|
636
708
|
```
|
|
637
709
|
|
|
638
|
-
|
|
710
|
+
#### Diversity Selection
|
|
711
|
+
|
|
712
|
+
Diversity selection can be configured directly from a `DatasetQuery`. The example below showcases a simple case of selecting diverse samples.
|
|
639
713
|
|
|
640
714
|
```py
|
|
641
715
|
import lightly_studio as ls
|
|
@@ -653,6 +727,57 @@ dataset.query().selection().diverse(
|
|
|
653
727
|
ls.start_gui()
|
|
654
728
|
```
|
|
655
729
|
|
|
730
|
+
#### Metadata Weighting Selection
|
|
731
|
+
|
|
732
|
+
You can select samples based on the values of a metadata field. The example below showcases a simple case of selecting samples with the highest metadata value.
|
|
733
|
+
|
|
734
|
+
```py
|
|
735
|
+
import lightly_studio as ls
|
|
736
|
+
|
|
737
|
+
# Load your dataset
|
|
738
|
+
dataset = ls.Dataset.load_or_create()
|
|
739
|
+
dataset.add_samples_from_path(path="/path/to/image_dataset")
|
|
740
|
+
# Compute and store 'typicality' metadata.
|
|
741
|
+
dataset.compute_typicality_metadata(metadata_name="typicality")
|
|
742
|
+
|
|
743
|
+
# Select the 5 samples with the highest 'typicality' scores.
|
|
744
|
+
dataset.query().selection().metadata_weighting(
|
|
745
|
+
n_samples_to_select=5,
|
|
746
|
+
selection_result_tag_name="metadata_weighting_selection",
|
|
747
|
+
metadata_key="typicality",
|
|
748
|
+
)
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
#### Selection Based on Multiple Strategies
|
|
752
|
+
|
|
753
|
+
You can configure multiple strategies, the selection takes into account all of them at the same time, weighted by the `strength` parameter.
|
|
754
|
+
|
|
755
|
+
```py
|
|
756
|
+
import lightly_studio as ls
|
|
757
|
+
from lightly_studio.selection.selection_config import (
|
|
758
|
+
MetadataWeightingStrategy,
|
|
759
|
+
EmbeddingDiversityStrategy,
|
|
760
|
+
)
|
|
761
|
+
|
|
762
|
+
# Load your dataset
|
|
763
|
+
dataset = ls.Dataset.load_or_create()
|
|
764
|
+
dataset.add_samples_from_path(path="/path/to/image_dataset")
|
|
765
|
+
# Compute typicality and store it as `typicality` metadata
|
|
766
|
+
dataset.compute_typicality_metadata(metadata_name="typicality")
|
|
767
|
+
|
|
768
|
+
# Select 10 samples by combining typicality and diversity, diversity having double the strength.
|
|
769
|
+
dataset.query().selection().multi_strategies(
|
|
770
|
+
n_samples_to_select=10,
|
|
771
|
+
selection_result_tag_name="multi_strategy_selection",
|
|
772
|
+
selection_strategies=[
|
|
773
|
+
MetadataWeightingStrategy(metadata_key="typicality", strength=1.0),
|
|
774
|
+
EmbeddingDiversityStrategy(embedding_model_name="my_model_name", strength=2.0),
|
|
775
|
+
],
|
|
776
|
+
)
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
#### Exporting Selected Samples
|
|
780
|
+
|
|
656
781
|
The selected sample paths can be exported via the GUI, or by a script:
|
|
657
782
|
|
|
658
783
|
```py
|