Module datatap.droplet

This module provides classes for working with ML data. Specifically, it provides methods for creating new ML data objects, converting ML data objects to and from the JSON droplet format, and manipulating ML data objects.

Expand source code
"""
This module provides classes for working with ML data.  Specifically, it provides methods for creating new ML data
objects, converting ML data objects to and from the JSON droplet format, and manipulating ML data objects.
"""

from .bounding_box import BoundingBox, BoundingBoxJson
from .class_annotation import ClassAnnotation, ClassAnnotationJson
from .image import Image, ImageJson
from .image_annotation import ImageAnnotation, ImageAnnotationJson
from .instance import Instance, InstanceJson
from .keypoint import Keypoint, KeypointJson
from .multi_instance import MultiInstance, MultiInstanceJson
from .segmentation import Segmentation, SegmentationJson

__all__ = [
        "BoundingBox",
        "BoundingBoxJson",
        "ClassAnnotation",
        "ClassAnnotationJson",
        "Image",
        "ImageJson",
        "ImageAnnotation",
        "ImageAnnotationJson",
        "Instance",
        "InstanceJson",
        "Keypoint",
        "KeypointJson",
        "MultiInstance",
        "MultiInstanceJson",
        "Segmentation",
        "SegmentationJson",
]

Sub-modules

datatap.droplet.bounding_box
datatap.droplet.class_annotation
datatap.droplet.image
datatap.droplet.image_annotation
datatap.droplet.instance
datatap.droplet.keypoint
datatap.droplet.multi_instance
datatap.droplet.segmentation

Classes

class BoundingBox (rectangle: Rectangle, *, confidence: Optional[float] = None)

A BoundingBox represents the area within an image taken up by a detection, specified as an axis-aligned rectangle.

Expand source code
class BoundingBox:
        """
        A `BoundingBox` represents the area within an image taken up by a detection,
        specified as an axis-aligned rectangle.
        """

        rectangle: Rectangle
        """
        The area within the image where the corresponding detection appears.
        """

        confidence: Optional[float]
        """
        The confidence associated with this bounding box.
        """

        @staticmethod
        def from_json(json: BoundingBoxJson) -> BoundingBox:
                """
                Constructs a `BoundingBox` from a `BoundingBoxJson`.
                """
                return BoundingBox(
                        Rectangle.from_json(json["rectangle"]),
                        confidence = json.get("confidence")
                )

        def __init__(self, rectangle: Rectangle, *, confidence: Optional[float] = None):
                self.rectangle = rectangle
                self.confidence = confidence

                self.rectangle.assert_valid()

        def __repr__(self) -> str:
                return basic_repr("BoundingBox", self.rectangle, confidence = self.confidence)

        def __eq__(self, other: Any) -> bool:
                if not isinstance(other, BoundingBox):
                        return NotImplemented
                return self.rectangle == other.rectangle and self.confidence == other.confidence

        def to_json(self) -> BoundingBoxJson:
                """
                Serializes this `BoundingBox` to a `BoundingBoxJson`.
                """
                json: BoundingBoxJson = {
                        "rectangle": self.rectangle.to_json()
                }

                if self.confidence is not None:
                        json["confidence"] = self.confidence

                return json

        def meets_confidence_threshold(self, threshold: float) -> bool:
                """
                Returns `True` if and only if the confidence of this bounding box is
                either unset or it is at least the given `threshold`.
                """
                return self.confidence is None or self.confidence >= threshold

Class variables

var confidence : Union[float, NoneType]

The confidence associated with this bounding box.

var rectangleRectangle

The area within the image where the corresponding detection appears.

Static methods

def from_json(json: BoundingBoxJson) ‑> BoundingBox

Constructs a BoundingBox from a BoundingBoxJson.

Expand source code
@staticmethod
def from_json(json: BoundingBoxJson) -> BoundingBox:
        """
        Constructs a `BoundingBox` from a `BoundingBoxJson`.
        """
        return BoundingBox(
                Rectangle.from_json(json["rectangle"]),
                confidence = json.get("confidence")
        )

Methods

def meets_confidence_threshold(self, threshold: float) ‑> bool

Returns True if and only if the confidence of this bounding box is either unset or it is at least the given threshold.

Expand source code
def meets_confidence_threshold(self, threshold: float) -> bool:
        """
        Returns `True` if and only if the confidence of this bounding box is
        either unset or it is at least the given `threshold`.
        """
        return self.confidence is None or self.confidence >= threshold
def to_json(self) ‑> BoundingBoxJson

Serializes this BoundingBox to a BoundingBoxJson.

Expand source code
def to_json(self) -> BoundingBoxJson:
        """
        Serializes this `BoundingBox` to a `BoundingBoxJson`.
        """
        json: BoundingBoxJson = {
                "rectangle": self.rectangle.to_json()
        }

        if self.confidence is not None:
                json["confidence"] = self.confidence

        return json
class BoundingBoxJson (*args, **kwargs)

The serialized JSON representation of a bounding box.

Expand source code
class BoundingBoxJson(_BoundingBoxJsonOptional, TypedDict):
        """
        The serialized JSON representation of a bounding box.
        """
        rectangle: RectangleJson

Ancestors

  • builtins.dict

Class variables

var confidence : float
var rectangle : Tuple[Tuple[float, float], Tuple[float, float]]
class ClassAnnotation (*, instances: Sequence[Instance], multi_instances: Sequence[MultiInstance] = [])

A ClassAnnotation represents the set of detections for a given class. These may either be individual instances, or "multi instances" that describe a visual clustering of the class.

