Skip to content

Commit a9ed38a

Browse files
committed
new schema version
1 parent 5856d38 commit a9ed38a

File tree

6 files changed

+50
-70
lines changed

6 files changed

+50
-70
lines changed

nucleus/__init__.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969

7070
from .dataset import Dataset
7171
from .dataset_item import DatasetItem
72-
from .annotation import AnnotationItem, BoxAnnotation, PolygonAnnotation
72+
from .annotation import BoxAnnotation, PolygonAnnotation
7373
from .prediction import BoxPrediction, PolygonPrediction
7474
from .model_run import ModelRun
7575
from .slice import Slice
@@ -462,14 +462,15 @@ def exception_handler(request, exception):
462462
def annotate_dataset(
463463
self,
464464
dataset_id: str,
465-
annotations: List[AnnotationItem],
466-
edit_mode: str,
465+
annotations: List[Union[BoxAnnotation, PolygonAnnotation]],
466+
update: bool,
467467
batch_size: int = 100,
468468
):
469469
"""
470470
Uploads ground truth annotations for a given dataset.
471471
:param dataset_id: id of the dataset
472-
:param annotations: List[AnnotationItem]
472+
:param annotations: List[Union[BoxAnnotation, PolygonAnnotation]]
473+
:param update: whether to update or ignore conflicting annotations
473474
:return: {"dataset_id: str, "annotations_processed": int}
474475
"""
475476

@@ -486,7 +487,7 @@ def annotate_dataset(
486487
tqdm_batches = self.tqdm_bar(batches)
487488

488489
for batch in tqdm_batches:
489-
payload = construct_annotation_payload(batch, edit_mode)
490+
payload = construct_annotation_payload(batch, update)
490491
response = self._make_request(
491492
payload, f"dataset/{dataset_id}/annotate"
492493
)

nucleus/annotation.py

Lines changed: 23 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from enum import Enum
22
from typing import Dict, Optional, Any, Union, List
33
from .constants import (
4-
ANNOTATIONS_KEY,
54
ANNOTATION_ID_KEY,
65
DATASET_ITEM_ID_KEY,
76
REFERENCE_ID_KEY,
@@ -34,14 +33,22 @@ def __init__(
3433
y: Union[float, int],
3534
width: Union[float, int],
3635
height: Union[float, int],
36+
reference_id: Optional[str] = None,
37+
item_id: Optional[str] = None,
3738
annotation_id: Optional[str] = None,
3839
metadata: Optional[Dict] = None,
3940
):
41+
if bool(reference_id) == bool(item_id):
42+
raise Exception(
43+
"You must specify either a reference_id or an item_id for an annotation."
44+
)
4045
self.label = label
4146
self.x = x
4247
self.y = y
4348
self.width = width
4449
self.height = height
50+
self.reference_id = reference_id
51+
self.item_id = item_id
4552
self.annotation_id = annotation_id
4653
self.metadata = metadata if metadata else {}
4754

@@ -54,6 +61,8 @@ def from_json(cls, payload: dict):
5461
y=geometry.get(Y_KEY, 0),
5562
width=geometry.get(WIDTH_KEY, 0),
5663
height=geometry.get(HEIGHT_KEY, 0),
64+
reference_id=payload.get(REFERENCE_ID_KEY, None),
65+
item_id=payload.get(DATASET_ITEM_ID_KEY, None),
5766
annotation_id=payload.get(ANNOTATION_ID_KEY, None),
5867
metadata=payload.get(METADATA_KEY, {}),
5968
)
@@ -68,6 +77,7 @@ def to_payload(self) -> dict:
6877
WIDTH_KEY: self.width,
6978
HEIGHT_KEY: self.height,
7079
},
80+
REFERENCE_ID_KEY: self.reference_id,
7181
ANNOTATION_ID_KEY: self.annotation_id,
7282
METADATA_KEY: self.metadata,
7383
}
@@ -82,11 +92,19 @@ def __init__(
8292
self,
8393
label: str,
8494
vertices: List[Any],
95+
reference_id: Optional[str] = None,
96+
item_id: Optional[str] = None,
8597
annotation_id: Optional[str] = None,
8698
metadata: Optional[Dict] = None,
8799
):
100+
if bool(reference_id) == bool(item_id):
101+
raise Exception(
102+
"You must specify either a reference_id or an item_id for an annotation."
103+
)
88104
self.label = label
89105
self.vertices = vertices
106+
self.reference_id = reference_id
107+
self.item_id = item_id
90108
self.annotation_id = annotation_id
91109
self.metadata = metadata if metadata else {}
92110

