Source code for supervisely.annotation.obj_class_collection

# coding: utf-8
"""collection with :class:`ObjClass<supervisely.annotation.obj_class.ObjClass>` instances"""

# docs
from __future__ import annotations

from collections import defaultdict
from typing import Dict, Iterator, List, Optional

from supervisely import logger
from supervisely.annotation.obj_class import ObjClass
from supervisely.annotation.renamer import Renamer
from supervisely.collection.key_indexed_collection import KeyIndexedCollection
from supervisely.imaging.color import hex2rgb, rgb2hex
from supervisely.io.json import JsonSerializable


[docs]class ObjClassCollection(KeyIndexedCollection, JsonSerializable): """ Collection with :class:`ObjClass<supervisely.annotation.obj_class.ObjClass>` instances. :class:`ObjClassCollection<ObjClassCollection>` object is immutable. :raises: :class:`DuplicateKeyError` if instance with given name already exist :Usage example: .. code-block:: python import supervisely as sly # Create ObjClass (see class ObjClass for more information) class_lemon = sly.ObjClass('lemon', sly.Rectangle) class_kiwi = sly.ObjClass('kiwi', sly.Bitmap) class_arr = [class_lemon, class_kiwi] # Create ObjClassCollection from ObjClasses classes = sly.ObjClassCollection(class_arr) # Add items to ObjClassCollection class_potato = sly.ObjClass('potato', sly.Bitmap) # Remember that ObjClassCollection is immutable, and we need to assign new instance of ObjClassCollection to a new variable classes = classes.add(class_potato) # You can also add multiple items to collection class_cabbage = sly.ObjClass('cabbage', sly.Rectangle) class_carrot = sly.ObjClass('carrot', sly.Bitmap) class_turnip = sly.ObjClass('turnip', sly.Polygon) classes = classes.add_items([class_cabbage, class_carrot, class_turnip]) # Has key, checks if given key exist in collection classes.has_key("cabbage") # Output: True # Intersection, finds intersection of given list of instances with collection items class_dog = sly.ObjClass('dog', sly.Rectangle) class_cat = sly.ObjClass('cat', sly.Rectangle) class_turtle = sly.ObjClass('turtle', sly.Rectangle) classes_animals = sly.ObjClassCollection([class_dog, class_cat, class_turtle]) classes_intersections = classes.intersection(classes_animals) print(classes_intersections.to_json()) # Output: [] # Let's add the potato ObjClass from another collection and compare them again classes_animals = classes_animals.add(class_potato) classes_intersections = classes.intersection(classes_animals) print(classes_intersections.to_json()) # Output: [ # { # "title":"potato", # "shape":"bitmap", # "color":"#8A570F", # "geometry_config":{}, # "hotkey":"" # } # ] # Difference, finds difference between collection and given list of ObjClass or ObjClassCollection class_car = sly.ObjClass('car', sly.Rectangle) class_bicycle = sly.ObjClass('bicycle', sly.Rectangle) classes_vehicles = sly.ObjClassCollection([class_car, class_bicycle]) class_pedestrian = sly.ObjClass('pedestrian', sly.Rectangle) class_road = sly.ObjClass('road', sly.Rectangle) difference = classes_vehicles.difference([class_pedestrian, class_road]) print(difference.to_json()) # Output: [ # { # "title":"car", # "shape":"rectangle", # "color":"#8A0F3B", # "geometry_config":{}, # "hotkey":"" # }, # { # "title":"bicycle", # "shape":"rectangle", # "color":"#0F8A1F", # "geometry_config":{}, # "hotkey":"" # } # ] # Merge, merges collection and given list of ObjClasses c_1 = sly.ObjClassCollection([class_car, class_bicycle]) c_2 = sly.ObjClassCollection([class_pedestrian, class_road]) c_3 = c_1.merge(c_2) print(c_3.to_json()) # Output: [ # { # "title":"pedestrian", # "shape":"rectangle", # "color":"#8A0F27", # "geometry_config":{}, # "hotkey":"" # }, # { # "title":"road", # "shape":"rectangle", # "color":"#8A620F", # "geometry_config":{}, # "hotkey":"" # }, # { # "title":"car", # "shape":"rectangle", # "color":"#8A0F3B", # "geometry_config":{}, # "hotkey":"" # }, # { # "title":"bicycle", # "shape":"rectangle", # "color":"#0F8A1F", # "geometry_config":{}, # "hotkey":"" # } # ] # Merge will raise ValueError if item name from given list is in collection but items in both are different class_bicycle_1 = sly.ObjClass('bicycle', sly.Rectangle) class_bicycle_2 = sly.ObjClass('bicycle', sly.Bitmap) classes_1 = sly.ObjClassCollection([class_bicycle_1]) classes_2 = sly.ObjClassCollection([class_bicycle_2]) test_merge = classes_1.merge(classes_2) # Output: ValueError: Error during merge for key 'bicycle': values are different # Let's try to create now a collection where ObjClasses have identical names class_cow = sly.ObjClass('cow', sly.Rectangle) class_chicken = sly.ObjClass('cow', sly.Rectangle) test_classes = sly.ObjClassCollection([class_cow, class_chicken]) # Output: DuplicateKeyError: "Key 'cow' already exists" """ item_type = ObjClass
[docs] def to_json(self) -> List[Dict]: """ Convert the ObjClassCollection to a list of json dicts. Read more about `Supervisely format <https://docs.supervise.ly/data-organization/00_ann_format_navi>`_. :return: List of dicts in json format :rtype: :class:`List[dict]` :Usage example: .. code-block:: python import supervisely as sly class_lemon = sly.ObjClass('lemon', sly.Rectangle) class_kiwi = sly.ObjClass('kiwi', sly.Bitmap) # Add ObjClasses to ObjClassCollection classes = sly.ObjClassCollection([class_lemon, class_kiwi]) classes_json = classes.to_json() print(classes_json) # Output: [ # { # "title": "lemon", # "shape": "rectangle", # "color": "#300F8A", # "geometry_config": {}, # "hotkey": "" # }, # { # "title": "kiwi", # "shape": "bitmap", # "color": "#7C0F8A", # "geometry_config": {}, # "hotkey": "" # } # ] """ return [obj_class.to_json() for obj_class in self]
[docs] @classmethod def from_json(cls, data: List[Dict]) -> ObjClassCollection: """ Convert a list with dicts in json format to ObjClassCollection. Read more about `Supervisely format <https://docs.supervise.ly/data-organization/00_ann_format_navi>`_. :param data: List with dicts in json format. :type data: List[dict] :return: ObjClassCollection object :rtype: :class:`ObjClassCollection<ObjClassCollection>` :Usage example: .. code-block:: python import supervisely as sly data = [ { "title": "lemon", "shape": "rectangle", "color": "#300F8A", "hotkey": "" }, { "title": "kiwi", "shape": "bitmap", "color": "#7C0F8A", "hotkey": "" } ] classes = sly.ObjClassCollection.from_json(data) """ obj_classes = [ObjClass.from_json(obj_class_json) for obj_class_json in data] return cls(obj_classes)
[docs] def validate_classes_colors(self, logger: Optional[logger] = None) -> str or None: """ Checks for unique colors in the ObjClassCollection. :param logger: Input logger. :type logger: logger, optional :return: Notification if there are objects with the same colors, otherwise :class:`None` :rtype: :class:`str` or :class:`NoneType` :Usage example: .. code-block:: python import supervisely as sly # Let's create 2 ObjClasses with the same color class_lemon = sly.ObjClass('lemon', sly.Rectangle, [0, 0, 0]) class_kiwi = sly.ObjClass('kiwi', sly.Bitmap, [0, 0, 0]) # Add them to ObjClassCollection classes = sly.ObjClassCollection([class_lemon, class_kiwi]) print(classes.validate_classes_colors()) # Output: Classes ['lemon', 'kiwi'] have the same RGB color = [0, 0, 0] # Now let's change colors of our ObjClasses class_lemon = sly.ObjClass('lemon', sly.Rectangle, [255, 0, 0]) class_kiwi = sly.ObjClass('kiwi', sly.Bitmap, [0, 0, 255]) classes = sly.ObjClassCollection([class_lemon, class_kiwi]) print(classes.validate_classes_colors()) # Output: None """ color_names = defaultdict(list) for obj_class in self: hex = rgb2hex(obj_class.color) color_names[hex].append(obj_class.name) class_colors_notify = None for hex_color, class_names in color_names.items(): if len(class_names) > 1: warn_str = "Classes {!r} have the same RGB color = {!r}".format( class_names, hex2rgb(hex_color) ) if logger is not None: logger.warn(warn_str) if class_colors_notify is None: class_colors_notify = "" class_colors_notify += warn_str + "\n\n" return class_colors_notify
def __iter__(self) -> Iterator[ObjClass]: return next(self)
[docs] def refresh_ids_from(self, classes: ObjClassCollection) -> None: """Updates ids of classes in the collection from given classes. :param classes: Collection with classes to update ids from. :type classes: ObjClassCollection """ for new_class in classes: my_class = self.get(new_class.name) if my_class is None: continue my_class._set_id(new_class.sly_id)
def make_renamed_classes( src_obj_classes: ObjClassCollection, renamer: Renamer, skip_missing: Optional[bool] = False, ) -> ObjClassCollection: """Returns a new ObjClassCollection with renamed classes. :param src_obj_classes: ObjClassCollection to rename. :type src_obj_classes: ObjClassCollection :param renamer: Renamer object, which will handle renaming process. :type renamer: Renamer :param skip_missing: If True, missing classes will be skipped, otherwise KeyError will be raised. :type skip_missing: Optional[bool] :return: New ObjClassCollection with renamed classes. :rtype: ObjClassCollection :raises KeyError: If skip_missing is False and some classes could not be renamed. """ renamed_classes = [] for src_cls in src_obj_classes: renamed_name = renamer.rename(src_cls.name) if renamed_name is not None: renamed_classes.append(src_cls.clone(name=renamed_name)) elif not skip_missing: raise KeyError( "Object class name {} could not be mapped to a destination name.".format( src_cls.name ) ) return ObjClassCollection(items=renamed_classes)