libredr/camera/
perspective.rs1use std::cmp::max;
2use nalgebra as na;
3use pyo3::prelude::*;
4use ndarray::{Axis, Array1, Array3};
5use anyhow::{Result, anyhow, ensure};
6use numpy::{PyReadonlyArray2, PyArray3, IntoPyArray, PyUntypedArrayMethods};
7use super::from_ray_corner;
8
9pub fn perspective_ray(
18 resolution: &[usize; 2],
19 intrinsic: na::MatrixView<f32, na::Const<3>, na::Const<3>, na::Dyn, na::Dyn>,
20 extrinsic: na::MatrixView<f32, na::Const<4>, na::Const<4>, na::Dyn, na::Dyn>) -> Result<Array3<f32>> {
21 ensure!(intrinsic.fold(true, |acc, v| { acc && v.is_finite() }),
22 "camera::perspective_ray: `intrinsic` is not finite");
23 ensure!(extrinsic.fold(true, |acc, v| { acc && v.is_finite() }),
24 "camera::perspective_ray: `extrinsic` is not finite");
25 let intrinsic_inv = intrinsic.try_inverse().ok_or(anyhow!("camera::perspective_ray: `intrinsic` is not invertible"))?;
26 let extrinsic_inv = extrinsic.try_inverse().ok_or(anyhow!("camera::perspective_ray: `extrinsic` is not invertible"))?;
27 let resolution_max = max(resolution[0], resolution[1]);
28 let axis_range = [resolution[0] as f32 / resolution_max as f32, resolution[1] as f32 / resolution_max as f32];
29 let axis_x = Array1::linspace(0.5 - 0.5 * axis_range[0], 0.5 + 0.5 * axis_range[0], resolution[0] + 1);
30 let axis_y = Array1::linspace(0.5 - 0.5 * axis_range[1], 0.5 + 0.5 * axis_range[1], resolution[1] + 1);
31 let ret_r = extrinsic_inv * na::Vector4::new(0., 0., 0., 1.);
32 let mut ray_corner = Array3::zeros((6, resolution[1] + 1, resolution[0] + 1));
33 ray_corner.axis_iter_mut(Axis(2)).enumerate().for_each(|(x, mut ray_corner)| {
34 ray_corner.axis_iter_mut(Axis(1)).enumerate().for_each(|(y, mut ray_corner)| {
35 let axis_xyz = intrinsic_inv * na::Vector3::new(axis_x[x], axis_y[y], 1.);
36 let ret_rd = extrinsic_inv * na::Vector4::new(axis_xyz[0], axis_xyz[1], axis_xyz[2], 0.);
37 for i in 0..3 {
38 ray_corner[i] = ret_r[i];
39 ray_corner[i + 3] = ret_rd[i];
40 }
41 });
42 });
43 from_ray_corner(ray_corner)
44}
45
46#[pyfunction]
48#[pyo3(name = "perspective_ray")]
49pub fn py_perspective_ray<'py>(
50 py: Python<'py>,
51 resolution: [usize; 2],
52 intrinsic: PyReadonlyArray2<f32>,
53 extrinsic: PyReadonlyArray2<f32>) -> Result<Bound<'py, PyArray3<f32>>> {
54 ensure!(intrinsic.shape() == [3, 3],
55 "camera::perspective_ray: `intrinsic` expected shape {:?}, found {:?}", [3, 3], intrinsic.shape());
56 let intrinsic = intrinsic.try_as_matrix().expect("nalgebra dynamic stride");
57 ensure!(extrinsic.shape() == [4, 4],
58 "camera::perspective_ray: `extrinsic` expected shape {:?}, found {:?}", [4, 4], extrinsic.shape());
59 let extrinsic = extrinsic.try_as_matrix().expect("nalgebra dynamic stride");
60 let ray = perspective_ray(&resolution, intrinsic, extrinsic)?;
61 Ok(ray.into_pyarray(py))
62}