Skip to content

Commit ecbaf89

Browse files
committed
add numeric annotation
1 parent f4e03e0 commit ecbaf89

5 files changed

Lines changed: 89 additions & 2 deletions

File tree

datamint/api/endpoints/annotations_api.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
CoordinateSystem,
3131
ImageClassification,
3232
LineAnnotation,
33+
NumericAnnotation,
3334
annotation_from_dict,
3435
)
3536
from datamint.exceptions import ItemNotFoundError, ServerError
@@ -1151,6 +1152,46 @@ def create_image_classification(self,
11511152
raise TypeError('Expected a single annotation id for image classification creation.')
11521153
return created
11531154

1155+
def create_numeric_annotation(self,
1156+
resource: str | Resource,
1157+
identifier: str,
1158+
value: int | float,
1159+
units: str | None = None,
1160+
imported_from: str | None = None,
1161+
model_id: str | None = None,
1162+
source: str | None = 'imported',
1163+
) -> str:
1164+
"""
1165+
Create a numeric value annotation (e.g. a measurement or count).
1166+
1167+
Args:
1168+
resource: The resource unique id or Resource instance.
1169+
identifier: The annotation identifier/label.
1170+
value: The numeric value. ``int`` maps to :attr:`AnnotationType.INTEGER`,
1171+
``float`` maps to :attr:`AnnotationType.FLOAT`.
1172+
units: Optional unit label for the value (e.g. 'years', 'mm').
1173+
imported_from: The imported from source value.
1174+
model_id: The model unique id.
1175+
source: Annotation source tag. Defaults to 'imported' since this is a direct API
1176+
entry point; :meth:`upload_predictions` overrides it with 'model_pipeline'/'model_deploy'.
1177+
1178+
Returns:
1179+
The id of the created annotation.
1180+
"""
1181+
annotation = NumericAnnotation(
1182+
name=identifier,
1183+
value=value,
1184+
units=units,
1185+
imported_from=imported_from,
1186+
model_id=model_id,
1187+
source=source,
1188+
)
1189+
1190+
created = self.create(resource, annotation)
1191+
if not isinstance(created, str):
1192+
raise TypeError('Expected a single annotation id for numeric annotation creation.')
1193+
return created
1194+
11541195
def upload_predictions(
11551196
self,
11561197
resource: str | Resource,

datamint/entities/annotations/__init__.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from .box_annotation import BoxAnnotation
55
from .geometry import BoxGeometry, CoordinateSystem, Geometry, LineGeometry
66
from .line_annotation import LineAnnotation
7+
from .numeric_annotation import NumericAnnotation
78
from .volume_segmentation import VolumeSegmentation
89
from .types import AnnotationType
910

@@ -15,6 +16,10 @@ def annotation_from_dict(data: dict) -> Annotation:
1516
1617
* ``'segmentation'`` with a ``class_map`` → :class:`VolumeSegmentation`
1718
* ``'segmentation'`` without ``class_map`` → :class:`ImageSegmentation`
19+
* ``'category'`` → :class:`ImageClassification`
20+
* ``'integer'``/``'float'`` → :class:`NumericAnnotation`
21+
* ``'line'`` → :class:`LineAnnotation`
22+
* ``'square'`` → :class:`BoxAnnotation`
1823
* anything else → :class:`Annotation`
1924
2025
``segmentation_data`` dicts are automatically deserialised by the
@@ -40,6 +45,9 @@ def annotation_from_dict(data: dict) -> Annotation:
4045
if annotation_type in (AnnotationType.CATEGORY, AnnotationType.CATEGORY.value):
4146
return ImageClassification(**normalized_data)
4247

48+
if annotation_type in (AnnotationType.INTEGER, AnnotationType.FLOAT):
49+
return NumericAnnotation(**normalized_data)
50+
4351
if annotation_type in (AnnotationType.LINE, AnnotationType.LINE.value):
4452
return LineAnnotation(**normalized_data)
4553

@@ -59,6 +67,7 @@ def annotation_from_dict(data: dict) -> Annotation:
5967
"Geometry",
6068
"LineAnnotation",
6169
"LineGeometry",
70+
"NumericAnnotation",
6271
"VolumeSegmentation",
6372
"AnnotationType",
6473
"annotation_from_dict",

datamint/entities/annotations/annotation.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ def _normalize_annotation_data(data: dict[str, Any]) -> dict[str, Any]:
4747
if 'scope' not in converted_data:
4848
converted_data['scope'] = 'image' if converted_data.get('frame_index') is None else 'frame'
4949

50+
if converted_data.get('annotation_type') in (AnnotationType.INTEGER, AnnotationType.FLOAT):
51+
raw_value = converted_data.pop('text_value', None)
52+
if raw_value is not None and converted_data.get('numeric_value') is None:
53+
converted_data['numeric_value'] = raw_value
54+
5055
return converted_data
5156

5257

@@ -290,7 +295,7 @@ def _to_create_dto(self):
290295
identifier=self.identifier,
291296
scope=self.scope,
292297
annotation_worklist_id=self.annotation_worklist_id,
293-
value=self.text_value,
298+
value=self.text_value if self.text_value is not None else self.numeric_value,
294299
imported_from=self.imported_from,
295300
import_author=self.import_author,
296301
frame_index=self.frame_index,
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from __future__ import annotations
2+
3+
from typing import Any
4+
5+
from .annotation import Annotation
6+
from .types import AnnotationType
7+
8+
9+
class NumericAnnotation(Annotation):
10+
def __init__(
11+
self,
12+
name: str | None = None,
13+
value: int | float | None = None,
14+
units: str | None = None,
15+
confiability: float = 1.0,
16+
**kwargs: Any,
17+
) -> None:
18+
if name is not None:
19+
kwargs.setdefault('identifier', name)
20+
if value is not None:
21+
kwargs.setdefault('numeric_value', value)
22+
is_int = isinstance(value, int) and not isinstance(value, bool)
23+
kwargs.setdefault('annotation_type', AnnotationType.INTEGER if is_int else AnnotationType.FLOAT)
24+
25+
if units is not None:
26+
kwargs.setdefault('units', units)
27+
28+
kwargs.setdefault('confiability', confiability)
29+
kwargs.setdefault('scope', 'image')
30+
super().__init__(**kwargs)

datamint/entities/annotations/types.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,6 @@ class AnnotationType(StrEnum):
1717
SQUARE = 'square'
1818
CIRCLE = 'circle'
1919
CATEGORY = 'category'
20-
LABEL = 'label'
20+
LABEL = 'label'
21+
INTEGER = 'integer'
22+
FLOAT = 'float'

0 commit comments

Comments
 (0)