akaze-util 0.1.0

Utilities for akaze-rust.
Documentation
#[macro_use]
extern crate log;

use akaze::match_features;
use akaze::types::evolution::Config;
use akaze::types::feature_match;
use akaze_util::*;
use clap::{App, Arg};
use std::path::Path;
use std::time::SystemTime;

fn main() {
    let matches = App::new("Extract and match KAZE image features..")
        .version("0.1")
        .about(
            "A Rust implementation of the KAZE visual feature extractor and matching.
       See https://github.com/pablofdezalc/kaze for the original authors' project. 
       Set AKAZE_LOG to debug for more verbose output. This executable runs the entire
       pipeline end-to-end for two images. For more granular control, see the binaries
       extract_features and match_features.",
        )
        .author("John Stalbaum")
        .arg(
            Arg::with_name("INPUT_0")
                .help("The first input image.")
                .required(true)
                .index(1),
        )
        .arg(
            Arg::with_name("INPUT_1")
                .help("The second input image.")
                .required(true)
                .index(2),
        )
        .arg(
            Arg::with_name("OUTPUT_PREFIX")
                .help("The output prefix for all files.")
                .required(true)
                .index(3),
        )
        .arg(
            Arg::with_name("match_image_path")
                .short("m")
                .long("match_image")
                .value_name("IMAGE_FILE_PATH")
                .help("Sets a path to write the match image to.")
                .takes_value(true),
        )
        .get_matches();

    let start = SystemTime::now();
    let env = env_logger::Env::default().filter_or("AKAZE_LOG", "info");
    env_logger::Builder::from_env(env).init();
    let input_path_0 = matches.value_of("INPUT_0").unwrap();
    let input_path_1 = matches.value_of("INPUT_1").unwrap();
    let output_prefix = matches.value_of("OUTPUT_PREFIX").unwrap();
    let threshold: f64 = matches
        .value_of("threshold")
        .unwrap_or("10")
        .parse()
        .unwrap();
    info!(
        "Input image paths are {}/{}, output extractions path is {}, threshold is {}.",
        input_path_0, input_path_1, output_prefix, threshold,
    );
    let options = Config::default();
    let prefix_string: String = output_prefix.to_owned();
    let mut extractions_0_path = prefix_string.clone();
    extractions_0_path.push_str("-extractions_0.cbor");
    let mut extractions_1_path = prefix_string.clone();
    extractions_1_path.push_str("-extractions_1.cbor");
    let mut matches_path = prefix_string.clone();
    matches_path.push_str("-matches.cbor");

    let (_, keypoints, descriptors) =
        akaze::extract_features(Path::new(input_path_0).to_owned(), options);
    let features_0 = Features {
        keypoints,
        descriptors,
    };
    serialize_features_to_file(&features_0, extractions_0_path)
        .expect("failed to write first image features to file");
    info!(
        "Done, extracted {} features from image 0.",
        features_0.keypoints.len()
    );

    let (_, keypoints, descriptors) =
        akaze::extract_features(Path::new(input_path_1).to_owned(), options);
    let features_1 = Features {
        keypoints,
        descriptors,
    };
    serialize_features_to_file(&features_1, extractions_1_path)
        .expect("failed to write second image features to file");
    info!(
        "Done, extracted {} features from image 1, proceeding with matching.",
        features_1.keypoints.len()
    );

    let output_matches = match_features(
        &features_0.keypoints,
        &features_0.descriptors,
        &features_1.keypoints,
        &features_1.descriptors,
        0.86,
        1000,
        3.0,
    );
    info!("Got {} matches.", output_matches.len());

    serialize_matches_to_file(&output_matches, matches_path)
        .expect("failed to write matches to file");
    match matches.value_of("match_image_path") {
        Some(match_image_path) => {
            info!("Writing scale space");
            let input_image_0 = image::open(input_path_0).unwrap().to_rgb();
            let input_image_1 = image::open(input_path_1).unwrap().to_rgb();
            let matches_image = feature_match::draw_matches(
                &input_image_0,
                &input_image_1,
                &features_0.keypoints,
                &features_1.keypoints,
                &output_matches,
            );
            match matches_image.save(match_image_path.to_owned()) {
                Ok(_val) => debug!("Wrote matches image successfully."),
                Err(_e) => debug!("Could not write matches image for some reason, skipping."),
            }
        }
        None => {
            debug!("Argument --match_image_path/-i was not given, not writing matches image.");
        }
    }
    debug!("Total duration: {:?}", start.elapsed().unwrap());
}