Source code for otx.api.entities.label

"""This module define the label entity."""
# Copyright (C) 2021-2022 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
#


import datetime
import os
from enum import Enum, auto
from typing import Optional

from otx.api.entities.color import Color
from otx.api.entities.id import ID
from otx.api.utils.time_utils import now


[docs] class Domain(Enum): """Describes an algorithm domain like classification, detection, etc.""" NULL = auto() CLASSIFICATION = auto() DETECTION = auto() SEGMENTATION = auto() ANOMALY_CLASSIFICATION = auto() ANOMALY_DETECTION = auto() ANOMALY_SEGMENTATION = auto() INSTANCE_SEGMENTATION = auto() ROTATED_DETECTION = auto() if os.getenv("FEATURE_FLAGS_OTX_ACTION_TASKS", "0") == "1": ACTION_CLASSIFICATION = auto() ACTION_DETECTION = auto() if os.getenv("FEATURE_FLAGS_OTX_VISUAL_PROMPTING_TASKS", "0") == "1": VISUAL_PROMPTING = auto() def __str__(self): """Returns Domain name.""" return str(self.name)
[docs] class LabelEntity: """This represents a label. The Label is the object that the user annotates and the tasks predict. For example, a label with name "car" can be constructed as follows. >>> car = LabelEntity(name="car", domain=Domain.DETECTION) .. rubric:: About Empty Label In addition to representing the presence of a certain object, the label can also be used to represent the absence of objects in the image (or other media types). Such a label is referred to as empty label. The empty label is constructed as follows: >>> empty = LabelEntity(name="empty", domain=Domain.DETECTION, is_empty=True) Empty label is used to declare that there is nothing of interest inside this image. For example, let's assume a car detection project. During annotation process, for positive images (images with cars), the users are asked to annotate the images with bounding boxes with car label. However, when the user sees a negative image (no car), the user needs to annotate this image with an empty label. The empty label is particularly useful to distinguish images with no objects of interest from images that have not been annotated, especially in task-chain scenario. Let's assume car detection task that is followed with with another detection task which detects the driver inside the car. There are two issues here: 1. The user can (intentionally or unintentionally) miss to annotate the driver inside a car. 2. There is no driver inside the car. Without empty label, these two cases cannot be distinguished. This is why an empty label is introduced. The empty label makes an explicit distinction between missing annotations and "negative" images. Args: name: the name of the label domain: the algorithm domain this label is associated to color: the color of the label (See :class:`Color`) hotkey: key or combination of keys to select this label in the UI creation_date: the date time of the label creation is_empty: set to True if the label is an empty label. id: the ID of the label. Set to ID() so that a new unique ID will be assigned upon saving. If the argument is None, it will be set to ID() is_anomalous: boolean that indicates whether the label is the Anomalous label. Always set to False for non- anomaly projects. """ # pylint: disable=redefined-builtin, too-many-instance-attributes, too-many-arguments; Requires refactor def __init__( self, name: str, domain: Domain, color: Optional[Color] = None, hotkey: str = "", creation_date: Optional[datetime.datetime] = None, is_empty: bool = False, id: Optional[ID] = None, is_anomalous: bool = False, ): id = ID() if id is None else id color = Color.random() if color is None else color creation_date = now() if creation_date is None else creation_date self._name = name self._color = color self._hotkey = hotkey self._domain = domain self._is_empty = is_empty self._creation_date = creation_date self.__id_ = id self.is_anomalous = is_anomalous @property def name(self): """Returns the label name.""" return self._name @name.setter def name(self, value): self._name = value @property def color(self) -> Color: """Returns the Color object for the label.""" return self._color @color.setter def color(self, value): self._color = value @property def hotkey(self) -> str: """Returns the hotkey for the label.""" return self._hotkey @hotkey.setter def hotkey(self, value): self._hotkey = value @property def domain(self): """Returns the algorithm domain associated to this label.""" return self._domain @domain.setter def domain(self, value): self._domain = value @property def is_empty(self) -> bool: """Returns a boolean indicating if the label is an empty label.""" return self._is_empty @property def creation_date(self) -> datetime.datetime: """Returns the creation date of the label.""" return self._creation_date @property def id_(self) -> ID: """Returns the label id.""" return self.__id_ @id_.setter def id_(self, value: ID): self.__id_ = value @property def id(self) -> ID: """DEPRECATED.""" return self.__id_ @id.setter def id(self, value: ID): """DEPRECATED.""" self.__id_ = value def __repr__(self): """String representation of the label.""" return ( f"LabelEntity({self.id_}, name={self.name}, hotkey={self.hotkey}, " f"domain={self.domain}, color={self.color}, is_anomalous={self.is_anomalous})" ) def __eq__(self, other): """Returns True if the two labels are equal.""" if isinstance(other, LabelEntity): return ( self.id_ == other.id_ and self.name == other.name and self.color == other.color and self.hotkey == other.hotkey and self.domain == other.domain and self.is_anomalous == other.is_anomalous ) return False def __lt__(self, other): """Returns True if self.id < other.id.""" if isinstance(other, LabelEntity): return self.id_ < other.id_ return False def __gt__(self, other): """Returns True if self.id is greater than other.id.""" if isinstance(other, LabelEntity): return self.id_ > other.id_ return False def __hash__(self): """Returns hash of the label.""" return hash(str(self))