@@ -96,6 +114,8 @@ def from_json(cls, payload: dict):
96114
return cls(
97115
label=payload.get(LABEL_KEY, 0),
98116
vertices=geometry.get(VERTICES_KEY, []),
117+
reference_id=payload.get(REFERENCE_ID_KEY, None),
118+
item_id=payload.get(DATASET_ITEM_ID_KEY, None),
99119
annotation_id=payload.get(ANNOTATION_ID_KEY, None),
100120
metadata=payload.get(METADATA_KEY, {}),
101121
)
@@ -105,47 +125,10 @@ def to_payload(self) -> dict:
105125
LABEL_KEY: self.label,
106126
TYPE_KEY: POLYGON_TYPE,
107127
GEOMETRY_KEY: {VERTICES_KEY: self.vertices},
128+
REFERENCE_ID_KEY: self.reference_id,
108129
ANNOTATION_ID_KEY: self.annotation_id,
109130
METADATA_KEY: self.metadata,
110131
}
111132

112133
def __str__(self):
113-
return str(self.to_payload())
114-
115-
116-
class AnnotationItem:
117-
def __init__(
118-
self,
119-
annotations: List[Union[PolygonAnnotation, BoxAnnotation]],
120-
reference_id: Optional[str] = None,
121-
item_id: Optional[str] = None,
122-
):
123-
if bool(reference_id) == bool(item_id):
124-
raise Exception(
125-
"You must specify either a reference_id or an item_id for an annotation."
126-
)
127-
self.annotations = annotations
128-
self.reference_id = reference_id
129-
self.item_id = item_id
130-
131-
@classmethod
132-
def from_json(cls, payload: dict):
133-
return cls(
134-
annotations=[
135-
BoxAnnotation.from_json(ann)
136-
if ann[TYPE_KEY] == "box"
137-
else PolygonAnnotation.from_json(ann)
138-
for ann in payload.get(ANNOTATIONS_KEY)
139-
],
140-
reference_id=payload.get(REFERENCE_ID_KEY, None),
141-
item_id=payload.get(DATASET_ITEM_ID_KEY, None),
142-
)
143-
144-
def to_payload(self) -> dict:
145-
return {
146-
ANNOTATIONS_KEY: [annotation.to_payload() for annotation in self.annotations],
147-
REFERENCE_ID_KEY: self.reference_id,
148-
}
149-
150-
def __str__(self):
151-
return str(self.to_payload())
134+
return str(self.to_payload())

nucleus/constants.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,8 @@
1818
ANNOTATION_ID_KEY = "annotation_id"
1919
ANNOTATIONS_PROCESSED_KEY = "annotations_processed"
2020
PREDICTIONS_PROCESSED_KEY = "predictions_processed"
21-
EDIT_MODE_KEY = "edit_mode"
22-
ANNOTATION_EDIT_MODES = ["update", "insert", "rewrite"]
23-
DEFAULT_ANNOTATION_EDIT_MODE = "insert"
21+
ANNOTATION_UPDATE_KEY = "update"
22+
DEFAULT_ANNOTATION_UPDATE_MODE = False
2423
STATUS_CODE_KEY = "status_code"
2524
SUCCESS_STATUS_CODES = [200, 201]
2625
ERRORS_KEY = "errors"

nucleus/dataset.py

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
from typing import List, Dict, Any, Optional
1+
from typing import List, Dict, Any, Optional, Union
22
from .dataset_item import DatasetItem
3-
from .annotation import AnnotationItem, BoxAnnotation, PolygonAnnotation
3+
from .annotation import BoxAnnotation, PolygonAnnotation
44
from .constants import (
55
DATASET_NAME_KEY,
66
DATASET_MODEL_RUNS_KEY,
@@ -10,9 +10,8 @@
1010
REFERENCE_IDS_KEY,
1111
NAME_KEY,
1212
ITEM_KEY,
13-
ANNOTATION_EDIT_MODES,
13+
DEFAULT_ANNOTATION_UPDATE_MODE,
1414
ANNOTATIONS_KEY,
15-
DEFAULT_ANNOTATION_EDIT_MODE,
1615
)
1716
from .payload_constructor import construct_model_run_creation_payload
1817

