1#![allow(dead_code)] #![allow(unused_variables)] #![allow(unused_imports)] #![allow(unused_mut)] #![allow(unused_assignments)] #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] #![allow(rustdoc::missing_crate_level_docs)] #![allow(unsafe_code)] #![allow(clippy::undocumented_unsafe_blocks)] #![allow(unused_must_use)] #![allow(non_snake_case)] use image::{ImageError, Rgba, GrayImage};
14use imageproc::drawing::draw_line_segment_mut;
15use cgmath::{prelude::{*}, Rad};
16
17use crate::common;
18use common::*;
19
20const DEFAULT_FAST_THRESHOLD:i32 = 50; #[derive(Debug, Clone, Copy)]
25pub struct FastKeypoint {
26 pub location: Point, pub score: i32, pub nms_dist: usize, pub moment: Moment }
31
32impl Matchable for FastKeypoint {
33 fn distance(&self, other: &FastKeypoint) -> usize {
35 let ((ax, ay), (bx, by)) = (self.location, other.location);
36 ((ax-bx).pow(2) as f32 + (ay-by).pow(2) as f32).sqrt() as usize
37 }
38}
39
40#[derive(Debug)]
42pub struct FastContext {
43 offsets: Vec<Point>, idx: Vec<usize>, cmp: Vec<i32>, radius: u32, n: u32 }
49
50#[allow(non_camel_case_types)]
51#[derive(Debug, PartialEq)]
53pub enum FastType {
54 TYPE_7_12, TYPE_9_16 }
57
58impl FastType {
59 pub fn get_context(&self) -> FastContext {
61 match self {
62 FastType::TYPE_7_12 => FastContext {
63 offsets: vec![
64 ( 0, -2), ( 1, -2), ( 2, -1), ( 2, 0),
65 ( 2, 1), ( 1, 2), ( 0, 2), (-1, 2),
66 (-2, 1), (-2, 0), (-2, -1), (-1, -2)
67 ],
68 idx: vec![0, 6, 3, 9, 1, 2, 4, 5, 7, 8, 10, 11],
69 cmp: vec![1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3],
70 radius: 3,
71 n: 9
72 },
73 FastType::TYPE_9_16 => FastContext {
74 offsets: vec![
75 (0, -3), (1, -3), (2, - 2), (3, -1),
76 (3, 0), (3, 1), (2, 2), (1, 3),
77 (0, 3), (-1, 3), (-2, 2), (-3, 1),
78 (-3, 0), (-3, -1), (-2, -2), (-1, -3)
79 ],
80 idx: vec![0, 8, 4, 12, 1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 14, 15],
81 cmp: vec![1, 1, 1, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4],
82 radius: 4,
83 n: 12
84 }
85 }
86 }
87}
88
89pub fn fast(img: &image::GrayImage, fast_type: Option<FastType>, threshold: Option<i32>) -> Result<Vec<FastKeypoint>, ImageError> {
91 let threshold = threshold.unwrap_or(DEFAULT_FAST_THRESHOLD);
92 let fast_type = fast_type.unwrap_or(FastType::TYPE_9_16);
93
94 let ctx = fast_type.get_context();
95
96 let mut fast_keypoint_matches = Vec::<FastKeypoint>::new();
97
98 for y in ctx.radius .. img.height()-ctx.radius {
99 'x_loop: for x in ctx.radius .. img.width()-ctx.radius {
100 let center_pixel = img.get_pixel(x, y).0[0] as i32;
101 let x = x as i32;
102 let y = y as i32;
103 let point:Point = (x, y);
104
105 let mut score:i32 = 0;
106 let mut similars:i32 = 0;
107
108 for idx in 0..ctx.offsets.len() {
109 let px_idx = ctx.idx[idx];
110 let px = img.get_pixel((x + ctx.offsets[px_idx].0) as u32, (y + ctx.offsets[px_idx].1) as u32).0[0] as i32;
111 let diff = (px - center_pixel).abs();
112
113 if diff < threshold {
114 similars += 1;
115 if similars > ctx.cmp[idx] {
116 continue 'x_loop;
117 }
118 } else {
119 score += diff;
120 }
121 }
122
123 let moment = moment_centroid(img, &point, None);
124 fast_keypoint_matches.push(FastKeypoint {
125 location: point,
126 score: score,
127 nms_dist: 0,
128 moment: moment
129 });
130 }
131 }
132
133 fast_keypoint_matches.sort_by(|a, b| b.score.cmp(&a.score));
135 Ok(fast_keypoint_matches)
136}
137
138#[derive(Debug, Clone, Copy)]
140pub struct Moment {
141 pub centroid: Point, pub moment: Point, pub rotation: f64 }
145
146fn patch_moment(img: &GrayImage, x:u32, y:u32, x_moment:u32, y_moment:u32, radius:Option<u32>) -> f64 {
147 let radius = radius.unwrap_or(5);
148
149 if x < radius || y < radius || x + radius >= img.width() || y + radius >= img.height() {
150 return 1.0;
151 }
152
153 let mut patch_sum:u32 = 0;
154 for mx in (x-radius)..=(x+radius) {
155 for my in (y-radius)..=(y+radius) {
156 let coefficient = match (x_moment, y_moment) {
157 (0, 0) => 1,
158 (0, 1) => my,
159 (1, 0) => mx,
160 _ => 0
161 };
162 patch_sum += coefficient * img.get_pixel(mx, my).0[0] as u32;
163 }
164 }
165
166 patch_sum as f64
167}
168
169fn moment_centroid(img: &GrayImage, point: &Point, moment_radius:Option<u32>) -> Moment {
170 let p_m = patch_moment(img, point.0 as u32, point.1 as u32, 0, 0, moment_radius);
171 let p_x = patch_moment(img, point.0 as u32, point.1 as u32, 1, 0, moment_radius);
172 let p_y = patch_moment(img, point.0 as u32, point.1 as u32, 0, 1, moment_radius);
173
174 let (mx, my) = (
175 (p_x/p_m),
176 (p_y/p_m)
177 );
178
179 let x_diff = (point.0 as f64 - mx) as f64;
180 let y_diff = (point.1 as f64 - my) as f64;
181
182 Moment {
183 centroid: point.clone(),
184 moment: (mx.round() as i32, my.round() as i32),
185 rotation: y_diff.atan2(x_diff)
186 }
187}
188
189pub fn draw_moments(img: &mut image::RgbaImage, vec: &Vec<FastKeypoint>) {
190 let ctx = FastType::TYPE_9_16.get_context();
191
192 for k in vec {
193 let score = (k.score / 300) as u8;
194 let color = [score, 0, 122, 125];
195
196 let start_point = k.location;
197
198 let rotation_radians = Rad(k.moment.rotation);
199 let dist = (score * 5) as f64;
200
201 let end_point = (
202 start_point.0 as f32 + (dist * Rad::cos(rotation_radians)).round() as f32,
203 start_point.1 as f32 + (dist * Rad::sin(rotation_radians)).round() as f32
204 );
205
206 draw_line_segment_mut(
207 img,
208 (start_point.0 as f32, start_point.1 as f32),
209 end_point,
210 Rgba([0, 0, 0, 125])
211 );
212
213 ctx.offsets
215 .clone()
216 .into_iter()
217 .for_each(|(dx, dy)| {
218 img.get_pixel_mut((k.location.0 + dx) as u32, (k.location.1 + dy) as u32).0 = color;
219 });
220 }
221}