diff --git a/dgp/annotations/__init__.py b/dgp/annotations/__init__.py index 59b18d41..ba3d9091 100644 --- a/dgp/annotations/__init__.py +++ b/dgp/annotations/__init__.py @@ -19,6 +19,7 @@ from dgp.annotations.panoptic_segmentation_2d_annotation import PanopticSegmentation2DAnnotation # isort:skip from dgp.annotations.semantic_segmentation_2d_annotation import SemanticSegmentation2DAnnotation # isort:skip from dgp.annotations.key_line_2d_annotation import KeyLine2DAnnotationList # isort:skip +from dgp.annotations.key_line_3d_annotation import KeyLine3DAnnotationList # isort:skip from dgp.annotations.key_point_2d_annotation import KeyPoint2DAnnotationList # isort:skip from dgp.annotations.depth_annotation import DenseDepthAnnotation # isort:skip @@ -31,7 +32,9 @@ "instance_segmentation_2d": InstanceSegmentationOntology, "instance_segmentation_3d": InstanceSegmentationOntology, "key_point_2d": KeyPointOntology, + "key_point_3d": KeyPointOntology, "key_line_2d": KeyLineOntology, + "key_line_3d": KeyLineOntology, "agent_behavior": AgentBehaviorOntology, "depth": None, "surface_normals_2d": None, @@ -48,6 +51,7 @@ "instance_segmentation_2d": PanopticSegmentation2DAnnotation, "key_point_2d": KeyPoint2DAnnotationList, "key_line_2d": KeyLine2DAnnotationList, + "key_line_3d": KeyLine3DAnnotationList, "depth": DenseDepthAnnotation } diff --git a/dgp/annotations/key_line_3d_annotation.py b/dgp/annotations/key_line_3d_annotation.py new file mode 100644 index 00000000..b4797bad --- /dev/null +++ b/dgp/annotations/key_line_3d_annotation.py @@ -0,0 +1,140 @@ +# Copyright 2022 Woven Planet. All rights reserved. +import numpy as np + +from dgp.annotations.base_annotation import Annotation +from dgp.annotations.ontology import KeyLineOntology +from dgp.proto.annotations_pb2 import KeyLine3DAnnotation, KeyLine3DAnnotations +from dgp.utils.protobuf import ( + generate_uid_from_pbobject, + open_pbobject, + save_pbobject_as_json, +) +from dgp.utils.structures.key_line_3d import KeyLine3D +from dgp.utils.structures.key_point_3d import KeyPoint3D + + +class KeyLine3DAnnotationList(Annotation): + """Container for 3D keyline annotations. + + Parameters + ---------- + ontology: KeyLineOntology + Ontology for 3D keyline tasks. + + linelist: list[KeyLine3D] + List of KeyLine3D objects. See `dgp/utils/structures/key_line_3d` for more details. + """ + def __init__(self, ontology, linelist): + super().__init__(ontology) + assert isinstance(self._ontology, KeyLineOntology), "Trying to load annotation with wrong type of ontology!" + for line in linelist: + assert isinstance( + line, KeyLine3D + ), f"Can only instantate an annotation from a list of KeyLine3D, not {type(line)}" + self._linelist = linelist + + @classmethod + def load(cls, annotation_file, ontology): + """Load annotation from annotation file and ontology. + + Parameters + ---------- + annotation_file: str + Full path to annotation + + ontology: KeyLineOntology + Ontology for 3D keyline tasks. + + Returns + ------- + KeyLine3DAnnotationList + Annotation object instantiated from file. + """ + _annotation_pb2 = open_pbobject(annotation_file, KeyLine3DAnnotations) + linelist = [ + KeyLine3D( + line=np.float32([[vertex.x, vertex.y, vertex.z] for vertex in ann.vertices]), + class_id=ontology.class_id_to_contiguous_id[ann.class_id], + color=ontology.colormap[ann.class_id], + attributes=getattr(ann, "attributes", {}), + ) for ann in _annotation_pb2.annotations + ] + return cls(ontology, linelist) + + def to_proto(self): + """Return annotation as pb object. + + Returns + ------- + KeyLine3DAnnotations + Annotation as defined in `proto/annotations.proto` + """ + return KeyLine3DAnnotations( + annotations=[ + KeyLine3DAnnotation( + class_id=self._ontology.contiguous_id_to_class_id[line.class_id], + vertices=[ + KeyPoint3D( + point=np.float32([x, y, z]), + class_id=line.class_id, + instance_id=line.instance_id, + color=line.color, + attributes=line.attributes + ).to_proto() for x, y, z in zip(line.x, line.y, line.z) + ], + attributes=line.attributes + ) for line in self._linelist + ] + ) + + def save(self, save_dir): + """Serialize Annotation object and saved to specified directory. Annotations are saved in format /. + + Parameters + ---------- + save_dir: str + Directory in which annotation is saved. + + Returns + ------- + output_annotation_file: str + Full path to saved annotation. + """ + return save_pbobject_as_json(self.to_proto(), save_path=save_dir) + + def __len__(self): + return len(self._linelist) + + def __getitem__(self, index): + """Return a single 3D keyline""" + return self._linelist[index] + + def render(self): + """Batch rendering function for keylines.""" + raise NotImplementedError + + @property + def xyz(self): + """Return lines as (N, 3) np.ndarray in format ([x, y, z])""" + return np.array([line.xyz.tolist() for line in self._linelist], dtype=np.float32) + + @property + def class_ids(self): + """Return class ID for each line, with ontology applied: + class IDs mapped to a contiguous set. + """ + return np.array([line.class_id for line in self._linelist], dtype=np.int64) + + @property + def attributes(self): + """Return a list of dictionaries of attribute name to value.""" + return [line.attributes for line in self._linelist] + + @property + def instance_ids(self): + return np.array([line.instance_id for line in self._linelist], dtype=np.int64) + + @property + def hexdigest(self): + """Reproducible hash of annotation.""" + return generate_uid_from_pbobject(self.to_proto()) diff --git a/dgp/annotations/key_point_3d_annotation.py b/dgp/annotations/key_point_3d_annotation.py new file mode 100644 index 00000000..46055801 --- /dev/null +++ b/dgp/annotations/key_point_3d_annotation.py @@ -0,0 +1,134 @@ +# Copyright 2022 Woven Planet. All rights reserved. +import numpy as np + +from dgp.annotations.base_annotation import Annotation +from dgp.annotations.ontology import KeyPointOntology +from dgp.proto.annotations_pb2 import ( + KeyPoint3DAnnotation, + KeyPoint3DAnnotations, +) +from dgp.utils.protobuf import ( + generate_uid_from_pbobject, + open_pbobject, + save_pbobject_as_json, +) +from dgp.utils.structures.key_point_3d import KeyPoint3D + + +class KeyPoint3DAnnotationList(Annotation): + """Container for 3D keypoint annotations. + + Parameters + ---------- + ontology: KeyPointOntology + Ontology for 3D keypoint tasks. + + pointlist: list[KeyPoint3D] + List of KeyPoint3D objects. See `dgp/utils/structures/key_point_3d` for more details. + """ + def __init__(self, ontology, pointlist): + super().__init__(ontology) + assert isinstance(self._ontology, KeyPointOntology), "Trying to load annotation with wrong type of ontology!" + for point in pointlist: + assert isinstance( + point, KeyPoint3D + ), f"Can only instantate an annotation from a list of KeyPoint3D, not {type(point)}" + self._pointlist = pointlist + + @classmethod + def load(cls, annotation_file, ontology): + """Load annotation from annotation file and ontology. + + Parameters + ---------- + annotation_file: str + Full path to annotation + + ontology: KeyPointOntology + Ontology for 3D keypoint tasks. + + Returns + ------- + KeyPoint3DAnnotationList + Annotation object instantiated from file. + """ + _annotation_pb2 = open_pbobject(annotation_file, KeyPoint3DAnnotations) + pointlist = [ + KeyPoint3D( + point=np.float32([ann.point.x, ann.point.y, ann.point.z]), + class_id=ontology.class_id_to_contiguous_id[ann.class_id], + color=ontology.colormap[ann.class_id], + attributes=getattr(ann, "attributes", {}), + ) for ann in _annotation_pb2.annotations + ] + return cls(ontology, pointlist) + + def to_proto(self): + """Return annotation as pb object. + + Returns + ------- + KeyPoint3DAnnotations + Annotation as defined in `proto/annotations.proto` + """ + return KeyPoint3DAnnotations( + annotations=[ + KeyPoint3DAnnotation( + class_id=self._ontology.contiguous_id_to_class_id[point.class_id], + point=point.to_proto(), + attributes=point.attributes + ) for point in self._pointlist + ] + ) + + def save(self, save_dir): + """Serialize Annotation object and saved to specified directory. Annotations are saved in format /. + + Parameters + ---------- + save_dir: str + Directory in which annotation is saved. + + Returns + ------- + output_annotation_file: str + Full path to saved annotation + """ + return save_pbobject_as_json(self.to_proto(), save_path=save_dir) + + def __len__(self): + return len(self._pointlist) + + def __getitem__(self, index): + """Return a single 3D keypoint""" + return self._pointlist[index] + + def render(self): + """Batch rendering function for keypoints.""" + raise NotImplementedError + + @property + def xyz(self): + """Return points as (N, 3) np.ndarray in format ([x, y, z])""" + return np.array([point.xyz for point in self._pointlist], dtype=np.float32) + + @property + def class_ids(self): + """Return class ID for each point, with ontology applied: + 0 is background, class IDs mapped to a contiguous set. + """ + return np.array([point.class_id for point in self._pointlist], dtype=np.int64) + + @property + def attributes(self): + """Return a list of dictionaries of attribut name to value.""" + return [point.attributes for point in self._pointlist] + + @property + def instance_ids(self): + return np.array([point.instance_id for point in self._pointlist], dtype=np.int64) + + @property + def hexdigest(self): + """Reproducible hash of annotation.""" + return generate_uid_from_pbobject(self.to_proto()) diff --git a/dgp/utils/structures/key_line_3d.py b/dgp/utils/structures/key_line_3d.py new file mode 100644 index 00000000..694ce4ca --- /dev/null +++ b/dgp/utils/structures/key_line_3d.py @@ -0,0 +1,100 @@ +# Copyright 2022 Woven Planet. All rights reserved. +import hashlib + +import numpy as np + +import dgp.proto.annotations_pb2 as annotations_pb2 + +GENERIC_OBJECT_CLASS_ID = 1 + + +class KeyLine3D: + """3D keyline object. + + Parameters + ---------- + line: np.ndarray[np.float32] + Array of (N,3) floats describing keyline coordinates [x, y, z]. + + class_id: int, default: GENERIC_OBJECT_CLASS_ID + Integer class ID + + instance_id: int, default: None + Unique instance ID for keyline. If None provided, the ID is a hash of the keyline + location and class. + + color: tuple, default: (0, 0, 0) + RGB tuple for keyline color + + attributes: dict, default: None + Dictionary of attributes associated with keyline. If None provided, + defaults to empty dict. + + """ + def __init__(self, line, class_id=GENERIC_OBJECT_CLASS_ID, instance_id=None, color=(0, 0, 0), attributes=None): + assert line.dtype in (np.float32, np.float64) + assert line.shape[1] == 3 + + self._point = line + self.x = [] + self.y = [] + self.z = [] + for point in line: + self.x.append(point[0]) + self.y.append(point[1]) + self.z.append(point[2]) + + self._class_id = class_id + self._instance_id = instance_id + self._color = color + self._attributes = dict(attributes) if attributes is not None else {} + + @property + def xyz(self): + return np.array([self.x, self.y, self.z], dtype=np.float32) + + @property + def class_id(self): + return self._class_id + + @property + def instance_id(self): + if self._instance_id is None: + return self.hexdigest + return self._instance_id + + @property + def color(self): + return self._color + + @property + def attributes(self): + return self._attributes + + @property + def hexdigest(self): + return hashlib.md5(self.xyz.tobytes() + bytes(self._class_id)).hexdigest() + + def __repr__(self): + return "{}[{}, Class: {}, Attributes: {}]".format( + self.__class__.__name__, list(self.xyz), self.class_id, self.attributes + ) + + def __eq__(self, other): + return self.hexdigest == other.hexdigest + + def to_proto(self): + """Serialize keyline to proto object. + + Does not serialize class or instance information, just line geometry. + To serialize a complete annotation, see `dgp/annotations/key_line_3d_annotation.py` + + Returns + ------- + KeyLine3D.pb2 + As defined in `proto/annotations.proto` + """ + return [ + annotations_pb2.KeyPoint3D(x=int(self.x[j]), y=int(self.y[j]), z=int(self.z[j])) + for j, _ in enumerate(self.x) + ] diff --git a/dgp/utils/structures/key_point_3d.py b/dgp/utils/structures/key_point_3d.py new file mode 100644 index 00000000..b2ce80c8 --- /dev/null +++ b/dgp/utils/structures/key_point_3d.py @@ -0,0 +1,94 @@ +# Copyright 2022 Woven Planet. All rights reserved. +import hashlib + +import numpy as np + +import dgp.proto.annotations_pb2 as annotations_pb2 + +GENERIC_OBJECT_CLASS_ID = 1 + + +class KeyPoint3D: + """3D keypoint object. + + Parameters + ---------- + point: np.ndarray[np.float32] + Array of 3 floats describing keypoint coordinates [x, y, z]. + + class_id: int, default: GENERIC_OBJECT_CLASS_ID + Integer class ID (0 reserved for background). + + instance_id: int, default: None + Unique instance ID for keypoint. If None provided, the ID is a hash of the keypoint + location and class. + + color: tuple, default: (0, 0, 0) + RGB tuple for keypoint color + + attributes: dict, default: None + Dictionary of attributes associated with keypoint. If None provided, + defaults to empty dict. + + """ + def __init__(self, point, class_id=GENERIC_OBJECT_CLASS_ID, instance_id=None, color=(0, 0, 0), attributes=None): + assert point.dtype in (np.float32, np.float64) + assert point.shape[0] == 3 + + self._point = point + + self.x = point[0] + self.y = point[1] + self.z = point[2] + + self._class_id = class_id + self._instance_id = instance_id + self._color = color + self._attributes = dict(attributes) if attributes is not None else {} + + @property + def xyz(self): + return np.array([self.x, self.y, self.z], dtype=np.float32) + + @property + def class_id(self): + return self._class_id + + @property + def instance_id(self): + if self._instance_id is None: + return self.hexdigest + return self._instance_id + + @property + def color(self): + return self._color + + @property + def attributes(self): + return self._attributes + + @property + def hexdigest(self): + return hashlib.md5(self.xyz.tobytes() + bytes(self._class_id)).hexdigest() + + def __repr__(self): + return "{}[{}, Class: {}, Attributes: {}]".format( + self.__class__.__name__, list(self.xyz), self.class_id, self.attributes + ) + + def __eq__(self, other): + return self.hexdigest == other.hexdigest + + def to_proto(self): + """Serialize keypoint to proto object. + + Does not serialize class or instance information, just point geometry. + To serialize a complete annotation, see `dgp/annotations/key_point_3d_annotation.py` + + Returns + ------- + KeyPoint3D.pb2 + As defined in `proto/annotations.proto` + """ + return annotations_pb2.KeyPoint3D(x=int(self.x), y=int(self.y), z=int(self.z)) diff --git a/tests/annotation/test_key_line_3d_annotation.py b/tests/annotation/test_key_line_3d_annotation.py new file mode 100644 index 00000000..c41bb1e0 --- /dev/null +++ b/tests/annotation/test_key_line_3d_annotation.py @@ -0,0 +1,68 @@ +import os + +import numpy as np +import pytest + +from dgp.annotations.key_line_3d_annotation import KeyLine3DAnnotationList +from dgp.datasets.synchronized_dataset import SynchronizedSceneDataset +from dgp.utils.structures.key_line_3d import KeyLine3D +from tests import TEST_DATA_DIR + + +def get_ontology_kl(scene_dataset_json, annotation_type): + dataset = SynchronizedSceneDataset( + scene_dataset_json, + split='train', + datum_names=['lcm_25tm'], + backward_context=0, + requested_annotations=("key_line_3d", ) + ) + return dataset.dataset_metadata.ontology_table.get(annotation_type, None) + + +@pytest.fixture +def kl_ontology(): + DGP_TEST_DATASET_DIR = os.path.join(TEST_DATA_DIR, "dgp") + scenes_dataset_json = os.path.join(DGP_TEST_DATASET_DIR, "key_line_3d", "scene_dataset.json") + return get_ontology_kl(scene_dataset_json=scenes_dataset_json, annotation_type="key_line_3d") + + +def test_kl3d_annotation(kl_ontology): + keylines = [KeyLine3D(np.array([[i + j, i + 1, i + 2] for i in range(5)], dtype=np.float32)) for j in range(5)] + annotation_list = KeyLine3DAnnotationList(kl_ontology, keylines) + assert len(annotation_list.xyz) == 5 + + +def test_kl3d_load(kl_ontology): + DGP_TEST_DATASET_DIR = os.path.join(TEST_DATA_DIR, "dgp") + expected_output = "ac354" + scenes_dataset_json = os.path.join( + DGP_TEST_DATASET_DIR, + "key_line_3d/scene_000000/key_line_3d/lcm_25tm/000000000000000005_21e2436af96fb6388eb0c64cc029cfdc928a3e95.json" + ) + kl3d_list = KeyLine3DAnnotationList.load(scenes_dataset_json, kl_ontology) + assert kl3d_list.hexdigest[0:5] == expected_output + + +def test_kl3d_proto(kl_ontology): + DGP_TEST_DATASET_DIR = os.path.join(TEST_DATA_DIR, "dgp") + scenes_dataset_json = os.path.join( + DGP_TEST_DATASET_DIR, + "key_line_3d/scene_000000/key_line_3d/lcm_25tm/000000000000000005_21e2436af96fb6388eb0c64cc029cfdc928a3e95.json" + ) + kl3d_list = KeyLine3DAnnotationList.load(scenes_dataset_json, kl_ontology) + output_proto = kl3d_list.to_proto() + assert output_proto.__sizeof__() == 80 + + +def test_kl3d_save(kl_ontology): + DGP_TEST_DATASET_DIR = os.path.join(TEST_DATA_DIR, "dgp") + scenes_dataset_json = os.path.join( + DGP_TEST_DATASET_DIR, + "key_line_3d/scene_000000/key_line_3d/lcm_25tm/000000000000000005_21e2436af96fb6388eb0c64cc029cfdc928a3e95.json" + ) + kl3d_list = KeyLine3DAnnotationList.load(scenes_dataset_json, kl_ontology) + kl3d_list.save(".") + filepath = "./ac35449091ebdd374aaa743be74794db561ec86a.json" + assert os.path.exists(filepath) + os.remove(filepath) diff --git a/tests/data/dgp/key_line_3d/scene_000000/calibration/0a6700143aaff8364a37e018e0768085b0564e47.json b/tests/data/dgp/key_line_3d/scene_000000/calibration/0a6700143aaff8364a37e018e0768085b0564e47.json new file mode 100644 index 00000000..cc4cd059 --- /dev/null +++ b/tests/data/dgp/key_line_3d/scene_000000/calibration/0a6700143aaff8364a37e018e0768085b0564e47.json @@ -0,0 +1,29 @@ +{ + "names": [ + "lcm_25tm" + ], + "intrinsics": [ + { + "fx": 1283.641357421875, + "fy": 1283.641357421875, + "cx": 639.5, + "cy": 383.5, + "skew": 0.0 + } + ], + "extrinsics": [ + { + "translation": { + "x": 0.4555000066757202, + "y": -0.003000000026077032, + "z": 1.3170000314712524 + }, + "rotation": { + "qx": -0.48292216221687323, + "qy": 0.48292216221687323, + "qz": -0.5165134898913871, + "qw": 0.516513489891387 + } + } + ] +} \ No newline at end of file diff --git a/tests/data/dgp/key_line_3d/scene_000000/key_line_3d/lcm_25tm/000000000000000005_21e2436af96fb6388eb0c64cc029cfdc928a3e95.json b/tests/data/dgp/key_line_3d/scene_000000/key_line_3d/lcm_25tm/000000000000000005_21e2436af96fb6388eb0c64cc029cfdc928a3e95.json new file mode 100644 index 00000000..a3d464bd --- /dev/null +++ b/tests/data/dgp/key_line_3d/scene_000000/key_line_3d/lcm_25tm/000000000000000005_21e2436af96fb6388eb0c64cc029cfdc928a3e95.json @@ -0,0 +1,841 @@ +{ + "annotations": [ + { + "vertices": [ + { + "x": -2.089055, + "y": 1.6612228, + "z": 5.5502887 + }, + { + "x": -2.144171, + "y": 1.9179286, + "z": 9.2943735 + }, + { + "x": -2.211071, + "y": 2.1553297, + "z": 12.736838 + }, + { + "x": -2.2840962, + "y": 2.3885832, + "z": 16.158052 + }, + { + "x": -2.3597474, + "y": 2.6154032, + "z": 19.573105 + }, + { + "x": -2.4369226, + "y": 2.8339982, + "z": 22.989477 + }, + { + "x": -2.5164948, + "y": 3.0463562, + "z": 26.414253 + }, + { + "x": -2.6024356, + "y": 3.2565043, + "z": 29.861938 + }, + { + "x": -2.702379, + "y": 3.4674394, + "z": 33.34829 + }, + { + "x": -2.810339, + "y": 3.6844406, + "z": 36.85309 + }, + { + "x": -2.923361, + "y": 3.9080756, + "z": 40.382607 + }, + { + "x": -3.0466492, + "y": 4.1394486, + "z": 43.973923 + }, + { + "x": -3.1899557, + "y": 4.381072, + "z": 47.69494 + }, + { + "x": -3.3420885, + "y": 4.6252284, + "z": 51.463062 + }, + { + "x": -3.4969435, + "y": 4.8672733, + "z": 55.24915 + }, + { + "x": -3.6537743, + "y": 5.103134, + "z": 59.045055 + }, + { + "x": -3.811098, + "y": 5.3314614, + "z": 62.84453 + }, + { + "x": -3.9694698, + "y": 5.559306, + "z": 66.63612 + }, + { + "x": -4.1311097, + "y": 5.7895617, + "z": 70.43898 + }, + { + "x": -4.2989254, + "y": 6.020749, + "z": 74.22436 + }, + { + "x": -4.48259, + "y": 6.2473016, + "z": 77.96835 + }, + { + "x": -4.6718235, + "y": 6.4677234, + "z": 81.68087 + }, + { + "x": -4.8680606, + "y": 6.690045, + "z": 85.47157 + }, + { + "x": -5.0575347, + "y": 6.9028425, + "z": 89.10022 + }, + { + "x": -5.251365, + "y": 7.1159596, + "z": 92.79421 + }, + { + "x": -5.447508, + "y": 7.326644, + "z": 96.472084 + }, + { + "x": -5.647868, + "y": 7.5375195, + "z": 100.107086 + }, + { + "x": -5.8591833, + "y": 7.744711, + "z": 103.62032 + }, + { + "x": -5.9136868, + "y": 7.7970524, + "z": 104.497826 + }, + { + "x": -6.073763, + "y": 7.9507794, + "z": 107.07507 + }, + { + "x": -6.2905993, + "y": 8.156952, + "z": 110.50898 + }, + { + "x": -6.508053, + "y": 8.362639, + "z": 113.93518 + }, + { + "x": -6.72815, + "y": 8.56981, + "z": 117.36675 + }, + { + "x": -6.9520125, + "y": 8.779772, + "z": 120.814575 + }, + { + "x": -7.18666, + "y": 8.992638, + "z": 124.311134 + }, + { + "x": -7.444804, + "y": 9.2088785, + "z": 127.90669 + }, + { + "x": -7.7122903, + "y": 9.422916, + "z": 131.52982 + }, + { + "x": -7.983929, + "y": 9.634734, + "z": 135.17487 + }, + { + "x": -8.259978, + "y": 9.845639, + "z": 138.83958 + }, + { + "x": -8.539335, + "y": 10.0573435, + "z": 142.50996 + }, + { + "x": -8.728814, + "y": 10.19964, + "z": 144.93065 + }, + { + "x": -8.829059, + "y": 10.274923, + "z": 146.21132 + }, + { + "x": -9.140108, + "y": 10.503232, + "z": 149.96384 + }, + { + "x": -9.460176, + "y": 10.741443, + "z": 153.72722 + }, + { + "x": -9.782717, + "y": 10.9853945, + "z": 157.47879 + }, + { + "x": -10.109095, + "y": 11.230266, + "z": 161.22397 + }, + { + "x": -10.4388075, + "y": 11.469406, + "z": 164.93329 + }, + { + "x": -10.774219, + "y": 11.694452, + "z": 168.5102 + }, + { + "x": -11.10969, + "y": 11.910816, + "z": 172.01965 + }, + { + "x": -11.451755, + "y": 12.129138, + "z": 175.56058 + }, + { + "x": -11.78558, + "y": 12.345325, + "z": 179.00099 + }, + { + "x": -12.123556, + "y": 12.56595, + "z": 182.45448 + }, + { + "x": -12.464241, + "y": 12.786173, + "z": 185.88411 + }, + { + "x": -12.810647, + "y": 13.001762, + "z": 189.22997 + }, + { + "x": -13.157112, + "y": 13.214485, + "z": 192.52365 + }, + { + "x": -13.508577, + "y": 13.428223, + "z": 195.83115 + }, + { + "x": -13.862669, + "y": 13.641442, + "z": 199.1452 + }, + { + "x": -14.228256, + "y": 13.856666, + "z": 202.51787 + }, + { + "x": -14.616015, + "y": 14.082532, + "z": 205.99913 + }, + { + "x": -15.01267, + "y": 14.316065, + "z": 209.51979 + }, + { + "x": -15.412209, + "y": 14.555406, + "z": 213.05066 + }, + { + "x": -15.811806, + "y": 14.798351, + "z": 216.57367 + }, + { + "x": -16.597677, + "y": 15.27746, + "z": 223.47319 + }, + { + "x": -16.965359, + "y": 15.495051, + "z": 226.66666 + }, + { + "x": -17.33191, + "y": 15.706276, + "z": 229.84041 + }, + { + "x": -17.714159, + "y": 15.927859, + "z": 233.13756 + } + ], + "attributes": { + "radius": "30.501609241201393", + "id": "0", + "line_side": "left", + "line_type": "DASHED", + "line_id": "0" + }, + "class_id": 0, + "key": "" + }, + { + "vertices": [ + { + "x": 1.9111695, + "y": 1.6615186, + "z": 5.5492544 + }, + { + "x": 1.9067647, + "y": 1.6830662, + "z": 5.8708277 + }, + { + "x": 1.8551489, + "y": 1.9199955, + "z": 9.362753 + }, + { + "x": 1.7880803, + "y": 2.1561732, + "z": 12.818354 + }, + { + "x": 1.7150108, + "y": 2.3874795, + "z": 16.245478 + }, + { + "x": 1.6395504, + "y": 2.6132312, + "z": 19.656515 + }, + { + "x": 1.5619266, + "y": 2.8363657, + "z": 23.08165 + }, + { + "x": 1.4821188, + "y": 3.0551112, + "z": 26.509645 + }, + { + "x": 1.3957512, + "y": 3.2708907, + "z": 29.967133 + }, + { + "x": 1.2954808, + "y": 3.4841976, + "z": 33.464348 + }, + { + "x": 1.1877551, + "y": 3.6968973, + "z": 36.969093 + }, + { + "x": 1.0745144, + "y": 3.9153097, + "z": 40.51347 + }, + { + "x": 0.9509736, + "y": 4.1401086, + "z": 44.119183 + }, + { + "x": 0.8073877, + "y": 4.3745847, + "z": 47.854336 + }, + { + "x": 0.6577025, + "y": 4.612326, + "z": 51.629 + }, + { + "x": 0.5030512, + "y": 4.851601, + "z": 55.425587 + }, + { + "x": 0.34778368, + "y": 5.0891433, + "z": 59.21479 + }, + { + "x": 0.19249244, + "y": 5.323003, + "z": 63.014713 + }, + { + "x": 0.036289044, + "y": 5.5568743, + "z": 66.81471 + }, + { + "x": -0.12257662, + "y": 5.793621, + "z": 70.61382 + }, + { + "x": -0.28644034, + "y": 6.028242, + "z": 74.41185 + }, + { + "x": -0.46490127, + "y": 6.255222, + "z": 78.16932 + }, + { + "x": -0.65159535, + "y": 6.4777493, + "z": 81.93981 + }, + { + "x": -0.8376442, + "y": 6.6923103, + "z": 85.6074 + }, + { + "x": -0.9119874, + "y": 6.776422, + "z": 87.045006 + }, + { + "x": -1.0300293, + "y": 6.9099655, + "z": 89.32736 + }, + { + "x": -1.2239664, + "y": 7.1272187, + "z": 93.00762 + }, + { + "x": -1.4218007, + "y": 7.3441067, + "z": 96.68277 + }, + { + "x": -1.6249701, + "y": 7.5629306, + "z": 100.33665 + }, + { + "x": -1.83864, + "y": 7.7771273, + "z": 103.86255 + }, + { + "x": -2.0535915, + "y": 7.987728, + "z": 107.315025 + }, + { + "x": -2.26837, + "y": 8.196615, + "z": 110.75809 + }, + { + "x": -2.4802217, + "y": 8.4016075, + "z": 114.17983 + }, + { + "x": -2.69483, + "y": 8.605378, + "z": 117.62206 + }, + { + "x": -2.9162083, + "y": 8.809844, + "z": 121.076515 + }, + { + "x": -3.1530406, + "y": 9.017441, + "z": 124.58788 + }, + { + "x": -3.4182506, + "y": 9.228895, + "z": 128.19868 + }, + { + "x": -3.6958077, + "y": 9.439253, + "z": 131.8347 + }, + { + "x": -3.9776742, + "y": 9.649599, + "z": 135.49788 + }, + { + "x": -4.259713, + "y": 9.859129, + "z": 139.14267 + }, + { + "x": -4.5440197, + "y": 10.072251, + "z": 142.8032 + }, + { + "x": -4.8418183, + "y": 10.295004, + "z": 146.53111 + }, + { + "x": -5.154051, + "y": 10.528058, + "z": 150.29591 + }, + { + "x": -5.474724, + "y": 10.768479, + "z": 154.06502 + }, + { + "x": -5.798274, + "y": 11.011871, + "z": 157.82866 + }, + { + "x": -6.1251135, + "y": 11.253085, + "z": 161.5809 + }, + { + "x": -6.4551225, + "y": 11.487661, + "z": 165.29713 + }, + { + "x": -6.792296, + "y": 11.710512, + "z": 168.89513 + }, + { + "x": -7.128951, + "y": 11.927342, + "z": 172.41724 + }, + { + "x": -7.4666405, + "y": 12.144079, + "z": 175.91202 + }, + { + "x": -7.8034506, + "y": 12.362533, + "z": 179.38284 + }, + { + "x": -8.120223, + "y": 12.569137, + "z": 182.61943 + }, + { + "x": -8.143975, + "y": 12.584624, + "z": 182.86191 + }, + { + "x": -8.478287, + "y": 12.801822, + "z": 186.22679 + }, + { + "x": -8.832062, + "y": 13.024227, + "z": 189.64536 + }, + { + "x": -8.935206, + "y": 13.088279, + "z": 190.62552 + }, + { + "x": -9.181927, + "y": 13.241483, + "z": 192.96992 + }, + { + "x": -9.5317, + "y": 13.4567, + "z": 196.26012 + }, + { + "x": -9.886347, + "y": 13.672505, + "z": 199.57784 + }, + { + "x": -10.24753, + "y": 13.888017, + "z": 202.94589 + }, + { + "x": -10.632698, + "y": 14.111747, + "z": 206.44354 + }, + { + "x": -11.0278015, + "y": 14.34444, + "z": 209.96808 + }, + { + "x": -11.4284, + "y": 14.584588, + "z": 213.5003 + }, + { + "x": -11.83377, + "y": 14.830897, + "z": 217.04475 + }, + { + "x": -12.623721, + "y": 15.312908, + "z": 223.92596 + }, + { + "x": -12.991851, + "y": 15.532324, + "z": 227.12274 + }, + { + "x": -13.358001, + "y": 15.743514, + "z": 230.29294 + }, + { + "x": -13.741195, + "y": 15.96096, + "z": 233.60095 + } + ], + "attributes": { + "line_side": "right", + "id": "1", + "line_id": "1", + "line_type": "SOLID", + "radius": "29.77469035588595" + }, + "class_id": 0, + "key": "" + }, + { + "vertices": [ + { + "x": -17.714926, + "y": 15.928299, + "z": 233.14418 + }, + { + "x": -18.069895, + "y": 16.169653, + "z": 236.14883 + }, + { + "x": -18.19339, + "y": 16.253994, + "z": 237.19315 + }, + { + "x": -18.399847, + "y": 16.395016, + "z": 238.93892 + }, + { + "x": -18.62715, + "y": 16.549133, + "z": 240.86249 + }, + { + "x": -18.780735, + "y": 16.64647, + "z": 242.15746 + }, + { + "x": -18.889278, + "y": 16.713177, + "z": 243.0872 + }, + { + "x": -19.002413, + "y": 16.779633, + "z": 244.04662 + }, + { + "x": -19.786108, + "y": 17.227652, + "z": 250.62985 + }, + { + "x": -20.006945, + "y": 17.3423, + "z": 252.33084 + }, + { + "x": -20.256626, + "y": 17.471125, + "z": 254.25897 + } + ], + "attributes": { + "line_side": "left", + "line_type": "DASHED", + "id": "2", + "radius": "17.82871331054196", + "line_id": "0" + }, + "class_id": 0, + "key": "" + }, + { + "vertices": [ + { + "x": -13.741962, + "y": 15.961398, + "z": 233.60757 + }, + { + "x": -14.101324, + "y": 16.202288, + "z": 236.65076 + }, + { + "x": -14.42633, + "y": 16.422832, + "z": 239.39967 + }, + { + "x": -14.654303, + "y": 16.577078, + "z": 241.32912 + }, + { + "x": -14.6733465, + "y": 16.589153, + "z": 241.4897 + }, + { + "x": -14.807882, + "y": 16.674463, + "z": 242.62416 + }, + { + "x": -14.916425, + "y": 16.741295, + "z": 243.55391 + }, + { + "x": -15.028143, + "y": 16.80719, + "z": 244.50175 + }, + { + "x": -15.507923, + "y": 17.087702, + "z": 248.59627 + }, + { + "x": -15.816144, + "y": 17.263704, + "z": 251.14905 + }, + { + "x": -16.032705, + "y": 17.379366, + "z": 252.84259 + }, + { + "x": -16.214134, + "y": 17.475554, + "z": 254.26033 + } + ], + "attributes": { + "line_type": "SOLID", + "line_side": "right", + "id": "3", + "radius": "20.3193714043737", + "line_id": "1" + }, + "class_id": 0, + "key": "" + } + ] +} \ No newline at end of file diff --git a/tests/data/dgp/key_line_3d/scene_000000/ontology/a5b601b09ac109191fdbdc4a21fa18dfbe69b1a1.json b/tests/data/dgp/key_line_3d/scene_000000/ontology/a5b601b09ac109191fdbdc4a21fa18dfbe69b1a1.json new file mode 100644 index 00000000..ffaf27f7 --- /dev/null +++ b/tests/data/dgp/key_line_3d/scene_000000/ontology/a5b601b09ac109191fdbdc4a21fa18dfbe69b1a1.json @@ -0,0 +1,103 @@ +{ + "items": [ + { + "name": "ego_line", + "color": { + "r": 255, + "g": 153, + "b": 0 + }, + "isthing": true, + "id": 0, + "supercategory": "" + }, + { + "name": "ego_line_extrapolated_to_horizon", + "id": 1, + "color": { + "r": 255, + "g": 0, + "b": 0 + }, + "isthing": true, + "supercategory": "" + }, + { + "name": "adjacent1_left", + "id": 2, + "color": { + "g": 255, + "r": 0, + "b": 0 + }, + "isthing": true, + "supercategory": "" + }, + { + "name": "adjacent1_right", + "id": 3, + "color": { + "g": 255, + "r": 0, + "b": 0 + }, + "isthing": true, + "supercategory": "" + }, + { + "name": "adjacent2_left", + "id": 4, + "color": { + "r": 255, + "g": 255, + "b": 0 + }, + "isthing": true, + "supercategory": "" + }, + { + "name": "adjacent2_right", + "id": 5, + "color": { + "r": 255, + "g": 255, + "b": 0 + }, + "isthing": true, + "supercategory": "" + }, + { + "name": "other_ego_line", + "id": 6, + "color": { + "r": 46, + "g": 181, + "b": 140 + }, + "isthing": true, + "supercategory": "" + }, + { + "name": "other_line", + "id": 7, + "color": { + "r": 244, + "g": 204, + "b": 204 + }, + "isthing": true, + "supercategory": "" + }, + { + "name": "line_break_due_to_grad", + "id": 8, + "color": { + "b": 255, + "r": 0, + "g": 0 + }, + "isthing": true, + "supercategory": "" + } + ] +} \ No newline at end of file diff --git a/tests/data/dgp/key_line_3d/scene_000000/scene_23d1725bfb52a43491ce3609073ce4d75b7785a5.json b/tests/data/dgp/key_line_3d/scene_000000/scene_23d1725bfb52a43491ce3609073ce4d75b7785a5.json new file mode 100644 index 00000000..b12df42b --- /dev/null +++ b/tests/data/dgp/key_line_3d/scene_000000/scene_23d1725bfb52a43491ce3609073ce4d75b7785a5.json @@ -0,0 +1,98 @@ +{ + "name": "scene_000000", + "samples": [ + { + "id": { + "timestamp": "2022-11-10T01:00:27.752Z", + "log": "", + "name": "", + "index": "0" + }, + "datum_keys": [ + "2ea9448ddf12dd58062335f3ec1889d87b0ef6202211707cf351f90b4a6ab83b" + ], + "calibration_key": "0a6700143aaff8364a37e018e0768085b0564e47", + "metadata": {} + } + ], + "metadata": { + "TOTORO": { + "@type": "type.googleapis.com/dgp.proto.ParallelDomainSceneMetadata", + "location": "motorway_37_673810_-122_389930_0_006000", + "time_of_day": "DAYTIME", + "fog_intensity": 0.45, + "rain_intensity": 0.9, + "wetness": 0.9, + "version": 1, + "cloud_cover": 0.8, + "attributes": { + "is_curved_highway": "True" + }, + "region_type": "SYNTHETIC_REGION", + "scene_type": "SYNTHETIC_SCENE", + "sun_elevation": 0.0, + "sun_azimuth": 0.0, + "street_lights": 0.0, + "batch_id": 0 + } + }, + "data": [ + { + "id": { + "name": "lcm_25tm", + "timestamp": "2022-11-10T01:00:27.752Z", + "log": "", + "index": "0" + }, + "key": "2ea9448ddf12dd58062335f3ec1889d87b0ef6202211707cf351f90b4a6ab83b", + "datum": { + "image": { + "filename": "rgb/lcm_25tm/000000000000000005.png", + "height": 768, + "width": 1280, + "channels": 4, + "annotations": { + "17": "key_line_3d/lcm_25tm/000000000000000005_21e2436af96fb6388eb0c64cc029cfdc928a3e95.json" + }, + "metadata": { + "TOTORO": { + "@type": "type.googleapis.com/dgp.proto.ParallelDomainSampleMetadata", + "attributes": { + "type_of_scene": "NORMAL", + "lane_change": "False", + "elevation_variation": "False", + "road": "HIGHWAY", + "sensor_id": "lcm_25tm", + "branching_in_view": "False", + "merging_in_view": "False", + "ego_merging": "False", + "road_radius": "0.0", + "partition_0": "PD" + } + } + }, + "pose": { + "translation": { + "x": -110.28236389160156, + "y": -1078.1033935546875, + "z": 8.57644271850586 + }, + "rotation": { + "qx": -0.6510899207948903, + "qy": 0.1869702528681094, + "qz": -0.2021379216584355, + "qw": 0.7072936449660401 + } + } + } + }, + "next_key": "", + "prev_key": "" + } + ], + "ontologies": { + "17": "a5b601b09ac109191fdbdc4a21fa18dfbe69b1a1" + }, + "description": "", + "log": "" +} \ No newline at end of file diff --git a/tests/data/dgp/key_line_3d/scene_dataset.json b/tests/data/dgp/key_line_3d/scene_dataset.json new file mode 100644 index 00000000..f2c40b30 --- /dev/null +++ b/tests/data/dgp/key_line_3d/scene_dataset.json @@ -0,0 +1,22 @@ +{ + "metadata": { + "name": "nehal-mamgain", + "version": "1.0", + "creation_date": "2022-12-06T00:44:56.237604Z", + "creator": "Woven Planet", + "origin": "INTERNAL", + "available_annotation_types": [ + 17 + ], + "metadata": {}, + "description": "", + "frame_per_second": 0.0 + }, + "scene_splits": { + "0": { + "filenames": [ + "scene_000000/scene_23d1725bfb52a43491ce3609073ce4d75b7785a5.json" + ] + } + } +} diff --git a/tests/utils/structures/test_key_line_3d.py b/tests/utils/structures/test_key_line_3d.py new file mode 100644 index 00000000..af767f59 --- /dev/null +++ b/tests/utils/structures/test_key_line_3d.py @@ -0,0 +1,35 @@ +import numpy as np +import pytest +from numpy.lib.ufunclike import fix + +from dgp.utils.structures.key_line_3d import KeyLine3D + + +@pytest.fixture +def keyline(): + k = np.float32([[.5, 2, -1], [-4, 0, 3], [0, -1, 2], [.25, 1.25, -.25], [100, 1, 200]]) + return KeyLine3D(k) + + +def test_keyline_class_id(keyline): + assert keyline.class_id == 1 + + +def test_keyline_instance_id(keyline): + assert keyline.instance_id == '6b144d77fb6c1f915f56027b4fe34f5e' + + +def test_keyline_color(keyline): + assert keyline.color == (0, 0, 0) + + +def test_keyline_attributes(keyline): + assert keyline.attributes == {} + + +def test_keyline_hexdigest(keyline): + assert keyline.hexdigest == '6b144d77fb6c1f915f56027b4fe34f5e' + + +def test_keyline_to_proto(keyline): + assert len(keyline.to_proto()) == 5