1use std::ops::Mul;
2
3use image::Rgba32FImage;
4use nalgebra::Matrix3;
5use nalgebra::{ArrayStorage, Matrix1x2, Matrix2, Matrix2x1, Matrix3x1};
6
7pub fn warp_into(input: &Rgba32FImage, matrix: Matrix3<f32>, output: &mut Rgba32FImage) {
8 let inverse = matrix.try_inverse().unwrap();
9
10 let in_width = input.width();
11 let in_height = input.height();
12
13 let out_width = output.width();
14 let out_height = output.height();
15
16 for out_row in 0..out_width {
17 for out_col in 0..out_height {
18 let point = Matrix3x1::<f32>::new(out_row as f32, out_col as f32, 1f32);
19
20 let in_pixel = inverse * point;
21
22 let in_row = in_pixel.x as i32;
23 let in_col = in_pixel.y as i32;
24
25 if (0 <= in_row)
26 && (in_row < in_width as i32)
27 && (0 <= in_col)
28 && (in_col < in_height as i32)
29 {
30 let px = input.get_pixel(in_row as _, in_col as _);
31
32 output[(out_row, out_col)] = *px;
33 }
34 }
35 }
36}
37
38pub fn umeyama<const R: usize>(src: &[(f32, f32); R], dst: &[(f32, f32); R]) -> Matrix3<f32> {
41 let src_x_sum: f32 = src.iter().map(|v| v.0).sum();
42 let src_x_mean = src_x_sum / (R as f32);
43
44 let src_y_sum: f32 = src.iter().map(|v| v.1).sum();
45 let src_y_mean = src_y_sum / (R as f32);
46
47 let dst_x_sum: f32 = dst.iter().map(|v| v.0).sum();
48 let dst_x_mean = dst_x_sum / (R as f32);
49
50 let dst_y_sum: f32 = dst.iter().map(|v| v.1).sum();
51 let dst_y_mean = dst_y_sum / (R as f32);
52
53 let src_demean_s = ArrayStorage(src.map(|v| [v.0 - src_x_mean, v.1 - src_y_mean]));
54 let dst_demean_s = ArrayStorage(dst.map(|v| [v.0 - dst_x_mean, v.1 - dst_y_mean]));
55
56 let src_demean = nalgebra::Matrix::from_array_storage(src_demean_s);
57 let dst_demean = nalgebra::Matrix::from_array_storage(dst_demean_s);
58
59 let a = std::ops::Mul::mul(dst_demean, &src_demean.transpose()) / (R as f32);
60 let svd = nalgebra::Matrix::svd(a, true, true);
61
62 let determinant = a.determinant();
63
64 let mut d = [1f32; 2];
65
66 if determinant < 0.0f32 {
67 d[2 - 1] = -1.0f32;
68 }
69
70 let mut t = Matrix2::<f32>::identity();
71 let s = svd.singular_values;
72 let u = svd.u.unwrap();
73 let v = svd.v_t.unwrap();
74
75 let rank = a.rank(0.00001f32);
76
77 if rank == 0 {
78 panic!("Matrix rank is 0.");
79 } else if rank == 2 - 1 {
80 if u.determinant() * v.determinant() > 0.0 {
82 u.mul_to(&v, &mut t);
83 } else {
84 let s = d[2 - 1];
85 d[2 - 1] = -1f32;
86 let dg = Matrix2::<f32>::new(d[0], 0f32, 0f32, d[1]);
87
88 let udg = u.mul(&dg);
89 udg.mul_to(&v, &mut t);
90 d[2 - 1] = s;
91 }
92 } else {
93 let dg = Matrix2::<f32>::new(d[0], 0f32, 0f32, d[1]);
94 let udg = u.mul(&dg);
95 udg.mul_to(&v, &mut t);
96 }
97
98 let ddd = Matrix1x2::new(d[0], d[1]);
99 let d_x_s = ddd.mul(s);
100
101 let var0 = src_demean.remove_row(0).variance();
102 let var1 = src_demean.remove_row(1).variance();
103
104 let varsum = var0 + var1;
105
106 let scale = d_x_s.get((0, 0)).unwrap() / varsum;
107
108 let dst_mean = Matrix2x1::<f32>::new(dst_x_mean, dst_y_mean);
109 let src_mean = Matrix2x1::<f32>::new(src_x_mean, src_y_mean);
110 let t_x_srcmean = t.mul(&src_mean);
111
112 let xxx = scale * t_x_srcmean;
113 let yyy = dst_mean - xxx;
114
115 let m13 = *yyy.get(0).unwrap();
116 let m23 = *yyy.get(1).unwrap();
117
118 let m00x22 = t * scale;
119
120 let m11 = m00x22.m11;
121 let m21 = m00x22.m21;
122 let m12 = m00x22.m12;
123 let m22 = m00x22.m22;
124
125 let tt = Matrix3::<f32>::new(m11, m12, m13, m21, m22, m23, 0f32, 0f32, 1f32);
126
127 return tt;
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133 const SRC: [(f32, f32); 5] = [
134 (491.7426, 321.8467),
135 (541.7967, 332.23264),
136 (507.47015, 366.4012),
137 (485.72678, 369.63214),
138 (533.7206, 378.40567),
139 ];
140
141 const DST: [(f32, f32); 5] = [
142 (38.2946f32, 51.6963f32),
143 (73.5318f32, 51.5014f32),
144 (56.0252f32, 71.7366f32),
145 (41.5493f32, 92.3655f32),
146 (70.7299f32, 92.2041f32),
147 ];
148
149 #[test]
150 fn estimate() {
151 let result = umeyama(&SRC, &DST);
152
153 const R: nalgebra::Matrix<
154 f32,
155 nalgebra::Const<3>,
156 nalgebra::Const<3>,
157 ArrayStorage<f32, 3, 3>,
158 > = Matrix3::<f32>::new(
159 0.7000762f32,
160 0.12366913f32,
161 -346.2191f32,
162 -0.12366913f32,
163 0.7000762f32,
164 -112.38885f32,
165 0f32,
166 0f32,
167 1f32,
168 );
169
170 assert_eq!(result, R);
171 }
172}