Expand source code
class ClassAnnotation:
        """
        A `ClassAnnotation` represents the set of detections for a given
        class. These may either be individual instances, or "multi instances"
        that describe a visual clustering of the class.
        """

        instances: Sequence[Instance]
        """
        A sequence of individual instances of this class.
        """

        multi_instances: Sequence[MultiInstance]
        """
        A sequence of multi-instances of this class. An example of a
        multi instance would be a crowd of people (labeled as such).
        """

        @staticmethod
        def from_json(json: ClassAnnotationJson) -> ClassAnnotation:
                """
                Constructs a `ClassAnnotation` from a `ClassAnnotationJson`.
                """
                return ClassAnnotation(
                        instances = [Instance.from_json(instance) for instance in json["instances"]] if "instances" in json else [],
                        multi_instances = [MultiInstance.from_json(multi_instance) for multi_instance in json["multiInstances"]] if "multiInstances" in json else []
                )

        def __init__(self, *, instances: Sequence[Instance], multi_instances: Sequence[MultiInstance] = []):
                self.instances = instances
                self.multi_instances = multi_instances

        def filter_detections(
                self,
                *,
                instance_filter: Callable[[Instance], bool],
                multi_instance_filter: Callable[[MultiInstance], bool]
        ) -> ClassAnnotation:
                """
                Returns a new class annotation consisting only of the instances and
                multi-instances that meet the given constraints.
                """
                return ClassAnnotation(
                        instances = [
                                instance
                                for instance in self.instances
                                if instance_filter(instance)
                        ],
                        multi_instances = [
                                multi_instance
                                for multi_instance in self.multi_instances
                                if multi_instance_filter(multi_instance)
                        ]
                )

        def __repr__(self) -> str:
                return basic_repr("ClassAnnotation", instances = self.instances, multi_instances = self.multi_instances)

        def __eq__(self, other: Any) -> bool:
                if not isinstance(other, ClassAnnotation):
                        return NotImplemented
                return self.instances == other.instances and self.multi_instances == other.multi_instances

        def __add__(self, other: Any) -> ClassAnnotation:
                if not isinstance(other, ClassAnnotation):
                        return NotImplemented

                instances = list(self.instances) + list(other.instances)
                multi_instances = list(self.multi_instances) + list(other.multi_instances)

                return ClassAnnotation(
                        instances = instances,
                        multi_instances = multi_instances,
                )

        def to_json(self) -> ClassAnnotationJson:
                """
                Serializes this `ClassAnnotation` into a `ClassAnnotationJson`.
                """

                return {
                        "instances": [instance.to_json() for instance in self.instances],
                        "multiInstances": [multi_instance.to_json() for multi_instance in self.multi_instances]
                }

Class variables

var instances : Sequence[Instance]

A sequence of individual instances of this class.

var multi_instances : Sequence[MultiInstance]

A sequence of multi-instances of this class. An example of a multi instance would be a crowd of people (labeled as such).

Static methods

def from_json(json: ClassAnnotationJson) ‑> ClassAnnotation
Expand source code
@staticmethod
def from_json(json: ClassAnnotationJson) -> ClassAnnotation:
        """
        Constructs a `ClassAnnotation` from a `ClassAnnotationJson`.
        """
        return ClassAnnotation(
                instances = [Instance.from_json(instance) for instance in json["instances"]] if "instances" in json else [],
                multi_instances = [MultiInstance.from_json(multi_instance) for multi_instance in json["multiInstances"]] if "multiInstances" in json else []
        )

Methods

def filter_detections(self, *, instance_filter: Callable[[Instance], bool], multi_instance_filter: Callable[[MultiInstance], bool]) ‑> ClassAnnotation

Returns a new class annotation consisting only of the instances and multi-instances that meet the given constraints.

Expand source code
def filter_detections(
        self,
        *,
        instance_filter: Callable[[Instance], bool],
        multi_instance_filter: Callable[[MultiInstance], bool]
) -> ClassAnnotation:
        """
        Returns a new class annotation consisting only of the instances and
        multi-instances that meet the given constraints.
        """
        return ClassAnnotation(
                instances = [
                        instance
                        for instance in self.instances
                        if instance_filter(instance)
                ],
                multi_instances = [
                        multi_instance
                        for multi_instance in self.multi_instances
                        if multi_instance_filter(multi_instance)
                ]
        )
def to_json(self) ‑> ClassAnnotationJson

Serializes this ClassAnnotation into a ClassAnnotationJson.

Expand source code
def to_json(self) -> ClassAnnotationJson:
        """
        Serializes this `ClassAnnotation` into a `ClassAnnotationJson`.
        """

        return {
                "instances": [instance.to_json() for instance in self.instances],
                "multiInstances": [multi_instance.to_json() for multi_instance in self.multi_instances]
        }
class ClassAnnotationJson (*args, **kwargs)

The serialized JSON representation of a class annotation.

Expand source code
class ClassAnnotationJson(TypedDict, total = False):
        """
        The serialized JSON representation of a class annotation.
        """
        instances: Sequence[InstanceJson]
        multiInstances: Sequence[MultiInstanceJson]

Ancestors

  • builtins.dict

Class variables

var instances : Sequence[InstanceJson]
var multiInstances : Sequence[MultiInstanceJson]
class Image (*, paths: Sequence[str])

The Image class contains information about what image was labeled by a given annotation. It also includes utilities for loading and manipulating images.

