calib_targets_chessboard/
rectified_view.rs1use calib_targets_core::{
2 estimate_homography_rect_to_img, warp_perspective_gray, GrayImage, GrayImageView, Homography,
3 LabeledCorner,
4};
5use nalgebra::Point2;
6
7#[derive(thiserror::Error, Debug)]
8pub enum RectifyError {
9 #[error("not enough labeled inlier corners with grid coords (need >=4)")]
10 NotEnoughPoints,
11 #[error("homography estimation failed")]
12 HomographyFailed,
13 #[error("homography not invertible")]
14 NonInvertible,
15}
16
17#[derive(Clone, Debug)]
18pub struct RectifiedBoardView {
19 pub rect: GrayImage,
20 pub h_img_from_rect: Homography,
21 pub h_rect_from_img: Homography,
22 pub min_i: i32,
23 pub min_j: i32,
24 pub max_i: i32,
25 pub max_j: i32,
26 pub px_per_square: f32,
27}
28
29pub fn rectify_from_chessboard_result(
30 src: &GrayImageView<'_>,
31 det_corners: &[LabeledCorner],
32 inliers: &[usize],
33 px_per_square: f32,
34 margin_squares: f32, ) -> Result<RectifiedBoardView, RectifyError> {
36 let mut img_pts = Vec::<Point2<f32>>::new();
38 let mut grid = Vec::<(i32, i32)>::new();
39
40 for &idx in inliers {
41 if let Some(c) = det_corners.get(idx) {
42 if let Some(g) = c.grid {
43 img_pts.push(Point2::new(c.position.x, c.position.y));
44 grid.push((g.i, g.j));
45 }
46 }
47 }
48 if img_pts.len() < 4 {
49 return Err(RectifyError::NotEnoughPoints);
50 }
51
52 let (mut min_i, mut min_j) = (i32::MAX, i32::MAX);
54 let (mut max_i, mut max_j) = (i32::MIN, i32::MIN);
55 for &(i, j) in &grid {
56 min_i = min_i.min(i);
57 min_j = min_j.min(j);
58 max_i = max_i.max(i);
59 max_j = max_j.max(j);
60 }
61
62 let mi = (min_i as f32 - margin_squares).floor();
64 let mj = (min_j as f32 - margin_squares).floor();
65 let ma = (max_i as f32 + margin_squares).ceil();
66 let mb = (max_j as f32 + margin_squares).ceil();
67
68 let min_i_m = mi as i32;
69 let min_j_m = mj as i32;
70 let max_i_m = ma as i32;
71 let max_j_m = mb as i32;
72
73 let out_w = ((max_i_m - min_i_m) as f32 * px_per_square)
74 .round()
75 .max(1.0) as usize;
76 let out_h = ((max_j_m - min_j_m) as f32 * px_per_square)
77 .round()
78 .max(1.0) as usize;
79
80 let mut rect_pts = Vec::<Point2<f32>>::with_capacity(grid.len());
82 for &(i, j) in &grid {
83 let x = (i - min_i_m) as f32 * px_per_square;
84 let y = (j - min_j_m) as f32 * px_per_square;
85 rect_pts.push(Point2::new(x, y));
86 }
87
88 let h_img_from_rect = estimate_homography_rect_to_img(&rect_pts, &img_pts)
90 .ok_or(RectifyError::HomographyFailed)?;
91
92 let h_rect_from_img = h_img_from_rect
93 .inverse()
94 .ok_or(RectifyError::NonInvertible)?;
95
96 let rect = warp_perspective_gray(src, h_img_from_rect, out_w, out_h);
98
99 Ok(RectifiedBoardView {
100 rect,
101 h_img_from_rect,
102 h_rect_from_img,
103 min_i: min_i_m,
104 min_j: min_j_m,
105 max_i: max_i_m,
106 max_j: max_j_m,
107 px_per_square,
108 })
109}