Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Loading and saving redo #55

Merged
merged 67 commits into from
Nov 14, 2024
Merged
Changes from 1 commit
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
fee3aff
refactor: delete everything related to fromdict and asdict
tklockau Nov 4, 2024
fb36164
refactor: extract all parent class information from Bbox
tklockau Nov 11, 2024
d751da8
feat: implement Bbox.from_json()
tklockau Nov 11, 2024
1d8431b
feat: implement Bbox.name()
tklockau Nov 11, 2024
dc9146e
feat: implement Cuboid.from_json()
tklockau Nov 11, 2024
e27755b
feat: implement Cuboid.name()
tklockau Nov 11, 2024
a2d6e24
feat: implement Poly2d.from_json()
tklockau Nov 11, 2024
94d4565
feat: implement Poly2d.name()
tklockau Nov 11, 2024
031b819
feat: implement Poly3d.from_json()
tklockau Nov 11, 2024
04f68ac
feat: implement Poly3d.name()
tklockau Nov 11, 2024
87d99f1
feat: implement Seg3d.from_json()
tklockau Nov 11, 2024
4e7b08a
feat: implement Seg3d.name()
tklockau Nov 11, 2024
06c5b77
feat: implement Num.from_dict()
tklockau Nov 11, 2024
cb5afee
feat: implement StreamReference.from_dict()
tklockau Nov 11, 2024
691304e
feat: implement Frame.from_json()
tklockau Nov 11, 2024
e7b4201
refactor: remove unused parent class
Nov 11, 2024
dd864a4
refactor: remove legacy files
Nov 11, 2024
7f3cc6d
feat: implement Object.from_json()
Nov 11, 2024
33d3f7e
feat: implement Camera.from_json()
Nov 11, 2024
995a110
refactor: remove sensor class
Nov 11, 2024
f1fd41e
feat: implement Radar.from_json()
Nov 11, 2024
f0362c9
feat: implement Lidar.from_json()
Nov 11, 2024
6885445
feat: implement GpsImu.from_json()
Nov 11, 2024
a3a9da0
feat: implement OtherSensor.from_json()
Nov 11, 2024
838b7f4
feat: implement Metadata.from_json()
Nov 12, 2024
3f51b48
feat: implement additional parameters in Metadata
Nov 12, 2024
1f4b995
fix: remove deprecated pydantic expression
Nov 12, 2024
6bccf44
feat: implement Scene.from_json() for sensors
Nov 12, 2024
c7dc145
fix: typing for python 3.8 and 3.9
Nov 13, 2024
9c8b7ab
fix: union types in python 3.8 and 3.9 with eval-type-backport
Nov 13, 2024
50b5b00
fix: dependency definition of eval-type-backport
Nov 13, 2024
0fa89ec
refactor: remove unused dependencies
Nov 13, 2024
68b463c
test: extract object uids into separate fixtures
Nov 13, 2024
f63f2ba
feat: implement Scene.from_json() for objects
Nov 13, 2024
64f00ea
feat: implement Scene.from_json() for frames
Nov 13, 2024
db19b75
feat: implement raillabel.load()
Nov 13, 2024
956d9d2
feat: implement FrameInterval.to_json()
Nov 13, 2024
b00df0c
feat: implement Point2d.to_json()
Nov 13, 2024
078d7a9
feat: implement Point3d.to_json()
Nov 13, 2024
97c4d26
feat: implement Size2d.to_json()
Nov 13, 2024
3caefff
feat: implement Size3d.to_json()
Nov 13, 2024
c9214e4
feat: implement Quaternion.to_json()
Nov 13, 2024
738aa43
feat: implement Transform.to_json()
Nov 13, 2024
04b1044
feat: implement _attributes_to_json()
Nov 13, 2024
8e48908
refactor: rename Transform fields
Nov 13, 2024
b1ebe26
feat: implement Bbox.to_json()
Nov 13, 2024
f6bd567
refactor: rename object field to object_uid in Annotation classes
Nov 13, 2024
2fd4a60
refactor: rename all *_uid fields to *_id
Nov 13, 2024
ef1fac6
docs: adapt annotation class object_id description
Nov 13, 2024
1ba0221
refactor: rename sensor field to sensor_id in Annotation classes
Nov 13, 2024
7397672
feat: implement Cuboid.to_json()
Nov 14, 2024
342f627
feat: implement Poly2d.to_json()
Nov 14, 2024
6d354b5
feat: implement Poly3d.to_json()
Nov 14, 2024
6db3bed
feat: implement Seg3d.to_json()
Nov 14, 2024
43db3fc
feat: implement Num.to_json()
Nov 14, 2024
5e6e4c7
feat: implement SensorReference.to_json()
Nov 14, 2024
d1bd3b9
fix: extra fields allowed in json format classes
Nov 14, 2024
07428e1
feat: implement Frame.to_json()
Nov 14, 2024
7d8c225
feat: implement Camera.to_json()
Nov 14, 2024
3807d93
feat: implement Radar.to_json()
Nov 14, 2024
e1c6121
feat: implement Lidar.to_json()
Nov 14, 2024
0060945
feat: implement GpsImu.to_json()
Nov 14, 2024
43d91f9
feat: implement OtherSensor.to_json()
Nov 14, 2024
69e08a8
feat: implement Object.to_json()
Nov 14, 2024
e404739
feat: implement Scene.to_json()
Nov 14, 2024
342bf96
refactor: create parent class for all json format classes
Nov 14, 2024
737a97b
feat: implement raillabel.save()
Nov 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: implement Frame.from_json()
  • Loading branch information