Expand source code
class Image:
        """
        The `Image` class contains information about what image was
        labeled by a given annotation. It also includes utilities
        for loading and manipulating images.
        """

        paths: Sequence[str]
        """
        A sequence of URIs where this image can be found. The loader
        will try them in order until it finds one it can load.

        Loading is performed by [fsspec](https://filesystem-spec.readthedocs.io/en/latest/api.html).

        Supported schemes include `http(s):`, `file:`, and `ftp:`, among others.
        Some protocols may require additional packages to be installed (such as
        `s3fs` for the `s3:` scheme).
        """

        _pil_image: Optional[PIL.Image.Image]

        @staticmethod
        def from_json(json: ImageJson) -> Image:
                """
                Creates an `Image` from an `ImageJson`.
                """
                return Image(paths = json["paths"])

        @staticmethod
        def from_pil(pil_image: PIL.Image.Image) -> Image:
                """
                Creates an `Image` from an existing PIL Image. Note that an
                image created this way will not have any `paths` set, but will
                still be able to load the image via `get_pil_image`.
                """
                image = Image(
                        paths = [],
                )
                image._pil_image = pil_image
                return image

        def __init__(self, *, paths: Sequence[str]):
                self.paths = paths
                self._pil_image = None

        def __repr__(self) -> str:
                return basic_repr("Image", paths = self.paths)

        def __eq__(self, other: Any) -> bool:
                if not isinstance(other, Image):
                        return NotImplemented
                return self.paths == other.paths

        # TODO(mdsavage): consider using functools.cache here if we upgrade to Python >= 3.9
        def get_pil_image(self, quiet: bool = False, attempts: int = 3) -> PIL.Image.Image:
                """
                Attempts to load the image specified by this reference. Resolution happpens in this order:

                1. Load from an internal cache (either from a previous load, or from `from_pil`)
                2. Try loading every path in order, returning once one loads

                Warning! `get_pil_image` may attempt to read from the local file system or from private
                networks. Please ensure that the annotation you are loading is trusted.
                """
                if self._pil_image is not None:
                        return self._pil_image

                for path in self.paths:
                        for i in range(attempts):
                                try:
                                        with fsspec.open(path) as f:
                                                pil_image = PIL.Image.open(BytesIO(f.read()))
                                                # self._pil_image = pil_image # TODO(mdsavage): figure out if/how we can reenable caching
                                                return pil_image
                                except Exception as e:
                                        if not quiet:
                                                print(f"Cannot load image {path}, with error {str(e)}, attempt ({i + 1}/{attempts})", file = sys.stderr)

                raise FileNotFoundError("All paths for image failed to load", self.paths)

        def to_json(self) -> ImageJson:
                """
                Serializes this `Image` into an `ImageJson`.
                """
                return {
                        "paths": self.paths
                }

Class variables

var paths : Sequence[str]

A sequence of URIs where this image can be found. The loader will try them in order until it finds one it can load.

Loading is performed by fsspec.

Supported schemes include http(s):, file:, and ftp:, among others. Some protocols may require additional packages to be installed (such as s3fs for the s3: scheme).

Static methods

def from_json(json: ImageJson) ‑> Image

Creates an Image from an ImageJson.

Expand source code
@staticmethod
def from_json(json: ImageJson) -> Image:
        """
        Creates an `Image` from an `ImageJson`.
        """
        return Image(paths = json["paths"])
def from_pil(pil_image: PIL.Image.Image) ‑> Image

Creates an Image from an existing PIL Image. Note that an image created this way will not have any paths set, but will still be able to load the image via get_pil_image.

Expand source code
@staticmethod
def from_pil(pil_image: PIL.Image.Image) -> Image:
        """
        Creates an `Image` from an existing PIL Image. Note that an
        image created this way will not have any `paths` set, but will
        still be able to load the image via `get_pil_image`.
        """
        image = Image(
                paths = [],
        )
        image._pil_image = pil_image
        return image

Methods

def get_pil_image(self, quiet: bool = False, attempts: int = 3) ‑> PIL.Image.Image

Attempts to load the image specified by this reference. Resolution happpens in this order:

  1. Load from an internal cache (either from a previous load, or from from_pil)
  2. Try loading every path in order, returning once one loads

Warning! get_pil_image may attempt to read from the local file system or from private networks. Please ensure that the annotation you are loading is trusted.

Expand source code
def get_pil_image(self, quiet: bool = False, attempts: int = 3) -> PIL.Image.Image:
        """
        Attempts to load the image specified by this reference. Resolution happpens in this order:

        1. Load from an internal cache (either from a previous load, or from `from_pil`)
        2. Try loading every path in order, returning once one loads

        Warning! `get_pil_image` may attempt to read from the local file system or from private
        networks. Please ensure that the annotation you are loading is trusted.
        """
        if self._pil_image is not None:
                return self._pil_image

        for path in self.paths:
                for i in range(attempts):
                        try:
                                with fsspec.open(path) as f:
                                        pil_image = PIL.Image.open(BytesIO(f.read()))
                                        # self._pil_image = pil_image # TODO(mdsavage): figure out if/how we can reenable caching
                                        return pil_image
                        except Exception as e:
                                if not quiet:
                                        print(f"Cannot load image {path}, with error {str(e)}, attempt ({i + 1}/{attempts})", file = sys.stderr)

        raise FileNotFoundError("All paths for image failed to load", self.paths)
def to_json(self) ‑> ImageJson

Serializes this Image into an ImageJson.

Expand source code
def to_json(self) -> ImageJson:
        """
        Serializes this `Image` into an `ImageJson`.
        """
        return {
                "paths": self.paths
        }
class ImageAnnotation (*, image: Image, classes: Mapping[str, ClassAnnotation], mask: Optional[Mask] = None, uid: Optional[str] = None)

A collection of class annotations that annotate a given image.

