Source code for supervisely.annotation.obj_class

# coding: utf-8
"""General information about :class:`Label<supervisely.annotation.label.LabelBase>`"""

# docs
from __future__ import annotations

from copy import deepcopy
from typing import Dict, List, Optional, Union

from supervisely._utils import take_with_default
from supervisely.annotation.json_geometries_map import GET_GEOMETRY_FROM_STR
from supervisely.collection.key_indexed_collection import KeyObject
from supervisely.geometry.any_geometry import AnyGeometry
from supervisely.geometry.geometry import Geometry
from supervisely.geometry.graph import GraphNodes, KeypointsTemplate
from supervisely.imaging.color import _validate_color, hex2rgb, random_rgb, rgb2hex
from supervisely.io.json import JsonSerializable
from supervisely.sly_logger import logger


[docs]class ObjClassJsonFields: """Json fields for :class:`Annotation<supervisely.annotation.obj_class.ObjClass>`""" ID = "id" """""" NAME = "title" """""" GEOMETRY_TYPE = "shape" """""" COLOR = "color" """""" GEOMETRY_CONFIG = "geometry_config" """""" HOTKEY = "hotkey" """""" DESCRIPTION = "description" """"""
[docs]class ObjClass(KeyObject, JsonSerializable): """ General information about :class:`Label<supervisely.annotation.label.Label>`. :class:`ObjClass` object is immutable. :param name: Class name. :type name: str :param geometry_type: Defines the shape of ObjClass: :class:`Bitmap<supervisely.geometry.bitmap.Bitmap>`, :class:`Cuboid<supervisely.geometry.cuboid.Cuboid>`, :class:`Graph<supervisely.geometry.graph.GraphNodes>`, :class:`Point<supervisely.geometry.point.Point>`, :class:`Polygon<supervisely.geometry.polygon.Polygon>`, :class:`Polyline<supervisely.geometry.polyline.Polyline>`, :class:`Rectangle<supervisely.geometry.rectangle.Rectangle>`. :type geometry_type: dict, optional :param color: :class:`[R, G, B]`, generates random color by default. :type color: List[int, int, int], optional :param geometry_config: Additional settings of the geometry. :type geometry_config: dict, optional :param sly_id: ID in Supervisely server. :type sly_id: int, optional :param hotkey: Hotkey for ObjClass in annotation tool UI. :type hotkey: str, optional :raises: :class:`ValueError`, if color is not list or tuple, or doesn't have exactly 3 values :Usage example: .. code-block:: python import supervisely as sly # Simple ObjClass example class_lemon = sly.ObjClass('lemon', sly.Rectangle) # More complex ObjClass example class_cucumber = sly.ObjClass('cucumber', sly.Bitmap, color=[128, 0, 255], hotkey='d') """ def __init__( self, name: str, geometry_type: type, color: Optional[List[int]] = None, geometry_config: Optional[Union[Dict, KeypointsTemplate]] = None, sly_id: Optional[int] = None, hotkey: Optional[str] = None, description: Optional[str] = None, ): self._name = name self._geometry_type = geometry_type self._color = random_rgb() if color is None else deepcopy(color) if geometry_type == GraphNodes and geometry_config is None: raise ValueError("sly.GraphNodes requires geometry_config to be passed to sly.ObjClass") if isinstance(geometry_config, KeypointsTemplate): geometry_config = geometry_config.config self._geometry_config = deepcopy(take_with_default(geometry_config, {})) self._sly_id = sly_id self._hotkey = take_with_default(hotkey, "") _validate_color(self._color) self._description = take_with_default(description, "") @property def name(self) -> str: """ Name. :return: Name :rtype: :class:`str` :Usage example: .. code-block:: python class_lemon = sly.ObjClass('lemon', sly.Rectangle) print(class_lemon.name) # Output: 'lemon' """ return self._name @property def description(self) -> str: """ Description. :return: Description :rtype: :class:`str` :Usage example: .. code-block:: python class_lemon = sly.ObjClass('lemon', sly.Rectangle) print(class_lemon.description) # Output: 'lemon class description' """ return self._description
[docs] def key(self) -> str: """ Used as a key in ObjClassCollection (like key in dict) :return: string name of the ObjectClass :rtype: :class:`Str` """ return self.name
@property def geometry_type(self) -> type: """ Type of the geometry that is associated with ObjClass. :return: Geometry type :rtype: :class:`type` :Usage example: .. code-block:: python class_lemon = sly.ObjClass('lemon', sly.Rectangle) print(class_lemon.geometry_type) # Output: <class 'supervisely.geometry.rectangle.Rectangle'> class_kiwi = sly.ObjClass('kiwi', sly.Bitmap) print(class_kiwi.geometry_type) # Output: <class 'supervisely.geometry.bitmap.Bitmap'> """ return self._geometry_type @property def geometry_config(self) -> Dict: # """""" # SPHINX ERROR: >>> line = doc.splitlines()[0] # IndexError: list index out of range return deepcopy(self._geometry_config) @property def color(self) -> List[int, int, int]: """ :class:`[R,G,B]` color. :return: Color :rtype: :class:`List[int, int, int]` :Usage example: .. code-block:: python class_lemon = sly.ObjClass('lemon', sly.Rectangle, color=[255,120,0]) print(class_lemon.color) # Output: [255,120,0] """ return deepcopy(self._color) @property def sly_id(self) -> int: """ Class ID in Supervisely server. :return: ID :rtype: :class:`int` :Usage example: .. code-block:: python class_lemon = sly.ObjClass('lemon', sly.Rectangle, sly_id=38584) print(class_lemon.sly_id) # Output: 38584 """ return self._sly_id @property def hotkey(self) -> str: """ Hotkey for ObjClass in annotation tool UI.. :return: Hotkey :rtype: :class:`str` :Usage example: .. code-block:: python class_lemon = sly.ObjClass('lemon', sly.Rectangle, hotkey='M') print(class_lemon.hotkey) # Output: 'M' """ return self._hotkey
[docs] def to_json(self) -> Dict: """ Convert the ObjClass to a json dict. Read more about `Supervisely format <https://docs.supervise.ly/data-organization/00_ann_format_navi>`_. :return: Json format as a dict :rtype: :class:`dict` :Usage example: .. code-block:: python import supervisely as sly class_lemon = sly.ObjClass('lemon', sly.Rectangle) lemon_json = class_lemon.to_json() print(lemon_json) # Output: { # "title": "lemon", # "shape": "rectangle", # "color": "#8A2F0F", # "geometry_config": {}, # "hotkey": "" # } """ res = { ObjClassJsonFields.NAME: self.name, ObjClassJsonFields.DESCRIPTION: self.description, ObjClassJsonFields.GEOMETRY_TYPE: self.geometry_type.geometry_name(), ObjClassJsonFields.COLOR: rgb2hex(self.color), ObjClassJsonFields.GEOMETRY_CONFIG: self.geometry_type.config_to_json( self._geometry_config ), } if self.sly_id is not None: res[ObjClassJsonFields.ID] = self.sly_id if self._hotkey is not None: res[ObjClassJsonFields.HOTKEY] = self.hotkey return res
[docs] @classmethod def from_json(cls, data: Dict) -> ObjClass: """ Convert a json dict to ObjClass. Read more about `Supervisely format <https://docs.supervise.ly/data-organization/00_ann_format_navi>`_. :param data: ObjClass in json format as a dict. :type data: dict :return: ObjClass object :rtype: :class:`ObjClass<ObjClass>` :Usage example: .. code-block:: python import supervisely as sly data = { "title": "lemon", "shape": "rectangle", "color": "#0F6E8A", "hotkey": "Q" } class_lemon = sly.ObjClass.from_json(data) """ name = data[ObjClassJsonFields.NAME] geometry_type = GET_GEOMETRY_FROM_STR(data[ObjClassJsonFields.GEOMETRY_TYPE]) try: color = hex2rgb(data[ObjClassJsonFields.COLOR]) except ValueError as e: if str(e) == "Supported only HEX RGB string format!": color = random_rgb() logger.warning( f"The HEX color value of the object class '{name}' is incorrect, the automatically generated RGB {color} will be used." ) else: raise e geometry_config = geometry_type.config_from_json( data.get(ObjClassJsonFields.GEOMETRY_CONFIG) ) sly_id = data.get(ObjClassJsonFields.ID, None) hotkey = data.get(ObjClassJsonFields.HOTKEY, "") description = data.get(ObjClassJsonFields.DESCRIPTION, "") return cls( name=name, geometry_type=geometry_type, color=color, geometry_config=geometry_config, sly_id=sly_id, hotkey=hotkey, description=description, )
def __eq__(self, other: ObjClass) -> bool: """ Checks that 2 ObjClass objects are equal by comparing their name, geometry type and geometry config. :param other: ObjClass object. :type other: ObjClass :return: True if comparable objects are equal, otherwise False :rtype: :class:`bool` :Usage example: .. code-block:: python import supervisely as sly # Let's create 2 identical ObjClasses and 1 different ObjClass and compare them to each other class_lemon_1 = sly.ObjClass('Lemon', sly.Rectangle) class_lemon_2 = sly.ObjClass('Lemon', sly.Rectangle) class_cucumber = sly.ObjClass('Cucumber', sly.Rectangle) # Compare identical ObjClasses class_lemon_1 == class_lemon_2 # True # Compare unidentical ObjClasses class_lemon_1 == class_cucumber # False """ return ( isinstance(other, ObjClass) and self.name == other.name and ( self.geometry_type == other.geometry_type or AnyGeometry in [self.geometry_type, other.geometry_type] ) and self.geometry_config == other.geometry_config ) def __ne__(self, other: ObjClass) -> bool: """ Checks that 2 ObjClass objects are opposite. :param other: ObjClass object. :type other: ObjClass :return: True if comparable objects are not equal, otherwise False :rtype: :class:`bool` :Usage example: .. code-block:: python import supervisely as sly # Let's create 2 identical ObjClasses class_lemon_1 = sly.ObjClass('Lemon', sly.Rectangle) class_lemon_2 = sly.ObjClass('Lemon', sly.Rectangle) # and 1 different ObjClass and compare them to each other class_cucumber = sly.ObjClass('Cucumber', sly.Rectangle) # Compare identical ObjClasses class_lemon_1 != class_lemon_2 # False # Compare unidentical ObjClasses class_lemon_1 != class_cucumber # True """ return not self == other def __str__(self): # Is need show geometry settings here? return "{:<7s}{:<10}{:<7s}{:<13}{:<7s}{:<15}{:<16s}{:<16}{:<7s}{:<7}".format( "Name:", self.name, "Shape:", self.geometry_type.__name__, "Color:", str(self.color), "Geom. settings:", str(self.geometry_config), "Hotkey", self.hotkey, )
[docs] @classmethod def get_header_ptable(cls): """ get_header_ptable """ return ["Name", "Shape", "Color", "Hotkey"] # Is need show geometry settings here?
[docs] def get_row_ptable(self): """get_row_ptable""" return [self.name, self.geometry_type.__name__, self.color, self.hotkey]
[docs] def clone( self, name: Optional[str] = None, geometry_type: Optional[Geometry] = None, color: Optional[List[int, int, int]] = None, geometry_config: Optional[Dict] = None, sly_id: Optional[int] = None, hotkey: Optional[str] = None, description: Optional[str] = None, ) -> ObjClass: """ Makes a copy of ObjClass with new fields, if fields are given, otherwise it will use fields of the original ObjClass. :param name: Class name. :type name: str :param geometry_type: Defines the shape of ObjClass: :class:`Bitmap<supervisely.geometry.bitmap.Bitmap>`, :class:`Cuboid<supervisely.geometry.cuboid.Cuboid>`, :class:`Point<supervisely.geometry.point.Point>`, :class:`Polygon<supervisely.geometry.polygon.Polygon>`, :class:`Polyline<supervisely.geometry.polyline.Polyline>`, :class:`Rectangle<supervisely.geometry.rectangle.Rectangle>`. :type geometry_type: type :param color: :class:`[R, G, B]`, generates random color by default. :type color: List[int, int, int], optional :param geometry_config: Additional settings of the geometry. :type geometry_config: dict, optional :param sly_id: ID in Supervisely server. :type sly_id: int, optional :param hotkey: Hotkey for ObjClass in annotation tool UI. :type hotkey: str, optional :param description: Description of the class. :type description: str, optional :return: New instance of ObjClass :rtype: :class:`ObjClass<ObjClass>` :Usage example: .. code-block:: python import supervisely as sly class_lemon = sly.ObjClass('lemon', sly.Rectangle) # Let's clone our ObjClass, but with different name # Remember that ObjClass object is immutable, and we need to assign new instance of ObjClass to a new variable clone_lemon_1 = class_lemon.clone(name="lemon clone") # Let's clone our ObjClass, but with different color and hotkey # Remember that ObjClass object is immutable, and we need to assign new instance of ObjClass to a new variable clone_lemon_2 = class_lemon.clone(color=[128, 0, 64], hotkey='Q') # Let's clone our ObjClass without new fields clone_lemon_3 = class_lemon.clone() """ return ObjClass( name=take_with_default(name, self.name), geometry_type=take_with_default(geometry_type, self.geometry_type), color=take_with_default(color, self.color), geometry_config=take_with_default(geometry_config, self.geometry_config), sly_id=take_with_default(sly_id, self.sly_id), hotkey=take_with_default(hotkey, self.hotkey), description=take_with_default(description, self.description), )
def __hash__(self): return hash((self.name, self.geometry_type.geometry_name())) def _set_id(self, id: int): self._sly_id = id