Skip to main content

sift/
lib.rs

1pub mod gpu_sift;
2pub mod gpu_sift_v2;
3pub mod keypoints;
4pub mod sift;
5pub mod utils;
6#[cfg(target_arch = "wasm32")]
7pub mod wasm;
8
9use std::str::FromStr;
10
11// Re-export main types
12pub use gpu_sift::{GpuSiftConfig, GpuSiftContext};
13pub use gpu_sift_v2::{GpuSiftConfigV2, GpuSiftV2};
14pub use keypoints::KeyPoint;
15pub use sift::{convert_f32_to_grayimage_normalized, load_image_dyn, save_gray_image, Sift};
16
17/// Backend selection for SIFT calculation.
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum SiftBackend {
20    /// CPU only.
21    Cpu,
22    /// WebGPU only (errors are propagated up).
23    WebGpu,
24    /// Try WebGPU first, silently fallback to CPU on error.
25    WebGpuWithCpuFallback,
26    /// Full GPU V2 pipeline (texture-based, targeting <20ms).
27    WebGpuV2,
28}
29
30impl Default for SiftBackend {
31    fn default() -> Self {
32        SiftBackend::WebGpuWithCpuFallback
33    }
34}
35
36impl FromStr for SiftBackend {
37    type Err = String;
38
39    fn from_str(value: &str) -> Result<Self, Self::Err> {
40        let normalized = value.trim().to_lowercase();
41        match normalized.as_str() {
42            "cpu" => Ok(SiftBackend::Cpu),
43            "webgpu" | "wgpu" | "gpu" => Ok(SiftBackend::WebGpu),
44            "auto" | "fallback" | "prefer-gpu" => Ok(SiftBackend::WebGpuWithCpuFallback),
45            "gpuv2" | "gpu-v2" | "webgpu-v2" | "v2" => Ok(SiftBackend::WebGpuV2),
46            _ => Err(format!("Unknown SIFT backend: {value}")),
47        }
48    }
49}
50
51// Original functions from your example if they are still needed externally
52use image::{open, DynamicImage, GrayImage, Rgb, RgbImage}; // Add RgbImage, Rgb
53use imageproc::drawing::{draw_filled_circle_mut, draw_line_segment_mut}; // Add drawing functions
54
55/// Loads an image and converts it to grayscale.
56pub fn load_and_convert_image(path: &str) -> GrayImage {
57    let img = open(path).expect("Failed to open image");
58    img.into_luma8()
59}
60
61/// Draws keypoints on an image.
62///
63/// # Arguments
64/// * `img` - Source image (`DynamicImage`).
65/// * `keypoints` - Slice of keypoints to draw.
66/// * `color` - Color to draw points (e.g., `Rgb([255u8, 0, 0])` for red).
67///
68/// # Returns
69/// * `RgbImage` - New image with drawn points.
70pub fn draw_keypoints_to_image(
71    img: &DynamicImage,
72    keypoints: &[KeyPoint],
73    color: Rgb<u8>,
74) -> RgbImage {
75    // Convert to Rgb8 to be able to draw in color
76    let mut rgb_image = img.to_rgb8();
77
78    // Create colored RGB image for drawing
79    for kp in keypoints {
80        let x = kp.x;
81        let y = kp.y;
82        let size: f32 = 2.0; //kp.size; // sigma of the point
83        let angle = kp.angle; // orientation in radians
84
85        // Circle radius is proportional to the point scale (sigma)
86        // Multiplier 3.0 is empirical to make the circle visible
87        let radius = (size * 3.0).round() as i32;
88        // Minimum radius so very small points are visible
89        let display_radius = radius.max(2);
90
91        // Draw circle
92        draw_filled_circle_mut(&mut rgb_image, (x as i32, y as i32), display_radius, color);
93
94        // End point for orientation line
95        let x_end = x + (display_radius as f32 * angle.cos());
96        let y_end = y + (display_radius as f32 * angle.sin());
97
98        // Draw orientation line
99        draw_line_segment_mut(
100            &mut rgb_image,
101            (x, y),         // Начало в центре
102            (x_end, y_end), // Конец по направлению угла
103            color,
104        );
105
106        // Опционально: можно нарисовать еще один круг поменьше в центре
107        draw_filled_circle_mut(
108            &mut rgb_image,
109            (x as i32, y as i32),
110            1, // Маленький радиус для центральной точки
111            color,
112        );
113    }
114
115    rgb_image
116}