Expand source code
class ImageAnnotation:
        """
        A collection of class annotations that annotate a given image.
        """

        image: Image
        """
        The image being annotated.
        """

        classes: Mapping[str, ClassAnnotation]
        """
        A mapping from class name to the annotations of that class.
        """

        uid: Optional[str]
        """
        A unique identifier for this image annotation.
        """

        mask: Optional[Mask]
        """
        An optional region-of-interest mask to indicate that only
        features within the mask have been annotated.
        """

        @staticmethod
        def from_json(json: Mapping[str, Any]) -> ImageAnnotation:
                """
                Constructs an `ImageAnnotation` from an `ImageAnnotationJson`.
                """
                return ImageAnnotation(
                        image = Image.from_json(json["image"]),
                        classes = {
                                class_name: ClassAnnotation.from_json(json["classes"][class_name])
                                for class_name in json["classes"]
                        },
                        mask = Mask.from_json(json["mask"]) if "mask" in json else None,
                        uid = json.get("uid")
                )

        def __init__(
                self,
                *,
                image: Image,
                classes: Mapping[str, ClassAnnotation],
                mask: Optional[Mask] = None,
                uid: Optional[str] = None
        ):
                self.image = image
                self.classes = classes
                self.mask = mask
                self.uid = uid

        def filter_detections(
                self,
                *,
                instance_filter: Callable[[Instance], bool],
                multi_instance_filter: Callable[[MultiInstance], bool]
        ) -> ImageAnnotation:
                """
                Returns a new image annotation consisting only of the instances and
                multi-instances that meet the given constraints.
                """
                return ImageAnnotation(
                        image = self.image,
                        mask = self.mask,
                        classes = {
                                class_name: class_annotation.filter_detections(
                                        instance_filter = instance_filter,
                                        multi_instance_filter = multi_instance_filter
                                )
                                for class_name, class_annotation in self.classes.items()
                        }
                )

        def apply_bounding_box_confidence_threshold(self, threshold: float) -> ImageAnnotation:
                """
                Returns a new image annotation consisting only of the instances and
                multi-instances that have bounding boxes which either do not have a
                confidence specified or which have a confience meeting the given
                threshold.
                """
                return self.filter_detections(
                        instance_filter = lambda instance: (
                                instance.bounding_box is not None
                                        and instance.bounding_box.meets_confidence_threshold(threshold)
                        ),
                        multi_instance_filter = lambda multi_instance: (
                                multi_instance.bounding_box is not None
                                        and multi_instance.bounding_box.meets_confidence_threshold(threshold)
                        )
                )

        def apply_segmentation_confidence_threshold(self, threshold: float) -> ImageAnnotation:
                """
                Returns a new image annotation consisting only of the instances and
                multi-instances that have segmentations which either do not have a
                confidence specified or which have a confience meeting the given
                threshold.
                """
                return self.filter_detections(
                        instance_filter = lambda instance: (
                                instance.segmentation is not None
                                        and instance.segmentation.meets_confidence_threshold(threshold)
                        ),
                        multi_instance_filter = lambda multi_instance: (
                                multi_instance.segmentation is not None
                                        and multi_instance.segmentation.meets_confidence_threshold(threshold)
                        )
                )

        def __repr__(self) -> str:
                return basic_repr(
                        "ImageAnnotation",
                        uid = self.uid,
                        image = self.image,
                        mask = self.mask,
                        classes = self.classes
                )

        def __eq__(self, other: Any) -> bool:
                if not isinstance(other, ImageAnnotation):
                        return NotImplemented
                return self.image == other.image and self.classes == other.classes and self.mask == other.mask

        def __add__(self, other: Any) -> ImageAnnotation:
                if not isinstance(other, ImageAnnotation):
                        return NotImplemented

                classes: Dict[str, ClassAnnotation] = {}

                for key, value in self.classes.items():
                        classes[key] = value

                for key, value in other.classes.items():
                        if key in classes:
                                classes[key] += value
                        else:
                                classes[key] = value

                return ImageAnnotation(
                        image = self.image,
                        classes = classes,
                        mask = self.mask
                )

        def to_json(self) -> ImageAnnotationJson:
                """
                Serializes this image annotation into an `ImageAnnotationJson`.
                """
                json: ImageAnnotationJson = {
                        "kind": "ImageAnnotation",
                        "image": self.image.to_json(),
                        "classes": {
                                name: class_annotation.to_json()
                                for name, class_annotation in self.classes.items()
                        }
                }

                if self.mask is not None:
                        json["mask"] = self.mask.to_json()

                if self.uid is not None:
                        json["uid"] = self.uid

                return json

        def get_visualization_url(self) -> str:
                """
                Generates a URL on the dataTap platform that can be visited to view a
                visualization of this `ImageAnnotation`.
                """
                params = {
                        "annotation": json.dumps(self.to_json(), separators = (",", ":"))
                }

                return f"{Environment.BASE_URI}/visualizer/single#{urlencode(params, quote_via = quote)}"

        def get_comparison_url(self, other: ImageAnnotation) -> str:
                """
                Generates a URL on the dataTap platform that can be visited to view a
                visual comparison of this `ImageAnnotation` (which is treated as the
                "ground truth") and the `other` argument (which is treated as the
                "proposal").

                This method does not check that the two annotations agree on what image
                they are annotating, and will always use this `ImageAnnotation`'s
                image.
                """
                params = {
                        "groundTruth": json.dumps(self.to_json(), separators = (",", ":")),
                        "proposal": json.dumps(other.to_json(), separators = (",", ":"))
                }

                return f"{Environment.BASE_URI}/visualizer/compare#{urlencode(params, quote_via = quote)}"

Class variables

var classes : Mapping[str, ClassAnnotation]

A mapping from class name to the annotations of that class.

var imageImage

The image being annotated.

var mask : Union[Mask, NoneType]

An optional region-of-interest mask to indicate that only features within the mask have been annotated.

var uid : Union[str, NoneType]

A unique identifier for this image annotation.

Static methods

def from_json(json: Mapping[str, Any]) ‑> ImageAnnotation

Constructs an ImageAnnotation from an ImageAnnotationJson.

Expand source code
@staticmethod
def from_json(json: Mapping[str, Any]) -> ImageAnnotation:
        """
        Constructs an `ImageAnnotation` from an `ImageAnnotationJson`.
        """
        return ImageAnnotation(
                image = Image.from_json(json["image"]),
                classes = {
                        class_name: ClassAnnotation.from_json(json["classes"][class_name])
                        for class_name in json["classes"]
                },
                mask = Mask.from_json(json["mask"]) if "mask" in json else None,
                uid = json.get("uid")
        )

