Source code for stardust.components.annotations.box3d

from typing import Union, Sequence, Dict
from scipy.spatial.transform import Rotation

import numpy as np
import open3d as o3d

from .base_annotation import AnnotationObject
from .point3d import Point3D
from stardust.utils.matrix4 import Matrix4


[docs] class Box3D(AnnotationObject): def __init__(self, center: Union[Point3D, Sequence], size: Union[Point3D, Sequence], rotation: Sequence, rotation_order="XYZ", **kwargs ) -> None: """ This class defines the structure of 3d geometry box. Args: center: like: [4.133927784395542, -3.777526126541506, 0.7891658530222055] or Point3D obj size: like [7.298632312111001, 2.192134290714646, 1.9819562591803783], Iterable, extent in x, y and z direction or Point3D obj rotation: like [0, 0, -1.5713694508648803], Iterable rotation_order: default to be XYZ Returns: Box3D instance Examples: .. code-block:: python box = Box3D(Point3D(4.13, -3.77, 0.78),Point3D(1, 5, 1),[0, 0, -1.57]) box.volume >>> output: >>> 5 """ # 目前只支持XYZ顺序 assert len(rotation) == 3 self._set_val("center", center) self._set_val("size", size) if volume := self._verify_is_box(): self.volume = volume else: raise ValueError("输入不足以组成3D框") self.rotation = rotation self.rotation_order = rotation_order AnnotationObject.__init__(self, **kwargs) def _set_val(self, key, value): if isinstance(value, Sequence): assert len(value) == 3 self.__dict__[key] = list(value) elif isinstance(value, Point3D): self.__dict__[key] = [value.x, value.y, value.z] else: raise TypeError('不支持的数据类型') def _verify_is_box(self) -> Union[bool, float]: """ To verify if the input can form a 3D box Returns: bool/float """ volume = self.size[0] * self.size[1] * self.size[2] # 检查有向包围盒的体积是否为正 if volume > 0: return volume else: return False @property def vertices(self) -> np.array: """ The 8 vertices of the current 3D frame object Returns: np.array, the points from box3d Examples: .. code-block:: python box.vertices >>> output: >>> np.array([[ 1.63421476 -3.2760934 0.28916585] [ 1.63364163 -4.27609323 0.28916585] [ 6.63421394 -3.27895902 0.28916585] [ 1.63421476 -3.2760934 1.28916585] [ 6.63364081 -4.27895885 1.28916585] [ 6.63421394 -3.27895902 1.28916585] [ 1.63364163 -4.27609323 1.28916585] [ 6.63364081 -4.27895885 0.28916585]]) """ obb = o3d.geometry.OrientedBoundingBox() center = self.center size = self.size rotation = self.rotation obb.center = np.array([center.x, center.y, center.z]) if isinstance(center, Point3D) else center obb.extent = np.array([size.x, size.y, size.z]) if isinstance(size, Point3D) else size obb.R = Rotation.from_euler(self.rotation_order, rotation).as_matrix() return np.array(obb.get_box_points())
[docs] @staticmethod def gen_box3d(slot, children_lst: Dict = None, parent_id: str = None, label_kind: str = None, team_id: int = None): """ generate the Box3D obj Args: slot: rosetta slot children_lst: rosetta children parent_id: Upper floor ID Returns: a Box3D instance """ if not slot.get("box", None): return box = Matrix4(slot['box']).box_info children_lst = children_lst if children_lst else {} parent_id = parent_id if parent_id else "" label_kind = label_kind if label_kind else "" box3d = Box3D( id=slot['id'], center=[(p := box['position'])['x'], p['y'], p['z']], size=[(s := box['scale'])['height'], s['width'], s['depth'] if s.get('depth') else s.get('length')], rotation=[(r := box['position'])['x'], r['y'], r['z']], rotation_order="XYZ", parent=parent_id, label=label_kind, children=children_lst, team_id=team_id ) return box3d