tklockau committed Nov 11, 2024
commit 691304ecfba2416c0d1c8ad24f69fd8650eee74c
138 changes: 95 additions & 43 deletions raillabel/format/frame.py
Original file line number Diff line number Diff line change
@@ -3,61 +3,113 @@

from __future__ import annotations

import decimal
from dataclasses import dataclass, field
from decimal import Decimal
from uuid import UUID

from ._object_annotation import _ObjectAnnotation
from raillabel.json_format import JSONFrame, JSONFrameProperties, JSONObjectData

from .bbox import Bbox
from .cuboid import Cuboid
from .num import Num
from .poly2d import Poly2d
from .poly3d import Poly3d
from .seg3d import Seg3d
from .sensor_reference import SensorReference


@dataclass
class Frame:
"""A container of dynamic, timewise, information.

Parameters
----------
timestamp: decimal.Decimal, optional
Timestamp containing the Unix epoch time of the frame with up to nanosecond precision.
sensors: dict of raillabel.format.SensorReference, optional
References to the sensors with frame specific information like timestamp and uri.
Default is {}.
frame_data: dict, optional
Dictionary containing data directly connected to the frame and not to any object, like
gps/imu data. Dictionary keys are the ID-strings of the variable the data belongs to.
Default is {}.
annotations: dict[str, _ObjectAnnotation subclass], optional
Dictionary containing all annotations of this frame. Keys are annotation uids.

Read-Only Attributes
--------------------
object_data: dict[str, dict[str, _ObjectAnnotation subclass]]
Annotations categorized by object. Keys are object uids and values are the annotations
as a dict, that are part of the object.

"""

timestamp: decimal.Decimal | None = None
"""A container of dynamic, timewise, information."""

timestamp: Decimal | None = None
"Timestamp containing the Unix epoch time of the frame with up to nanosecond precision."

sensors: dict[str, SensorReference] = field(default_factory=dict)
"References to the sensors with frame specific information like timestamp and uri."

frame_data: dict[str, Num] = field(default_factory=dict)
annotations: dict[str, type[_ObjectAnnotation]] = field(default_factory=dict)
"""Dictionary containing data directly connected to the frame and not to any object, like
gps/imu data. Dictionary keys are the ID-strings of the variable the data belongs to."""

annotations: dict[UUID, Bbox | Cuboid | Poly2d | Poly3d | Seg3d] = field(default_factory=dict)
"All annotations of this frame."

@classmethod
def from_json(cls, json: JSONFrame) -> Frame:
"""Construct an instant of this class from RailLabel JSON data."""
return Frame(
timestamp=_timestamp_from_dict(json.frame_properties),
sensors=_sensors_from_dict(json.frame_properties),
frame_data=_frame_data_from_dict(json.frame_properties),
annotations=_annotations_from_json(json.objects),
)


def _timestamp_from_dict(frame_properties: JSONFrameProperties | None) -> Decimal | None:
if frame_properties is None:
return None

if frame_properties.timestamp is None:
return None

return Decimal(frame_properties.timestamp)


def _sensors_from_dict(frame_properties: JSONFrameProperties | None) -> dict[str, SensorReference]:
if frame_properties is None:
return {}

if frame_properties.streams is None:
return {}

return {
sensor_id: SensorReference.from_json(sensor_ref)
for sensor_id, sensor_ref in frame_properties.streams.items()
}


def _frame_data_from_dict(frame_properties: JSONFrameProperties | None) -> dict[str, Num]:
if frame_properties is None:
return {}

if frame_properties.frame_data is None:
return {}

if frame_properties.frame_data.num is None:
return {}

return {num.name: Num.from_json(num) for num in frame_properties.frame_data.num}


