Source code for xrfeitoria.camera.camera_parameter

import copy
import math
from typing import List, Tuple, Union

import numpy as np
import numpy.typing as npt
from loguru import logger
from xrprimer.data_structure.camera import PinholeCameraParameter
from xrprimer.transform.convention.camera import convert_camera_parameter

from ..data_structure.constants import PathLike, Vector
from ..utils import ConverterUnreal


[docs] class CameraParameter(PinholeCameraParameter): """Camera parameter class for pinhole camera model. Inherit from XRPrimer. """
[docs] def __init__( self, K: Union[List, np.ndarray], R: Union[List, np.ndarray], T: Union[List, np.ndarray], convention: str = 'opencv', world2cam: bool = False, ): """A camera parameter class for pinhole camera model. Args: K (Union[list, np.ndarray]): Nested list of float32, 4x4 or 3x3 K mat. R (Union[list, np.ndarray]): Nested list of float32, 3x3 rotation mat. T (Union[list, np.ndarray, None]): List of float32, T vector. convention (str, optional): Convention name of this camera. Defaults to 'opencv'. world2cam (bool, optional): Whether the R, T transform points from world space to camera space. Defaults to True. """ width, height = int(K[0][2] * 2), int(K[1][2] * 2) super().__init__( K, R, T, width=width, height=height, convention=convention, world2cam=world2cam, logger=logger, ) # type hinting self.intrinsic: npt.NDArray[np.float32] # 4x4 self.extrinsic_r: npt.NDArray[np.float32] # 3x3 self.extrinsic_t: npt.NDArray[np.float32] # 3 self.height: int self.width: int self.convention: str self.world2cam: bool
[docs] def clone(self) -> 'CameraParameter': """Clone a new CameraParameter instance like self. Returns: CameraParameter """ new_cam_param = self.__class__( K=copy.deepcopy(self.get_intrinsic(k_dim=4)), R=copy.deepcopy(self.extrinsic_r), T=copy.deepcopy(self.extrinsic_t), world2cam=self.world2cam, convention=self.convention, ) return new_cam_param
@property def projection_matrix(self) -> npt.NDArray[np.float32]: """Get the camera matrix of ``K@RT``. Returns: ndarray: An ndarray of float32, 3x4 ``K@RT`` mat. """ return self.intrinsic33() @ self.extrinsic @property def extrinsic(self) -> npt.NDArray[np.float32]: """Get the extrinsic matrix of RT. Returns: ndarray: An ndarray of float32, 3x4 RT mat. """ return np.concatenate([self.extrinsic_r, self.extrinsic_t.reshape(3, 1)], axis=1)
[docs] def get_projection_matrix(self) -> List: """Get the camera matrix of ``K@RT``. Returns: List: A list of float32, 3x4 ``K@RT`` mat. """ return self.projection_matrix.tolist()
[docs] def get_extrinsic(self) -> List: """Get the camera matrix of RT. Returns: List: A list of float32, 3x4 RT mat. """ return self.extrinsic.tolist()
[docs] def model_dump(self) -> dict: """Dump camera parameters to a dict.""" return { 'class_name': 'PinholeCameraParameter', 'convention': self.convention, 'extrinsic_r': self.extrinsic_r.tolist(), 'extrinsic_t': self.extrinsic_t.tolist(), 'height': self.height, 'width': self.width, 'intrinsic': self.intrinsic.tolist(), 'name': self.name, 'world2cam': self.world2cam, }
[docs] @classmethod def from_dict(cls, data: dict) -> 'CameraParameter': """Construct a camera parameter data structure from a dict. Args: data (dict): The camera parameter data. Returns: CameraParameter: An instance of CameraParameter class. """ return cls( K=data['intrinsic'], R=data['extrinsic_r'], T=data['extrinsic_t'], convention=data['convention'], world2cam=data['world2cam'], )
[docs] @classmethod def fromfile(cls, file: PathLike) -> 'CameraParameter': """Construct a camera parameter data structure from a json file. Args: filename (PathLike): Path to the dumped json file. Returns: CameraParameter: An instance of CameraParameter class. """ file = str(file) ret_cam = PinholeCameraParameter.fromfile(file) return cls._from_pinhole(ret_cam)
[docs] @classmethod def from_bin(cls, file: PathLike) -> 'CameraParameter': """Construct a camera parameter data structure from a binary file. Args: file (PathLike): Path to the dumped binary file. Returns: CameraParameter: An instance of CameraParameter class. """ # read camera parameters with open(file, 'rb') as f: dat = np.frombuffer(f.read(), np.float32).reshape(9) location = dat[:3] rotation = dat[3:6] camera_fov = dat[6] image_size = (dat[7], dat[8]) # (width, height) return cls.from_unreal_convention(location, rotation, camera_fov, image_size)
[docs] @classmethod def from_unreal_convention( cls, location: Vector, rotation: Vector, fov: float, image_size: Tuple[int, int], # (width, height) ) -> 'CameraParameter': """Converts camera parameters from Unreal Engine convention to CameraParameter object. Args: location (Vector): The camera location in Unreal Engine convention. rotation (Vector): The camera rotation in Unreal Engine convention. fov (float): The camera field of view in degrees. image_size (Tuple[int, int]): The size of the camera image in pixels (width, height). Returns: CameraParameter: The converted camera parameters. """ # intrinsic matrix K fov = math.radians(fov) focal = max(image_size) / 2 / math.tan(fov / 2) fx = fy = focal K = np.array( [ [fx, 0, image_size[0] / 2], [0, fy, image_size[1] / 2], [0, 0, 1], ] ) # extrinsic matrix RT R = ConverterUnreal.rotation_camera_from_ue(rotation, degrees=True) _T = ConverterUnreal.location_from_ue(location) T = -R @ _T # construct camera parameter cam_param = cls(K=K, R=R, T=T, world2cam=True) return cam_param
@classmethod def _from_pinhole(cls, pinhole: PinholeCameraParameter) -> 'CameraParameter': """Construct a camera parameter data structure from a pinhole camera parameter. Args: pinhole (PinholeCameraParameter): An instance of PinholeCameraParameter class. Returns: CameraParameter: An instance of CameraParameter class. """ ret_cam = cls( K=pinhole.get_intrinsic(k_dim=4), R=pinhole.extrinsic_r, T=pinhole.extrinsic_t, convention=pinhole.convention, world2cam=pinhole.world2cam, ) return ret_cam
[docs] def convert_convention(self, dst: str): """Convert the convention of this camera parameter. In-place. Args: dst (str): The name of destination convention. One of ['opencv', 'blender', 'unreal']. """ cam_param = convert_camera_parameter(self, dst) self.set_KRT(K=cam_param.intrinsic, R=cam_param.extrinsic_r, T=cam_param.extrinsic_t) self.convention = dst self.world2cam = cam_param.world2cam
def __repr__(self) -> str: return f'CameraParameter(T={self.extrinsic_t}, convention="{self.convention}", world2cam={self.world2cam})'