Skip to main content

bye_orb_rs/
fast.rs

1#![allow(dead_code)] // 允许未使用的代码
2#![allow(unused_variables)] // 允许未使用的变量
3#![allow(unused_imports)] // 允许未使用的导入
4#![allow(unused_mut)] // 允许未使用的可变变量
5#![allow(unused_assignments)] // 允许未使用的赋值
6#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // 在非调试模式下设置Windows子系统
7#![allow(rustdoc::missing_crate_level_docs)] // 允许缺少crate级别的文档
8#![allow(unsafe_code)] // 允许使用unsafe代码
9#![allow(clippy::undocumented_unsafe_blocks)] // 允许未文档化的unsafe块
10#![allow(unused_must_use)] // 允许未使用的must_use结果
11#![allow(non_snake_case)] // 允许非蛇形命名
12
13use image::{ImageError, Rgba, GrayImage};
14use imageproc::drawing::draw_line_segment_mut;
15use cgmath::{prelude::{*}, Rad};
16
17use crate::common;
18use common::*;
19
20// 常量
21const DEFAULT_FAST_THRESHOLD:i32 = 50; // 默认的FAST阈值
22
23/// 表示一个FAST关键点
24#[derive(Debug, Clone, Copy)]
25pub struct FastKeypoint {
26    pub location: Point, // 关键点的位置
27    pub score: i32, // 关键点的得分
28    pub nms_dist: usize, // 非极大值抑制距离
29    pub moment: Moment // 关键点的矩
30}
31
32impl Matchable for FastKeypoint {
33    /// 计算两个FAST关键点之间的距离
34    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/// 表示FAST算法的上下文
41#[derive(Debug)]
42pub struct FastContext {
43    offsets: Vec<Point>, // 偏移量向量
44    idx: Vec<usize>, // 索引向量
45    cmp: Vec<i32>, // 比较向量
46    radius: u32, // 半径
47    n: u32 // 数量
48}
49
50#[allow(non_camel_case_types)]
51/// 表示FAST算法的类型
52#[derive(Debug, PartialEq)]
53pub enum FastType {
54    TYPE_7_12, // 类型7_12
55    TYPE_9_16 // 类型9_16
56}
57
58impl FastType {
59    /// 获取FAST算法的上下文
60    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
89/// 执行FAST算法以检测图像中的关键点
90pub 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    // 按得分排序
134    fast_keypoint_matches.sort_by(|a, b| b.score.cmp(&a.score));
135    Ok(fast_keypoint_matches)
136}
137
138/// 表示图像的矩信息
139#[derive(Debug, Clone, Copy)]
140pub struct Moment {
141    pub centroid: Point, // 质心
142    pub moment: Point, // 矩
143    pub rotation: f64 // 旋转角度
144}
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        // draw circle
214        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}