def _annotations_from_json(
json_object_data: dict[UUID, JSONObjectData] | None,
) -> dict[UUID, Bbox | Cuboid | Poly2d | Poly3d | Seg3d]:
if json_object_data is None:
return {}

annotations: dict[UUID, Bbox | Cuboid | Poly2d | Poly3d | Seg3d] = {}

for object_uid, object_data in json_object_data.items():
for json_bbox in _resolve_none_to_empty_list(object_data.bbox):
annotations[json_bbox.uid] = Bbox.from_json(json_bbox, object_uid)

for json_cuboid in _resolve_none_to_empty_list(object_data.cuboid):
annotations[json_cuboid.uid] = Cuboid.from_json(json_cuboid, object_uid)

for json_poly2d in _resolve_none_to_empty_list(object_data.poly2d):
annotations[json_poly2d.uid] = Poly2d.from_json(json_poly2d, object_uid)

@property
def object_data(self) -> dict[str, dict[str, type[_ObjectAnnotation]]]:
"""Return annotations categorized by Object-Id.
for json_poly3d in _resolve_none_to_empty_list(object_data.poly3d):
annotations[json_poly3d.uid] = Poly3d.from_json(json_poly3d, object_uid)

Returns
-------
dict[str, dict[UUID, _ObjectAnnotation subclass]]
Dictionary of annotations. Keys are object uids and values are annotations, that are
contained in the object.
for json_seg3d in _resolve_none_to_empty_list(object_data.vec):
annotations[json_seg3d.uid] = Seg3d.from_json(json_seg3d, object_uid)

"""
object_data: dict[str, dict[str, type[_ObjectAnnotation]]] = {}
for ann_id, annotation in self.annotations.items():
if annotation.object.uid not in object_data:
object_data[annotation.object.uid] = {}
return annotations

object_data[annotation.object.uid][ann_id] = annotation

return object_data
def _resolve_none_to_empty_list(optional_list: list | None) -> list:
if optional_list is None:
return []
return optional_list
4 changes: 3 additions & 1 deletion raillabel/json_format/__init__.py
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@
from .coordinate_system import JSONCoordinateSystem
from .cuboid import JSONCuboid
from .element_data_pointer import JSONElementDataPointer
from .frame import JSONFrame
from .frame import JSONFrame, JSONFrameData, JSONFrameProperties
from .frame_interval import JSONFrameInterval
from .metadata import JSONMetadata
from .num import JSONNum
@@ -36,6 +36,8 @@
"JSONElementDataPointer",
"JSONFrameInterval",
"JSONFrame",
"JSONFrameData",
"JSONFrameProperties",
"JSONMetadata",
"JSONNumAttribute",
"JSONNum",
12 changes: 12 additions & 0 deletions tests/test_raillabel/format/conftest.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
# Copyright DB InfraGO AG and contributors
# SPDX-License-Identifier: Apache-2.0
from .test_attributes import attributes_multiple_types, attributes_multiple_types_json
from .test_bbox import bbox, bbox_json, bbox_uid
from .test_cuboid import cuboid, cuboid_json, cuboid_uid
from .test_frame_interval import frame_interval, frame_interval_json
from .test_intrinsics_pinhole import intrinsics_pinhole, intrinsics_pinhole_json
from .test_intrinsics_radar import intrinsics_radar, intrinsics_radar_json
from .test_num import num, num_json
from .test_point2d import point2d, point2d_json, another_point2d, another_point2d_json
from .test_point3d import point3d, point3d_json, another_point3d, another_point3d_json
from .test_poly2d import poly2d, poly2d_json, poly2d_uid
from .test_poly3d import poly3d, poly3d_json, poly3d_uid
from .test_quaternion import quaternion, quaternion_json
from .test_size2d import size2d, size2d_json
from .test_size3d import size3d, size3d_json
from .test_seg3d import seg3d, seg3d_json, seg3d_uid
from .test_sensor_reference import (
another_sensor_reference,
another_sensor_reference_json,
sensor_reference,
sensor_reference_json,
)
from .test_transform import transform, transform_json
7 changes: 6 additions & 1 deletion tests/test_raillabel/format/test_bbox.py
Original file line number Diff line number Diff line change
@@ -20,14 +20,19 @@ def bbox_json(
size2d_json,
) -> JSONBbox:
return JSONBbox(
uid="78f0ad89-2750-4a30-9d66-44c9da73a714",
uid="2811f67c-124C-4fac-a275-20807d0471de",
name="rgb_middle__bbox__person",
val=point2d_json + size2d_json,
coordinate_system="rgb_middle",
attributes=attributes_multiple_types_json,
)


