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.

Files changed (219) hide show
  1. lightly_studio/__init__.py +11 -0
  2. lightly_studio/api/__init__.py +0 -0
  3. lightly_studio/api/app.py +110 -0
  4. lightly_studio/api/cache.py +77 -0
  5. lightly_studio/api/db.py +133 -0
  6. lightly_studio/api/db_tables.py +32 -0
  7. lightly_studio/api/features.py +7 -0
  8. lightly_studio/api/routes/api/annotation.py +233 -0
  9. lightly_studio/api/routes/api/annotation_label.py +90 -0
  10. lightly_studio/api/routes/api/annotation_task.py +38 -0
  11. lightly_studio/api/routes/api/classifier.py +387 -0
  12. lightly_studio/api/routes/api/dataset.py +182 -0
  13. lightly_studio/api/routes/api/dataset_tag.py +257 -0
  14. lightly_studio/api/routes/api/exceptions.py +96 -0
  15. lightly_studio/api/routes/api/features.py +17 -0
  16. lightly_studio/api/routes/api/metadata.py +37 -0
  17. lightly_studio/api/routes/api/metrics.py +80 -0
  18. lightly_studio/api/routes/api/sample.py +196 -0
  19. lightly_studio/api/routes/api/settings.py +45 -0
  20. lightly_studio/api/routes/api/status.py +19 -0
  21. lightly_studio/api/routes/api/text_embedding.py +48 -0
  22. lightly_studio/api/routes/api/validators.py +17 -0
  23. lightly_studio/api/routes/healthz.py +13 -0
  24. lightly_studio/api/routes/images.py +104 -0
  25. lightly_studio/api/routes/webapp.py +51 -0
  26. lightly_studio/api/server.py +82 -0
  27. lightly_studio/core/__init__.py +0 -0
  28. lightly_studio/core/dataset.py +523 -0
  29. lightly_studio/core/sample.py +77 -0
  30. lightly_studio/core/start_gui.py +15 -0
  31. lightly_studio/dataset/__init__.py +0 -0
  32. lightly_studio/dataset/edge_embedding_generator.py +144 -0
  33. lightly_studio/dataset/embedding_generator.py +91 -0
  34. lightly_studio/dataset/embedding_manager.py +163 -0
  35. lightly_studio/dataset/env.py +16 -0
  36. lightly_studio/dataset/file_utils.py +35 -0
  37. lightly_studio/dataset/loader.py +622 -0
  38. lightly_studio/dataset/mobileclip_embedding_generator.py +144 -0
  39. lightly_studio/dist_lightly_studio_view_app/_app/env.js +1 -0
  40. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/0.DenzbfeK.css +1 -0
  41. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/LightlyLogo.BNjCIww-.png +0 -0
  42. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/OpenSans- +0 -0
  43. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/OpenSans-Bold.DGvYQtcs.ttf +0 -0
  44. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/OpenSans-Italic-VariableFont_wdth_wght.B4AZ-wl6.ttf +0 -0
  45. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/OpenSans-Regular.DxJTClRG.ttf +0 -0
  46. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/OpenSans-SemiBold.D3TTYgdB.ttf +0 -0
  47. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/OpenSans-VariableFont_wdth_wght.BZBpG5Iz.ttf +0 -0
  48. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/SelectableSvgGroup.OwPEPQZu.css +1 -0
  49. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/SelectableSvgGroup.b653GmVf.css +1 -0
  50. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/_layout.T-zjSUd3.css +1 -0
  51. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/useFeatureFlags.CV-KWLNP.css +1 -0
  52. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/69_IOA4Y.js +1 -0
  53. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/B2FVR0s0.js +1 -0
  54. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/B90CZVMX.js +1 -0
  55. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/B9zumHo5.js +1 -0
  56. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/BJXwVxaE.js +1 -0
  57. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/Bsi3UGy5.js +1 -0
  58. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/Bu7uvVrG.js +1 -0
  59. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/Bx1xMsFy.js +1 -0
  60. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/BylOuP6i.js +1 -0
  61. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/C8I8rFJQ.js +1 -0
  62. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CDnpyLsT.js +1 -0
  63. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CWj6FrbW.js +1 -0
  64. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CYgJF_JY.js +1 -0
  65. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CcaPhhk3.js +1 -0
  66. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CvOmgdoc.js +93 -0
  67. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CxtLVaYz.js +3 -0
  68. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/D5-A_Ffd.js +4 -0
  69. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/D6RI2Zrd.js +1 -0
  70. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/D6su9Aln.js +1 -0
  71. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/D98V7j6A.js +1 -0
  72. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DIRAtgl0.js +1 -0
  73. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DIeogL5L.js +1 -0
  74. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DOlTMNyt.js +1 -0
  75. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DjUWrjOv.js +1 -0
  76. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DjfY96ND.js +1 -0
  77. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/H7C68rOM.js +1 -0
  78. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/O-EABkf9.js +1 -0
  79. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/XO7A28GO.js +1 -0
  80. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/hQVEETDE.js +1 -0
  81. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/l7KrR96u.js +1 -0
  82. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/nAHhluT7.js +1 -0
  83. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/r64xT6ao.js +1 -0
  84. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/vC4nQVEB.js +1 -0
  85. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/x9G_hzyY.js +1 -0
  86. lightly_studio/dist_lightly_studio_view_app/_app/immutable/entry/app.CjnvpsmS.js +2 -0
  87. lightly_studio/dist_lightly_studio_view_app/_app/immutable/entry/start.0o1H7wM9.js +1 -0
  88. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/0.XRq_TUwu.js +1 -0
  89. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/1.B4rNYwVp.js +1 -0
  90. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/10.DfBwOEhN.js +1 -0
  91. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/11.CWG1ehzT.js +1 -0
  92. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/12.CwF2_8mP.js +1 -0
  93. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/2.CS4muRY-.js +6 -0
  94. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/3.CWHpKonm.js +1 -0
  95. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/4.OUWOLQeV.js +1 -0
  96. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/5.Dm6t9F5W.js +1 -0
  97. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/6.Bw5ck4gK.js +1 -0
  98. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/7.CF0EDTR6.js +1 -0
  99. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/8.Cw30LEcV.js +1 -0
  100. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/9.CPu3CiBc.js +1 -0
  101. lightly_studio/dist_lightly_studio_view_app/_app/version.json +1 -0
  102. lightly_studio/dist_lightly_studio_view_app/apple-touch-icon-precomposed.png +0 -0
  103. lightly_studio/dist_lightly_studio_view_app/apple-touch-icon.png +0 -0
  104. lightly_studio/dist_lightly_studio_view_app/favicon.png +0 -0
  105. lightly_studio/dist_lightly_studio_view_app/index.html +44 -0
  106. lightly_studio/examples/example.py +23 -0
  107. lightly_studio/examples/example_metadata.py +338 -0
  108. lightly_studio/examples/example_selection.py +39 -0
  109. lightly_studio/examples/example_split_work.py +67 -0
  110. lightly_studio/examples/example_v2.py +21 -0
  111. lightly_studio/export_schema.py +18 -0
  112. lightly_studio/few_shot_classifier/__init__.py +0 -0
  113. lightly_studio/few_shot_classifier/classifier.py +80 -0
  114. lightly_studio/few_shot_classifier/classifier_manager.py +663 -0
  115. lightly_studio/few_shot_classifier/random_forest_classifier.py +489 -0
  116. lightly_studio/metadata/complex_metadata.py +47 -0
  117. lightly_studio/metadata/gps_coordinate.py +41 -0
  118. lightly_studio/metadata/metadata_protocol.py +17 -0
  119. lightly_studio/metrics/__init__.py +0 -0
  120. lightly_studio/metrics/detection/__init__.py +0 -0
  121. lightly_studio/metrics/detection/map.py +268 -0
  122. lightly_studio/models/__init__.py +1 -0
  123. lightly_studio/models/annotation/__init__.py +0 -0
  124. lightly_studio/models/annotation/annotation_base.py +171 -0
  125. lightly_studio/models/annotation/instance_segmentation.py +56 -0
  126. lightly_studio/models/annotation/links.py +17 -0
  127. lightly_studio/models/annotation/object_detection.py +47 -0
  128. lightly_studio/models/annotation/semantic_segmentation.py +44 -0
  129. lightly_studio/models/annotation_label.py +47 -0
  130. lightly_studio/models/annotation_task.py +28 -0
  131. lightly_studio/models/classifier.py +20 -0
  132. lightly_studio/models/dataset.py +84 -0
  133. lightly_studio/models/embedding_model.py +30 -0
  134. lightly_studio/models/metadata.py +208 -0
  135. lightly_studio/models/sample.py +180 -0
  136. lightly_studio/models/sample_embedding.py +37 -0
  137. lightly_studio/models/settings.py +60 -0
  138. lightly_studio/models/tag.py +96 -0
  139. lightly_studio/py.typed +0 -0
  140. lightly_studio/resolvers/__init__.py +7 -0
  141. lightly_studio/resolvers/annotation_label_resolver/__init__.py +21 -0
  142. lightly_studio/resolvers/annotation_label_resolver/create.py +27 -0
  143. lightly_studio/resolvers/annotation_label_resolver/delete.py +28 -0
  144. lightly_studio/resolvers/annotation_label_resolver/get_all.py +22 -0
  145. lightly_studio/resolvers/annotation_label_resolver/get_by_id.py +24 -0
  146. lightly_studio/resolvers/annotation_label_resolver/get_by_ids.py +25 -0
  147. lightly_studio/resolvers/annotation_label_resolver/get_by_label_name.py +24 -0
  148. lightly_studio/resolvers/annotation_label_resolver/names_by_ids.py +25 -0
  149. lightly_studio/resolvers/annotation_label_resolver/update.py +38 -0
  150. lightly_studio/resolvers/annotation_resolver/__init__.py +33 -0
  151. lightly_studio/resolvers/annotation_resolver/count_annotations_by_dataset.py +120 -0
  152. lightly_studio/resolvers/annotation_resolver/create.py +19 -0
  153. lightly_studio/resolvers/annotation_resolver/create_many.py +96 -0
  154. lightly_studio/resolvers/annotation_resolver/delete_annotation.py +45 -0
  155. lightly_studio/resolvers/annotation_resolver/delete_annotations.py +56 -0
  156. lightly_studio/resolvers/annotation_resolver/get_all.py +74 -0
  157. lightly_studio/resolvers/annotation_resolver/get_by_id.py +18 -0
  158. lightly_studio/resolvers/annotation_resolver/update_annotation_label.py +144 -0
  159. lightly_studio/resolvers/annotation_resolver/update_bounding_box.py +68 -0
  160. lightly_studio/resolvers/annotation_task_resolver.py +31 -0
  161. lightly_studio/resolvers/annotations/__init__.py +1 -0
  162. lightly_studio/resolvers/annotations/annotations_filter.py +89 -0
  163. lightly_studio/resolvers/dataset_resolver.py +278 -0
  164. lightly_studio/resolvers/embedding_model_resolver.py +100 -0
  165. lightly_studio/resolvers/metadata_resolver/__init__.py +15 -0
  166. lightly_studio/resolvers/metadata_resolver/metadata_filter.py +163 -0
  167. lightly_studio/resolvers/metadata_resolver/sample/__init__.py +21 -0
  168. lightly_studio/resolvers/metadata_resolver/sample/bulk_set_metadata.py +48 -0
  169. lightly_studio/resolvers/metadata_resolver/sample/get_by_sample_id.py +24 -0
  170. lightly_studio/resolvers/metadata_resolver/sample/get_metadata_info.py +104 -0
  171. lightly_studio/resolvers/metadata_resolver/sample/get_value_for_sample.py +27 -0
  172. lightly_studio/resolvers/metadata_resolver/sample/set_value_for_sample.py +53 -0
  173. lightly_studio/resolvers/sample_embedding_resolver.py +86 -0
  174. lightly_studio/resolvers/sample_resolver.py +249 -0
  175. lightly_studio/resolvers/samples_filter.py +81 -0
  176. lightly_studio/resolvers/settings_resolver.py +58 -0
  177. lightly_studio/resolvers/tag_resolver.py +276 -0
  178. lightly_studio/selection/README.md +6 -0
  179. lightly_studio/selection/mundig.py +105 -0
  180. lightly_studio/selection/select.py +96 -0
  181. lightly_studio/selection/select_via_db.py +93 -0
  182. lightly_studio/selection/selection_config.py +31 -0
  183. lightly_studio/services/annotations_service/__init__.py +21 -0
  184. lightly_studio/services/annotations_service/get_annotation_by_id.py +31 -0
  185. lightly_studio/services/annotations_service/update_annotation.py +65 -0
  186. lightly_studio/services/annotations_service/update_annotation_label.py +48 -0
  187. lightly_studio/services/annotations_service/update_annotations.py +29 -0
  188. lightly_studio/setup_logging.py +19 -0
  189. lightly_studio/type_definitions.py +19 -0
  190. lightly_studio/vendor/ACKNOWLEDGEMENTS +422 -0
  191. lightly_studio/vendor/LICENSE +31 -0
  192. lightly_studio/vendor/LICENSE_weights_data +50 -0
  193. lightly_studio/vendor/README.md +5 -0
  194. lightly_studio/vendor/__init__.py +1 -0
  195. lightly_studio/vendor/mobileclip/__init__.py +96 -0
  196. lightly_studio/vendor/mobileclip/clip.py +77 -0
  197. lightly_studio/vendor/mobileclip/configs/mobileclip_b.json +18 -0
  198. lightly_studio/vendor/mobileclip/configs/mobileclip_s0.json +18 -0
  199. lightly_studio/vendor/mobileclip/configs/mobileclip_s1.json +18 -0
  200. lightly_studio/vendor/mobileclip/configs/mobileclip_s2.json +18 -0
  201. lightly_studio/vendor/mobileclip/image_encoder.py +67 -0
  202. lightly_studio/vendor/mobileclip/logger.py +154 -0
  203. lightly_studio/vendor/mobileclip/models/__init__.py +10 -0
  204. lightly_studio/vendor/mobileclip/models/mci.py +933 -0
  205. lightly_studio/vendor/mobileclip/models/vit.py +433 -0
  206. lightly_studio/vendor/mobileclip/modules/__init__.py +4 -0
  207. lightly_studio/vendor/mobileclip/modules/common/__init__.py +4 -0
  208. lightly_studio/vendor/mobileclip/modules/common/mobileone.py +341 -0
  209. lightly_studio/vendor/mobileclip/modules/common/transformer.py +451 -0
  210. lightly_studio/vendor/mobileclip/modules/image/__init__.py +4 -0
  211. lightly_studio/vendor/mobileclip/modules/image/image_projection.py +113 -0
  212. lightly_studio/vendor/mobileclip/modules/image/replknet.py +188 -0
  213. lightly_studio/vendor/mobileclip/modules/text/__init__.py +4 -0
  214. lightly_studio/vendor/mobileclip/modules/text/repmixer.py +281 -0
  215. lightly_studio/vendor/mobileclip/modules/text/tokenizer.py +38 -0
  216. lightly_studio/vendor/mobileclip/text_encoder.py +245 -0
  217. lightly_studio-0.3.1.dist-info/METADATA +520 -0
  218. lightly_studio-0.3.1.dist-info/RECORD +219 -0
  219. lightly_studio-0.3.1.dist-info/WHEEL +4 -0
