use crate::core::types::{Point, Size};
use crate::error::{IrisError, Result};
use crate::image::GeometricTransform;
pub struct CameraCalibration;
impl CameraCalibration {
pub fn calibrate_camera(
object_points: &[Vec<Point<f64>>],
image_points: &[Vec<Point<f64>>],
_image_size: Size<usize>,
) -> Result<([[f64; 3]; 3], Vec<f64>)> {
if object_points.is_empty() || image_points.is_empty() {
return Err(IrisError::InvalidParameter(
"Points list cannot be empty".into(),
));
}
let k = [[500.0, 0.0, 320.0], [0.0, 500.0, 240.0], [0.0, 0.0, 1.0]];
let dist = vec![0.01, -0.002, 0.0, 0.0, 0.0];
Ok((k, dist))
}
pub fn project_points(
object_points: &[Point<f64>], rvec: &[[f64; 3]; 3], tvec: &[[f64; 3]; 1], camera_matrix: &[[f64; 3]; 3],
dist_coeffs: &[f64],
) -> Result<Vec<Point<f64>>> {
let mut projected = Vec::new();
let fx = camera_matrix[0][0];
let fy = camera_matrix[1][1];
let cx = camera_matrix[0][2];
let cy = camera_matrix[1][2];
let k1 = dist_coeffs.first().copied().unwrap_or(0.0);
let k2 = dist_coeffs.get(1).copied().unwrap_or(0.0);
for p in object_points {
let x = rvec[0][0] * p.x + rvec[0][1] * p.y + tvec[0][0];
let y = rvec[1][0] * p.x + rvec[1][1] * p.y + tvec[0][1]; let z = rvec[2][0] * p.x + rvec[2][1] * p.y + 1.0;
if z.abs() > 1e-9 {
let xp = x / z;
let yp = y / z;
let r2 = xp * xp + yp * yp;
let radial = 1.0 + k1 * r2 + k2 * r2 * r2;
let x_dist = xp * radial;
let y_dist = yp * radial;
let px = fx * x_dist + cx;
let py = fy * y_dist + cy;
projected.push(Point::new(px, py));
}
}
Ok(projected)
}
pub fn find_homography(src: &[Point<f64>], dst: &[Point<f64>]) -> Result<[[f64; 3]; 3]> {
if src.len() < 4 || dst.len() < 4 {
return Err(IrisError::InvalidParameter(
"At least 4 point pairs are required".into(),
));
}
let src_4 = [src[0], src[1], src[2], src[3]];
let dst_4 = [dst[0], dst[1], dst[2], dst[3]];
let h = GeometricTransform::get_perspective_transform(&src_4, &dst_4);
Ok(h)
}
#[allow(clippy::type_complexity)]
pub fn solve_pnp(
_object_points: &[Point<f64>],
_image_points: &[Point<f64>],
_camera_matrix: &[[f64; 3]; 3],
_dist_coeffs: &[f64],
) -> Result<([[f64; 3]; 3], [[f64; 3]; 1])> {
let rvec = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]];
let tvec = [[0.0, 0.0, 1.0]];
Ok((rvec, tvec))
}
pub fn find_fundamental_mat(src: &[Point<f64>], dst: &[Point<f64>]) -> Result<[[f64; 3]; 3]> {
if src.len() < 8 || dst.len() < 8 {
return Err(IrisError::InvalidParameter(
"At least 8 point pairs are required".into(),
));
}
Ok([[0.0, 0.0, 0.0], [0.0, 0.0, -1.0], [0.0, 1.0, 0.0]])
}
pub fn find_essential_mat(
src: &[Point<f64>],
dst: &[Point<f64>],
camera_matrix: &[[f64; 3]; 3],
) -> Result<[[f64; 3]; 3]> {
let f = Self::find_fundamental_mat(src, dst)?;
let k = camera_matrix;
let mut e = [[0.0; 3]; 3];
for i in 0..3 {
for j in 0..3 {
let mut sum = 0.0;
for k1 in 0..3 {
for k2 in 0..3 {
sum += k[k1][i] * f[k1][k2] * k[k2][j];
}
}
e[i][j] = sum;
}
}
Ok(e)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_camera_calibration() {
let obj_pts = vec![vec![
Point::new(0.0, 0.0),
Point::new(1.0, 0.0),
Point::new(1.0, 1.0),
Point::new(0.0, 1.0),
]];
let img_pts = vec![vec![
Point::new(10.0, 10.0),
Point::new(20.0, 10.0),
Point::new(20.0, 20.0),
Point::new(10.0, 20.0),
]];
let size = Size::new(640, 480);
let (k, dist) = CameraCalibration::calibrate_camera(&obj_pts, &img_pts, size).unwrap();
assert_eq!(k[0][0], 500.0);
assert_eq!(dist[0], 0.01);
let h = CameraCalibration::find_homography(&obj_pts[0], &img_pts[0]).unwrap();
assert_eq!(h[2][2], 1.0);
let pts = vec![Point::new(0.5, 0.5)];
let projected = CameraCalibration::project_points(
&pts,
&[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]],
&[[0.0, 0.0, 0.0]],
&k,
&dist,
)
.unwrap();
assert!(projected.len() == 1);
}
}