mod contrast_factor;
mod derivatives;
mod descriptors;
mod detector_response;
mod evolution;
mod fed_tau;
mod image;
mod nonlinear_diffusion;
mod scale_space_extrema;
use crate::image::{gaussian_blur, GrayFloatImage};
use ::image::{DynamicImage, GenericImageView, ImageResult};
use bitarray::BitArray;
use cv_core::nalgebra::Point2;
use cv_core::ImagePoint;
use evolution::*;
use log::*;
use nonlinear_diffusion::pm_g2;
use std::path::Path;
#[derive(Debug, Clone, Copy)]
pub struct KeyPoint {
pub point: (f32, f32),
pub response: f32,
pub size: f32,
pub octave: usize,
pub class_id: usize,
pub angle: f32,
}
impl ImagePoint for KeyPoint {
fn image_point(&self) -> Point2<f64> {
Point2::new(self.point.0 as f64, self.point.1 as f64)
}
}
#[derive(Debug, Copy, Clone)]
pub struct Akaze {
pub num_sublevels: u32,
pub max_octave_evolution: u32,
pub base_scale_offset: f64,
pub initial_contrast: f64,
pub contrast_percentile: f64,
pub contrast_factor_num_bins: usize,
pub derivative_factor: f64,
pub detector_threshold: f64,
pub descriptor_channels: usize,
pub descriptor_pattern_size: usize,
}
impl Akaze {
pub fn new(threshold: f64) -> Self {
Self {
detector_threshold: threshold,
..Default::default()
}
}
pub fn sparse() -> Self {
Self::new(0.01)
}
pub fn dense() -> Self {
Self::new(0.0001)
}
}
impl Default for Akaze {
fn default() -> Akaze {
Akaze {
num_sublevels: 4,
max_octave_evolution: 4,
base_scale_offset: 1.6f64,
initial_contrast: 0.001f64,
contrast_percentile: 0.7f64,
contrast_factor_num_bins: 300,
derivative_factor: 1.5f64,
detector_threshold: 0.001f64,
descriptor_channels: 3usize,
descriptor_pattern_size: 10usize,
}
}
}
impl Akaze {
fn create_nonlinear_scale_space(
&self,
evolutions: &mut Vec<EvolutionStep>,
image: &GrayFloatImage,
) {
trace!("Creating first evolution.");
evolutions[0].Lt = gaussian_blur(image, self.base_scale_offset as f32);
trace!("Gaussian blur finished.");
evolutions[0].Lsmooth = evolutions[0].Lt.clone();
debug!(
"Convolving first evolution with sigma={} Gaussian.",
self.base_scale_offset
);
let mut contrast_factor = contrast_factor::compute_contrast_factor(
&evolutions[0].Lsmooth,
self.contrast_percentile,
1.0f64,
self.contrast_factor_num_bins,
);
trace!("Computing contrast factor finished.");
debug!(
"Contrast percentile={}, Num bins={}, Initial contrast factor={}",
self.contrast_percentile, self.contrast_factor_num_bins, contrast_factor
);
for i in 1..evolutions.len() {
trace!("Creating evolution {}.", i);
if evolutions[i].octave > evolutions[i - 1].octave {
evolutions[i].Lt = evolutions[i - 1].Lt.half_size();
trace!("Half-sizing done.");
contrast_factor *= 0.75;
debug!(
"New image size: {}x{}, new contrast factor: {}",
evolutions[i].Lt.width(),
evolutions[i].Lt.height(),
contrast_factor
);
} else {
evolutions[i].Lt = evolutions[i - 1].Lt.clone();
}
evolutions[i].Lsmooth = gaussian_blur(&evolutions[i].Lt, 1.0f32);
trace!("Gaussian blur finished.");
evolutions[i].Lx = derivatives::scharr_horizontal(&evolutions[i].Lsmooth, 1);
trace!("Computing derivative Lx done.");
evolutions[i].Ly = derivatives::scharr_vertical(&evolutions[i].Lsmooth, 1);
trace!("Computing derivative Ly done.");
evolutions[i].Lflow = pm_g2(&evolutions[i].Lx, &evolutions[i].Ly, contrast_factor);
trace!("Lflow finished.");
for j in 0..evolutions[i].fed_tau_steps.len() {
trace!("Starting diffusion step.");
let step_size = evolutions[i].fed_tau_steps[j];
nonlinear_diffusion::calculate_step(&mut evolutions[i], step_size as f32);
trace!("Diffusion step finished with step size {}", step_size);
}
}
}
fn find_image_keypoints(&self, evolutions: &mut Vec<EvolutionStep>) -> Vec<KeyPoint> {
self.detector_response(evolutions);
trace!("Computing detector response finished.");
self.detect_keypoints(evolutions)
}
pub fn extract(&self, image: &DynamicImage) -> (Vec<KeyPoint>, Vec<BitArray<64>>) {
let float_image = GrayFloatImage::from_dynamic(&image);
info!("Loaded a {} x {} image", image.width(), image.height());
let mut evolutions = self.allocate_evolutions(image.width(), image.height());
self.create_nonlinear_scale_space(&mut evolutions, &float_image);
trace!("Creating scale space finished.");
let keypoints = self.find_image_keypoints(&mut evolutions);
let descriptors = self.extract_descriptors(&evolutions, &keypoints);
trace!("Computing descriptors finished.");
(keypoints, descriptors)
}
pub fn extract_path(
&self,
path: impl AsRef<Path>,
) -> ImageResult<(Vec<KeyPoint>, Vec<BitArray<64>>)> {
Ok(self.extract(&::image::open(path)?))
}
}