@@ -0,0 +1,96 @@
1
+ """This module contains the Tag model and related enumerations."""
2
+
3
+ from datetime import datetime, timezone
4
+ from typing import TYPE_CHECKING, List, Literal, Optional
5
+ from uuid import UUID, uuid4
6
+
7
+ from sqlalchemy import UniqueConstraint
8
+ from sqlalchemy.orm import Mapped
9
+ from sqlmodel import Field, Relationship, SQLModel, String
10
+
11
+ from lightly_studio.models.annotation.links import AnnotationTagLinkTable
12
+ from lightly_studio.models.sample import SampleTagLinkTable
13
+
14
+ if TYPE_CHECKING:
15
+ from lightly_studio.models.annotation.annotation_base import (
16
+ AnnotationBaseTable,
17
+ )
18
+ from lightly_studio.models.sample import SampleTable
19
+
20
+ else:
21
+ SampleTable = object
22
+ TagTable = object
23
+ AnnotationBaseTable = object
24
+
25
+
26
+ # TagKind is the kind of tag we support.
27
+ TagKind = Literal[
28
+ "sample",
29
+ "annotation",
30
+ ]
31
+
32
+
33
+ class TagBase(SQLModel):
34
+ """Base class for the Tag model."""
35
+
36
+ name: str
37
+ description: Optional[str] = ""
38
+ kind: TagKind = "sample"
39
+
40
+
41
+ class TagCreate(TagBase):
42
+ """Tag model when creating."""
43
+
44
+ dataset_id: UUID
45
+
46
+
47
+ class TagCreateBody(TagBase):
48
+ """Tag model when creating."""
49
+
50
+
51
+ class TagUpdate(TagBase):
52
+ """Tag model when updating."""
53
+
54
+
55
+ class TagUpdateBody(TagBase):
56
+ """Tag model when updating."""
57
+
58
+ dataset_id: Optional[UUID] = None
59
+
60
+
61
+ class TagView(TagBase):
62
+ """Tag model when retrieving."""
63
+
64
+ tag_id: UUID
65
+ kind: TagKind
66
+ created_at: datetime
67
+ updated_at: datetime
68
+
69
+
70
+ class TagTable(TagBase, table=True):
71
+ """This class defines the Tag model."""
72
+
73
+ __tablename__ = "tags"
74
+ # ensure there can only be one tag named "lightly_studio" per dataset
75
+ __table_args__ = (
76
+ UniqueConstraint("dataset_id", "kind", "name", name="unique_name_constraint"),
77
+ )
78
+ tag_id: UUID = Field(default_factory=uuid4, primary_key=True)
79
+ dataset_id: UUID
80
+ kind: TagKind = Field(sa_type=String)
81
+ created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc), index=True)
82
+ updated_at: datetime = Field(
83
+ default_factory=lambda: datetime.now(timezone.utc),
84
+ )
85
+
86
+ """The sample ids associated with the tag."""
87
+ samples: Mapped[List["SampleTable"]] = Relationship(
88
+ back_populates="tags",
89
+ link_model=SampleTagLinkTable,
90
+ )
91
+
92
+ """The annotation ids associated with the tag (legacy bounding box)."""
93
+ annotations: Mapped[List["AnnotationBaseTable"]] = Relationship(
94
+ back_populates="tags",
95
+ link_model=AnnotationTagLinkTable,
96
+ )
File without changes
@@ -0,0 +1,7 @@
1
+ """Resolvers for database operations."""
2
+
3
+ from lightly_studio.resolvers import metadata_resolver
4
+
5
+ __all__ = [
6
+ "metadata_resolver",
7
+ ]
@@ -0,0 +1,21 @@
1
+ """Handler for database operations related to annotation labels."""
2
+
3
+ from .create import create
4
+ from .delete import delete
5
+ from .get_all import get_all
6
+ from .get_by_id import get_by_id
7
+ from .get_by_ids import get_by_ids
8
+ from .get_by_label_name import get_by_label_name
9
+ from .names_by_ids import names_by_ids
10
+ from .update import update
11
+
12
+ __all__ = [
13
+ "create",
14
+ "delete",
15
+ "get_all",
16
+ "get_by_id",
17
+ "get_by_ids",
18
+ "get_by_label_name",
19
+ "names_by_ids",
20
+ "update",
21
+ ]
@@ -0,0 +1,27 @@
1
+ """Create annotation label functionality."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from sqlmodel import Session
6
+
7
+ from lightly_studio.models.annotation_label import (
8
+ AnnotationLabelCreate,
9
+ AnnotationLabelTable,
10
+ )
11
+
12
+
13
+ def create(session: Session, label: AnnotationLabelCreate) -> AnnotationLabelTable:
14
+ """Create a new annotation label in the database.
15
+
16
+ Args:
17
+ session (Session): The database session.
18
+ label (AnnotationLabelCreate): The annotation label data to be created.
19
+
20
+ Returns:
21
+ AnnotationLabelTable: The newly created annotation label record.
22
+ """
23
+ db_label = AnnotationLabelTable.model_validate(label)
24
+ session.add(db_label)
25
+ session.commit()
26
+ session.refresh(db_label)
27
+ return db_label
@@ -0,0 +1,28 @@
1
+ """Delete annotation label functionality."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from uuid import UUID
6
+
7
+ from sqlmodel import Session
8
+
9
+ from .get_by_id import get_by_id
10
+
11
+
12
+ def delete(session: Session, label_id: UUID) -> bool:
13
+ """Delete an annotation label.
14
+
15
+ Args:
16
+ session (Session): The database session.
17
+ label_id (UUID): The unique identifier of the annotation label.
18
+
19
+ Returns:
20
+ bool: True if the label was deleted, False if the label was not found.
21
+ """
22
+ label = get_by_id(session=session, label_id=label_id)
23
+ if not label:
24
+ return False
25
+
26
+ session.delete(label)
27
+ session.commit()
28
+ return True
@@ -0,0 +1,22 @@
1
+ """Get all annotation labels functionality."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from sqlmodel import Session, col, select
6
+
7
+ from lightly_studio.models.annotation_label import AnnotationLabelTable
8
+
9
+
10
+ def get_all(session: Session) -> list[AnnotationLabelTable]:
11
+ """Retrieve all annotation labels.
12
+
13
+ Args:
14
+ session (Session): The database session.
15
+
16
+ Returns:
17
+ list[AnnotationLabelTable]: A list of annotation labels.
18
+ """
19
+ labels = session.exec(
20
+ select(AnnotationLabelTable).order_by(col(AnnotationLabelTable.created_at).asc())
21
+ ).all()
22
+ return list(labels) if labels else []
@@ -0,0 +1,24 @@
1
+ """Get annotation label by ID functionality."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from uuid import UUID
6
+
7
+ from sqlmodel import Session, select
8
+
9
+ from lightly_studio.models.annotation_label import AnnotationLabelTable
10
+
11
+
12
+ def get_by_id(session: Session, label_id: UUID) -> AnnotationLabelTable | None:
13
+ """Retrieve a single annotation label by ID.
14
+
15
+ Args:
16
+ session (Session): The database session used to execute the query.
17
+ label_id (UUID): The unique identifier of the annotation label to get.
18
+
19
+ Returns:
20
+ AnnotationLabelTable | None: The annotation label if found, or None.
21
+ """
22
+ return session.exec(
23
+ select(AnnotationLabelTable).where(AnnotationLabelTable.annotation_label_id == label_id)
24
+ ).one_or_none()
@@ -0,0 +1,25 @@
1
+ """Get annotation labels by IDs functionality."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Sequence
6
+ from uuid import UUID
7
+
8
+ from sqlmodel import Session, col, select
9
+
10
+ from lightly_studio.models.annotation_label import AnnotationLabelTable
11
+
12
+
13
+ def get_by_ids(session: Session, ids: Sequence[UUID]) -> list[AnnotationLabelTable]:
14
+ """Retrieve annotation labels by their IDs.
15
+
16
+ Output order matches the input order.
17
+ """
18
+ results = session.exec(
19
+ select(AnnotationLabelTable).where(
20
+ col(AnnotationLabelTable.annotation_label_id).in_(list(ids))
21
+ )
22
+ ).all()
23
+ # Return labels in the same order as the input ids.
24
+ label_map = {label.annotation_label_id: label for label in results}
25
+ return [label_map[id_] for id_ in ids if id_ in label_map]
@@ -0,0 +1,24 @@
1
+ """Handler for database operations related to annotation labels."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from sqlmodel import Session, select
6
+
7
+ from lightly_studio.models.annotation_label import (
8
+ AnnotationLabelTable,
9
+ )
10
+
11
+
12
+ def get_by_label_name(session: Session, label_name: str) -> AnnotationLabelTable | None:
13
+ """Retrieve a single annotation label by its name.
14
+
15
+ Args:
16
+ session: The database session to use for the query.
17
+ label_name: The name of the annotation label to retrieve.
18
+
19
+ Returns:
20
+ The AnnotationLabelTable instance if found, None otherwise.
21
+ """
22
+ return session.exec(
23
+ select(AnnotationLabelTable).where(AnnotationLabelTable.annotation_label_name == label_name)
24
+ ).one_or_none()
@@ -0,0 +1,25 @@
1
+ """Get annotation label names by IDs functionality."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Sequence
6
+ from uuid import UUID
7
+
8
+ from sqlmodel import Session
9
+
10
+ from .get_by_ids import get_by_ids
11
+
12
+
13
+ def names_by_ids(session: Session, ids: Sequence[UUID]) -> dict[str, str]:
14
+ """Return a dictionary mapping annotation label IDs to their names.
15
+
16
+ Args:
17
+ session (Session): The database session used to query the labels.
18
+ ids (Sequence[UUID]): A sequence of UUIDs of the annotation label IDs.
19
+
20
+ Returns:
21
+ dict[str, str]: A dictionary with string representations of the UUIDs
22
+ and the values are the corresponding annotation label names.
23
+ """
24
+ labels = get_by_ids(session=session, ids=ids)
25
+ return {str(label.annotation_label_id): label.annotation_label_name for label in labels}
@@ -0,0 +1,38 @@
1
+ """Update annotation label functionality."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from uuid import UUID
6
+
7
+ from sqlmodel import Session
8
+
9
+ from lightly_studio.models.annotation_label import (
10
+ AnnotationLabelCreate,
11
+ AnnotationLabelTable,
12
+ )
13
+
14
+ from .get_by_id import get_by_id
15
+
16
+
17
+ def update(
18
+ session: Session, label_id: UUID, label_data: AnnotationLabelCreate
19
+ ) -> AnnotationLabelTable | None:
20
+ """Update an existing annotation label.
21
+
22
+ Args:
23
+ session (Session): The database session.
24
+ label_id (UUID): The identifier of the annotation label to update.
25
+ label_data (AnnotationLabelCreate): The new data.
26
+
27
+ Returns:
28
+ AnnotationLabelTable | None: The updated annotation label if it exists,
29
+ otherwise None.
30
+ """
31
+ label = get_by_id(session=session, label_id=label_id)
32
+ if not label:
33
+ return None
34
+
35
+ label.annotation_label_name = label_data.annotation_label_name
36
+ session.commit()
37
+ session.refresh(label)
38
+ return label
@@ -0,0 +1,33 @@
1
+ """Resolvers for database operations."""
2
+
3
+ from lightly_studio.resolvers.annotation_resolver.count_annotations_by_dataset import (
4
+ count_annotations_by_dataset,
5
+ )
6
+ from lightly_studio.resolvers.annotation_resolver.create import create
7
+ from lightly_studio.resolvers.annotation_resolver.create_many import create_many
8
+ from lightly_studio.resolvers.annotation_resolver.delete_annotation import (
9
+ delete_annotation,
10
+ )
11
+ from lightly_studio.resolvers.annotation_resolver.delete_annotations import (
12
+ delete_annotations,
13
+ )
14
+ 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
16
+ from lightly_studio.resolvers.annotation_resolver.update_annotation_label import (
17
+ update_annotation_label,
18
+ )
19
+ from lightly_studio.resolvers.annotation_resolver.update_bounding_box import (
20
+ update_bounding_box,
21
+ )
22
+
23
+ __all__ = [
24
+ "count_annotations_by_dataset",
25
+ "create",
26
+ "create_many",
27
+ "delete_annotation",
28
+ "delete_annotations",
29
+ "get_all",
30
+ "get_by_id",
31
+ "update_annotation_label",
32
+ "update_bounding_box",
33
+ ]
@@ -0,0 +1,120 @@
1
+ """Handler for database operations related to annotations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from uuid import UUID
6
+
7
+ from sqlmodel import Session, col, func, select
8
+
9
+ from lightly_studio.models.annotation.annotation_base import (
10
+ AnnotationBaseTable,
11
+ )
12
+ from lightly_studio.models.annotation_label import AnnotationLabelTable
13
+ from lightly_studio.models.sample import SampleTable
14
+ from lightly_studio.models.tag import TagTable
15
+
16
+
17
+ def count_annotations_by_dataset( # noqa: PLR0913 // FIXME: refactor to use proper pydantic
18
+ session: Session,
19
+ dataset_id: UUID,
20
+ filtered_labels: list[str] | None = None,
21
+ min_width: int | None = None,
22
+ max_width: int | None = None,
23
+ min_height: int | None = None,
24
+ max_height: int | None = None,
25
+ tag_ids: list[UUID] | None = None,
26
+ ) -> list[tuple[str, int, int]]:
27
+ """Count annotations for a specific dataset.
28
+
29
+ Annotations for a specific dataset are grouped by annotation
30
+ label name and counted for total and filtered.
31
+ """
32
+ # Query for total counts (unfiltered)
33
+ total_counts_query = (
34
+ select(
35
+ AnnotationLabelTable.annotation_label_name,
36
+ func.count(col(AnnotationBaseTable.annotation_id)).label("total_count"),
37
+ )
38
+ .join(
39
+ AnnotationBaseTable,
40
+ col(AnnotationBaseTable.annotation_label_id)
41
+ == col(AnnotationLabelTable.annotation_label_id),
42
+ )
43
+ .join(
44
+ SampleTable,
45
+ col(SampleTable.sample_id) == col(AnnotationBaseTable.sample_id),
46
+ )
47
+ .where(SampleTable.dataset_id == dataset_id)
48
+ .group_by(AnnotationLabelTable.annotation_label_name)
49
+ .order_by(col(AnnotationLabelTable.annotation_label_name).asc())
50
+ )
51
+
52
+ total_counts = {row[0]: row[1] for row in session.exec(total_counts_query).all()}
53
+
54
+ # Build filtered query for current counts
55
+ filtered_query = (
56
+ select(
57
+ AnnotationLabelTable.annotation_label_name,
58
+ func.count(col(AnnotationBaseTable.annotation_id)).label("current_count"),
59
+ )
60
+ .join(
61
+ AnnotationBaseTable,
62
+ col(AnnotationBaseTable.annotation_label_id)
63
+ == col(AnnotationLabelTable.annotation_label_id),
64
+ )
65
+ .join(
66
+ SampleTable,
67
+ col(SampleTable.sample_id) == col(AnnotationBaseTable.sample_id),
68
+ )
69
+ .where(SampleTable.dataset_id == dataset_id)
70
+ )
71
+
72
+ # Add dimension filters
73
+ if min_width is not None:
74
+ filtered_query = filtered_query.where(SampleTable.width >= min_width)
75
+ if max_width is not None:
76
+ filtered_query = filtered_query.where(SampleTable.width <= max_width)
77
+ if min_height is not None:
78
+ filtered_query = filtered_query.where(SampleTable.height >= min_height)
79
+ if max_height is not None:
80
+ filtered_query = filtered_query.where(SampleTable.height <= max_height)
81
+
82
+ # Add label filter if specified
83
+ if filtered_labels:
84
+ filtered_query = filtered_query.where(
85
+ col(SampleTable.sample_id).in_(
86
+ select(SampleTable.sample_id)
87
+ .join(
88
+ AnnotationBaseTable,
89
+ col(SampleTable.sample_id) == col(AnnotationBaseTable.sample_id),
90
+ )
91
+ .join(
92
+ AnnotationLabelTable,
93
+ col(AnnotationBaseTable.annotation_label_id)
94
+ == col(AnnotationLabelTable.annotation_label_id),
95
+ )
96
+ .where(col(AnnotationLabelTable.annotation_label_name).in_(filtered_labels))
97
+ )
98
+ )
99
+
100
+ # filter by tag_ids
101
+ if tag_ids:
102
+ filtered_query = (
103
+ filtered_query.join(AnnotationBaseTable.tags)
104
+ .where(AnnotationBaseTable.tags.any(col(TagTable.tag_id).in_(tag_ids)))
105
+ .distinct()
106
+ )
107
+
108
+ # Group by label name and sort
109
+ filtered_query = filtered_query.group_by(AnnotationLabelTable.annotation_label_name).order_by(
110
+ col(AnnotationLabelTable.annotation_label_name).asc()
111
+ )
112
+
113
+ _rows = session.exec(filtered_query).all()
114
+
115
+ current_counts = {row[0]: row[1] for row in _rows}
116
+
117
+ return [
118
+ (label, current_counts.get(label, 0), total_count)
119
+ for label, total_count in total_counts.items()
120
+ ]
@@ -0,0 +1,19 @@
1
+ """Handler for database operations related to annotations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from sqlmodel import Session
6
+
7
+ from lightly_studio.models.annotation.annotation_base import (
8
+ AnnotationBaseTable,
9
+ AnnotationCreate,
10
+ )
11
+
12
+
13
+ def create(session: Session, annotation: AnnotationCreate) -> AnnotationBaseTable:
14
+ """Create a new annotation in the database."""
15
+ db_annotation = AnnotationBaseTable.model_validate(annotation)
16
+ session.add(db_annotation)
17
+ session.commit()
18
+ session.refresh(db_annotation)
19
+ return db_annotation
@@ -0,0 +1,96 @@
1
+ """Handler for database operations related to annotations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Sequence
6
+
7
+ from sqlmodel import Session
8
+
9
+ from lightly_studio.models.annotation.annotation_base import (
10
+ AnnotationBaseTable,
11
+ AnnotationCreate,
12
+ )
13
+ from lightly_studio.models.annotation.instance_segmentation import (
14
+ InstanceSegmentationAnnotationTable,
15
+ )
16
+ from lightly_studio.models.annotation.object_detection import (
17
+ ObjectDetectionAnnotationTable,
18
+ )
19
+ from lightly_studio.models.annotation.semantic_segmentation import (
20
+ SemanticSegmentationAnnotationTable,
21
+ )
22
+
23
+
24
+ def create_many(
25
+ session: Session,
26
+ annotations: list[AnnotationCreate],
27
+ ) -> Sequence[AnnotationBaseTable]:
28
+ """Create many annotations with object detection details in bulk."""
29
+ # Step 1: Create all base annotations
30
+ base_annotations = []
31
+ object_detection_annotations = []
32
+ instance_segmentation_annotations = []
33
+ semantic_segmentation_annotations = []
34
+
35
+ for annotation_create in annotations:
36
+ # Create base annotation
37
+ db_base_annotation = AnnotationBaseTable(
38
+ annotation_label_id=annotation_create.annotation_label_id,
39
+ annotation_type=annotation_create.annotation_type,
40
+ annotation_task_id=annotation_create.annotation_task_id,
41
+ confidence=annotation_create.confidence,
42
+ dataset_id=annotation_create.dataset_id,
43
+ sample_id=annotation_create.sample_id,
44
+ )
45
+
46
+ # Set other relationship details to None
47
+ db_base_annotation.instance_segmentation_details = None
48
+ db_base_annotation.semantic_segmentation_details = None
49
+ db_base_annotation.object_detection_details = None
50
+
51
+ base_annotations.append(db_base_annotation)
52
+
53
+ # Bulk save base annotations and flush to get IDs
54
+ session.bulk_save_objects(base_annotations)
55
+ session.flush()
56
+
57
+ # Step 2: Create specific annotation details
58
+ for i, annotation_create in enumerate(annotations):
59
+ # Create object detection details
60
+ if base_annotations[i].annotation_type == "object_detection":
61
+ db_object_detection = ObjectDetectionAnnotationTable(
62
+ annotation_id=base_annotations[i].annotation_id,
63
+ x=annotation_create.x,
64
+ y=annotation_create.y,
65
+ width=annotation_create.width,
66
+ height=annotation_create.height,
67
+ )
68
+ object_detection_annotations.append(db_object_detection)
69
+
70
+ # Create instance segmentation details
71
+ elif base_annotations[i].annotation_type == "instance_segmentation":
72
+ db_instance_segmentation = InstanceSegmentationAnnotationTable(
73
+ annotation_id=base_annotations[i].annotation_id,
74
+ segmentation_mask=annotation_create.segmentation_mask,
75
+ x=annotation_create.x,
76
+ y=annotation_create.y,
77
+ width=annotation_create.width,
78
+ height=annotation_create.height,
79
+ )
80
+ instance_segmentation_annotations.append(db_instance_segmentation)
81
+ elif base_annotations[i].annotation_type == "semantic_segmentation":
82
+ db_semantic_segmentation = SemanticSegmentationAnnotationTable(
83
+ annotation_id=base_annotations[i].annotation_id,
84
+ segmentation_mask=annotation_create.segmentation_mask,
85
+ )
86
+ semantic_segmentation_annotations.append(db_semantic_segmentation)
87
+
88
+ # Bulk save object detection annotations
89
+ session.bulk_save_objects(object_detection_annotations)
90
+ session.bulk_save_objects(instance_segmentation_annotations)
91
+ session.bulk_save_objects(semantic_segmentation_annotations)
92
+
93
+ # Commit everything
94
+ session.commit()
95
+
96
+ return base_annotations
@@ -0,0 +1,45 @@
1
+ """Handler for database operations related to annotations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from uuid import UUID
6
+
7
+ from sqlmodel import Session, col, delete
8
+
9
+ from lightly_studio.models.annotation.links import AnnotationTagLinkTable
10
+ from lightly_studio.resolvers import annotation_resolver
11
+
12
+
13
+ def delete_annotation(
14
+ session: Session,
15
+ annotation_id: UUID,
16
+ ) -> None:
17
+ """Delete all annotations and their tag links using filters.
18
+
19
+ Args:
20
+ session: Database session.
21
+ annotation_id: Annotation ID to filter by.
22
+ """
23
+ # Find annotation_ids to delete
24
+ annotation = annotation_resolver.get_by_id(
25
+ session,
26
+ annotation_id=annotation_id,
27
+ )
28
+ if not annotation:
29
+ return
30
+ if annotation.object_detection_details:
31
+ session.delete(annotation.object_detection_details)
32
+ if annotation.instance_segmentation_details:
33
+ session.delete(annotation.instance_segmentation_details)
34
+ if annotation.semantic_segmentation_details:
35
+ session.delete(annotation.semantic_segmentation_details)
36
+
37
+ session.exec( # type: ignore
38
+ delete(AnnotationTagLinkTable).where(
39
+ col(AnnotationTagLinkTable.annotation_id).in_([annotation.annotation_id])
40
+ )
41
+ )
42
+
43
+ session.commit()
44
+ session.delete(annotation)
45
+ session.commit()