Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(label): add "MultiPolyline2DSubcatalog" & "LabeledMultiPolyline2D"
Browse files Browse the repository at this point in the history
zhen.chen committed Jul 13, 2021
1 parent 529b06b commit 76df851
Showing 6 changed files with 259 additions and 43 deletions.
5 changes: 1 addition & 4 deletions tensorbay/geometry/polygon.py
Original file line number Diff line number Diff line change
@@ -132,9 +132,6 @@ def __init__(self, point_lists: Optional[Iterable[Iterable[Iterable[float]]]] =
def _loads(self: _P, contents: List[List[Dict[str, float]]]) -> None:
self._data = [self._ElementType.loads(point_list) for point_list in contents]

def _dumps(self) -> List[List[Dict[str, float]]]:
return [point_list.dumps() for point_list in self._data]

@classmethod
def loads(cls: Type[_P], contents: List[List[Dict[str, float]]]) -> _P:
"""Loads a :class:`MultiPointList2D` from the given contents.
@@ -167,7 +164,7 @@ def dumps(self) -> List[List[Dict[str, float]]]:
All the information of the :class:`MultiPointList2D`.
"""
return self._dumps()
return [point_list.dumps() for point_list in self._data]

def bounds(self) -> Box2D:
"""Calculate the bounds of multiple point lists.
17 changes: 0 additions & 17 deletions tensorbay/geometry/polyline.py
Original file line number Diff line number Diff line change
@@ -256,20 +256,3 @@ def loads(cls: Type[_P], contents: List[List[Dict[str, float]]]) -> _P:
"""
return common_loads(cls, contents)

def dumps(self) -> List[List[Dict[str, float]]]:
"""Dumps a :class:`MultiPolyline2D` into a polyline list.
Returns:
All the information of the :class:`MultiPolyline2D`.
Examples:
>>> multipolyline = MultiPolyline2D([[[1, 1], [1, 2], [2, 2]], [[2, 3], [3, 5]]])
>>> multipolyline.dumps()
[
[{'x': 1, 'y': 1}, {'x': 1, 'y': 2}, {'x': 2, 'y': 2}],
[{'x': 2, 'y': 3}, {'x': 3, 'y': 5}
]
"""
return self._dumps()
13 changes: 10 additions & 3 deletions tensorbay/label/__init__.py
Original file line number Diff line number Diff line change
@@ -13,7 +13,12 @@
from .label_classification import Classification, ClassificationSubcatalog
from .label_keypoints import Keypoints2DSubcatalog, LabeledKeypoints2D
from .label_polygon import LabeledPolygon2D, Polygon2DSubcatalog
from .label_polyline import LabeledPolyline2D, Polyline2DSubcatalog
from .label_polyline import (
LabeledMultiPolyline2D,
LabeledPolyline2D,
MultiPolyline2DSubcatalog,
Polyline2DSubcatalog,
)
from .label_sentence import LabeledSentence, SentenceSubcatalog, Word
from .supports import CategoryInfo, KeypointsInfo

@@ -25,17 +30,19 @@
"CategoryInfo",
"Classification",
"ClassificationSubcatalog",
"KeypointsInfo",
"Items",
"Keypoints2DSubcatalog",
"KeypointsInfo",
"Label",
"LabelType",
"LabeledBox2D",
"LabeledBox3D",
"LabeledKeypoints2D",
"LabeledMultiPolyline2D",
"LabeledPolygon2D",
"LabeledPolyline2D",
"LabeledSentence",
"Items",
"MultiPolyline2DSubcatalog",
"Polygon2DSubcatalog",
"Polyline2DSubcatalog",
"SentenceSubcatalog",
1 change: 1 addition & 0 deletions tensorbay/label/basic.py
Original file line number Diff line number Diff line change
@@ -42,6 +42,7 @@ class LabelType(TypeEnum):
BOX3D = "box3d"
POLYGON2D = "polygon2d"
POLYLINE2D = "polyline2d"
MULTI_POLYLINE2D = "multi_polyline2d"
KEYPOINTS2D = "keypoints2d"
SENTENCE = "sentence"

180 changes: 179 additions & 1 deletion tensorbay/label/label_polyline.py
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@

from typing import Any, Dict, Iterable, Optional, Type, TypeVar

from ..geometry import Polyline2D
from ..geometry import MultiPolyline2D, Polyline2D
from ..utility import ReprType, SubcatalogTypeRegister, TypeRegister, attr_base, common_loads
from .basic import LabelType, SubcatalogBase, _LabelBase
from .supports import AttributesMixin, CategoriesMixin, IsTrackingMixin
@@ -194,3 +194,181 @@ def dumps(self) -> Dict[str, Any]: # type: ignore[override]
"""
return self._dumps()


@SubcatalogTypeRegister(LabelType.MULTI_POLYLINE2D)
class MultiPolyline2DSubcatalog( # pylint: disable=too-many-ancestors
SubcatalogBase, IsTrackingMixin, CategoriesMixin, AttributesMixin
):
"""This class defines the subcatalog for 2D multiple polyline type of labels.
Arguments:
is_tracking: A boolean value indicates whether the corresponding
subcatalog contains tracking information.
Attributes:
description: The description of the entire 2D multiple polyline subcatalog.
categories: All the possible categories in the corresponding dataset
stored in a :class:`~tensorbay.utility.name.NameList`
with the category names as keys
and the :class:`~tensorbay.label.supports.CategoryInfo` as values.
category_delimiter: The delimiter in category values indicating parent-child relationship.
attributes: All the possible attributes in the corresponding dataset
stored in a :class:`~tensorbay.utility.name.NameList`
with the attribute names as keys
and the :class:`~tensorbay.label.attribute.AttributeInfo` as values.
is_tracking: Whether the Subcatalog contains tracking information.
Examples:
*Initialization Method 1:* Init from ``MultiPolyline2DSubcatalog.loads()`` method.
>>> catalog = {
... "MULTI_POLYLINE2D": {
... "isTracking": True,
... "categories": [{"name": "0"}, {"name": "1"}],
... "attributes": [{"name": "gender", "enum": ["male", "female"]}],
... }
... }
>>> MultiPolyline2DSubcatalog.loads(catalog["MULTI_POLYLINE2D"])
MultiPolyline2DSubcatalog(
(is_tracking): True,
(categories): NameList [...],
(attributes): NameList [...]
)
*Initialization Method 2:* Init an empty MultiPolyline2DSubcatalog
and then add the attributes.
>>> from tensorbay.label import CategoryInfo, AttributeInfo
>>> from tensorbay.utility import NameList
>>> categories = NameList()
>>> categories.append(CategoryInfo("a"))
>>> attributes = NameList()
>>> attributes.append(AttributeInfo("gender", enum=["female", "male"]))
>>> multi_polyline2d_subcatalog = MultiPolyline2DSubcatalog()
>>> multi_polyline2d_subcatalog.is_tracking = True
>>> multi_polyline2d_subcatalog.categories = categories
>>> multi_polyline2d_subcatalog.attributes = attributes
>>> multi_polyline2d_subcatalog
MultiPolyline2DSubcatalog(
(is_tracking): True,
(categories): NameList [...],
(attributes): NameList [...]
)
"""

def __init__(self, is_tracking: bool = False) -> None:
SubcatalogBase.__init__(self)
IsTrackingMixin.__init__(self, is_tracking)


@TypeRegister(LabelType.MULTI_POLYLINE2D)
class LabeledMultiPolyline2D(_LabelBase, MultiPolyline2D): # pylint: disable=too-many-ancestors
"""This class defines the concept of multiPolyline2D label.
:class:`LabeledMultiPolyline2D` is the 2D multiple polyline type of label,
which is often used for CV tasks such as lane detection.
Arguments:
polylines: A list of polylines.
category: The category of the label.
attributes: The attributes of the label.
instance: The instance id of the label.
Attributes:
category: The category of the label.
attributes: The attributes of the label.
instance: The instance id of the label.
Examples:
>>> LabeledMultiPolyline2D(
... [[[1, 2], [2, 3]], [[3, 4], [6, 8]]],
... category="example",
... attributes={"key": "value"},
... instance="123",
... )
LabeledPolyline2D [
Polyline2D [...]
Polyline2D [...]
](
(category): 'example',
(attributes): {...},
(instance): '123'
)
"""

_T = TypeVar("_T", bound="LabeledMultiPolyline2D")

_repr_type = ReprType.SEQUENCE
_repr_attrs = _LabelBase._repr_attrs
_attrs_base: MultiPolyline2D = attr_base(key="multiPolyline2d")

def __init__(
self,
polylines: Optional[Iterable[Iterable[float]]] = None,
*,
category: Optional[str] = None,
attributes: Optional[Dict[str, Any]] = None,
instance: Optional[str] = None,
):
MultiPolyline2D.__init__(self, polylines) # type: ignore[arg-type]
_LabelBase.__init__(self, category, attributes, instance)

@classmethod
def loads(cls: Type[_T], contents: Dict[str, Any]) -> _T: # type: ignore[override]
"""Loads a LabeledMultiPolyline2D from a dict containing the information of the label.
Arguments:
contents: A dict containing the information of the 2D polyline label.
Returns:
The loaded :class:`LabeledMultiPolyline2D` object.
Examples:
>>> contents = {
... "multiPolyline2d": [[{'x': 1, 'y': 1}, {'x': 1, 'y': 2}, {'x': 2, 'y': 2}],
[{'x': 2, 'y': 3}, {'x': 3, 'y': 5}]],
... "category": "example",
... "attributes": {"key": "value"},
... "instance": "12345",
... }
>>> LabeledMultiPolyline2D.loads(contents)
LabeledPolyline2D [
Polyline2D [...]
Polyline2D [...]
](
(category): 'example',
(attributes): {...},
(instance): '12345'
)
"""
return common_loads(cls, contents)

def dumps(self) -> Dict[str, Any]: # type: ignore[override]
"""Dumps the current 2D multiple polyline label into a dict.
Returns:
A dict containing all the information of the 2D polyline label.
Examples:
>>> labeledmultipolyline2d = LabeledMultiPolyline2D(
... [[[1, 1], [1, 2], [2, 2]], [[2, 3], [3, 5]]],
... category="example",
... attributes={"key": "value"},
... instance="123",
... )
>>> labeledpolyline2d.dumps()
{
'category': 'example',
'attributes': {'key': 'value'},
'instance': '123',
'polyline2d': [
[{'x': 1, 'y': 1}, {'x': 1, 'y': 2}, {'x': 2, 'y': 2}],
[{'x': 2, 'y': 3}, {'x': 3, 'y': 5}],
}
"""
return self._dumps()
86 changes: 68 additions & 18 deletions tensorbay/label/tests/test_label_polyline.py
Original file line number Diff line number Diff line change
@@ -4,15 +4,29 @@

import pytest

from ...geometry import Vector2D
from .. import LabeledPolyline2D, Polyline2DSubcatalog

_LABELEDPOLYLINE2D_DATA = {
"polyline2d": [{"x": 1, "y": 2}],
from ...geometry import Polyline2D, Vector2D
from .. import (
LabeledMultiPolyline2D,
LabeledPolyline2D,
MultiPolyline2DSubcatalog,
Polyline2DSubcatalog,
)

_CONTENTS = {
"category": "cat",
"attributes": {"gender": "male"},
"instance": "12345",
}
_POLYLINE = {"polyline2d": [{"x": 1, "y": 2}]}
_MULTI_POLYLINE = {
"multiPolyline2d": [
[{"x": 1, "y": 1}, {"x": 1, "y": 2}, {"x": 2, "y": 2}],
[{"x": 2, "y": 3}, {"x": 3, "y": 5}],
]
}

_LABELED_POLYLINE2D_CONTENTS = {**_CONTENTS, **_POLYLINE}
_LABELED_MULTI_POLYLINE2D_CONTENTS = {**_CONTENTS, **_MULTI_POLYLINE}


@pytest.fixture
@@ -50,13 +64,7 @@ def test_eq(self):
assert polyline1 != polyline3

def test_loads(self):
contents = {
"polyline2d": [{"x": 1, "y": 2}],
"category": "cat",
"attributes": {"gender": "male"},
"instance": "12345",
}
labeledpolygonline2d = LabeledPolyline2D.loads(contents)
labeledpolygonline2d = LabeledPolyline2D.loads(_LABELED_POLYLINE2D_CONTENTS)

assert labeledpolygonline2d[0] == Vector2D(1, 2)
assert labeledpolygonline2d.category == "cat"
@@ -68,12 +76,7 @@ def test_dumps(self):
[(1, 2)], category="cat", attributes={"gender": "male"}, instance="12345"
)

assert labeledpolygonline2d.dumps() == {
"polyline2d": [{"x": 1, "y": 2}],
"category": "cat",
"attributes": {"gender": "male"},
"instance": "12345",
}
assert labeledpolygonline2d.dumps() == _LABELED_POLYLINE2D_CONTENTS


class TestPolyline2DSubcatalog:
@@ -117,3 +120,50 @@ def test_dumps(self, categories, attributes, subcatalog_polyline):
del subcatalog_polyline["isTracking"]

assert subcatalog.dumps() == subcatalog_polyline


class TestLabeledMultiPolyline2D:
def test_init(self):
labeledmultipolyline2d = LabeledMultiPolyline2D(
[[[1, 1], [1, 2], [2, 2]], [[2, 3], [3, 5]]],
category="cat",
attributes={"gender": "male"},
instance="12345",
)

assert labeledmultipolyline2d[0] == Polyline2D([[1, 1], [1, 2], [2, 2]])
assert labeledmultipolyline2d.category == "cat"
assert labeledmultipolyline2d.attributes == {"gender": "male"}
assert labeledmultipolyline2d.instance == "12345"

def test_eq(self):
multipolyline1 = LabeledMultiPolyline2D(
[[[1, 1], [1, 2]], [[2, 3], [3, 5]]], category="cat", attributes={"gender": "male"}
)
multipolyline2 = LabeledMultiPolyline2D(
[[[1, 1], [1, 2]], [[2, 3], [3, 5]]], category="cat", attributes={"gender": "male"}
)
multipolyline3 = LabeledMultiPolyline2D(
[[[1, 1], [1, 2]], [[2, 3], [4, 5]]], category="dog", attributes={"gender": "male"}
)

assert multipolyline1 == multipolyline2
assert multipolyline1 != multipolyline3

def test_loads(self):
labeledpolygonline2d = LabeledMultiPolyline2D.loads(_LABELED_MULTI_POLYLINE2D_CONTENTS)

assert labeledpolygonline2d[0] == Polyline2D([[1, 1], [1, 2], [2, 2]])
assert labeledpolygonline2d.category == "cat"
assert labeledpolygonline2d.attributes == {"gender": "male"}
assert labeledpolygonline2d.instance == "12345"

def test_dumps(self):
labeledpolygonline2d = LabeledMultiPolyline2D(
[[[1, 1], [1, 2], [2, 2]], [[2, 3], [3, 5]]],
category="cat",
attributes={"gender": "male"},
instance="12345",
)

assert labeledpolygonline2d.dumps() == _LABELED_MULTI_POLYLINE2D_CONTENTS

0 comments on commit 76df851

Please sign in to comment.