Source code for encord.objects.ontology_structure

from __future__ import annotations

from dataclasses import dataclass, field
from typing import List, Optional
from uuid import uuid4

from encord.objects.classification import Classification
from encord.objects.common import Shape
from encord.objects.ontology_object import Object

AVAILABLE_COLORS = (
    "#D33115",
    "#E27300",
    "#16406C",
    "#FE9200",
    "#FCDC00",
    "#DBDF00",
    "#A4DD00",
    "#68CCCA",
    "#73D8FF",
    "#AEA1FF",
    "#FCC400",
    "#B0BC00",
    "#68BC00",
    "#16A5A5",
    "#009CE0",
    "#7B64FF",
    "#FA28FF",
    "#B3B3B3",
    "#9F0500",
    "#C45100",
    "#FB9E00",
    "#808900",
    "#194D33",
    "#0C797D",
    "#0062B1",
    "#653294",
    "#AB149E",
)


[docs]@dataclass class OntologyStructure: """ This class is currently in BETA. Its API might change in future minor version releases. """ objects: List[Object] = field(default_factory=list) classifications: List[Classification] = field(default_factory=list)
[docs] @classmethod def from_dict(cls, d: dict) -> OntologyStructure: """ Args: d: a JSON blob of an "ontology structure" (e.g. from Encord web app) Raises: KeyError: If the dict is missing a required field. """ objects_ret: List[Object] = list() for object_dict in d["objects"]: objects_ret.append(Object.from_dict(object_dict)) classifications_ret: List[Classification] = list() for classification_dict in d["classifications"]: classifications_ret.append(Classification.from_dict(classification_dict)) return OntologyStructure(objects=objects_ret, classifications=classifications_ret)
[docs] def to_dict(self) -> dict: """ Returns: The dict equivalent to the ontology. Raises: KeyError: If the dict is missing a required field. """ ret = dict() ontology_objects = list() ret["objects"] = ontology_objects for ontology_object in self.objects: ontology_objects.append(ontology_object.to_dict()) ontology_classifications = list() ret["classifications"] = ontology_classifications for ontology_classification in self.classifications: ontology_classifications.append(ontology_classification.to_dict()) return ret
[docs] def add_object( self, name: str, shape: Shape, uid: Optional[int] = None, color: Optional[str] = None, feature_node_hash: Optional[str] = None, ) -> Object: """ Adds an object class definition to the structure. .. code:: structure = ontology_structure.OntologyStructure() eye = structure.add_object( name="Eye", ) nose = structure.add_object( name="Nose", ) nose_detail = nose.add_attribute( encord.objects.common.ChecklistAttribute, ) nose_detail.add_option(feature_node_hash="2bc17c88", label="Is it a cute nose?") nose_detail.add_option(feature_node_hash="86eaa4f2", label="Is it a wet nose? ") Args: name: the user-visible name of the object shape: the kind of object (bounding box, polygon, etc). See :py:class:`encord.objects.common.Shape` enum for possible values uid: integer identifier of the object. Normally auto-generated; omit this unless the aim is to create an exact clone of existing structure color: the color of the object in the label editor. Normally auto-assigned, should be in '#1A2B3F' syntax. feature_node_hash: global identifier of the object. Normally auto-generated; omit this unless the aim is to create an exact clone of existing structure Returns: the created object class that can be further customised with attributes. """ if uid is None: if self.objects: uid = max([obj.uid for obj in self.objects]) + 1 else: uid = 1 else: if any([obj.uid == uid for obj in self.objects]): raise ValueError(f"Duplicate uid '{uid}'") if color is None: color_index = 0 if self.objects: try: color_index = AVAILABLE_COLORS.index(self.objects[-1].color) + 1 if color_index >= len(AVAILABLE_COLORS): color_index = 0 except ValueError: pass color = AVAILABLE_COLORS[color_index] if feature_node_hash is None: feature_node_hash = str(uuid4())[:8] if any([obj.feature_node_hash == feature_node_hash for obj in self.objects]): raise ValueError(f"Duplicate feature_node_hash '{feature_node_hash}'") obj = Object(uid, name, color, shape, feature_node_hash) self.objects.append(obj) return obj
[docs] def add_classification( self, uid: Optional[int] = None, feature_node_hash: Optional[str] = None, ) -> Classification: """ Adds an classification definition to the ontology. .. code:: structure = ontology_structure.OntologyStructure() cls = structure.add_classification(feature_node_hash="a39d81c0") cat_standing = cls.add_attribute( encord.objects.common.RadioAttribute, feature_node_hash="a6136d14", name="Is the cat standing?", required=True, ) cat_standing.add_option(feature_node_hash="a3aeb48d", label="Yes") cat_standing.add_option(feature_node_hash="d0a4b373", label="No") Args: uid: integer identifier of the object. Normally auto-generated; omit this unless the aim is to create an exact clone of existing structure feature_node_hash: global identifier of the object. Normally auto-generated; omit this unless the aim is to create an exact clone of existing structure Returns: the created classification node. Note that classification attribute should be further specified by calling its `add_attribute()` method. """ if uid is None: if self.classifications: uid = max([cls.uid for cls in self.classifications]) + 1 else: uid = 1 else: if any([cls.uid == uid for cls in self.classifications]): raise ValueError(f"Duplicate uid '{uid}'") if feature_node_hash is None: feature_node_hash = str(uuid4())[:8] if any([cls.feature_node_hash == feature_node_hash for cls in self.classifications]): raise ValueError(f"Duplicate feature_node_hash '{feature_node_hash}'") cls = Classification(uid, feature_node_hash, list()) self.classifications.append(cls) return cls