@pytest.fixture
def bbox_uid() -> UUID:
return UUID("2811f67c-124C-4fac-a275-20807d0471de")


@pytest.fixture
def bbox(
point2d,
7 changes: 6 additions & 1 deletion tests/test_raillabel/format/test_cuboid.py
Original file line number Diff line number Diff line change
@@ -21,14 +21,19 @@ def cuboid_json(
size3d_json,
) -> JSONCuboid:
return JSONCuboid(
uid="78f0ad89-2750-4a30-9d66-44c9da73a714",
uid="51def938-20BA-4699-95be-d6330c44cb77",
name="lidar__cuboid__person",
val=point3d_json + quaternion_json + size3d_json,
coordinate_system="lidar",
attributes=attributes_multiple_types_json,
)


@pytest.fixture
def cuboid_uid() -> UUID:
return UUID("51def938-20BA-4699-95be-d6330c44cb77")


@pytest.fixture
def cuboid(
point3d,
92 changes: 92 additions & 0 deletions tests/test_raillabel/format/test_frame.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Copyright DB InfraGO AG and contributors
# SPDX-License-Identifier: Apache-2.0

from __future__ import annotations

from decimal import Decimal

import pytest

from raillabel.format import Frame
from raillabel.json_format import JSONFrame, JSONFrameData, JSONFrameProperties, JSONObjectData

# == Fixtures =========================


@pytest.fixture
def frame_json(
sensor_reference_json,
another_sensor_reference_json,
num_json,
bbox_json,
cuboid_json,
poly2d_json,
poly3d_json,
seg3d_json,
) -> JSONFrame:
return JSONFrame(
frame_properties=JSONFrameProperties(
timestamp=Decimal("1631337747.123123123"),
streams={
"rgb_middle": sensor_reference_json,
"lidar": another_sensor_reference_json,
},
frame_data=JSONFrameData(num=[num_json]),
),
objects={
"cfcf9750-3bc3-4077-9079-a82c0c63976a": JSONObjectData(
poly2d=[poly2d_json],
poly3d=[poly3d_json],
),
"b40ba3ad-0327-46ff-9c28-2506cfd6d934": JSONObjectData(
bbox=[bbox_json],
cuboid=[cuboid_json],
vec=[seg3d_json],
),
},
)


@pytest.fixture
def frame(
sensor_reference,
another_sensor_reference,
num,
bbox,
bbox_uid,
cuboid,
cuboid_uid,
poly2d,
poly2d_uid,
poly3d,
poly3d_uid,
seg3d,
seg3d_uid,
) -> dict:
return Frame(
timestamp=Decimal("1631337747.123123123"),
sensors={
"rgb_middle": sensor_reference,
"lidar": another_sensor_reference,
},
frame_data={num.name: num},
annotations={
bbox_uid: bbox,
cuboid_uid: cuboid,
poly2d_uid: poly2d,
poly3d_uid: poly3d,
seg3d_uid: seg3d,
},
)


# == Tests ============================


def test_from_json(frame, frame_json):
actual = Frame.from_json(frame_json)
assert actual == frame


if __name__ == "__main__":
pytest.main([__file__, "-v"])
11 changes: 8 additions & 3 deletions tests/test_raillabel/format/test_poly2d.py
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@ def poly2d_json(
attributes_multiple_types_json,
) -> JSONPoly2d:
return JSONPoly2d(
uid="78f0ad89-2750-4a30-9d66-44c9da73a714",
uid="013e7b34-62E5-435c-9412-87318c50f6d8",
name="rgb_middle__poly2d__person",
closed=True,
mode="MODE_POLY2D_ABSOLUTE",
@@ -30,6 +30,11 @@ def poly2d_json(
)


@pytest.fixture
def poly2d_uid() -> UUID:
return UUID("013e7b34-62E5-435c-9412-87318c50f6d8")


@pytest.fixture
def poly2d(
point2d,
@@ -41,15 +46,15 @@ def poly2d(
closed=True,
sensor="rgb_middle",
attributes=attributes_multiple_types,
object=UUID("b40ba3ad-0327-46ff-9c28-2506cfd6d934"),
object=UUID("cfcf9750-3BC3-4077-9079-a82c0c63976a"),
)


# == Tests ============================


def test_from_json(poly2d, poly2d_json):
actual = Poly2d.from_json(poly2d_json, object_uid=UUID("b40ba3ad-0327-46ff-9c28-2506cfd6d934"))
actual = Poly2d.from_json(poly2d_json, object_uid=UUID("cfcf9750-3BC3-4077-9079-a82c0c63976a"))
assert actual == poly2d


Loading