#!/usr/bin/env python3
#
# Copyright 2021 Graviti. Licensed under MIT License.
#
"""The implementation of the TensorBay 2D keypoint."""
from typing import Dict, Iterable, Mapping, Optional, Sequence, Type, TypeVar
from tensorbay.geometry.point_list import PointList2D
from tensorbay.geometry.vector import Vector2D
from tensorbay.utility import common_loads
_T = TypeVar("_T", bound=Vector2D)
[docs]class Keypoint2D(Vector2D):
"""This class defines the concept of Keypoint2D.
:class:`Keypoint2D` contains the information of 2D keypoint,
such as the coordinates and visible status(optional).
Arguments:
x: The x coordinate of the 2D keypoint.
y: The y coordinate of the 2D keypoint.
v: The visible status(optional) of the 2D keypoint.
Visible status can be "BINARY" or "TERNARY":
+---------------+-----------+-----------+---------+
| Visual Status | v = 0 | v = 1 | v = 2 |
+===============+===========+===========+=========+
| BINARY | invisible | visible | |
+---------------+-----------+-----------+---------+
| TERNARY | invisible | occluded | visible |
+---------------+-----------+-----------+---------+
Examples:
*Initialization Method 1:* Init from coordinates of x, y.
>>> Keypoint2D(1.0, 2.0)
Keypoint2D(1.0, 2.0)
*Initialization Method 2:* Init from coordinates and visible status.
>>> Keypoint2D(1.0, 2.0, 0)
Keypoint2D(1.0, 2.0, 0)
"""
def __init__( # pylint: disable=super-init-not-called
self, x: float, y: float, v: Optional[int] = None
) -> None:
self._data = (x, y, v) if v is not None else (x, y)
def __neg__(self) -> Vector2D: # type: ignore[override]
result: Vector2D = object.__new__(Vector2D)
result._data = tuple(-coordinate for coordinate in self._data[: self._DIMENSION])
return result
def __add__(self, other: Iterable[float]) -> Vector2D: # type: ignore[override]
# Result of adding Keypoint2D with another sequence should be a Vector2D.
# Add function of Vector2D should also add support for adding with a Keypoint2D.
# Will be implemented in the future.
return NotImplemented
[docs] @classmethod
def loads(cls: Type[_T], contents: Mapping[str, float]) -> _T:
"""Load a :class:`Keypoint2D` from a dict containing coordinates of a 2D keypoint.
Arguments:
contents: A dict containing coordinates and visible status(optional)
of a 2D keypoint.
Returns:
The loaded :class:`Keypoint2D` object.
Examples:
>>> contents = {"x":1.0,"y":2.0,"v":1}
>>> Keypoint2D.loads(contents)
Keypoint2D(1.0, 2.0, 1)
"""
return cls(**contents)
@property
def v(self) -> Optional[int]: # pylint: disable=invalid-name
"""Return the visible status of the 2D keypoint.
Returns:
Visible status of the 2D keypoint.
Examples:
>>> keypoint = Keypoint2D(3.0, 2.0, 1)
>>> keypoint.v
1
"""
if len(self._data) != self._DIMENSION:
return self._data[2] # type: ignore[return-value]
return None
[docs] def dumps(self) -> Dict[str, float]:
"""Dumps the :class:`Keypoint2D` into a dict.
Returns:
A dict containing coordinates and visible status(optional) of the 2D keypoint.
Examples:
>>> keypoint = Keypoint2D(1.0, 2.0, 1)
>>> keypoint.dumps()
{'x': 1.0, 'y': 2.0, 'v': 1}
"""
contents = {"x": self._data[0], "y": self._data[1]}
if len(self._data) != self._DIMENSION:
contents["v"] = self._data[2]
return contents
[docs]class Keypoints2D(PointList2D[Keypoint2D]):
"""This class defines the concept of Keypoints2D.
:class:`Keypoints2D` contains a list of 2D keypoint and is based on
:class:`~tensorbay.geometry.polygon.PointList2D`.
Examples:
>>> Keypoints2D([[1, 2], [2, 3]])
Keypoints2D [
Keypoint2D(1, 2),
Keypoint2D(2, 3)
]
"""
_P = TypeVar("_P", bound="Keypoints2D")
_ElementType = Keypoint2D
[docs] @classmethod
def loads(cls: Type[_P], contents: Sequence[Mapping[str, float]]) -> _P:
"""Load a :class:`Keypoints2D` from a list of dict.
Arguments:
contents: A list of dictionaries containing 2D keypoint.
Returns:
The loaded :class:`Keypoints2D` object.
Examples:
>>> contents = [{"x": 1.0, "y": 1.0, "v": 1}, {"x": 2.0, "y": 2.0, "v": 2}]
>>> Keypoints2D.loads(contents)
Keypoints2D [
Keypoint2D(1.0, 1.0, 1),
Keypoint2D(2.0, 2.0, 2)
]
"""
return common_loads(cls, contents)