Methods

def apply_bounding_box_confidence_threshold(self, threshold: float) ‑> ImageAnnotation

Returns a new image annotation consisting only of the instances and multi-instances that have bounding boxes which either do not have a confidence specified or which have a confience meeting the given threshold.

Expand source code
def apply_bounding_box_confidence_threshold(self, threshold: float) -> ImageAnnotation:
        """
        Returns a new image annotation consisting only of the instances and
        multi-instances that have bounding boxes which either do not have a
        confidence specified or which have a confience meeting the given
        threshold.
        """
        return self.filter_detections(
                instance_filter = lambda instance: (
                        instance.bounding_box is not None
                                and instance.bounding_box.meets_confidence_threshold(threshold)
                ),
                multi_instance_filter = lambda multi_instance: (
                        multi_instance.bounding_box is not None
                                and multi_instance.bounding_box.meets_confidence_threshold(threshold)
                )
        )
def apply_segmentation_confidence_threshold(self, threshold: float) ‑> ImageAnnotation

Returns a new image annotation consisting only of the instances and multi-instances that have segmentations which either do not have a confidence specified or which have a confience meeting the given threshold.

Expand source code
def apply_segmentation_confidence_threshold(self, threshold: float) -> ImageAnnotation:
        """
        Returns a new image annotation consisting only of the instances and
        multi-instances that have segmentations which either do not have a
        confidence specified or which have a confience meeting the given
        threshold.
        """
        return self.filter_detections(
                instance_filter = lambda instance: (
                        instance.segmentation is not None
                                and instance.segmentation.meets_confidence_threshold(threshold)
                ),
                multi_instance_filter = lambda multi_instance: (
                        multi_instance.segmentation is not None
                                and multi_instance.segmentation.meets_confidence_threshold(threshold)
                )
        )
def filter_detections(self, *, instance_filter: Callable[[Instance], bool], multi_instance_filter: Callable[[MultiInstance], bool]) ‑> ImageAnnotation

Returns a new image annotation consisting only of the instances and multi-instances that meet the given constraints.

Expand source code
def filter_detections(
        self,
        *,
        instance_filter: Callable[[Instance], bool],
        multi_instance_filter: Callable[[MultiInstance], bool]
) -> ImageAnnotation:
        """
        Returns a new image annotation consisting only of the instances and
        multi-instances that meet the given constraints.
        """
        return ImageAnnotation(
                image = self.image,
                mask = self.mask,
                classes = {
                        class_name: class_annotation.filter_detections(
                                instance_filter = instance_filter,
                                multi_instance_filter = multi_instance_filter
                        )
                        for class_name, class_annotation in self.classes.items()
                }
        )
def get_comparison_url(self, other: ImageAnnotation) ‑> str

Generates a URL on the dataTap platform that can be visited to view a visual comparison of this ImageAnnotation (which is treated as the "ground truth") and the other argument (which is treated as the "proposal").

This method does not check that the two annotations agree on what image they are annotating, and will always use this ImageAnnotation's image.

Expand source code
def get_comparison_url(self, other: ImageAnnotation) -> str:
        """
        Generates a URL on the dataTap platform that can be visited to view a
        visual comparison of this `ImageAnnotation` (which is treated as the
        "ground truth") and the `other` argument (which is treated as the
        "proposal").

        This method does not check that the two annotations agree on what image
        they are annotating, and will always use this `ImageAnnotation`'s
        image.
        """
        params = {
                "groundTruth": json.dumps(self.to_json(), separators = (",", ":")),
                "proposal": json.dumps(other.to_json(), separators = (",", ":"))
        }

        return f"{Environment.BASE_URI}/visualizer/compare#{urlencode(params, quote_via = quote)}"
def get_visualization_url(self) ‑> str

Generates a URL on the dataTap platform that can be visited to view a visualization of this ImageAnnotation.

Expand source code
def get_visualization_url(self) -> str:
        """
        Generates a URL on the dataTap platform that can be visited to view a
        visualization of this `ImageAnnotation`.
        """
        params = {
                "annotation": json.dumps(self.to_json(), separators = (",", ":"))
        }

        return f"{Environment.BASE_URI}/visualizer/single#{urlencode(params, quote_via = quote)}"
def to_json(self) ‑> ImageAnnotationJson

Serializes this image annotation into an ImageAnnotationJson.

Expand source code
def to_json(self) -> ImageAnnotationJson:
        """
        Serializes this image annotation into an `ImageAnnotationJson`.
        """
        json: ImageAnnotationJson = {
                "kind": "ImageAnnotation",
                "image": self.image.to_json(),
                "classes": {
                        name: class_annotation.to_json()
                        for name, class_annotation in self.classes.items()
                }
        }

        if self.mask is not None:
                json["mask"] = self.mask.to_json()

        if self.uid is not None:
                json["uid"] = self.uid

        return json
class ImageAnnotationJson (*args, **kwargs)

The serialized JSON representation of an image annotation.

Expand source code
class ImageAnnotationJson(_ImageAnnotationJsonOptional, TypedDict):
        """
        The serialized JSON representation of an image annotation.
        """

        kind: Literal["ImageAnnotation"]
        image: ImageJson
        classes: Mapping[str, ClassAnnotationJson]

Ancestors

  • builtins.dict

Class variables

var classes : Mapping[str, ClassAnnotationJson]
var imageImageJson
var kind : Literal['ImageAnnotation']
var mask : Sequence[Sequence[Tuple[float, float]]]
var uid : str
class ImageJson (*args, **kwargs)

The serialized JSON representation of an Image.

Expand source code
class ImageJson(TypedDict):
        """
        The serialized JSON representation of an `Image`.
        """
        paths: Sequence[str]

