Source code for supervisely.project.project_meta

# coding: utf-8

# docs
from __future__ import annotations

from typing import Dict, List, Optional, Tuple, Union

from supervisely._utils import take_with_default
from supervisely.annotation.obj_class import ObjClass
from supervisely.annotation.obj_class_collection import ObjClassCollection
from supervisely.annotation.tag_meta import TagMeta, TagValueType
from supervisely.annotation.tag_meta_collection import TagMetaCollection
from supervisely.geometry.bitmap import Bitmap
from supervisely.geometry.polygon import Polygon
from supervisely.geometry.rectangle import Rectangle
from supervisely.io.json import JsonSerializable
from supervisely.project.project_settings import ProjectSettings
from supervisely.project.project_type import ProjectType
from supervisely.sly_logger import logger


[docs]class ProjectMetaJsonFields: OBJ_CLASSES = "classes" IMG_TAGS = "tags_images" OBJ_TAGS = "tags_objects" TAGS = "tags" PROJECT_TYPE = "projectType" PROJECT_SETTINGS = "projectSettings"
def _merge_img_obj_tag_metas( img_tag_metas: ObjClassCollection, obj_tag_metas: ObjClassCollection ) -> ObjClassCollection: obj_tag_metas_to_add = [] for obj_tag_meta in obj_tag_metas: img_tag_meta_same_key = img_tag_metas.get(obj_tag_meta.key(), None) if img_tag_meta_same_key is None: obj_tag_metas_to_add.append(obj_tag_meta) elif not img_tag_meta_same_key.is_compatible(obj_tag_meta): raise ValueError( "Unable to merge tag metas for images and objects. Found tags with the same name, but incompatible " "values. \n Image-level tag meta: {}\n Object-level tag meta: {}.\n Rename one of the tags to have a " "unique name to be able to load project meta.".format( str(img_tag_meta_same_key), str(obj_tag_meta) ) ) return img_tag_metas.add_items(obj_tag_metas_to_add)
[docs]class ProjectMeta(JsonSerializable): """ General information about ProjectMeta. :class:`ProjectMeta<ProjectMeta>` object is immutable. :param obj_classes: ObjClassCollection or just list that stores ObjClass instances with unique names. :type obj_classes: ObjClassCollection or List[ObjClass], optional :param tag_metas: TagMetaCollection or just list that stores TagMeta instances with unique names. :type tag_metas: TagMetaCollection or List[TagMeta], optional :param project_type: Type of items in project: images, videos, volumes, point_clouds. :type project_type: ProjectType, optional :param project_settings: Additional project properties. For example, multi-view settings. :type project_settings: dict or ProjectSettings, optional :Usage example: .. code-block:: python import supervisely as sly # Example 1: Empty ProjectMeta meta = sly.ProjectMeta() print(meta) # Output: # ProjectMeta: # Object Classes # +------+-------+-------+--------+ # | Name | Shape | Color | Hotkey | # +------+-------+-------+--------+ # +------+-------+-------+--------+ # Tags # +------+------------+-----------------+--------+---------------+--------------------+ # | Name | Value type | Possible values | Hotkey | Applicable to | Applicable classes | # +------+------------+-----------------+--------+---------------+--------------------+ # +------+------------+-----------------+--------+---------------+--------------------+ # Example 2: Complex ProjectMeta lemon = sly.ObjClass('lemon', sly.Rectangle) kiwi = sly.ObjClass('kiwi', sly.Polygon) tag_fruit = sly.TagMeta('fruit', sly.TagValueType.ANY_STRING) objects = sly.ObjClassCollection([lemon, kiwi]) # or objects = [lemon, kiwi] tags = sly.TagMetaCollection([tag_fruit]) # or tags = [tag_fruit] meta = sly.ProjectMeta(obj_classes=objects, tag_metas=tags, project_type=sly.ProjectType.IMAGES) print(meta) # Output: # +-------+-----------+----------------+--------+ # | Name | Shape | Color | Hotkey | # +-------+-----------+----------------+--------+ # | lemon | Rectangle | [108, 15, 138] | | # | kiwi | Polygon | [15, 98, 138] | | # +-------+-----------+----------------+--------+ # Tags # +-------+------------+-----------------+--------+---------------+--------------------+ # | Name | Value type | Possible values | Hotkey | Applicable to | Applicable classes | # +-------+------------+-----------------+--------+---------------+--------------------+ # | fruit | any_string | None | | all | [] | # +-------+------------+-----------------+--------+---------------+--------------------+ # Example 3: Add multi-view to the project lemon = sly.ObjClass('lemon', sly.Rectangle) kiwi = sly.ObjClass('kiwi', sly.Polygon) tag_fruit = sly.TagMeta('fruit', sly.TagValueType.ANY_STRING) settings = sly.ProjectSettings( multiview_enabled=True, multiview_tag_name=tag_fruit.name, multiview_is_synced=False, ) meta = sly.ProjectMeta( obj_classes=[lemon, kiwi], tag_metas=tag_fruit, project_type=sly.ProjectType.IMAGES, project_settings=settings ) # Example 4: Custom color cat_class = sly.ObjClass("cat", sly.Rectangle, color=[0, 255, 0]) scene_tag = sly.TagMeta("scene", sly.TagValueType.ANY_STRING) meta = sly.ProjectMeta(obj_classes=[cat_class], tag_metas=[scene_tag]) """ def __init__( self, obj_classes: Optional[Union[ObjClassCollection, List[ObjClass]]] = None, tag_metas: Optional[Union[TagMetaCollection, List[TagMeta]]] = None, project_type: Optional[ProjectType] = None, project_settings: Optional[Union[ProjectSettings, Dict]] = None, ): if obj_classes is None: self._obj_classes = ObjClassCollection() elif isinstance(obj_classes, list): self._obj_classes = ObjClassCollection(obj_classes) elif isinstance(obj_classes, ObjClassCollection): self._obj_classes = obj_classes else: raise TypeError(f"obj_classes argument has unknown type {type(obj_classes)}") if tag_metas is None: self._tag_metas = TagMetaCollection() elif isinstance(tag_metas, list): self._tag_metas = TagMetaCollection(tag_metas) elif isinstance(tag_metas, TagMetaCollection): self._tag_metas = tag_metas else: raise TypeError(f"tag_metas argument has unknown type {type(tag_metas)}") self._project_type = project_type if isinstance(project_settings, dict): self._project_settings = ProjectSettings.from_json(project_settings) else: self._project_settings = project_settings if project_settings is None: self._project_settings = ProjectSettings() self._project_settings.validate(self) @property def obj_classes(self) -> ObjClassCollection: """ Collection of ObjClasses in ProjectMeta. :return: ObjClassCollection object :rtype: :class:`ObjClassCollection<supervisely.annotation.obj_class_collection.ObjClassCollection>` :Usage example: .. code-block:: python import supervisely as sly lemon = sly.ObjClass('lemon', sly.Rectangle) kiwi = sly.ObjClass('kiwi', sly.Polygon) objects = sly.ObjClassCollection([lemon, kiwi]) # or objects = [lemon, kiwi] meta = sly.ProjectMeta(obj_classes=objects, project_type=sly.ProjectType.IMAGES) meta_classes = meta.obj_classes print(meta_classes.to_json()) # Output: [ # { # "title":"lemon", # "shape":"rectangle", # "color":"#6C0F8A", # "geometry_config":{ # # }, # "hotkey":"" # }, # { # "title":"kiwi", # "shape":"polygon", # "color":"#0F628A", # "geometry_config":{ # # }, # "hotkey":"" # } # ] """ return self._obj_classes @property def tag_metas(self) -> TagMetaCollection: """ Collection of TagMetas in ProjectMeta. :return: TagMetaCollection object :rtype: :class:`TagMetaCollection<supervisely.annotation.tag_meta_collection.TagMetaCollection>` :Usage example: .. code-block:: python import supervisely as sly tag_fruit = sly.TagMeta('fruit', sly.TagValueType.ANY_STRING) tags = sly.TagMetaCollection([tag_fruit]) # or tags = [tag_fruit] meta = sly.ProjectMeta(tag_metas=tags) meta_tags = meta.tag_metas print(meta_tags.to_json()) # Output: [ # { # "name":"fruit", # "value_type":"any_string", # "color":"#818A0F", # "hotkey":"", # "applicable_type":"all", # "classes":[] # } # ] """ return self._tag_metas @property def project_type(self) -> str: """ Type of project. See possible value types in :class:`ProjectType<supervisely.project.project_type.ProjectType>`. :return: Project type :rtype: :class:`str` :Usage example: .. code-block:: python import supervisely as sly meta = sly.ProjectMeta(project_type=sly.ProjectType.IMAGES) print(meta.project_type) # Output: 'images' """ return self._project_type @property def project_settings(self) -> ProjectSettings: """ Settings of the project. See possible values in :class: `ProjectSettings`. :return: Project settings :rtype: :class: `Dict[str, str]` :Usage example: .. code-block:: python import supervisely as sly s = sly.ProjectSettings( multiview_enabled=True, multiview_tag_name='multi_tag', multiview_is_synced=False, ) meta = sly.ProjectMeta(project_settings=s) print(meta.project_settings) # Output: <class 'supervisely.project.project_settings.ProjectSettings'> """ return self._project_settings
[docs] def to_json(self) -> Dict: """ Convert the ProjectMeta 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 meta_json = meta.to_json() print(meta_json) # Output: { # "classes": [ # { # "title": "lemon", # "shape": "rectangle", # "color": "#720F8A", # "geometry_config": {}, # "hotkey": "" # }, # { # "title": "kiwi", # "shape": "polygon", # "color": "#8A0F6F", # "geometry_config": {}, # "hotkey": "" # } # ], # "tags": [ # { # "name": "fruit", # "value_type": "any_string", # "color": "#788A0F", # "hotkey": "", # "applicable_type": "all", # "classes": [] # } # ] # } """ res = { ProjectMetaJsonFields.OBJ_CLASSES: self._obj_classes.to_json(), ProjectMetaJsonFields.TAGS: self._tag_metas.to_json(), } if self._project_type is not None: res[ProjectMetaJsonFields.PROJECT_TYPE] = self._project_type if self._project_settings is not None: res[ProjectMetaJsonFields.PROJECT_SETTINGS] = self._project_settings.to_json() return res
[docs] @classmethod def from_json(cls, data: Dict) -> ProjectMeta: """ Convert a json dict to ProjectMeta. Read more about `Supervisely format <https://docs.supervise.ly/data-organization/00_ann_format_navi>`_. :param data: ProjectMeta in json format as a dict. :type data: dict :return: ProjectMeta object :rtype: :class:`ProjectMeta<ProjectMeta>` :Usage example: .. code-block:: python import supervisely as sly meta_json = { "classes": [ { "title": "lemon", "shape": "rectangle", "color": "#8A0F7B" }, { "title": "kiwi", "shape": "polygon", "color": "#8A0F50" } ], "tags": [ { "name": "fruit", "value_type": "any_string", "color": "#0F6F8A" } ] } meta = sly.ProjectMeta.from_json(meta_json) """ tag_metas_json = data.get(ProjectMetaJsonFields.TAGS, []) img_tag_metas_json = data.get(ProjectMetaJsonFields.IMG_TAGS, []) obj_tag_metas_json = data.get(ProjectMetaJsonFields.OBJ_TAGS, []) project_type = data.get(ProjectMetaJsonFields.PROJECT_TYPE, None) if len(tag_metas_json) > 0: # New format - all project tags in a single collection. if any(len(x) > 0 for x in [img_tag_metas_json, obj_tag_metas_json]): raise ValueError( "Project meta JSON contains both the {!r} section (current format merged tags for all of " "the project) and {!r} or {!r} sections (legacy format with separate collections for images " "and labeled objects). Either new format only or legacy format only are supported, but not a " "mix.".format( ProjectMetaJsonFields.TAGS, ProjectMetaJsonFields.IMG_TAGS, ProjectMetaJsonFields.OBJ_TAGS, ) ) tag_metas = TagMetaCollection.from_json(tag_metas_json) else: img_tag_metas = TagMetaCollection.from_json(img_tag_metas_json) obj_tag_metas = TagMetaCollection.from_json(obj_tag_metas_json) tag_metas = _merge_img_obj_tag_metas(img_tag_metas, obj_tag_metas) obj_classes_json = data.get(ProjectMetaJsonFields.OBJ_CLASSES, None) if obj_classes_json is None: raise KeyError( f"Key '{ProjectMetaJsonFields.OBJ_CLASSES}' with the list of annotation classes " "not found in meta.json file. Check the annotation format documentation at: " "https://developer.supervisely.com/api-references/supervisely-annotation-json-format/project-classes-and-tags" ) try: obj_classes = ObjClassCollection.from_json(obj_classes_json) except Exception as e: raise Exception( f"Failed to deserialize classes from Project meta JSON. " "Check the annotation format documentation at: " "https://developer.supervisely.com/api-references/supervisely-annotation-json-format/project-classes-and-tags" ) from e project_settings_json = data.get(ProjectMetaJsonFields.PROJECT_SETTINGS, None) if project_settings_json is None: project_settings = None else: try: project_settings = ProjectSettings.from_json(project_settings_json) except Exception as e: raise Exception( f"Failed to deserialize settings from Project meta JSON. " "Check the annotation format documentation at: " # TODO "https://developer.supervisely.com/api-references/supervisely-annotation-json-format/project-classes-and-tags" ) from e return cls( obj_classes=obj_classes, tag_metas=tag_metas, project_type=project_type, project_settings=project_settings, )
[docs] def merge(self, other: ProjectMeta) -> ProjectMeta: """ Merge all instances from given ProjectMeta into a single ProjectMeta object. :param other: ProjectMeta object. :type other: ProjectMeta :return: New instance of ProjectMeta object :rtype: :class:`ProjectMeta<ProjectMeta>` :raises: :class:`ValueError` Upon attempt to merge metas which contain the same obj class or tag meta :Usage example: .. code-block:: python import supervisely as sly meta_1 = sly.ProjectMeta() class_cat = sly.ObjClass('cat', sly.Rectangle) tag_cat = sly.TagMeta('cat_tag', sly.TagValueType.ANY_STRING) meta_1 = meta_1.add_obj_class(class_cat) meta_1 = meta_1.add_tag_meta(tag_cat) meta_2 = sly.ProjectMeta() class_dog = sly.ObjClass('dog', sly.Rectangle) tag_dog = sly.TagMeta('dog_tag', sly.TagValueType.ANY_STRING) meta_2 = meta_2.add_obj_class(class_dog) meta_2 = meta_2.add_tag_meta(tag_dog) merge_meta = meta_1.merge(meta_2) merge_meta_json = merge_meta.to_json() print(json.dumps(merge_meta_json, indent=4)) # Output: { # "classes": [ # { # "title": "dog", # "shape": "rectangle", # "color": "#0F8A62", # "geometry_config": {}, # "hotkey": "" # }, # { # "title": "cat", # "shape": "rectangle", # "color": "#340F8A", # "geometry_config": {}, # "hotkey": "" # } # ], # "tags": [ # { # "name": "dog_tag", # "value_type": "any_string", # "color": "#380F8A", # "hotkey": "", # "applicable_type": "all", # "classes": [] # }, # { # "name": "cat_tag", # "value_type": "any_string", # "color": "#8A0F82", # "hotkey": "", # "applicable_type": "all", # "classes": [] # } # ] # } """ return self.clone( obj_classes=self._obj_classes.merge(other.obj_classes), tag_metas=self._tag_metas.merge(other._tag_metas), )
[docs] def clone( self, obj_classes: Optional[Union[ObjClassCollection, List[ObjClass]]] = None, tag_metas: Optional[Union[TagMetaCollection, List[TagMeta]]] = None, project_type: Optional[str] = None, project_settings: Optional[Union[dict, ProjectSettings]] = None, ) -> ProjectMeta: """ Clone makes a copy of ProjectMeta with new fields, if fields are given, otherwise it will use original ProjectMeta fields. :param obj_classes: ObjClassCollection or just list that stores ObjClass instances with unique names. :type obj_classes: ObjClassCollection or List[ObjClass], optional :param tag_metas: TagMetaCollection that stores TagMeta instances with unique names. :type tag_metas: TagMetaCollection or List[TagMeta], optional :param project_type: Type of items in project: images, videos, volumes, point_clouds. :type project_type: str, optional :param project_settings: Additional project properties. For example, multi-view settings :type project_settings: dict or ProjectSettings, optional :return: New instance of ProjectMeta object :rtype: :class:`ProjectMeta<ProjectMeta>` :Usage example: .. code-block:: python import supervisely as sly meta = sly.ProjectMeta() class_cat = sly.ObjClass('cat', sly.Rectangle) collection_cat = sly.ObjClassCollection([class_cat]) # or collection_cat = [class_cat] tag_cat = sly.TagMeta('cat_tag', sly.TagValueType.ANY_STRING) collection_tag_cat = sly.TagMetaCollection([tag_cat]) # or collection_tag_cat = [tag_cat] # Remember that ProjectMeta object is immutable, and we need to assign new instance of ProjectMeta to a new variable new_meta = meta.clone(obj_classes=collection_cat, tag_metas=collection_tag_cat) new_meta_json = new_meta.to_json() print(json.dumps(new_meta_json, indent=4)) # Output: { # "classes": [ # { # "title": "cat", # "shape": "rectangle", # "color": "#190F8A", # "geometry_config": {}, # "hotkey": "" # } # ], # "tags": [ # { # "name": "cat_tag", # "value_type": "any_string", # "color": "#8A6D0F", # "hotkey": "", # "applicable_type": "all", # "classes": [] # } # ] # } """ return ProjectMeta( obj_classes=take_with_default(obj_classes, self.obj_classes), tag_metas=take_with_default(tag_metas, self.tag_metas), project_type=take_with_default(project_type, self.project_type), project_settings=take_with_default(project_settings, self.project_settings), )
[docs] def add_obj_class(self, new_obj_class: ObjClass) -> ProjectMeta: """ Adds given ObjClass to ProjectMeta. :param new_obj_class: ObjClass object. :type new_obj_class: ObjClass :return: New instance of ProjectMeta object :rtype: :class:`ProjectMeta<ProjectMeta>` :Usage example: .. code-block:: python import supervisely as sly meta = sly.ProjectMeta() class_cat = sly.ObjClass('cat', sly.Rectangle) # Remember that ProjectMeta object is immutable, and we need to assign new instance of ProjectMeta to a new variable meta = meta.add_obj_class(class_cat) meta_json = meta.to_json() print(json.dumps(meta_json, indent=4)) # Output: { # "classes": [ # { # "title": "cat", # "shape": "rectangle", # "color": "#178A0F", # "geometry_config": {}, # "hotkey": "" # } # ], # "tags": [] # } """ return self.add_obj_classes([new_obj_class])
[docs] def add_obj_classes( self, new_obj_classes: Union[List[ObjClass], ObjClassCollection] ) -> ProjectMeta: """ Adds given ObjClasses to ProjectMeta. :param new_obj_classes: List of ObjClass objects. :type new_obj_classes: ObjClassCollection or List[ObjClass] :return: New instance of ProjectMeta object :rtype: :class:`ProjectMeta<ProjectMeta>` :Usage example: .. code-block:: python import supervisely as sly meta = sly.ProjectMeta() class_cat = sly.ObjClass('cat', sly.Rectangle) class_dog = sly.ObjClass('dog', sly.Bitmap) # Remember that ProjectMeta object is immutable, and we need to assign new instance of ProjectMeta to a new variable meta = meta.add_obj_classes([class_cat, class_dog]) meta_json = meta.to_json() print(json.dumps(meta_json, indent=4)) # Output: { # "classes": [ # { # "title": "cat", # "shape": "rectangle", # "color": "#8A0F3F", # "geometry_config": {}, # "hotkey": "" # }, # { # "title": "dog", # "shape": "bitmap", # "color": "#8A0F56", # "geometry_config": {}, # "hotkey": "" # } # ], # "tags": [] # } """ return self.clone(obj_classes=self.obj_classes.add_items(new_obj_classes))
[docs] def add_tag_meta(self, new_tag_meta: TagMeta) -> ProjectMeta: """ Adds given TagMeta to ProjectMeta. :param new_tag_meta: TagMeta object. :type new_tag_meta: TagMeta :return: New instance of ProjectMeta object :rtype: :class:`ProjectMeta<ProjectMeta>` :Usage example: .. code-block:: python import supervisely as sly meta = sly.ProjectMeta() tag_cat = sly.TagMeta('cat_tag', sly.TagValueType.ANY_STRING) # Remember that ProjectMeta object is immutable, and we need to assign new instance of ProjectMeta to a new variable meta = meta.add_tag_meta(tag_cat) meta_json = meta.to_json() print(json.dumps(meta_json, indent=4)) # Output: { # "classes": [], # "tags": [ # { # "name": "cat_tag", # "value_type": "any_string", # "color": "#178A0F", # "hotkey": "", # "applicable_type": "all", # "classes": [] # } # ] # } """ return self.add_tag_metas([new_tag_meta])
[docs] def add_tag_metas(self, new_tag_metas: List[TagMeta]) -> ProjectMeta: """ Adds given TagMetas to ProjectMeta. :param new_tag_metas: List of TagMeta objects. :type new_tag_metas: List[TagMeta] :return: New instance of ProjectMeta object :rtype: :class:`ProjectMeta<ProjectMeta>` :Usage example: .. code-block:: python import supervisely as sly meta = sly.ProjectMeta() tag_cat = sly.TagMeta('cat_tag', sly.TagValueType.ANY_STRING) tag_dog = sly.TagMeta('dog_tag', sly.TagValueType.ANY_STRING) # Remember that ProjectMeta object is immutable, and we need to assign new instance of ProjectMeta to a new variable meta = meta.add_tag_metas([tag_cat, tag_dog]) meta_json = meta.to_json() print(json.dumps(meta_json, indent=4)) # Output: { # "classes": [], # "tags": [ # { # "name": "cat_tag", # "value_type": "any_string", # "color": "#0F248A", # "hotkey": "", # "applicable_type": "all", # "classes": [] # }, # { # "name": "dog_tag", # "value_type": "any_string", # "color": "#8A5C0F", # "hotkey": "", # "applicable_type": "all", # "classes": [] # } # ] # } """ return self.clone(tag_metas=self.tag_metas.add_items(new_tag_metas))
@staticmethod def _delete_items(collection, item_names): """ :param collection: ObjClassCollection or TagMetaCollection instance :param item_names: list of item names to delete :return: list of items, which are in collection and not in given list of items to delete """ names_to_delete = set(item_names) res_items = [] for item in collection: if item.key() not in names_to_delete: res_items.append(item) return res_items
[docs] def delete_obj_class(self, obj_class_name: str) -> ProjectMeta: """ Removes given ObjClass by name from ProjectMeta. :param obj_class_name: ObjClass name. :type obj_class_name: str :return: New instance of ProjectMeta object :rtype: :class:`ProjectMeta<ProjectMeta>` :Usage example: .. code-block:: python import supervisely as sly meta = sly.ProjectMeta() class_cat = sly.ObjClass('cat', sly.Rectangle) # Remember that ProjectMeta object is immutable, and we need to assign new instance of ProjectMeta to a new variable meta = meta.add_obj_class(class_cat) meta_json = meta.to_json() print(json.dumps(meta_json, indent=4)) # Output: { # "classes": [ # { # "title": "cat", # "shape": "rectangle", # "color": "#268A0F", # "geometry_config": {}, # "hotkey": "" # } # ], # "tags": [] # } # Remember that ProjectMeta object is immutable, and we need to assign new instance of ProjectMeta to a new variable meta = meta.delete_obj_class('cat') meta_json = meta.to_json() print(json.dumps(meta_json, indent=4)) # Output: { # "classes": [], # "tags": [] # } """ return self.delete_obj_classes([obj_class_name])
[docs] def delete_obj_classes(self, obj_class_names: List[str]) -> ProjectMeta: """ Removes given ObjClasses by names from ProjectMeta. :param obj_class_names: List of ObjClasses names. :type obj_class_names: List[str] :return: New instance of ProjectMeta object :rtype: :class:`ProjectMeta<ProjectMeta>` :Usage example: .. code-block:: python import supervisely as sly meta = sly.ProjectMeta() class_cat = sly.ObjClass('cat', sly.Rectangle) class_dog = sly.ObjClass('dog', sly.Bitmap) # Remember that ProjectMeta object is immutable, and we need to assign new instance of ProjectMeta to a new variable meta = meta.add_obj_classes([class_cat, class_dog]) meta_json = meta.to_json() print(json.dumps(meta_json, indent=4)) # Output: { # "classes": [ # { # "title": "cat", # "shape": "rectangle", # "color": "#8A0F18", # "geometry_config": {}, # "hotkey": "" # }, # { # "title": "dog", # "shape": "bitmap", # "color": "#0F8A7F", # "geometry_config": {}, # "hotkey": "" # } # ], # "tags": [] # } # Remember that ProjectMeta object is immutable, and we need to assign new instance of ProjectMeta to a new variable meta = meta.delete_obj_classes(['cat', 'dog']) meta_json = meta.to_json() print(json.dumps(meta_json, indent=4)) # Output: { # "classes": [], # "tags": [] # } """ res_items = self._delete_items(self._obj_classes, obj_class_names) return self.clone(obj_classes=ObjClassCollection(res_items))
[docs] def delete_tag_meta(self, tag_name: str) -> ProjectMeta: """ Removes given TagMeta by name from ProjectMeta. :param tag_name: TagMeta name. :type tag_name: str :return: New instance of ProjectMeta object :rtype: :class:`ProjectMeta<ProjectMeta>` :Usage example: .. code-block:: python import supervisely as sly meta = sly.ProjectMeta() tag_cat = sly.TagMeta('cat_tag', sly.TagValueType.ANY_STRING) # Remember that ProjectMeta object is immutable, and we need to assign new instance of ProjectMeta to a new variable meta = meta.add_tag_meta(tag_cat) meta_json = meta.to_json() print(json.dumps(meta_json, indent=4)) # Output: { # "classes": [], # "tags": [ # { # "name": "cat_tag", # "value_type": "any_string", # "color": "#8A540F", # "hotkey": "", # "applicable_type": "all", # "classes": [] # } # ] # } # Remember that ProjectMeta object is immutable, and we need to assign new instance of ProjectMeta to a new variable meta = meta.delete_tag_meta('cat_tag') meta_json = meta.to_json() print(json.dumps(meta_json, indent=4)) # Output: { # "classes": [], # "tags": [] # } """ return self.delete_tag_metas([tag_name])
[docs] def delete_tag_metas(self, tag_names: List[str]) -> ProjectMeta: """ Removes given TagMetas by names from ProjectMeta. :param tag_names: List of TagMetas names. :type tag_names: List[TagMeta] :return: New instance of ProjectMeta object :rtype: :class:`ProjectMeta<ProjectMeta>` :Usage example: .. code-block:: python import supervisely as sly meta = sly.ProjectMeta() tag_cat = sly.TagMeta('cat_tag', sly.TagValueType.ANY_STRING) tag_dog = sly.TagMeta('dog_tag', sly.TagValueType.ANY_STRING) # Remember that ProjectMeta object is immutable, and we need to assign new instance of ProjectMeta to a new variable meta = meta.add_tag_metas([tag_cat, tag_dog]) meta_json = meta.to_json() print(json.dumps(meta_json, indent=4)) # Output: { # "classes": [], # "tags": [ # { # "name": "cat_tag", # "value_type": "any_string", # "color": "#0F298A", # "hotkey": "", # "applicable_type": "all", # "classes": [] # }, # { # "name": "dog_tag", # "value_type": "any_string", # "color": "#8A410F", # "hotkey": "", # "applicable_type": "all", # "classes": [] # } # ] # } # Remember that ProjectMeta object is immutable, and we need to assign new instance of ProjectMeta to a new variable meta = meta.delete_tag_metas(['cat_tag', 'dog_tag']) meta_json = meta.to_json() print(json.dumps(meta_json, indent=4)) # Output: { # "classes": [], # "tags": [] # } """ res_items = self._delete_items(self._tag_metas, tag_names) return self.clone(tag_metas=TagMetaCollection(res_items))
[docs] def get_obj_class(self, obj_class_name: str) -> ObjClass: """ Get given ObjClass by name from ProjectMeta. :param obj_class_name: ObjClass name. :type obj_class_name: str :return: ObjClass object :rtype: :class:`ObjClass<supervisely.annotation.obj_class.ObjClass>` :Usage example: .. code-block:: python import supervisely as sly meta = sly.ProjectMeta() class_cat = sly.ObjClass('cat', sly.Rectangle) class_dog = sly.ObjClass('dog', sly.Bitmap) # Remember that ProjectMeta object is immutable, and we need to assign new instance of ProjectMeta to a new variable meta = meta.add_obj_classes([class_cat, class_dog]) meta_json = meta.to_json() print(json.dumps(meta_json, indent=4)) # Output: { # "classes": [ # { # "title": "cat", # "shape": "rectangle", # "color": "#8A140F", # "geometry_config": {}, # "hotkey": "" # }, # { # "title": "dog", # "shape": "bitmap", # "color": "#0F8A35", # "geometry_config": {}, # "hotkey": "" # } # ], # "tags": [] # } class_cat = meta.get_obj_class('cat') print(class_cat) # Output: # Name: cat Shape: Rectangle Color: [138, 20, 15] Geom. settings: {} Hotkey class_elephant = meta.get_obj_class('elephant') print(class_elephant) # Output: # None """ return self._obj_classes.get(obj_class_name)
[docs] def get_obj_class_by_id(self, obj_class_id: int) -> Optional[ObjClass]: """ Get given ObjClass by name from ProjectMeta. :param obj_class_id: ObjClass id. :type obj_class_id: int :return: ObjClass object or None :rtype: :class:`ObjClass<supervisely.annotation.obj_class.ObjClass>` :Usage example: .. code-block:: python import supervisely as sly meta = sly.ProjectMeta.from_json(api.project.get_meta(project_id)) obj_class_id = 123 """ for obj_class in self.obj_classes: if obj_class.sly_id == obj_class_id: return obj_class
[docs] def get_tag_meta(self, tag_name: str) -> Optional[TagMeta]: """ Get given TagMeta by name from ProjectMeta. :param tag_name: TagMeta name. :type tag_name: str :return: TagMeta object or None. :rtype: :class:`TagMeta<supervisely.annotation.tag_meta.TagMeta>` :Usage example: .. code-block:: python import supervisely as sly meta = sly.ProjectMeta() tag_cat = sly.TagMeta('cat_tag', sly.TagValueType.ANY_STRING) tag_dog = sly.TagMeta('dog_tag', sly.TagValueType.ANY_STRING) # Remember that ProjectMeta object is immutable, and we need to assign new instance of ProjectMeta to a new variable meta = meta.add_tag_metas([tag_cat, tag_dog]) meta_json = meta.to_json() print(json.dumps(meta_json, indent=4)) # Output: { # "classes": [], # "tags": [ # { # "name": "cat_tag", # "value_type": "any_string", # "color": "#590F8A", # "hotkey": "", # "applicable_type": "all", # "classes": [] # }, # { # "name": "dog_tag", # "value_type": "any_string", # "color": "#0F8A88", # "hotkey": "", # "applicable_type": "all", # "classes": [] # } # ] # } tag_cat = meta.get_tag_meta('cat_tag') print(tag_cat) # Output: # Name: cat_tag Value type:any_string Possible values:None Hotkey Applicable toall Applicable classes[] tag_elephant = meta.get_tag_meta('elephant_tag') print(tag_elephant) # Output: # None """ return self._tag_metas.get(tag_name)
[docs] def get_tag_meta_by_id(self, tag_id: int) -> Optional[TagMeta]: """Return TagMeta with given id. :param tag_id: TagMeta id to search for. :type tag_id: int :return: TagMeta with given id. :rtype: TagMeta or None """ return self._tag_metas.get_by_id(tag_id)
[docs] def get_tag_name_by_id(self, tag_id: int) -> Optional[str]: """Return tag name with given id. :param tag_id: TagMeta id to search for. :type tag_id: int :return: tag name with given id. :rtype: tag name or None """ return self._tag_metas.get_tag_name_by_id(tag_id)
[docs] @staticmethod def merge_list(metas: List[ProjectMeta]) -> ProjectMeta: """ Merge ProjectMetas from given list of ProjectMetas into single ProjectMeta object. :param metas: List of ProjectMeta objects. :type metas: List[ProjectMeta] :return: New instance of ProjectMeta object :rtype: :class:`ProjectMeta<ProjectMeta>` :Usage example: .. code-block:: python import supervisely as sly meta = sly.ProjectMeta() meta_1 = sly.ProjectMeta() class_cat = sly.ObjClass('cat', sly.Rectangle) meta_1 = meta_1.add_obj_class(class_cat) meta_2 = sly.ProjectMeta() tag_dog = sly.TagMeta('dog_tag', sly.TagValueType.ANY_STRING) meta_2 = meta_2.add_tag_meta(tag_dog) # Remember that ProjectMeta object is immutable, and we need to assign new instance of ProjectMeta to a new variable meta = meta.merge_list([meta_1, meta_2]) meta_json = meta.to_json() print(json.dumps(meta_json, indent=4)) # Output: { # "classes": [ # { # "title": "cat", # "shape": "rectangle", # "color": "#0F8A45", # "geometry_config": {}, # "hotkey": "" # } # ], # "tags": [ # { # "name": "dog_tag", # "value_type": "any_string", # "color": "#320F8A", # "hotkey": "", # "applicable_type": "all", # "classes": [] # } # ] # } """ res_meta = ProjectMeta() for meta in metas: res_meta = res_meta.merge(meta) return res_meta
def __str__(self): result = "ProjectMeta:\n" result += "Object Classes\n{}\n".format(str(self._obj_classes)) result += "Tags\n{}\n".format(str(self._tag_metas)) return result def __eq__(self, other: ProjectMeta): if self.obj_classes == other.obj_classes and self.tag_metas == other.tag_metas: return True return False def __ne__(self, other: ProjectMeta): return not self == other
[docs] def to_segmentation_task( self, keep_geometries: Optional[List] = [Polygon, Bitmap], target_classes=None ) -> Tuple[ProjectMeta, Dict[ObjClass, ObjClass]]: """ Convert project meta classes geometries with keep_geometries types to Bitmaps and create new ProjectMeta. :param keep_geometries: List of geometries that can be converted. :type keep_geometries: List, optional :return: New project meta and dict correspondences of old classes to new :rtype: :class:`Tuple[ProjectMeta, Dict[ObjClass, ObjClass]]` :Usage example: .. code-block:: python import supervisely as sly meta = sly.ProjectMeta() class_cat = sly.ObjClass('cat', sly.Polygon) class_dog = sly.ObjClass('dog', sly.Bitmap) meta = meta.add_obj_classes([class_cat, class_dog]) meta_json = meta.to_json() print(json.dumps(meta_json, indent=4)) # Output: { # "classes": [ # { # "title": "cat", # "shape": "polygon", # "color": "#208A0F", # "geometry_config": {}, # "hotkey": "" # }, # { # "title": "dog", # "shape": "bitmap", # "color": "#8A570F", # "geometry_config": {}, # "hotkey": "" # } # ], # "tags": [] # } res_meta, mapping = meta.to_segmentation_task() res_meta_json = res_meta.to_json() print(json.dumps(res_meta_json, indent=4)) # Output: { # "classes": [ # { # "title": "cat", # "shape": "bitmap", # "color": "#208A0F", # "geometry_config": {}, # "hotkey": "" # }, # { # "title": "dog", # "shape": "bitmap", # "color": "#8A570F", # "geometry_config": {}, # "hotkey": "" # } # ], # "tags": [] # } """ mapping = {} res_classes = [] for obj_class in self.obj_classes: obj_class: ObjClass if target_classes is None or obj_class.name in target_classes: if obj_class.geometry_type in keep_geometries: if obj_class.geometry_type == Bitmap: mapping[obj_class] = obj_class res_classes.append(obj_class) else: new_class = obj_class.clone(geometry_type=Bitmap) mapping[obj_class] = new_class res_classes.append(new_class) else: mapping[obj_class] = None else: mapping[obj_class] = None res_meta = self.clone(obj_classes=ObjClassCollection(res_classes)) return res_meta, mapping
[docs] def to_detection_task( self, convert_classes: Optional[bool] = False ) -> Tuple[ProjectMeta, Dict[ObjClass, ObjClass]]: """ Convert project meta classes geometries to Rectangles or skip them and create new ProjectMeta. :param convert_classes: Convert classes with no Rectangle type to Rectangle or skip them. :type convert_classes: bool, optional :return: New project meta and dict correspondences of old classes to new :rtype: :class:`Tuple[ProjectMeta, Dict[ObjClass, ObjClass]]` :Usage example: .. code-block:: python import supervisely as sly meta = sly.ProjectMeta() class_cat = sly.ObjClass('cat', sly.Polygon) class_dog = sly.ObjClass('dog', sly.Bitmap) meta = meta.add_obj_classes([class_cat, class_dog]) meta_json = meta.to_json() print(json.dumps(meta_json, indent=4)) # Output: { # "classes": [ # { # "title": "cat", # "shape": "polygon", # "color": "#208A0F", # "geometry_config": {}, # "hotkey": "" # }, # { # "title": "dog", # "shape": "bitmap", # "color": "#8A570F", # "geometry_config": {}, # "hotkey": "" # } # ], # "tags": [] # } res_meta, mapping = meta.to_detection_task(convert_classes=True) res_meta_json = res_meta.to_json() print(json.dumps(res_meta_json, indent=4)) # Output: { # "classes": [ # { # "title": "cat", # "shape": "rectangle", # "color": "#3A0F8A", # "geometry_config": {}, # "hotkey": "" # }, # { # "title": "dog", # "shape": "rectangle", # "color": "#8A310F", # "geometry_config": {}, # "hotkey": "" # } # ], # "tags": [] # } """ mapping = {} res_classes = [] for obj_class in self.obj_classes: obj_class: ObjClass if obj_class.geometry_type == Rectangle: mapping[obj_class] = obj_class res_classes.append(obj_class) else: if convert_classes is True: new_class = obj_class.clone(geometry_type=Rectangle) mapping[obj_class] = new_class res_classes.append(new_class) else: # ignore class mapping[obj_class] = None res_meta = self.clone(obj_classes=ObjClassCollection(res_classes)) return res_meta, mapping