1#[derive(Debug, Clone, Copy)]
10pub struct CorrespondingPoint {
11 pub object_point: (f64, f64, f64),
12 pub image_point: (f64, f64),
13}
14
15#[derive(Debug, Clone)]
17pub struct CalibrationResult {
18 pub mean_reprojection_distance_pixels: f64,
20 pub camera_matrix: [f64; 9],
22 pub distortion_coeffs: [f64; 5],
24 pub image_width: u32,
25 pub image_height: u32,
26}
27
28#[derive(Debug)]
30pub enum Error {
31 Calibration(String),
32 Detection(String),
33}
34
35impl std::fmt::Display for Error {
36 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
37 match self {
38 Error::Calibration(s) => write!(f, "calibration error: {s}"),
39 Error::Detection(s) => write!(f, "chessboard detection error: {s}"),
40 }
41 }
42}
43
44impl std::error::Error for Error {}
45
46fn rgb_to_gray(rgb: &[u8], width: u32, height: u32) -> Vec<u8> {
51 let n = (width as usize) * (height as usize);
52 let mut gray = vec![0u8; n];
53 for i in 0..n {
54 let r = rgb[3 * i] as i32;
55 let g = rgb[3 * i + 1] as i32;
56 let b = rgb[3 * i + 2] as i32;
57 gray[i] = ((b * 4899 + g * 9617 + r * 1868 + 8192) >> 14) as u8;
61 }
62 gray
63}
64
65pub fn find_chessboard_corners(
66 rgb: &[u8],
67 width: u32,
68 height: u32,
69 pattern_width: usize,
70 pattern_height: usize,
71) -> Result<Option<Vec<(f32, f32)>>, Error> {
72 use checkerboard_calibrate::{CornerSubPixParams, GrayImageRef, corner_subpix};
73
74 let gray = rgb_to_gray(rgb, width, height);
75 let (w, h) = (width as usize, height as usize);
76 let corners = checkerboard_calibrate::chessboard::find_chessboard_corners(
77 &gray,
78 w,
79 h,
80 pattern_width,
81 pattern_height,
82 );
83 Ok(corners.map(|raw| {
85 corner_subpix(
86 GrayImageRef::new(&gray, w, h),
87 &raw,
88 &CornerSubPixParams::default(),
89 )
90 }))
91}
92
93pub fn calibrate_camera(
94 all_pts: &[Vec<CorrespondingPoint>],
95 width: i32,
96 height: i32,
97) -> Result<CalibrationResult, Error> {
98 let views: Vec<Vec<checkerboard_calibrate::calibrate::CorrespondingPoint>> = all_pts
99 .iter()
100 .map(|view| {
101 view.iter()
102 .map(|cp| checkerboard_calibrate::calibrate::CorrespondingPoint {
103 object_point: cp.object_point,
104 image_point: cp.image_point,
105 })
106 .collect()
107 })
108 .collect();
109
110 let res =
111 checkerboard_calibrate::calibrate::calibrate_camera(&views, width as u32, height as u32)
112 .map_err(|e| Error::Calibration(e.to_string()))?;
113
114 Ok(CalibrationResult {
115 mean_reprojection_distance_pixels: res.rms_reprojection_error,
116 camera_matrix: res.camera_matrix,
117 distortion_coeffs: res.distortion_coeffs,
118 image_width: res.image_width,
119 image_height: res.image_height,
120 })
121}