Ancestors

  • builtins.dict

Class variables

var paths : Sequence[str]
class Instance (*, bounding_box: Optional[BoundingBox] = None, segmentation: Optional[Segmentation] = None, keypoints: Optional[Mapping[str, Optional[Keypoint]]] = None, attributes: Optional[Mapping[str, str]] = None)

A single appearance of an object of a particular class within a given image.

Expand source code
class Instance:
        """
        A single appearance of an object of a particular class within a given image.
        """

        bounding_box: Optional[BoundingBox]
        """
        The bounding box of this instance.
        """

        segmentation: Optional[Segmentation]
        """
        The segmentation of this instance.
        """

        keypoints: Optional[Mapping[str, Optional[Keypoint]]]
        """
        A mapping from keypoint name to the keypoint within this instance.  If a key
        maps to `None`, then the annotation is reporting the _absence of_ that
        keypoint (i.e., that it is not visible in the image and does not have an
        inferrable position in the image).
        """

        attributes: Optional[Mapping[str, str]]
        """
        A mapping from attribute name to value.
        """

        @staticmethod
        def from_json(json: InstanceJson) -> Instance:
                """
                Creates an `Instance` from an `InstanceJson`.
                """

                return Instance(
                        bounding_box = BoundingBox.from_json(json["boundingBox"]) if "boundingBox" in json else None,
                        segmentation = Segmentation.from_json(json["segmentation"]) if "segmentation" in json else None,
                        keypoints = {
                                name: Keypoint.from_json(keypoint) if keypoint is not None else None
                                for name, keypoint in json["keypoints"].items()
                        } if "keypoints" in json else None,
                        attributes = json.get("attributes")
                )

        def __init__(
                self,
                *,
                bounding_box: Optional[BoundingBox] = None,
                segmentation: Optional[Segmentation] = None,
                keypoints: Optional[Mapping[str, Optional[Keypoint]]] = None,
                attributes: Optional[Mapping[str, str]] = None
        ):
                self.bounding_box = bounding_box
                self.segmentation = segmentation
                self.keypoints = keypoints
                self.attributes = attributes

        def __repr__(self) -> str:
                return basic_repr(
                        "Instance",
                        bounding_box = self.bounding_box,
                        segmentation = self.segmentation,
                        keypoints = self.keypoints,
                        attributes = self.attributes
                )

        def __eq__(self, other: Any) -> bool:
                if not isinstance(other, Instance):
                        return NotImplemented
                return (self.bounding_box == other.bounding_box and self.segmentation == other.segmentation
                        and self.keypoints == other.keypoints and self.attributes == other.attributes)

        def to_json(self) -> InstanceJson:
                """
                Serializes an `Instance` into an `InstanceJson`.
                """
                json: InstanceJson = {}

                if self.bounding_box is not None:
                        json["boundingBox"] = self.bounding_box.to_json()

                if self.segmentation is not None:
                        json["segmentation"] = self.segmentation.to_json()

                if self.keypoints is not None:
                        keypoints: Dict[str, Optional[KeypointJson]] = {}

                        for name, keypoint in self.keypoints.items():
                                keypoints[name] = keypoint.to_json() if keypoint is not None else None

                        json["keypoints"] = keypoints

                if self.attributes is not None:
                        json["attributes"] = self.attributes

                return json

Class variables

var attributes : Union[Mapping[str, str], NoneType]

A mapping from attribute name to value.

var bounding_box : Union[BoundingBox, NoneType]

The bounding box of this instance.

var keypoints : Union[Mapping[str, Union[Keypoint, NoneType]], NoneType]

A mapping from keypoint name to the keypoint within this instance. If a key maps to None, then the annotation is reporting the absence of that keypoint (i.e., that it is not visible in the image and does not have an inferrable position in the image).

var segmentation : Union[Segmentation, NoneType]

The segmentation of this instance.

Static methods

def from_json(json: InstanceJson) ‑> Instance

Creates an Instance from an InstanceJson.

Expand source code
@staticmethod
def from_json(json: InstanceJson) -> Instance:
        """
        Creates an `Instance` from an `InstanceJson`.
        """

        return Instance(
                bounding_box = BoundingBox.from_json(json["boundingBox"]) if "boundingBox" in json else None,
                segmentation = Segmentation.from_json(json["segmentation"]) if "segmentation" in json else None,
                keypoints = {
                        name: Keypoint.from_json(keypoint) if keypoint is not None else None
                        for name, keypoint in json["keypoints"].items()
                } if "keypoints" in json else None,
                attributes = json.get("attributes")
        )

Methods

def to_json(self) ‑> InstanceJson

Serializes an Instance into an InstanceJson.

Expand source code
def to_json(self) -> InstanceJson:
        """
        Serializes an `Instance` into an `InstanceJson`.
        """
        json: InstanceJson = {}

        if self.bounding_box is not None:
                json["boundingBox"] = self.bounding_box.to_json()

        if self.segmentation is not None:
                json["segmentation"] = self.segmentation.to_json()

        if self.keypoints is not None:
                keypoints: Dict[str, Optional[KeypointJson]] = {}

                for name, keypoint in self.keypoints.items():
                        keypoints[name] = keypoint.to_json() if keypoint is not None else None

                json["keypoints"] = keypoints

        if self.attributes is not None:
                json["attributes"] = self.attributes

        return json
class InstanceJson (*args, **kwargs)

The JSON serialization of an Instance.

Expand source code
class InstanceJson(TypedDict, total = False):
        """
        The JSON serialization of an `Instance`.
        """
        boundingBox: BoundingBoxJson
        segmentation: SegmentationJson
        keypoints: Mapping[str, Optional[KeypointJson]]
        attributes: Mapping[str, str]

Ancestors

  • builtins.dict

Class variables

