scale-nucleus 0.1.22__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.
Files changed (73) hide show
  1. cli/client.py +14 -0
  2. cli/datasets.py +77 -0
  3. cli/helpers/__init__.py +0 -0
  4. cli/helpers/nucleus_url.py +10 -0
  5. cli/helpers/web_helper.py +40 -0
  6. cli/install_completion.py +33 -0
  7. cli/jobs.py +42 -0
  8. cli/models.py +35 -0
  9. cli/nu.py +42 -0
  10. cli/reference.py +8 -0
  11. cli/slices.py +62 -0
  12. cli/tests.py +121 -0
  13. nucleus/__init__.py +453 -699
  14. nucleus/annotation.py +435 -80
  15. nucleus/autocurate.py +9 -0
  16. nucleus/connection.py +87 -0
  17. nucleus/constants.py +12 -2
  18. nucleus/data_transfer_object/__init__.py +0 -0
  19. nucleus/data_transfer_object/dataset_details.py +9 -0
  20. nucleus/data_transfer_object/dataset_info.py +26 -0
  21. nucleus/data_transfer_object/dataset_size.py +5 -0
  22. nucleus/data_transfer_object/scenes_list.py +18 -0
  23. nucleus/dataset.py +1139 -215
  24. nucleus/dataset_item.py +130 -26
  25. nucleus/dataset_item_uploader.py +297 -0
  26. nucleus/deprecation_warning.py +32 -0
  27. nucleus/errors.py +21 -1
  28. nucleus/job.py +71 -3
  29. nucleus/logger.py +9 -0
  30. nucleus/metadata_manager.py +45 -0
  31. nucleus/metrics/__init__.py +10 -0
  32. nucleus/metrics/base.py +117 -0
  33. nucleus/metrics/categorization_metrics.py +197 -0
  34. nucleus/metrics/errors.py +7 -0
  35. nucleus/metrics/filters.py +40 -0
  36. nucleus/metrics/geometry.py +198 -0
  37. nucleus/metrics/metric_utils.py +28 -0
  38. nucleus/metrics/polygon_metrics.py +480 -0
  39. nucleus/metrics/polygon_utils.py +299 -0
  40. nucleus/model.py +121 -15
  41. nucleus/model_run.py +34 -57
  42. nucleus/payload_constructor.py +30 -18
  43. nucleus/prediction.py +259 -17
  44. nucleus/pydantic_base.py +26 -0
  45. nucleus/retry_strategy.py +4 -0
  46. nucleus/scene.py +204 -19
  47. nucleus/slice.py +230 -67
  48. nucleus/upload_response.py +20 -9
  49. nucleus/url_utils.py +4 -0
  50. nucleus/utils.py +139 -35
  51. nucleus/validate/__init__.py +24 -0
  52. nucleus/validate/client.py +168 -0
  53. nucleus/validate/constants.py +20 -0
  54. nucleus/validate/data_transfer_objects/__init__.py +0 -0
  55. nucleus/validate/data_transfer_objects/eval_function.py +81 -0
  56. nucleus/validate/data_transfer_objects/scenario_test.py +19 -0
  57. nucleus/validate/data_transfer_objects/scenario_test_evaluations.py +11 -0
  58. nucleus/validate/data_transfer_objects/scenario_test_metric.py +12 -0
  59. nucleus/validate/errors.py +6 -0
  60. nucleus/validate/eval_functions/__init__.py +0 -0
  61. nucleus/validate/eval_functions/available_eval_functions.py +212 -0
  62. nucleus/validate/eval_functions/base_eval_function.py +60 -0
  63. nucleus/validate/scenario_test.py +143 -0
  64. nucleus/validate/scenario_test_evaluation.py +114 -0
  65. nucleus/validate/scenario_test_metric.py +14 -0
  66. nucleus/validate/utils.py +8 -0
  67. {scale_nucleus-0.1.22.dist-info → scale_nucleus-0.6.4.dist-info}/LICENSE +0 -0
  68. scale_nucleus-0.6.4.dist-info/METADATA +213 -0
  69. scale_nucleus-0.6.4.dist-info/RECORD +71 -0
  70. {scale_nucleus-0.1.22.dist-info → scale_nucleus-0.6.4.dist-info}/WHEEL +1 -1
  71. scale_nucleus-0.6.4.dist-info/entry_points.txt +3 -0
  72. scale_nucleus-0.1.22.dist-info/METADATA +0 -85
  73. scale_nucleus-0.1.22.dist-info/RECORD +0 -21
nucleus/prediction.py CHANGED
@@ -1,52 +1,112 @@
1
- from typing import Dict, Optional, List
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
- POLYGON_TYPE,
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
- # No need to define init or to_payload methods because
49
- # we default to functions defined in the parent class
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
+ )
@@ -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)
@@ -0,0 +1,4 @@
1
+ # TODO: use retry library instead of custom code. Tenacity is one option.
2
+ class RetryStrategy:
3
+ statuses = {503, 524, 520, 504}
4
+ sleep_times = [1, 3, 9]