Module datatap.droplet.image_annotation
Expand source code
from __future__ import annotations
import json
from typing import Any, Callable, Dict, Mapping, Optional
from urllib.parse import quote, urlencode
from datatap.utils import Environment
from typing_extensions import Literal, TypedDict
from ..geometry import Mask, MaskJson
from ..utils import basic_repr
from .class_annotation import ClassAnnotation, ClassAnnotationJson
from .image import Image, ImageJson
from .instance import Instance
from .multi_instance import MultiInstance
class _ImageAnnotationJsonOptional(TypedDict, total = False):
uid: str
mask: MaskJson
metadata: Mapping[str, Any]
class ImageAnnotationJson(_ImageAnnotationJsonOptional, TypedDict):
"""
The serialized JSON representation of an image annotation.
"""
kind: Literal["ImageAnnotation"]
image: ImageJson
classes: Mapping[str, ClassAnnotationJson]
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.
"""
metadata: Optional[Mapping[str, Any]]
"""
An optional field for storing metadata on the annotation.
"""
@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"),
metadata = json.get("metadata")
)
def __init__(
self,
*,
image: Image,
classes: Mapping[str, ClassAnnotation],
mask: Optional[Mask] = None,
uid: Optional[str] = None,
metadata: Optional[Mapping[str, Any]] = None
):
self.image = image
self.classes = classes
self.mask = mask
self.uid = uid
self.metadata = metadata
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()
},
uid = self.uid,
metadata = self.metadata
)
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 apply_metadata(self, metadata: Mapping[str, Any]) -> ImageAnnotation:
"""
Returns a new image annotation with the supplied metadata.
"""
return ImageAnnotation(
image = self.image,
mask = self.mask,
classes = self.classes,
uid = self.uid,
metadata = metadata
)
def __repr__(self) -> str:
return basic_repr(
"ImageAnnotation",
uid = self.uid,
image = self.image,
mask = self.mask,
classes = self.classes,
metadata = self.metadata
)
def __eq__(self, other: object) -> 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: ImageAnnotation) -> ImageAnnotation:
if not isinstance(other, ImageAnnotation): # type: ignore - pyright complains about the isinstance check being redundant
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,
uid = self.uid if self.uid is not None else other.uid,
metadata = self.metadata
)
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
if self.metadata is not None:
json["metadata"] = self.metadata
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)}"
Classes
class ImageAnnotation (*, image: Image, classes: Mapping[str, ClassAnnotation], mask: Optional[Mask] = None, uid: Optional[str] = None, metadata: Optional[Mapping[str, Any]] = 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. """ metadata: Optional[Mapping[str, Any]] """ An optional field for storing metadata on the annotation. """ @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"), metadata = json.get("metadata") ) def __init__( self, *, image: Image, classes: Mapping[str, ClassAnnotation], mask: Optional[Mask] = None, uid: Optional[str] = None, metadata: Optional[Mapping[str, Any]] = None ): self.image = image self.classes = classes self.mask = mask self.uid = uid self.metadata = metadata 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() }, uid = self.uid, metadata = self.metadata ) 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 apply_metadata(self, metadata: Mapping[str, Any]) -> ImageAnnotation: """ Returns a new image annotation with the supplied metadata. """ return ImageAnnotation( image = self.image, mask = self.mask, classes = self.classes, uid = self.uid, metadata = metadata ) def __repr__(self) -> str: return basic_repr( "ImageAnnotation", uid = self.uid, image = self.image, mask = self.mask, classes = self.classes, metadata = self.metadata ) def __eq__(self, other: object) -> 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: ImageAnnotation) -> ImageAnnotation: if not isinstance(other, ImageAnnotation): # type: ignore - pyright complains about the isinstance check being redundant 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, uid = self.uid if self.uid is not None else other.uid, metadata = self.metadata ) 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 if self.metadata is not None: json["metadata"] = self.metadata 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 image : Image
-
The image being annotated.
var mask : Optional[Mask]
-
An optional region-of-interest mask to indicate that only features within the mask have been annotated.
var metadata : Optional[Mapping[str, Any]]
-
An optional field for storing metadata on the annotation.
var uid : Optional[str]
-
A unique identifier for this image annotation.
Static methods
def from_json(json: Mapping[str, Any]) ‑> ImageAnnotation
-
Constructs an
ImageAnnotation
from anImageAnnotationJson
.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"), metadata = json.get("metadata") )
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_metadata(self, metadata: Mapping[str, Any]) ‑> ImageAnnotation
-
Returns a new image annotation with the supplied metadata.
Expand source code
def apply_metadata(self, metadata: Mapping[str, Any]) -> ImageAnnotation: """ Returns a new image annotation with the supplied metadata. """ return ImageAnnotation( image = self.image, mask = self.mask, classes = self.classes, uid = self.uid, metadata = metadata )
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() }, uid = self.uid, metadata = self.metadata )
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 theother
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 if self.metadata is not None: json["metadata"] = self.metadata 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 image : ImageJson
var kind : Literal['ImageAnnotation']
var mask : Sequence[Sequence[Tuple[float, float]]]
var metadata : Mapping[str, Any]
var uid : str