var attributes : Mapping[str, str]
var boundingBoxBoundingBoxJson
var keypoints : Mapping[str, Union[KeypointJson, NoneType]]
var segmentationSegmentationJson
class Keypoint (point: Point, *, occluded: Optional[bool] = None, confidence: Optional[float] = None)

An object representing a specific keypoint in a particular instance.

Expand source code
class Keypoint:
        """
        An object representing a specific keypoint in a particular instance.
        """

        point: Point
        """
        The point in the image where this keypoint appears.
        """

        occluded: Optional[bool]
        """
        Whether this keypoint is occluded.

        If `False`, the keypoint is visible within the image.
        If `True`, the keypoint is not visible in the image because it is blocked by some other object,
        but has an inferrable position that would lie within the frame of the image.
        If `None`, then the data source did not differentiate between occluded and unoccluded keypoints.
        """

        confidence: Optional[float]
        """
        The confidence associated with this keypoint.
        """

        @staticmethod
        def from_json(json: KeypointJson) -> Keypoint:
                """
                Creates a `Keypoint` from a `KeypointJson`.
                """
                return Keypoint(
                        Point.from_json(json["point"]),
                        occluded = json.get("occluded"),
                        confidence = json.get("confidence")
                )

        def __init__(self, point: Point, *, occluded: Optional[bool] = None, confidence: Optional[float] = None):
                self.point = point
                self.occluded = occluded
                self.confidence = confidence

                self.point.assert_valid()

        def __repr__(self) -> str:
                return basic_repr("Keypoint", self.point, occluded = self.occluded, confidence = self.confidence)

        def __eq__(self, other: Any) -> bool:
                if not isinstance(other, Keypoint):
                        return NotImplemented
                return self.point == other.point and self.occluded == other.occluded and self.confidence == other.confidence

        def to_json(self) -> KeypointJson:
                """
                Serializes this object into a `KeypointJson`.
                """
                json: KeypointJson = {
                        "point": self.point.to_json()
                }

                if self.occluded is not None:
                        json["occluded"] = self.occluded

                if self.confidence is not None:
                        json["confidence"] = self.confidence

                return json

Class variables

var confidence : Union[float, NoneType]

The confidence associated with this keypoint.

var occluded : Union[bool, NoneType]

Whether this keypoint is occluded.

If False, the keypoint is visible within the image. If True, the keypoint is not visible in the image because it is blocked by some other object, but has an inferrable position that would lie within the frame of the image. If None, then the data source did not differentiate between occluded and unoccluded keypoints.

var pointPoint

The point in the image where this keypoint appears.

Static methods

def from_json(json: KeypointJson) ‑> Keypoint

Creates a Keypoint from a KeypointJson.

Expand source code
@staticmethod
def from_json(json: KeypointJson) -> Keypoint:
        """
        Creates a `Keypoint` from a `KeypointJson`.
        """
        return Keypoint(
                Point.from_json(json["point"]),
                occluded = json.get("occluded"),
                confidence = json.get("confidence")
        )

Methods

def to_json(self) ‑> KeypointJson

Serializes this object into a KeypointJson.

Expand source code
def to_json(self) -> KeypointJson:
        """
        Serializes this object into a `KeypointJson`.
        """
        json: KeypointJson = {
                "point": self.point.to_json()
        }

        if self.occluded is not None:
                json["occluded"] = self.occluded

        if self.confidence is not None:
                json["confidence"] = self.confidence

        return json
class KeypointJson (*args, **kwargs)

The JSON serialization of a Keypoint.

Expand source code
class KeypointJson(_KeypointJsonOptional, TypedDict):
        """
        The JSON serialization of a `Keypoint`.
        """
        point: PointJson

Ancestors

  • builtins.dict

Class variables

var confidence : float
var occluded : bool
var point : Tuple[float, float]
class MultiInstance (*, bounding_box: Optional[BoundingBox] = None, segmentation: Optional[Segmentation] = None, count: Optional[int] = None)

An appearance of a group of objects of a particular class in a particular image.

There is not a strict definition as to when a group of instances should be categorized as a multi-instance. As such, when constructing a dataset, it is best to ensure that all of the DataSources agree on what constitutes a MultiInstance. These are most often used in public datasets when the cost of annotating every instance would be too high.

Expand source code
class MultiInstance:
        """
        An appearance of a group of objects of a particular class in a particular image.

        There is not a strict definition as to when a group of instances should be categorized as a multi-instance.
        As such, when constructing a dataset, it is best to ensure that all of the `DataSource`s agree on what
        constitutes a `MultiInstance`. These are most often used in public datasets when the cost of annotating
        every instance would be too high.
        """

        bounding_box: Optional[BoundingBox]
        """
        The bounding box of this multi-instance.
        """

        segmentation: Optional[Segmentation]
        """
        The segmentation of this multi-instance.
        """

        count: Optional[int]
        """
        A count of how many true instances are encapsulated in this multi-instance.
        """

        @staticmethod
        def from_json(json: MultiInstanceJson) -> MultiInstance:
                """
                Creates a `MultiInstance` from a `MultiInstanceJson`.
                """
                return MultiInstance(
                        bounding_box = BoundingBox.from_json(json["boundingBox"]) if "boundingBox" in json else None,
                        segmentation = Segmentation.from_json(json["segmentation"]) if "segmentation" in json else None,
                        count = json.get("count")
                )

        def __init__(
                self,
                *,
                bounding_box: Optional[BoundingBox] = None,
                segmentation: Optional[Segmentation] = None,
                count: Optional[int] = None
        ):
                self.bounding_box = bounding_box
                self.segmentation = segmentation
                self.count = count

        def __repr__(self) -> str:
                return basic_repr(
                        "MultiInstance",
                        bounding_box = self.bounding_box,
                        segmentation = self.segmentation,
                        count = self.count
                )

        def __eq__(self, other: Any) -> bool:
                if not isinstance(other, MultiInstance):
                        return NotImplemented
                return self.bounding_box == other.bounding_box and self.segmentation == other.segmentation and self.count == other.count

        def to_json(self) -> MultiInstanceJson:
                """
                Serializes this object as a `MultiInstanceJson`.
                """
                json: MultiInstanceJson = {}

                if self.bounding_box is not None:
                        json["boundingBox"] = self.bounding_box.to_json()

                if self.segmentation is not None:
                        json["segmentation"] = self.segmentation.to_json()

                if self.count is not None:
                        json["count"] = self.count

                return json

