use crate::core::{Feature, ColmapError};
use nalgebra::Point2;
use image::GrayImage;
use imageproc::corners::{corners_fast9, Corner};
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use crate::feature::fast::{FastDetector as AdvancedFastDetector, FastConfig};
pub trait FeatureDetector: Send + Sync {
fn detect(&self, image: &GrayImage) -> Result<Vec<Feature>, ColmapError>;
fn name(&self) -> &str;
fn params(&self) -> HashMap<String, f64>;
fn set_params(&mut self, params: HashMap<String, f64>) -> Result<(), ColmapError>;
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum DetectorType {
Sift,
Orb,
Surf,
Fast,
Harris,
}
#[derive(Debug, Clone)]
pub struct DetectorConfig {
pub detector_type: DetectorType,
pub max_features: usize,
pub threshold: f64,
pub edge_threshold: f64,
pub contrast_threshold: f64,
pub num_octaves: i32,
pub num_octave_layers: i32,
pub sigma: f64,
}
impl Default for DetectorConfig {
fn default() -> Self {
Self {
detector_type: DetectorType::Sift,
max_features: 8000,
threshold: 0.04,
edge_threshold: 10.0,
contrast_threshold: 0.04,
num_octaves: 4,
num_octave_layers: 3,
sigma: 1.6,
}
}
}
pub struct DetectorFactory;
impl DetectorFactory {
pub fn create(config: &DetectorConfig) -> Result<Box<dyn FeatureDetector>, ColmapError> {
match config.detector_type {
DetectorType::Sift => {
Ok(Box::new(SiftDetector::new(config)?))
},
DetectorType::Orb => {
Ok(Box::new(OrbDetector::new(config)?))
},
DetectorType::Surf => {
Ok(Box::new(SurfDetector::new(config)?))
},
DetectorType::Fast => {
let fast_config = FastConfig {
threshold: config.threshold as u8,
max_features: config.max_features,
non_max_suppression: true,
min_arc_length: 9,
};
Ok(Box::new(AdvancedFastDetector::new(fast_config)))
},
DetectorType::Harris => {
Ok(Box::new(HarrisDetector::new(config)?))
},
}
}
}
pub struct SiftDetector {
config: DetectorConfig,
}
impl SiftDetector {
pub fn new(config: &DetectorConfig) -> Result<Self, ColmapError> {
Ok(Self {
config: config.clone(),
})
}
fn detect_harris_corners(&self, image: &GrayImage) -> Result<Vec<Corner>, ColmapError> {
let corners = corners_fast9(
image,
self.config.threshold as u8,
);
Ok(corners)
}
}
impl FeatureDetector for SiftDetector {
fn detect(&self, image: &GrayImage) -> Result<Vec<Feature>, ColmapError> {
let corners = self.detect_harris_corners(image)?;
let mut features = Vec::new();
for corner in corners.into_iter().take(self.config.max_features) {
let feature = Feature {
point: Point2::new(corner.x as f64, corner.y as f64),
descriptor: Vec::new(),
response: corner.score,
angle: 0.0,
octave: 0,
scale: 1.0,
point3d_id: None,
};
features.push(feature);
}
Ok(features)
}
fn name(&self) -> &str {
"SIFT"
}
fn params(&self) -> HashMap<String, f64> {
let mut params = HashMap::new();
params.insert("max_features".to_string(), self.config.max_features as f64);
params.insert("threshold".to_string(), self.config.threshold);
params.insert("edge_threshold".to_string(), self.config.edge_threshold);
params.insert("contrast_threshold".to_string(), self.config.contrast_threshold);
params.insert("num_octaves".to_string(), self.config.num_octaves as f64);
params.insert("num_octave_layers".to_string(), self.config.num_octave_layers as f64);
params.insert("sigma".to_string(), self.config.sigma);
params
}
fn set_params(&mut self, params: HashMap<String, f64>) -> Result<(), ColmapError> {
for (key, value) in params {
match key.as_str() {
"max_features" => self.config.max_features = value as usize,
"threshold" => self.config.threshold = value,
"edge_threshold" => self.config.edge_threshold = value,
"contrast_threshold" => self.config.contrast_threshold = value,
"num_octaves" => self.config.num_octaves = value as i32,
"num_octave_layers" => self.config.num_octave_layers = value as i32,
"sigma" => self.config.sigma = value,
_ => return Err(ColmapError::InvalidParameter(format!("Unknown parameter: {}", key))),
}
}
Ok(())
}
}
pub struct OrbDetector {
config: DetectorConfig,
}
impl OrbDetector {
pub fn new(config: &DetectorConfig) -> Result<Self, ColmapError> {
Ok(Self {
config: config.clone(),
})
}
fn detect_fast_corners(&self, image: &GrayImage) -> Result<Vec<Corner>, ColmapError> {
let corners = corners_fast9(
image,
self.config.threshold as u8,
);
Ok(corners)
}
}
impl FeatureDetector for OrbDetector {
fn detect(&self, image: &GrayImage) -> Result<Vec<Feature>, ColmapError> {
let corners = self.detect_fast_corners(image)?;
let mut features = Vec::new();
for corner in corners.into_iter().take(self.config.max_features) {
let feature = Feature {
point: Point2::new(corner.x as f64, corner.y as f64),
descriptor: Vec::new(),
response: corner.score,
angle: 0.0,
octave: 0,
scale: 1.0,
point3d_id: None,
};
features.push(feature);
}
Ok(features)
}
fn name(&self) -> &str {
"ORB"
}
fn params(&self) -> HashMap<String, f64> {
let mut params = HashMap::new();
params.insert("max_features".to_string(), self.config.max_features as f64);
params
}
fn set_params(&mut self, params: HashMap<String, f64>) -> Result<(), ColmapError> {
for (key, value) in params {
match key.as_str() {
"max_features" => self.config.max_features = value as usize,
_ => return Err(ColmapError::InvalidParameter(format!("Unknown parameter: {}", key))),
}
}
Ok(())
}
}
pub struct FastDetector {
config: DetectorConfig,
}
impl FastDetector {
pub fn new(config: &DetectorConfig) -> Result<Self, ColmapError> {
Ok(Self {
config: config.clone(),
})
}
}
impl FeatureDetector for FastDetector {
fn detect(&self, image: &GrayImage) -> Result<Vec<Feature>, ColmapError> {
let corners = corners_fast9(
image,
self.config.threshold as u8,
);
let mut features = Vec::new();
for corner in corners.into_iter().take(self.config.max_features) {
let feature = Feature {
point: Point2::new(corner.x as f64, corner.y as f64),
descriptor: Vec::new(),
response: corner.score,
angle: 0.0, octave: 0,
scale: 1.0,
point3d_id: None,
};
features.push(feature);
}
Ok(features)
}
fn name(&self) -> &str {
"FAST"
}
fn params(&self) -> HashMap<String, f64> {
let mut params = HashMap::new();
params.insert("threshold".to_string(), self.config.threshold);
params
}
fn set_params(&mut self, params: HashMap<String, f64>) -> Result<(), ColmapError> {
for (key, value) in params {
match key.as_str() {
"threshold" => self.config.threshold = value,
_ => return Err(ColmapError::InvalidParameter(format!("Unknown parameter: {}", key))),
}
}
Ok(())
}
}
pub struct SurfDetector {
config: DetectorConfig,
}
impl SurfDetector {
pub fn new(config: &DetectorConfig) -> Result<Self, ColmapError> {
Ok(Self {
config: config.clone(),
})
}
fn detect_hessian_keypoints(&self, image: &GrayImage) -> Result<Vec<Corner>, ColmapError> {
let corners = corners_fast9(
image,
(self.config.threshold * 255.0) as u8,
);
Ok(corners)
}
}
impl FeatureDetector for SurfDetector {
fn detect(&self, image: &GrayImage) -> Result<Vec<Feature>, ColmapError> {
let corners = self.detect_hessian_keypoints(image)?;
let mut features = Vec::new();
for corner in corners.into_iter().take(self.config.max_features) {
let feature = Feature {
point: Point2::new(corner.x as f64, corner.y as f64),
descriptor: Vec::new(),
response: corner.score,
angle: 0.0, octave: 0,
scale: 1.0,
point3d_id: None,
};
features.push(feature);
}
Ok(features)
}
fn name(&self) -> &str {
"SURF"
}
fn params(&self) -> HashMap<String, f64> {
let mut params = HashMap::new();
params.insert("max_features".to_string(), self.config.max_features as f64);
params.insert("threshold".to_string(), self.config.threshold);
params
}
fn set_params(&mut self, params: HashMap<String, f64>) -> Result<(), ColmapError> {
for (key, value) in params {
match key.as_str() {
"max_features" => self.config.max_features = value as usize,
"threshold" => self.config.threshold = value,
_ => return Err(ColmapError::InvalidParameter(format!("Unknown parameter: {}", key))),
}
}
Ok(())
}
}
pub struct HarrisDetector {
config: DetectorConfig,
}
impl HarrisDetector {
pub fn new(config: &DetectorConfig) -> Result<Self, ColmapError> {
Ok(Self {
config: config.clone(),
})
}
}
impl FeatureDetector for HarrisDetector {
fn detect(&self, image: &GrayImage) -> Result<Vec<Feature>, ColmapError> {
let corners = corners_fast9(
image,
self.config.threshold as u8,
);
let mut features = Vec::new();
for corner in corners.into_iter().take(self.config.max_features) {
let feature = Feature {
point: Point2::new(corner.x as f64, corner.y as f64),
descriptor: Vec::new(),
response: corner.score,
angle: 0.0,
octave: 0,
scale: 1.0,
point3d_id: None,
};
features.push(feature);
}
Ok(features)
}
fn name(&self) -> &str {
"Harris"
}
fn params(&self) -> HashMap<String, f64> {
let mut params = HashMap::new();
params.insert("threshold".to_string(), self.config.threshold);
params.insert("max_features".to_string(), self.config.max_features as f64);
params
}
fn set_params(&mut self, params: HashMap<String, f64>) -> Result<(), ColmapError> {
for (key, value) in params {
match key.as_str() {
"threshold" => self.config.threshold = value,
"max_features" => self.config.max_features = value as usize,
_ => return Err(ColmapError::InvalidParameter(format!("Unknown parameter: {}", key))),
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_detector_config_default() {
let config = DetectorConfig::default();
assert_eq!(config.detector_type, DetectorType::Sift);
assert_eq!(config.max_features, 8000);
assert_eq!(config.threshold, 0.04);
}
#[test]
fn test_detector_factory() {
let config = DetectorConfig::default();
let detector = DetectorFactory::create(&config);
assert!(detector.is_ok());
assert_eq!(detector.unwrap().name(), "SIFT");
}
#[test]
fn test_sift_detector_creation() {
let config = DetectorConfig::default();
let detector = SiftDetector::new(&config);
assert!(detector.is_ok());
}
#[test]
fn test_orb_detector_creation() {
let config = DetectorConfig {
detector_type: DetectorType::Orb,
..Default::default()
};
let detector = OrbDetector::new(&config);
assert!(detector.is_ok());
}
#[test]
fn test_fast_detector_creation() {
let config = DetectorConfig {
detector_type: DetectorType::Fast,
threshold: 10.0,
..Default::default()
};
let detector = FastDetector::new(&config);
assert!(detector.is_ok());
}
#[test]
fn test_surf_detector_creation() {
let config = DetectorConfig {
detector_type: DetectorType::Surf,
..Default::default()
};
let detector = SurfDetector::new(&config);
assert!(detector.is_ok());
}
#[test]
fn test_surf_detector_factory() {
let config = DetectorConfig {
detector_type: DetectorType::Surf,
..Default::default()
};
let detector = DetectorFactory::create(&config);
assert!(detector.is_ok());
assert_eq!(detector.unwrap().name(), "SURF");
}
#[test]
fn test_harris_detector_creation() {
let config = DetectorConfig {
detector_type: DetectorType::Harris,
..Default::default()
};
let detector = HarrisDetector::new(&config);
assert!(detector.is_ok());
}
}