pub mod gpu_sift;
pub mod gpu_sift_v2;
pub mod keypoints;
pub mod sift;
pub mod utils;
#[cfg(target_arch = "wasm32")]
pub mod wasm;
use std::str::FromStr;
pub use gpu_sift::{GpuSiftConfig, GpuSiftContext};
pub use gpu_sift_v2::{GpuSiftConfigV2, GpuSiftV2};
pub use keypoints::KeyPoint;
pub use sift::{convert_f32_to_grayimage_normalized, load_image_dyn, save_gray_image, Sift};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SiftBackend {
Cpu,
WebGpu,
WebGpuWithCpuFallback,
WebGpuV2,
}
impl Default for SiftBackend {
fn default() -> Self {
SiftBackend::WebGpuWithCpuFallback
}
}
impl FromStr for SiftBackend {
type Err = String;
fn from_str(value: &str) -> Result<Self, Self::Err> {
let normalized = value.trim().to_lowercase();
match normalized.as_str() {
"cpu" => Ok(SiftBackend::Cpu),
"webgpu" | "wgpu" | "gpu" => Ok(SiftBackend::WebGpu),
"auto" | "fallback" | "prefer-gpu" => Ok(SiftBackend::WebGpuWithCpuFallback),
"gpuv2" | "gpu-v2" | "webgpu-v2" | "v2" => Ok(SiftBackend::WebGpuV2),
_ => Err(format!("Unknown SIFT backend: {value}")),
}
}
}
use image::{open, DynamicImage, GrayImage, Rgb, RgbImage}; use imageproc::drawing::{draw_filled_circle_mut, draw_line_segment_mut};
pub fn load_and_convert_image(path: &str) -> GrayImage {
let img = open(path).expect("Failed to open image");
img.into_luma8()
}
pub fn draw_keypoints_to_image(
img: &DynamicImage,
keypoints: &[KeyPoint],
color: Rgb<u8>,
) -> RgbImage {
let mut rgb_image = img.to_rgb8();
for kp in keypoints {
let x = kp.x;
let y = kp.y;
let size: f32 = 2.0; let angle = kp.angle;
let radius = (size * 3.0).round() as i32;
let display_radius = radius.max(2);
draw_filled_circle_mut(&mut rgb_image, (x as i32, y as i32), display_radius, color);
let x_end = x + (display_radius as f32 * angle.cos());
let y_end = y + (display_radius as f32 * angle.sin());
draw_line_segment_mut(
&mut rgb_image,
(x, y), (x_end, y_end), color,
);
draw_filled_circle_mut(
&mut rgb_image,
(x as i32, y as i32),
1, color,
);
}
rgb_image
}