Class variables

var bounding_box : Union[BoundingBox, NoneType]

The bounding box of this multi-instance.

var count : Union[int, NoneType]

A count of how many true instances are encapsulated in this multi-instance.

var segmentation : Union[Segmentation, NoneType]

The segmentation of this multi-instance.

Static methods

def from_json(json: MultiInstanceJson) ‑> MultiInstance
Expand source code
@staticmethod
def from_json(json: MultiInstanceJson) -> MultiInstance:
        """
        Creates a `MultiInstance` from a `MultiInstanceJson`.
        """
        return MultiInstance(
                bounding_box = BoundingBox.from_json(json["boundingBox"]) if "boundingBox" in json else None,
                segmentation = Segmentation.from_json(json["segmentation"]) if "segmentation" in json else None,
                count = json.get("count")
        )

Methods

def to_json(self) ‑> MultiInstanceJson

Serializes this object as a MultiInstanceJson.

Expand source code
def to_json(self) -> MultiInstanceJson:
        """
        Serializes this object as a `MultiInstanceJson`.
        """
        json: MultiInstanceJson = {}

        if self.bounding_box is not None:
                json["boundingBox"] = self.bounding_box.to_json()

        if self.segmentation is not None:
                json["segmentation"] = self.segmentation.to_json()

        if self.count is not None:
                json["count"] = self.count

        return json
class MultiInstanceJson (*args, **kwargs)

The JSON serialization of a MultiInstance.

Expand source code
class MultiInstanceJson(TypedDict, total = False):
        """
        The JSON serialization of a `MultiInstance`.
        """
        boundingBox: BoundingBoxJson
        segmentation: SegmentationJson
        count: int

Ancestors

  • builtins.dict

Class variables

var boundingBoxBoundingBoxJson
var count : int
var segmentationSegmentationJson
class Segmentation (mask: Mask, *, confidence: Optional[float] = None)

A Segmentation represents the area within an image taken up by a detection, specified as a Mask.

Expand source code
class Segmentation:
        """
        A `Segmentation` represents the area within an image taken up by a
        detection, specified as a `Mask`.
        """

        mask: Mask
        """
        The area within the image where the corresponding detection appears.
        """

        confidence: Optional[float]
        """
        The confidence associated with this segmentation.
        """

        @staticmethod
        def from_json(json: SegmentationJson) -> Segmentation:
                """
                Constructs a `Segmentation` from a `SegmentationJson`.
                """
                return Segmentation(
                        Mask.from_json(json["mask"]),
                        confidence = json.get("confidence")
                )

        def __init__(self, mask: Mask, *, confidence: Optional[float] = None):
                self.mask = mask
                self.confidence = confidence

                self.mask.assert_valid()

        def __repr__(self) -> str:
                return basic_repr("Segmentation", self.mask, confidence = self.confidence)

        def __eq__(self, other: Any) -> bool:
                if not isinstance(other, Segmentation):
                        return NotImplemented
                return self.mask == other.mask and self.confidence == other.confidence

        def to_json(self) -> SegmentationJson:
                """
                Serializes this `Segmentation` to a `SegmentationJson`.
                """
                json: SegmentationJson = {
                        "mask": self.mask.to_json()
                }

                if self.confidence is not None:
                        json["confidence"] = self.confidence

                return json

        def meets_confidence_threshold(self, threshold: float) -> bool:
                """
                Returns `True` if and only if the confidence of this segmentation is
                either unset or is at least the given `threshold`.
                """
                return self.confidence is None or self.confidence >= threshold

Class variables

var confidence : Union[float, NoneType]

The confidence associated with this segmentation.

var maskMask

The area within the image where the corresponding detection appears.

Static methods

def from_json(json: SegmentationJson) ‑> Segmentation

Constructs a Segmentation from a SegmentationJson.

Expand source code
@staticmethod
def from_json(json: SegmentationJson) -> Segmentation:
        """
        Constructs a `Segmentation` from a `SegmentationJson`.
        """
        return Segmentation(
                Mask.from_json(json["mask"]),
                confidence = json.get("confidence")
        )

Methods

def meets_confidence_threshold(self, threshold: float) ‑> bool

Returns True if and only if the confidence of this segmentation is either unset or is at least the given threshold.

Expand source code
def meets_confidence_threshold(self, threshold: float) -> bool:
        """
        Returns `True` if and only if the confidence of this segmentation is
        either unset or is at least the given `threshold`.
        """
        return self.confidence is None or self.confidence >= threshold
def to_json(self) ‑> SegmentationJson

Serializes this Segmentation to a SegmentationJson.

Expand source code
def to_json(self) -> SegmentationJson:
        """
        Serializes this `Segmentation` to a `SegmentationJson`.
        """
        json: SegmentationJson = {
                "mask": self.mask.to_json()
        }

        if self.confidence is not None:
                json["confidence"] = self.confidence

        return json
class SegmentationJson (*args, **kwargs)

The serialized JSON representation of a segmentation.

Expand source code
class SegmentationJson(_SegmentationJsonOptional, TypedDict):
        """
        The serialized JSON representation of a segmentation.
        """
        mask: MaskJson

Ancestors

  • builtins.dict

Class variables

var confidence : float
var mask : Sequence[Sequence[Tuple[float, float]]]