scale-nucleus 0.1.24__py3-none-any.whl → 0.6.4__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.
- cli/client.py +14 -0
- cli/datasets.py +77 -0
- cli/helpers/__init__.py +0 -0
- cli/helpers/nucleus_url.py +10 -0
- cli/helpers/web_helper.py +40 -0
- cli/install_completion.py +33 -0
- cli/jobs.py +42 -0
- cli/models.py +35 -0
- cli/nu.py +42 -0
- cli/reference.py +8 -0
- cli/slices.py +62 -0
- cli/tests.py +121 -0
- nucleus/__init__.py +446 -710
- nucleus/annotation.py +405 -85
- nucleus/autocurate.py +9 -0
- nucleus/connection.py +87 -0
- nucleus/constants.py +5 -1
- nucleus/data_transfer_object/__init__.py +0 -0
- nucleus/data_transfer_object/dataset_details.py +9 -0
- nucleus/data_transfer_object/dataset_info.py +26 -0
- nucleus/data_transfer_object/dataset_size.py +5 -0
- nucleus/data_transfer_object/scenes_list.py +18 -0
- nucleus/dataset.py +1137 -212
- nucleus/dataset_item.py +130 -26
- nucleus/dataset_item_uploader.py +297 -0
- nucleus/deprecation_warning.py +32 -0
- nucleus/errors.py +9 -0
- nucleus/job.py +71 -3
- nucleus/logger.py +9 -0
- nucleus/metadata_manager.py +45 -0
- nucleus/metrics/__init__.py +10 -0
- nucleus/metrics/base.py +117 -0
- nucleus/metrics/categorization_metrics.py +197 -0
- nucleus/metrics/errors.py +7 -0
- nucleus/metrics/filters.py +40 -0
- nucleus/metrics/geometry.py +198 -0
- nucleus/metrics/metric_utils.py +28 -0
- nucleus/metrics/polygon_metrics.py +480 -0
- nucleus/metrics/polygon_utils.py +299 -0
- nucleus/model.py +121 -15
- nucleus/model_run.py +34 -57
- nucleus/payload_constructor.py +29 -19
- nucleus/prediction.py +259 -17
- nucleus/pydantic_base.py +26 -0
- nucleus/retry_strategy.py +4 -0
- nucleus/scene.py +204 -19
- nucleus/slice.py +230 -67
- nucleus/upload_response.py +20 -9
- nucleus/url_utils.py +4 -0
- nucleus/utils.py +134 -37
- nucleus/validate/__init__.py +24 -0
- nucleus/validate/client.py +168 -0
- nucleus/validate/constants.py +20 -0
- nucleus/validate/data_transfer_objects/__init__.py +0 -0
- nucleus/validate/data_transfer_objects/eval_function.py +81 -0
- nucleus/validate/data_transfer_objects/scenario_test.py +19 -0
- nucleus/validate/data_transfer_objects/scenario_test_evaluations.py +11 -0
- nucleus/validate/data_transfer_objects/scenario_test_metric.py +12 -0
- nucleus/validate/errors.py +6 -0
- nucleus/validate/eval_functions/__init__.py +0 -0
- nucleus/validate/eval_functions/available_eval_functions.py +212 -0
- nucleus/validate/eval_functions/base_eval_function.py +60 -0
- nucleus/validate/scenario_test.py +143 -0
- nucleus/validate/scenario_test_evaluation.py +114 -0
- nucleus/validate/scenario_test_metric.py +14 -0
- nucleus/validate/utils.py +8 -0
- {scale_nucleus-0.1.24.dist-info → scale_nucleus-0.6.4.dist-info}/LICENSE +0 -0
- scale_nucleus-0.6.4.dist-info/METADATA +213 -0
- scale_nucleus-0.6.4.dist-info/RECORD +71 -0
- {scale_nucleus-0.1.24.dist-info → scale_nucleus-0.6.4.dist-info}/WHEEL +1 -1
- scale_nucleus-0.6.4.dist-info/entry_points.txt +3 -0
- scale_nucleus-0.1.24.dist-info/METADATA +0 -85
- scale_nucleus-0.1.24.dist-info/RECORD +0 -21
nucleus/prediction.py
CHANGED
@@ -1,52 +1,112 @@
|
|
1
|
-
|
1
|
+
"""
|
2
|
+
All of the prediction types supported. In general, prediction types are the same
|
3
|
+
as annotation types, but come with additional, optional data that can be attached
|
4
|
+
such as confidence or probability distributions.
|
5
|
+
"""
|
6
|
+
from dataclasses import dataclass, field
|
7
|
+
from typing import Dict, List, Optional, Union
|
8
|
+
|
2
9
|
from .annotation import (
|
3
10
|
BoxAnnotation,
|
11
|
+
CategoryAnnotation,
|
12
|
+
CuboidAnnotation,
|
4
13
|
Point,
|
14
|
+
Point3D,
|
5
15
|
PolygonAnnotation,
|
6
16
|
Segment,
|
7
17
|
SegmentationAnnotation,
|
8
|
-
CuboidAnnotation,
|
9
|
-
Point3D,
|
10
18
|
)
|
11
19
|
from .constants import (
|
12
20
|
ANNOTATION_ID_KEY,
|
21
|
+
ANNOTATIONS_KEY,
|
13
22
|
BOX_TYPE,
|
23
|
+
CATEGORY_TYPE,
|
24
|
+
CLASS_PDF_KEY,
|
25
|
+
CONFIDENCE_KEY,
|
14
26
|
CUBOID_TYPE,
|
15
|
-
|
16
|
-
REFERENCE_ID_KEY,
|
17
|
-
METADATA_KEY,
|
27
|
+
DIMENSIONS_KEY,
|
18
28
|
GEOMETRY_KEY,
|
29
|
+
HEIGHT_KEY,
|
19
30
|
LABEL_KEY,
|
31
|
+
MASK_URL_KEY,
|
32
|
+
METADATA_KEY,
|
33
|
+
POLYGON_TYPE,
|
34
|
+
POSITION_KEY,
|
35
|
+
REFERENCE_ID_KEY,
|
36
|
+
TAXONOMY_NAME_KEY,
|
20
37
|
TYPE_KEY,
|
38
|
+
VERTICES_KEY,
|
39
|
+
WIDTH_KEY,
|
21
40
|
X_KEY,
|
22
41
|
Y_KEY,
|
23
|
-
WIDTH_KEY,
|
24
|
-
HEIGHT_KEY,
|
25
|
-
CLASS_PDF_KEY,
|
26
|
-
CONFIDENCE_KEY,
|
27
|
-
VERTICES_KEY,
|
28
|
-
ANNOTATIONS_KEY,
|
29
|
-
MASK_URL_KEY,
|
30
|
-
POSITION_KEY,
|
31
|
-
DIMENSIONS_KEY,
|
32
42
|
YAW_KEY,
|
33
43
|
)
|
34
44
|
|
35
45
|
|
36
46
|
def from_json(payload: dict):
|
47
|
+
"""Instantiates prediction object from schematized JSON dict payload."""
|
37
48
|
if payload.get(TYPE_KEY, None) == BOX_TYPE:
|
38
49
|
return BoxPrediction.from_json(payload)
|
39
50
|
elif payload.get(TYPE_KEY, None) == POLYGON_TYPE:
|
40
51
|
return PolygonPrediction.from_json(payload)
|
41
52
|
elif payload.get(TYPE_KEY, None) == CUBOID_TYPE:
|
42
53
|
return CuboidPrediction.from_json(payload)
|
54
|
+
elif payload.get(TYPE_KEY, None) == CATEGORY_TYPE:
|
55
|
+
return CategoryPrediction.from_json(payload)
|
43
56
|
else:
|
44
57
|
return SegmentationPrediction.from_json(payload)
|
45
58
|
|
46
59
|
|
47
60
|
class SegmentationPrediction(SegmentationAnnotation):
|
48
|
-
|
49
|
-
|
61
|
+
"""Predicted segmentation mask on a 2D image.
|
62
|
+
|
63
|
+
::
|
64
|
+
|
65
|
+
from nucleus import SegmentationPrediction
|
66
|
+
|
67
|
+
segmentation = SegmentationPrediction(
|
68
|
+
mask_url="s3://your-bucket-name/pred-seg-masks/image_2_pred_mask_id1.png",
|
69
|
+
annotations=[
|
70
|
+
Segment(label="grass", index="1"),
|
71
|
+
Segment(label="road", index="2"),
|
72
|
+
Segment(label="bus", index="3", metadata={"vehicle_color": "yellow"}),
|
73
|
+
Segment(label="tree", index="4")
|
74
|
+
],
|
75
|
+
reference_id="image_2",
|
76
|
+
annotation_id="image_2_pred_mask_1",
|
77
|
+
)
|
78
|
+
|
79
|
+
Parameters:
|
80
|
+
mask_url (str): A URL pointing to the segmentation prediction mask which is
|
81
|
+
accessible to Scale. The mask is an HxW int8 array saved in PNG format,
|
82
|
+
with each pixel value ranging from [0, N), where N is the number of
|
83
|
+
possible classes (for semantic segmentation) or instances (for instance
|
84
|
+
segmentation).
|
85
|
+
|
86
|
+
The height and width of the mask must be the same as the
|
87
|
+
original image. One example for semantic segmentation: the mask is 0
|
88
|
+
for pixels where there is background, 1 where there is a car, and 2
|
89
|
+
where there is a pedestrian.
|
90
|
+
|
91
|
+
Another example for instance segmentation: the mask is 0 for one car,
|
92
|
+
1 for another car, 2 for a motorcycle and 3 for another motorcycle.
|
93
|
+
The class name for each value in the mask is stored in the list of
|
94
|
+
Segment objects passed for "annotations"
|
95
|
+
annotations (List[:class:`Segment`]): The list of mappings between the integer values contained
|
96
|
+
in mask_url and string class labels. In the semantic segmentation
|
97
|
+
example above these would map that 0 to background, 1 to car and 2 to
|
98
|
+
pedestrian. In the instance segmentation example above, 0 and 1 would
|
99
|
+
both be mapped to car, 2 and 3 would both be mapped to motorcycle
|
100
|
+
reference_id (str): User-defined ID of the image to which to apply this annotation.
|
101
|
+
annotation_id (Optional[str]): For segmentation predictions, this value is ignored
|
102
|
+
because there can only be one segmentation prediction per dataset item.
|
103
|
+
Therefore regardless of annotation ID, if there is an existing
|
104
|
+
segmentation on a dataset item, it will be ignored unless update=True
|
105
|
+
is passed to :meth:`Dataset.annotate`, in which case it will be overwritten.
|
106
|
+
Storing a custom ID here may be useful in order to tie this annotation
|
107
|
+
to an external database, and its value will be returned for any export.
|
108
|
+
"""
|
109
|
+
|
50
110
|
@classmethod
|
51
111
|
def from_json(cls, payload: dict):
|
52
112
|
return cls(
|
@@ -61,6 +121,36 @@ class SegmentationPrediction(SegmentationAnnotation):
|
|
61
121
|
|
62
122
|
|
63
123
|
class BoxPrediction(BoxAnnotation):
|
124
|
+
"""Prediction of a bounding box.
|
125
|
+
|
126
|
+
Parameters:
|
127
|
+
label (str): The label for this annotation (e.g. car, pedestrian, bicycle)
|
128
|
+
x (Union[float, int]): The distance, in pixels, between the left border
|
129
|
+
of the bounding box and the left border of the image.
|
130
|
+
y (Union[float, int]): The distance, in pixels, between the top border
|
131
|
+
of the bounding box and the top border of the image.
|
132
|
+
width (Union[float, int]): The width in pixels of the annotation.
|
133
|
+
height (Union[float, int]): The height in pixels of the annotation.
|
134
|
+
reference_id (str): User-defined ID of the image to which to apply this
|
135
|
+
annotation.
|
136
|
+
confidence: 0-1 indicating the confidence of the prediction.
|
137
|
+
annotation_id (Optional[str]): The annotation ID that uniquely
|
138
|
+
identifies this annotation within its target dataset item. Upon ingest,
|
139
|
+
a matching annotation id will be ignored by default, and updated if
|
140
|
+
update=True for dataset.annotate. If no annotation ID is passed, one
|
141
|
+
will be automatically generated using the label, x, y, width, and
|
142
|
+
height, so that you can make inserts idempotently and identical boxes
|
143
|
+
will be ignored.
|
144
|
+
metadata (Optional[Dict]): Arbitrary key/value dictionary of info to
|
145
|
+
attach to this annotation. Strings, floats and ints are supported best
|
146
|
+
by querying and insights features within Nucleus. For more details see
|
147
|
+
our `metadata guide <https://nucleus.scale.com/docs/upload-metadata>`_.
|
148
|
+
class_pdf: An optional complete class probability distribution on this
|
149
|
+
annotation. Each value should be between 0 and 1 (inclusive), and sum up to
|
150
|
+
1 as a complete distribution. This can be useful for computing entropy to
|
151
|
+
surface places where the model is most uncertain.
|
152
|
+
"""
|
153
|
+
|
64
154
|
def __init__(
|
65
155
|
self,
|
66
156
|
label: str,
|
@@ -114,6 +204,28 @@ class BoxPrediction(BoxAnnotation):
|
|
114
204
|
|
115
205
|
|
116
206
|
class PolygonPrediction(PolygonAnnotation):
|
207
|
+
"""Prediction of a polygon.
|
208
|
+
|
209
|
+
Parameters:
|
210
|
+
label (str): The label for this annotation (e.g. car, pedestrian, bicycle).
|
211
|
+
vertices List[:class:`Point`]: The list of points making up the polygon.
|
212
|
+
reference_id (str): User-defined ID of the image to which to apply this
|
213
|
+
annotation.
|
214
|
+
confidence: 0-1 indicating the confidence of the prediction.
|
215
|
+
annotation_id (Optional[str]): The annotation ID that uniquely identifies
|
216
|
+
this annotation within its target dataset item. Upon ingest, a matching
|
217
|
+
annotation id will be ignored by default, and updated if update=True
|
218
|
+
for dataset.annotate.
|
219
|
+
metadata (Optional[Dict]): Arbitrary key/value dictionary of info to
|
220
|
+
attach to this annotation. Strings, floats and ints are supported best
|
221
|
+
by querying and insights features within Nucleus. For more details see
|
222
|
+
our `metadata guide <https://nucleus.scale.com/docs/upload-metadata>`_.
|
223
|
+
class_pdf: An optional complete class probability distribution on this
|
224
|
+
annotation. Each value should be between 0 and 1 (inclusive), and sum up to
|
225
|
+
1 as a complete distribution. This can be useful for computing entropy to
|
226
|
+
surface places where the model is most uncertain.
|
227
|
+
"""
|
228
|
+
|
117
229
|
def __init__(
|
118
230
|
self,
|
119
231
|
label: str,
|
@@ -160,6 +272,29 @@ class PolygonPrediction(PolygonAnnotation):
|
|
160
272
|
|
161
273
|
|
162
274
|
class CuboidPrediction(CuboidAnnotation):
|
275
|
+
"""A prediction of 3D cuboid.
|
276
|
+
|
277
|
+
Parameters:
|
278
|
+
label (str): The label for this annotation (e.g. car, pedestrian, bicycle)
|
279
|
+
position (:class:`Point3D`): The point at the center of the cuboid
|
280
|
+
dimensions (:class:`Point3D`): The length (x), width (y), and height (z) of the cuboid
|
281
|
+
yaw (float): The rotation, in radians, about the Z axis of the cuboid
|
282
|
+
reference_id (str): User-defined ID of the image to which to apply this annotation.
|
283
|
+
confidence: 0-1 indicating the confidence of the prediction.
|
284
|
+
annotation_id (Optional[str]): The annotation ID that uniquely identifies this
|
285
|
+
annotation within its target dataset item. Upon ingest, a matching
|
286
|
+
annotation id will be ignored by default, and updated if update=True
|
287
|
+
for dataset.annotate.
|
288
|
+
metadata (Optional[str]): Arbitrary key/value dictionary of info to attach to this
|
289
|
+
annotation. Strings, floats and ints are supported best by querying
|
290
|
+
and insights features within Nucleus. For more details see our `metadata
|
291
|
+
guide <https://nucleus.scale.com/docs/upload-metadata>`_.
|
292
|
+
class_pdf: An optional complete class probability distribution on this
|
293
|
+
annotation. Each value should be between 0 and 1 (inclusive), and sum up to
|
294
|
+
1 as a complete distribution. This can be useful for computing entropy to
|
295
|
+
surface places where the model is most uncertain.
|
296
|
+
"""
|
297
|
+
|
163
298
|
def __init__(
|
164
299
|
self,
|
165
300
|
label: str,
|
@@ -207,3 +342,110 @@ class CuboidPrediction(CuboidAnnotation):
|
|
207
342
|
metadata=payload.get(METADATA_KEY, {}),
|
208
343
|
class_pdf=payload.get(CLASS_PDF_KEY, None),
|
209
344
|
)
|
345
|
+
|
346
|
+
|
347
|
+
class CategoryPrediction(CategoryAnnotation):
|
348
|
+
"""A prediction of a category.
|
349
|
+
|
350
|
+
Parameters:
|
351
|
+
label: The label for this annotation (e.g. car, pedestrian, bicycle).
|
352
|
+
reference_id: The reference ID of the image you wish to apply this annotation to.
|
353
|
+
taxonomy_name: The name of the taxonomy this annotation conforms to.
|
354
|
+
See :meth:`Dataset.add_taxonomy`.
|
355
|
+
confidence: 0-1 indicating the confidence of the prediction.
|
356
|
+
class_pdf: An optional complete class probability distribution on this
|
357
|
+
prediction. Each value should be between 0 and 1 (inclusive), and sum up to
|
358
|
+
1 as a complete distribution. This can be useful for computing entropy to
|
359
|
+
surface places where the model is most uncertain.
|
360
|
+
metadata: Arbitrary key/value dictionary of info to attach to this annotation.
|
361
|
+
Strings, floats and ints are supported best by querying and insights
|
362
|
+
features within Nucleus. For more details see our `metadata guide
|
363
|
+
<https://nucleus.scale.com/docs/upload-metadata>`_.
|
364
|
+
"""
|
365
|
+
|
366
|
+
def __init__(
|
367
|
+
self,
|
368
|
+
label: str,
|
369
|
+
reference_id: str,
|
370
|
+
taxonomy_name: Optional[str] = None,
|
371
|
+
confidence: Optional[float] = None,
|
372
|
+
metadata: Optional[Dict] = None,
|
373
|
+
class_pdf: Optional[Dict] = None,
|
374
|
+
):
|
375
|
+
super().__init__(
|
376
|
+
label=label,
|
377
|
+
taxonomy_name=taxonomy_name,
|
378
|
+
reference_id=reference_id,
|
379
|
+
metadata=metadata,
|
380
|
+
)
|
381
|
+
self.confidence = confidence
|
382
|
+
self.class_pdf = class_pdf
|
383
|
+
|
384
|
+
def to_payload(self) -> dict:
|
385
|
+
payload = super().to_payload()
|
386
|
+
if self.confidence is not None:
|
387
|
+
payload[CONFIDENCE_KEY] = self.confidence
|
388
|
+
if self.class_pdf is not None:
|
389
|
+
payload[CLASS_PDF_KEY] = self.class_pdf
|
390
|
+
|
391
|
+
return payload
|
392
|
+
|
393
|
+
@classmethod
|
394
|
+
def from_json(cls, payload: dict):
|
395
|
+
return cls(
|
396
|
+
label=payload.get(LABEL_KEY, 0),
|
397
|
+
taxonomy_name=payload.get(TAXONOMY_NAME_KEY, None),
|
398
|
+
reference_id=payload[REFERENCE_ID_KEY],
|
399
|
+
confidence=payload.get(CONFIDENCE_KEY, None),
|
400
|
+
metadata=payload.get(METADATA_KEY, {}),
|
401
|
+
class_pdf=payload.get(CLASS_PDF_KEY, None),
|
402
|
+
)
|
403
|
+
|
404
|
+
|
405
|
+
Prediction = Union[
|
406
|
+
BoxPrediction,
|
407
|
+
PolygonPrediction,
|
408
|
+
CuboidPrediction,
|
409
|
+
CategoryPrediction,
|
410
|
+
SegmentationPrediction,
|
411
|
+
]
|
412
|
+
|
413
|
+
|
414
|
+
@dataclass
|
415
|
+
class PredictionList:
|
416
|
+
"""Wrapper class separating a list of predictions by type."""
|
417
|
+
|
418
|
+
box_predictions: List[BoxPrediction] = field(default_factory=list)
|
419
|
+
polygon_predictions: List[PolygonPrediction] = field(default_factory=list)
|
420
|
+
cuboid_predictions: List[CuboidPrediction] = field(default_factory=list)
|
421
|
+
category_predictions: List[CategoryPrediction] = field(
|
422
|
+
default_factory=list
|
423
|
+
)
|
424
|
+
segmentation_predictions: List[SegmentationPrediction] = field(
|
425
|
+
default_factory=list
|
426
|
+
)
|
427
|
+
|
428
|
+
def add_predictions(self, predictions: List[Prediction]):
|
429
|
+
for prediction in predictions:
|
430
|
+
if isinstance(prediction, BoxPrediction):
|
431
|
+
self.box_predictions.append(prediction)
|
432
|
+
elif isinstance(prediction, PolygonPrediction):
|
433
|
+
self.polygon_predictions.append(prediction)
|
434
|
+
elif isinstance(prediction, CuboidPrediction):
|
435
|
+
self.cuboid_predictions.append(prediction)
|
436
|
+
elif isinstance(prediction, CategoryPrediction):
|
437
|
+
self.category_predictions.append(prediction)
|
438
|
+
else:
|
439
|
+
assert isinstance(
|
440
|
+
prediction, SegmentationPrediction
|
441
|
+
), f"Unexpected prediction type: {type(prediction)}"
|
442
|
+
self.segmentation_predictions.append(prediction)
|
443
|
+
|
444
|
+
def __len__(self):
|
445
|
+
return (
|
446
|
+
len(self.box_predictions)
|
447
|
+
+ len(self.polygon_predictions)
|
448
|
+
+ len(self.cuboid_predictions)
|
449
|
+
+ len(self.category_predictions)
|
450
|
+
+ len(self.segmentation_predictions)
|
451
|
+
)
|
nucleus/pydantic_base.py
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
from pydantic import BaseModel # pylint: disable=no-name-in-module
|
2
|
+
|
3
|
+
|
4
|
+
class ImmutableModel(BaseModel):
|
5
|
+
class Config:
|
6
|
+
allow_mutation = False
|
7
|
+
|
8
|
+
|
9
|
+
class DictCompatibleModel(BaseModel):
|
10
|
+
"""Backwards compatible wrapper where we transform dictionaries into Pydantic Models
|
11
|
+
|
12
|
+
Allows us to access model.key with model["key"].
|
13
|
+
"""
|
14
|
+
|
15
|
+
def __getitem__(self, key):
|
16
|
+
return getattr(self, key)
|
17
|
+
|
18
|
+
|
19
|
+
class DictCompatibleImmutableModel(ImmutableModel):
|
20
|
+
"""Backwards compatible wrapper where we transform dictionaries into Pydantic Models
|
21
|
+
|
22
|
+
Allows us to access model.key with model["key"].
|
23
|
+
"""
|
24
|
+
|
25
|
+
def __getitem__(self, key):
|
26
|
+
return getattr(self, key)
|