@@ -91,8 +90,8 @@ def create_model_run(
9190

9291
def annotate(
9392
self,
94-
annotations: List[AnnotationItem],
95-
edit_mode: Optional[str] = None,
93+
annotations: List[Union[BoxAnnotation | PolygonAnnotation]],
94+
update: Optional[bool] = DEFAULT_ANNOTATION_UPDATE_MODE,
9695
batch_size: int = 20,
9796
) -> dict:
9897
"""
@@ -107,15 +106,8 @@ def annotate(
107106
"ignored_items": int,
108107
}
109108
"""
110-
if edit_mode is None:
111-
edit_mode = DEFAULT_ANNOTATION_EDIT_MODE
112-
if edit_mode not in ANNOTATION_EDIT_MODES:
113-
raise Exception(
114-
f'Edit mode must be empty or one of {ANNOTATION_EDIT_MODES}!'
115-
)
116-
117109
return self._client.annotate_dataset(
118-
self.id, annotations, edit_mode=edit_mode, batch_size=batch_size
110+
self.id, annotations, update=update, batch_size=batch_size
119111
)
120112

121113
def ingest_tasks(self, task_ids: dict):

nucleus/payload_constructor.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from .annotation import BoxAnnotation, PolygonAnnotation
44
from .prediction import BoxPrediction, PolygonPrediction
55
from .constants import (
6+
ANNOTATION_UPDATE_KEY,
67
NAME_KEY,
78
METADATA_KEY,
89
REFERENCE_ID_KEY,
@@ -11,7 +12,6 @@
1112
ITEMS_KEY,
1213
FORCE_KEY,
1314
MODEL_ID_KEY,
14-
EDIT_MODE_KEY,
1515
)
1616

1717

@@ -30,14 +30,14 @@ def construct_append_payload(
3030

3131

3232
def construct_annotation_payload(
33-
box_annotations: List[Union[BoxAnnotation, PolygonAnnotation]],
34-
edit_mode: str,
33+
annotation_items: List[Union[BoxAnnotation, PolygonAnnotation]],
34+
update: bool,
3535
) -> dict:
3636
annotations = []
37-
for annotation in box_annotations:
38-
annotations.append(annotation.to_payload())
37+
for annotation_item in annotation_items:
38+
annotations.append(annotation_item.to_payload())
3939

40-
return {ANNOTATION_ITEMS_KEY: annotations, EDIT_MODE_KEY: edit_mode}
40+
return {ANNOTATION_ITEMS_KEY: annotations, ANNOTATION_UPDATE_KEY: update}
4141

4242

4343
def construct_box_predictions_payload(

nucleus/prediction.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from typing import Dict, Optional, List, Any
22
from .annotation import BoxAnnotation, PolygonAnnotation
33
from .constants import (
4+
ANNOTATION_ID_KEY,
45
DATASET_ITEM_ID_KEY,
56
REFERENCE_ID_KEY,
67
METADATA_KEY,
@@ -26,10 +27,11 @@ def __init__(
2627
reference_id: Optional[str] = None,
2728
item_id: Optional[str] = None,
2829
confidence: Optional[float] = None,
30+
annotation_id: Optional[str] = None,
2931
metadata: Optional[Dict] = None,
3032
):
3133
super().__init__(
32-
label, x, y, width, height, reference_id, item_id, metadata
34+
label, x, y, width, height, reference_id, item_id, annotation_id, metadata
3335
)
3436
self.confidence = confidence
3537

@@ -52,6 +54,7 @@ def from_json(cls, payload: dict):
5254
reference_id=payload.get(REFERENCE_ID_KEY, None),
5355
item_id=payload.get(DATASET_ITEM_ID_KEY, None),
5456
confidence=payload.get(CONFIDENCE_KEY, None),
57+
annotation_id=payload.get(ANNOTATION_ID_KEY, None),
5558
metadata=payload.get(METADATA_KEY, {}),
5659
)
5760

@@ -67,9 +70,10 @@ def __init__(
6770
reference_id: Optional[str] = None,
6871
item_id: Optional[str] = None,
6972
confidence: Optional[float] = None,
73+
annotation_id: Optional[str] = None,
7074
metadata: Optional[Dict] = None,
7175
):
72-
super().__init__(label, vertices, reference_id, item_id, metadata)
76+
super().__init__(label, vertices, reference_id, item_id, annotation_id, metadata)
7377
self.confidence = confidence
7478

7579
def to_payload(self) -> dict:
@@ -88,6 +92,7 @@ def from_json(cls, payload: dict):
8892
reference_id=payload.get(REFERENCE_ID_KEY, None),
8993
item_id=payload.get(DATASET_ITEM_ID_KEY, None),
9094
confidence=payload.get(CONFIDENCE_KEY, None),
95+
annotation_id=payload.get(ANNOTATION_ID_KEY, None),
9196
metadata=payload.get(METADATA_KEY, {}),
9297
)
9398

0 commit comments

Comments
 (0)