# coding: utf-8
"""Tag can be attached to whole image and/or to individual :class:`Label<supervisely.annotation.label.Label>`"""
# docs
from __future__ import annotations
from typing import List, Optional, Dict, Union
from supervisely.annotation.tag_meta import TagMeta
from supervisely.annotation.tag_meta_collection import TagMetaCollection
from supervisely.annotation.tag_meta import TagValueType
from supervisely.collection.key_indexed_collection import KeyObject
from supervisely._utils import take_with_default
[docs]class TagJsonFields:
"""Json fields for :class:`Annotation<supervisely.annotation.tag.Tag>`"""
TAG_NAME = "name"
""""""
VALUE = "value"
""""""
LABELER_LOGIN = "labelerLogin"
""""""
UPDATED_AT = "updatedAt"
""""""
CREATED_AT = "createdAt"
""""""
ID = "id"
""""""
# TAG_META_ID = 'tagId'
# """"""
[docs]class Tag(KeyObject):
"""
:class:`Tag<Tag>` can be attached to whole image and/or to individual :class:`Label<LabelBase>`. :class:`Tag<Tag>` object is immutable.
:param meta: General information about Tag.
:type meta: TagMeta
:param value: Tag value. Depends on :class:`TagValueType<TagValueType>` of :class:`TagMeta<TagMeta>`.
:type value: Optional[Union[str, int, float]]
:param sly_id: Tag ID in Supervisely server.
:type sly_id: int, optional
:param labeler_login: Login of user who created Tag.
:type labeler_login: str, optional
:param updated_at: Date and Time when Tag was modified last. Date Format: Year:Month:Day:Hour:Minute:Seconds. Example: '2021-01-22T19:37:50.158Z'.
:type updated_at: str, optional
:param created_at: Date and Time when Tag was created. Date Format is the same as in "updated_at" parameter.
:type created_at: str, optional
:raises: :class:`ValueError`, if meta is None or if Tag has incompatible value against it's meta value type
:Usage example:
.. code-block:: python
import supervisely as sly
# Let's create 3 Tags with different values
# First we need to initialize a TagMeta
meta_dog = sly.TagMeta('dog', sly.TagValueType.NONE)
# Now we can create a Tag using our TagMeta
tag_dog = sly.Tag(meta_dog)
# When you are creating a new Tag
# Tag.value is automatically cross-checked against your TagMeta value type to make sure the value is valid.
# If we now try to add a value to our newly created Tag, we receive "ValueError", because our TagMeta value type is "NONE"
tag_dog = sly.Tag(meta_dog, value="Husky")
# Output: ValueError: Tag dog can not have value Husky
# Let's create another Tag with a string value type
meta_cat = sly.TagMeta('cat', sly.TagValueType.ANY_STRING)
tag_cat = sly.Tag(meta_cat, value="Fluffy")
# Now let's create a Tag using TagMeta with "ONEOF_STRING" value type
# In order to use "oneof_string value type", you must initialize a variable with possible values(see class TagMeta for more information)
colors = ["brown", "white", "black", "red", "chocolate", "gold", "grey"]
meta_coat_color = sly.TagMeta('coat color', sly.TagValueType.ONEOF_STRING, possible_values=colors)
tag_coat_color = sly.Tag(meta_coat_color, value="white")
# If given value is not in a list of possible Tags, ValueError will be raised
tag_coat_color = sly.Tag(meta_coat_color, value="yellow")
# Output: ValueError: Tag coat color can not have value yellow
"""
def __init__(
self,
meta: TagMeta,
value: Optional[Union[str, int, float]] = None,
sly_id: Optional[int] = None,
labeler_login: Optional[str] = None,
updated_at: Optional[str] = None,
created_at: Optional[str] = None,
):
if meta is None:
raise ValueError("TagMeta is None")
self._meta = meta
self._value = value
if not self._meta.is_valid_value(value):
raise ValueError("Tag {} can not have value {}".format(self.meta.name, value))
self.labeler_login = labeler_login
self.updated_at = updated_at
self.created_at = created_at
self.sly_id = sly_id
@property
def meta(self) -> TagMeta:
"""
General information about Tag. When creating a new Tag, it's value is automatically cross-checked against :class:`TagValueType<supervisely.annotation.tag_meta.TagValueType>` to make sure that value is valid.
:return: TagMeta object
:rtype: :class:`TagMeta<supervisely.annotation.tag_meta.TagMeta>`
:Usage example:
.. code-block:: python
meta_dog = sly.TagMeta('dog', sly.TagValueType.NONE)
tag_dog = sly.Tag(meta_dog)
# Our TagMeta has value type 'NONE', if we try to add value to our Tag, "ValueError" error will be raised
tag_dog = sly.Tag(meta_dog, value="Husky")
# Output: ValueError: Tag dog can not have value Husky
"""
return self._meta
@property
def value(self) -> str or int or float:
"""
Tag value. Return type depends on :class:`TagValueType<supervisely.annotation.tag_meta.TagValueType>`.
:return: Tag value
:rtype: :class:`str`, :class:`int` or :class:`float` or :class:`None`
:Usage example:
.. code-block:: python
meta_dog = sly.TagMeta('dog', sly.TagValueType.ANY_STRING)
tag_dog = sly.Tag(meta_dog, value="Husky")
meta_age = sly.TagMeta('age', sly.TagValueType.ANY_NUMBER)
tag_age = sly.Tag(meta_age, value=9)
colors = ["Black", "White", "Golden", "Brown"]
meta_color = sly.TagMeta('coat color', sly.TagValueType.ONEOF_STRING, possible_values=colors)
tag_color = sly.Tag(meta_color, value="White")
type(tag_dog.value) # 'str'
type(tag_age.value) # 'int'
type(tag_color.value) # 'str'
"""
return self._value
@property
def name(self) -> str:
"""
Name property.
:return: Name
:rtype: :class:`str`
:Usage example:
.. code-block:: python
meta_dog = sly.TagMeta('dog', sly.TagValueType.ANY_STRING)
tag_dog = sly.Tag(meta_dog, value="Husky")
print(tag_dog.name)
# Output: "dog"
"""
return self._meta.name
[docs] def key(self):
"""
Get TagMeta key value.
:return: TagMeta key value
:rtype: str
:Usage example:
.. code-block:: python
import supervisely as sly
weather_conditions = ["Sunny", "Cloudy", "Snowy", "Foggy", "Rainy"]
meta_weather = sly.TagMeta("weather", sly.TagValueType.ONEOF_STRING, possible_values=weather_conditions)
tag_weather = sly.Tag(meta_weather, value="Sunny")
key = tag_weather.key()
print(key)
# Output: weather
"""
return self._meta.key()
[docs] def to_json(self) -> Dict:
"""
Convert the Tag 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
# Tag with all fields filled in
meta_dog = sly.TagMeta('dog', sly.TagValueType.ANY_STRING)
tag_dog = sly.Tag(meta=meta_dog, value="Husky", sly_id=38456, labeler_login="admin",
updated_at="2021-01-22T19:37:50.158Z", created_at="2021-01-22T18:00:00.000Z")
tag_dog_json = tag_dog.to_json()
print(tag_dog_json)
# Notice that sly_id won't print
# Output: {
# "name": "dog",
# "value": "Husky",
# "labelerLogin": "admin",
# "updatedAt": "2021-01-22T19:37:50.158Z",
# "createdAt": "2021-01-22T18:00:00.000Z"
# }
"""
res = {
TagJsonFields.TAG_NAME: self.meta.name
}
if self.meta.value_type != TagValueType.NONE:
res[TagJsonFields.VALUE] = self.value
if self.labeler_login is not None:
res[TagJsonFields.LABELER_LOGIN] = self.labeler_login
if self.updated_at is not None:
res[TagJsonFields.UPDATED_AT] = self.updated_at
if self.created_at is not None:
res[TagJsonFields.CREATED_AT] = self.created_at
return res
[docs] @classmethod
def from_json(cls, data: Dict, tag_meta_collection: TagMetaCollection) -> Tag:
"""
Convert a json dict to Tag. Read more about `Supervisely format <https://docs.supervise.ly/data-organization/00_ann_format_navi>`_.
:param data: Tag in json format as a dict.
:type data: dict
:param tag_meta_collection: TagMetaCollection object.
:type tag_meta_collection: TagMetaCollection
:return: Tag object
:rtype: :class:`Tag<Tag>`
:Usage example:
.. code-block:: python
import supervisely as sly
meta_dog = sly.TagMeta('dog', sly.TagValueType.ANY_STRING)
tag_metas = sly.TagMetaCollection([meta_dog])
data = {
"name": "dog",
"value": "Husky",
"labelerLogin": "admin",
"updatedAt": "2021-01-22T19:37:50.158Z",
"createdAt": "2021-01-22T18:00:00.000Z"
}
tag_dog = sly.Tag.from_json(data, tag_metas)
"""
if type(data) is str:
tag_name = data
value = None
labeler_login = None
updated_at = None
created_at = None
sly_id = None
else:
tag_name = data[TagJsonFields.TAG_NAME]
value = data.get(TagJsonFields.VALUE, None)
labeler_login = data.get(TagJsonFields.LABELER_LOGIN, None)
updated_at = data.get(TagJsonFields.UPDATED_AT, None)
created_at = data.get(TagJsonFields.CREATED_AT, None)
sly_id = data.get(TagJsonFields.ID, None)
meta = tag_meta_collection.get(tag_name)
return cls(
meta=meta,
value=value,
sly_id=sly_id,
labeler_login=labeler_login,
updated_at=updated_at,
created_at=created_at,
)
[docs] def get_compact_str(self) -> str:
"""
Displays information about Tag's name and value in string format.
:return: Name and value of the given Tag
:rtype: :class:`str`
:Usage example:
.. code-block:: python
import supervisely as sly
meta_dog = sly.TagMeta('dog', sly.TagValueType.ANY_STRING)
tag_dog = sly.Tag(meta_dog, value="Husky")
print(tag_dog.get_compact_str())
# Output: 'dog:Husky'
"""
if (self.meta.value_type != TagValueType.NONE) and (len(str(self.value)) > 0):
return "{}:{}".format(self.name, self.value)
return self.name
def __eq__(self, other: Tag) -> bool:
"""
Checks that 2 Tags are equal by comparing their meta and value.
:param other: Tag object.
:type other: Tag
: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 Tags
meta_lemon_1 = sly.TagMeta('Lemon', sly.TagValueType.NONE)
tag_lemon_1 = sly.Tag(meta_lemon_1)
meta_lemon_2 = sly.TagMeta('Lemon', sly.TagValueType.NONE)
tag_lemon_2 = sly.Tag(meta_lemon_2)
# and 1 different Tag to compare them
meta_cucumber = sly.TagMeta('Cucumber', sly.TagValueType.ANY_STRING)
tag_cucumber = sly.Tag(meta_cucumber, value="Fresh")
# Compare identical Tags
tag_lemon_1 == tag_lemon_2 # True
# Compare unidentical Tags
tag_lemon_1 == tag_cucumber # False
"""
return isinstance(other, Tag) and self.meta == other.meta and self.value == other.value
def __ne__(self, other: Tag) -> bool:
"""
Checks that 2 Tags are opposite.
:param other: Tag object.
:type other: Tag
: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 Tags
meta_lemon_1 = sly.TagMeta('Lemon', sly.TagValueType.NONE)
tag_lemon_1 = sly.Tag(meta_lemon_1)
meta_lemon_2 = sly.TagMeta('Lemon', sly.TagValueType.NONE)
tag_lemon_2 = sly.Tag(meta_lemon_2)
# and 1 different Tag to compare them
meta_cucumber = sly.TagMeta('Cucumber', sly.TagValueType.ANY_STRING)
tag_cucumber = sly.Tag(meta_cucumber, value="Fresh")
# Compare identical Tags
tag_lemon_1 != tag_lemon_2 # False
# Compare unidentical Tags
tag_lemon_1 != tag_cucumber # True
"""
return not self == other
[docs] def clone(
self,
meta: Optional[TagMeta] = None,
value: Optional[Union[str, int, float]] = None,
sly_id: Optional[int] = None,
labeler_login: Optional[str] = None,
updated_at: Optional[str] = None,
created_at: Optional[str] = None,
) -> Tag:
"""
Clone makes a copy of Tag with new fields, if fields are given, otherwise it will use original Tag fields.
:param meta: General information about Tag.
:type meta: TagMeta
:type value: str or int or float or None
:param sly_id: Tag ID in Supervisely server.
:type sly_id: int, optional
:param labeler_login: Login of user who created Tag.
:type labeler_login: str, optional
:param updated_at: Date and Time when Tag was modified last. Date Format: Year:Month:Day:Hour:Minute:Seconds. Example: '2021-01-22T19:37:50.158Z'.
:type updated_at: str, optional
:param created_at: Date and Time when Tag was created. Date Format is the same as in "updated_at" parameter.
:type created_at: str, optional
:return: New instance of Tag
:rtype: :class:`Tag<Tag>`
:Usage Example:
.. code-block:: python
import supervisely as sly
#Original Tag
weather_conditions = ["Sunny", "Cloudy", "Snowy", "Foggy", "Rainy"]
meta_weather = sly.TagMeta("weather", sly.TagValueType.ONEOF_STRING, possible_values=weather_conditions)
tag_weather = sly.Tag(meta_weather, value="Sunny")
# Let's create some more tags by cloning our original Tag
# Remember that Tag class object is immutable, and we need to assign new instance of Tag to a new variable
clone_weather_1 = tag_weather.clone(value="Snowy")
clone_weather_2 = tag_weather.clone(value="Cloudy")
clone_weather_3 = tag_weather.clone(value="Rainy")
"""
return Tag(
meta=take_with_default(meta, self.meta),
value=take_with_default(value, self.value),
sly_id=take_with_default(sly_id, self.sly_id),
labeler_login=take_with_default(labeler_login, self.labeler_login),
updated_at=take_with_default(updated_at, self.updated_at),
created_at=take_with_default(created_at, self.created_at),
)
def __str__(self):
return "{:<7s}{:<10}{:<7s} {:<13}{:<7s} {:<10}".format(
"Name:",
self._meta.name,
"Value type:",
self._meta.value_type,
"Value:",
str(self.value),
)
[docs] def get_row_ptable(self):
"""
Get row with tag properties.
:return: List of tag properties.
:rtype: List[str]
:Usage example:
.. code-block:: python
import supervisely as sly
weather_conditions = ["Sunny", "Cloudy", "Snowy", "Foggy", "Rainy"]
meta_weather = sly.TagMeta("weather", sly.TagValueType.ONEOF_STRING, possible_values=weather_conditions)
tag_weather = sly.Tag(meta_weather, value="Sunny")
row = tag_weather.get_row_ptable()
print(row)
# Output: ['weather', 'oneof_string', 'Sunny']
"""
return [self._meta.name, self._meta.value_type, self.value]
def _set_id(self, id: int):
self.sly_id = id
def _set_updated_at(self, updated_at: str):
self.updated_at = updated_at
def _set_created_at(self, created_at: str):
self.created_at = created_at
def _set_labeler_login(self, labeler_login: str):
self.labeler_login = labeler_login