From 0b28ddeb4c0096fea92f3a821567796593ccdf44 Mon Sep 17 00:00:00 2001 From: Drew Short Date: Tue, 19 Apr 2016 22:40:36 -0500 Subject: [PATCH] Fixed some formatting issues Looking to do micro-benchmarks with the critereon library --- Cargo.toml | 3 +- src/cache.rs | 2 +- src/hash/ahash.rs | 4 +- src/hash/dhash.rs | 4 +- src/hash/mod.rs | 26 +++---- src/hash/phash.rs | 27 +++++--- src/lib.rs | 169 +++++++++++++++++++++++++--------------------- src/main.rs | 62 +++++++++-------- 8 files changed, 162 insertions(+), 135 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0c74e27..11e9df4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,4 +27,5 @@ image = "0.9.0" num = "0.1.32" docopt = "0.6.80" flate2 = "0.2.13" -sha1 = "0.1.1" \ No newline at end of file +sha1 = "0.1.1" +criterion-stats = "0.1.0" \ No newline at end of file diff --git a/src/cache.rs b/src/cache.rs index cb93a15..0549020 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -161,7 +161,7 @@ impl<'a> Cache<'a> { sha1, CACHED_IMAGE_EXT); let cache_dir_str = format!("{}/image/{}x{}", self.cache_dir, size, size); - //println!("Saving: {}", cache_path_str); + // println!("Saving: {}", cache_path_str); match create_dir_all(cache_dir_str) { Ok(_) => { let cached_path = Path::new(&cache_path_str); diff --git a/src/hash/ahash.rs b/src/hash/ahash.rs index 06db899..6e32cdc 100644 --- a/src/hash/ahash.rs +++ b/src/hash/ahash.rs @@ -60,8 +60,8 @@ impl<'a> PerceptualHash for AHash<'a> { } // println!("Hash for {} is {}", prepared_image.orig_path, hash); hash - }, - None => 0u64 + } + None => 0u64, } } } diff --git a/src/hash/dhash.rs b/src/hash/dhash.rs index bc73b70..d5748d8 100644 --- a/src/hash/dhash.rs +++ b/src/hash/dhash.rs @@ -61,8 +61,8 @@ impl<'a> PerceptualHash for DHash<'a> { } hash - }, - None => 0u64 + } + None => 0u64, } } } diff --git a/src/hash/mod.rs b/src/hash/mod.rs index dd9e71e..4cb40ae 100644 --- a/src/hash/mod.rs +++ b/src/hash/mod.rs @@ -59,10 +59,13 @@ pub struct PerceptualHashes<'a> { impl<'a> PerceptualHashes<'a> { pub fn similar(&self, other: &'a PerceptualHashes<'a>) -> bool { if self.orig_path != other.orig_path && - calculate_hamming_distance(self.ahash, other.ahash) <= HAMMING_DISTANCE_SIMILARITY_LIMIT && - calculate_hamming_distance(self.dhash, other.dhash) <= HAMMING_DISTANCE_SIMILARITY_LIMIT && - calculate_hamming_distance(self.phash, other.phash) <= HAMMING_DISTANCE_SIMILARITY_LIMIT { - true + calculate_hamming_distance(self.ahash, other.ahash) <= + HAMMING_DISTANCE_SIMILARITY_LIMIT && + calculate_hamming_distance(self.dhash, other.dhash) <= + HAMMING_DISTANCE_SIMILARITY_LIMIT && + calculate_hamming_distance(self.phash, other.phash) <= + HAMMING_DISTANCE_SIMILARITY_LIMIT { + true } else { false } @@ -146,7 +149,7 @@ pub fn prepare_image<'a>(path: &'a Path, image: Some(image), } } - None => { + None => { let processed_image = process_image(&image_path, size); // Oh, and save it in a cache match processed_image.image { @@ -155,13 +158,13 @@ pub fn prepare_image<'a>(path: &'a Path, Ok(_) => {} Err(e) => println!("Unable to store image in cache. {}", e), }; - }, + } None => {} }; processed_image } } - }, + } None => process_image(&image_path, size), } } @@ -169,16 +172,15 @@ pub fn prepare_image<'a>(path: &'a Path, /** * Turn the image into something we can work with */ -fn process_image<'a>(image_path: &'a str, - size: u32) -> PreparedImage<'a> { +fn process_image<'a>(image_path: &'a str, size: u32) -> PreparedImage<'a> { // Otherwise let's do that work now and store it. - //println!("Path: {}", image_path); + // println!("Path: {}", image_path); let image = match image::open(Path::new(image_path)) { Ok(image) => { let small_image = image.resize_exact(size, size, FilterType::Lanczos3); Some(small_image.to_luma()) - }, - Err(e) => { + } + Err(e) => { println!("Error Processing Image [{}]: {} ", image_path, e); None } diff --git a/src/hash/phash.rs b/src/hash/phash.rs index 5e12c43..d529b5a 100644 --- a/src/hash/phash.rs +++ b/src/hash/phash.rs @@ -44,20 +44,24 @@ impl<'a> PerceptualHash for PHash<'a> { // Atleast compared to opening and processing the images let data_matrix: Vec> = match *cache { Some(ref c) => { - match c.get_matrix_from_cache(&Path::new(self.prepared_image.orig_path), width as u32) { + match c.get_matrix_from_cache(&Path::new(self.prepared_image.orig_path), + width as u32) { Some(matrix) => matrix, None => { let matrix = create_data_matrix(width, height, &image); // Store this DFT in the cache - match c.put_matrix_in_cache(&Path::new(self.prepared_image.orig_path), width as u32, &matrix) { + match c.put_matrix_in_cache(&Path::new(self.prepared_image + .orig_path), + width as u32, + &matrix) { Ok(_) => {} Err(e) => println!("Unable to store matrix in cache. {}", e), }; matrix } } - }, - None => create_data_matrix(width, height, &image) + } + None => create_data_matrix(width, height, &image), }; // Only need the top left quadrant @@ -92,13 +96,16 @@ impl<'a> PerceptualHash for PHash<'a> { } // println!("Hash for {} is {}", prepared_image.orig_path, hash); hash - }, - None => 0u64 + } + None => 0u64, } } } -fn create_data_matrix(width: usize, height: usize, image: &super::image::ImageBuffer, Vec>) -> Vec> { +fn create_data_matrix(width: usize, + height: usize, + image: &super::image::ImageBuffer, Vec>) + -> Vec> { let mut data_matrix: Vec> = Vec::new(); // Preparing the results for x in 0..width { @@ -106,10 +113,8 @@ fn create_data_matrix(width: usize, height: usize, image: &super::image::ImageBu for y in 0..height { let pos_x = x as u32; let pos_y = y as u32; - data_matrix[x] - .push(image - .get_pixel(pos_x, pos_y) - .channels()[0] as f64); + data_matrix[x].push(image.get_pixel(pos_x, pos_y) + .channels()[0] as f64); } } diff --git a/src/lib.rs b/src/lib.rs index 570d470..660f0ed 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,58 +20,66 @@ use cache::Cache; #[repr(C)] pub struct PIHash<'a> { - cache: Option> + cache: Option>, } impl<'a> PIHash<'a> { - /** - * Create a new pihash library, and initialize a cache of a path is passed. If none is passed then no cache is initialized or used with the library + /** + * Create a new pihash library, and initialize a cache of a path is passed. + * If none is passed then no cache is initialized or used with the library */ - pub fn new(cache_path: Option<&'a str>) -> PIHash<'a> { - match cache_path { - Some(path) => { - let cache = Cache { cache_dir: path, use_cache: true}; - match cache.init() { - Ok(_) => PIHash { cache: Some(cache) }, - Err(e) => { - println!("Error creating library with cache: {}", e); - PIHash { cache: None } - } - } - }, - None => PIHash { cache: None }, - } - } - - pub fn get_perceptual_hash(&self, path: &Path, precision: &hash::Precision, hash_type: &hash::HashType) -> u64 { - hash::get_perceptual_hash(&path, &precision, &hash_type, &self.cache) - } - - pub fn get_phashes(&self, path: &'a Path) -> hash::PerceptualHashes { - hash::get_perceptual_hashes(path, &hash::Precision::Medium, &self.cache) - } - - - pub fn get_ahash(&self, path: &Path) -> u64 { - hash::get_perceptual_hash(&path, - &hash::Precision::Medium, - &hash::HashType::AHash, - &self.cache) - } - - pub fn get_dhash(&self, path: &Path) -> u64 { - hash::get_perceptual_hash(&path, - &hash::Precision::Medium, - &hash::HashType::DHash, - &self.cache) - } - - pub fn get_phash(&self, path: &Path) -> u64 { - hash::get_perceptual_hash(&path, - &hash::Precision::Medium, - &hash::HashType::PHash, - &self.cache) - } + pub fn new(cache_path: Option<&'a str>) -> PIHash<'a> { + match cache_path { + Some(path) => { + let cache = Cache { + cache_dir: path, + use_cache: true, + }; + match cache.init() { + Ok(_) => PIHash { cache: Some(cache) }, + Err(e) => { + println!("Error creating library with cache: {}", e); + PIHash { cache: None } + } + } + } + None => PIHash { cache: None }, + } + } + + pub fn get_perceptual_hash(&self, + path: &Path, + precision: &hash::Precision, + hash_type: &hash::HashType) + -> u64 { + hash::get_perceptual_hash(&path, &precision, &hash_type, &self.cache) + } + + pub fn get_phashes(&self, path: &'a Path) -> hash::PerceptualHashes { + hash::get_perceptual_hashes(path, &hash::Precision::Medium, &self.cache) + } + + + pub fn get_ahash(&self, path: &Path) -> u64 { + hash::get_perceptual_hash(&path, + &hash::Precision::Medium, + &hash::HashType::AHash, + &self.cache) + } + + pub fn get_dhash(&self, path: &Path) -> u64 { + hash::get_perceptual_hash(&path, + &hash::Precision::Medium, + &hash::HashType::DHash, + &self.cache) + } + + pub fn get_phash(&self, path: &Path) -> u64 { + hash::get_perceptual_hash(&path, + &hash::Precision::Medium, + &hash::HashType::PHash, + &self.cache) + } } /** @@ -86,24 +94,24 @@ pub fn get_hamming_distance(hash1: u64, hash2: u64) -> u64 { #[no_mangle] pub extern "C" fn ext_init(cache_path_char: *const libc::c_char) -> *const libc::c_void { - unsafe { + unsafe { let path_cstr = CStr::from_ptr(cache_path_char); let path_str = match path_cstr.to_str() { - Ok(path) => Some(path), - Err(_) => None, + Ok(path) => Some(path), + Err(_) => None, }; - //println!("Created new lib, with cache at {}", path_str.unwrap()); + // println!("Created new lib, with cache at {}", path_str.unwrap()); let lib = Box::new(PIHash::new(path_str)); let ptr = Box::into_raw(lib) as *mut libc::c_void; ptr - } + } } #[no_mangle] pub extern "C" fn ext_free(raw_lib: *const libc::c_void) { - unsafe { - drop(Box::from_raw(raw_lib as *mut PIHash)); - } + unsafe { + drop(Box::from_raw(raw_lib as *mut PIHash)); + } } #[no_mangle] @@ -221,9 +229,7 @@ mod tests { let mut hashes: [u64; 3] = [0; 3]; for index in 0..image_paths.len() { let image_path = image_paths[index]; - let calculated_hash = lib.get_perceptual_hash(&image_path, - &hash_precision, - &hash_type); + let calculated_hash = lib.get_perceptual_hash(&image_path, &hash_precision, &hash_type); println!("Image hashes for [{}] expected: [{}] actual: [{}]", image_path.to_str().unwrap(), image_hashes[index], @@ -233,19 +239,20 @@ mod tests { } for index in 0..hashes.len() { - for index2 in 0..hashes.len() { - if index == index2 { - continue; - } else { - let distance = hash::calculate_hamming_distance(hashes[index], hashes[index2]); - println!("Hashes [{}] and [{}] have a hamming distance of [{}] of a max allowed distance of [{}]", - hashes[index], - hashes[index2], - distance, - max_hamming_distance); - assert!(distance <= max_hamming_distance); + for index2 in 0..hashes.len() { + if index == index2 { + continue; + } else { + let distance = hash::calculate_hamming_distance(hashes[index], hashes[index2]); + println!("Hashes [{}] and [{}] have a hamming distance of [{}] of a max \ + allowed distance of [{}]", + hashes[index], + hashes[index2], + distance, + max_hamming_distance); + assert!(distance <= max_hamming_distance); + } } - } } } @@ -443,28 +450,34 @@ mod tests { #[cfg(feature = "bench")] #[bench] fn bench_with_cache(bench: &mut Bencher) -> () { - // Prep_library + // Prep_library let lib = PIHash::new(Some(cache::DEFAULT_CACHE_DIR)); // Setup the caches to make sure we're good to properly bench // All phashes so that the matricies are pulled from cache as well - lib.get_perceptual_hash(&Path::new("./test_images/sample_01_large.jpg"),&hash::Precision::Medium, &hash::HashType::PHash); + lib.get_perceptual_hash(&Path::new("./test_images/sample_01_large.jpg"), + &hash::Precision::Medium, + &hash::HashType::PHash); bench.iter(|| { - // Sample_01 Bench - lib.get_perceptual_hash(&Path::new("./test_images/sample_01_large.jpg"), &hash::Precision::Medium, &hash::HashType::PHash); + // Sample_01 Bench + lib.get_perceptual_hash(&Path::new("./test_images/sample_01_large.jpg"), + &hash::Precision::Medium, + &hash::HashType::PHash); }) } #[cfg(feature = "bench")] #[bench] fn bench_without_cache(bench: &mut Bencher) -> () { - // Prep_library + // Prep_library let lib = PIHash::new(None); bench.iter(|| { - // Sample_01 Bench - lib.get_perceptual_hash(&Path::new("./test_images/sample_01_large.jpg"), &hash::Precision::Medium, &hash::HashType::PHash); + // Sample_01 Bench + lib.get_perceptual_hash(&Path::new("./test_images/sample_01_large.jpg"), + &hash::Precision::Medium, + &hash::HashType::PHash); }) } } diff --git a/src/main.rs b/src/main.rs index 842eb3b..2eddbdc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,7 @@ const VERSION: &'static str = env!("CARGO_PKG_VERSION"); // The usage description const USAGE: &'static str = " Perceptual Image Hashing (pihash) -Calculate the perceptual hash values for an input or compare the +Calculate the perceptual hash values for an input or compare the input file to a set of other images and return a list of the similar images. @@ -63,7 +63,8 @@ fn main() { let mut comparison_hashes: Vec = Vec::new(); for index in 0..args.arg_comparison.len() { - comparison_hashes.push(get_requested_perceptual_hashes(&lib, &Path::new(&args.arg_comparison[index]), &args)); + comparison_hashes.push(get_requested_perceptual_hashes(&lib, + &Path::new(&args.arg_comparison[index]), &args)); } let mut similar_images: Vec<&str> = Vec::new(); @@ -93,36 +94,41 @@ fn main() { hashes.ahash, hashes.dhash, hashes.phash); - println!("{}", hash_result); + println!("{}", hash_result); } } fn flags_get_all_perceptual_hashes(args: &Args) -> bool { (args.flag_ahash && args.flag_dhash && args.flag_phash) || - (!args.flag_ahash && !args.flag_dhash && !args.flag_phash) + (!args.flag_ahash && !args.flag_dhash && !args.flag_phash) } -fn get_requested_perceptual_hashes<'a>(lib: &pihash::PIHash, image_path: &'a Path, args: &Args) -> pihash::hash::PerceptualHashes<'a> { - let ahash = if args.flag_ahash || flags_get_all_perceptual_hashes(&args) { - lib.get_ahash(&image_path) - } else { - 0u64 - }; - - let dhash = if args.flag_dhash || flags_get_all_perceptual_hashes(&args) { - lib.get_ahash(&image_path) - } else { - 0u64 - }; - - let phash = if args.flag_phash || flags_get_all_perceptual_hashes(&args) { - lib.get_ahash(&image_path) - } else { - 0u64 - }; - - pihash::hash::PerceptualHashes {orig_path: image_path.to_str().unwrap(), - ahash: ahash, - dhash: dhash, - phash: phash} -} \ No newline at end of file +fn get_requested_perceptual_hashes<'a>(lib: &pihash::PIHash, + image_path: &'a Path, + args: &Args) + -> pihash::hash::PerceptualHashes<'a> { + let ahash = if args.flag_ahash || flags_get_all_perceptual_hashes(&args) { + lib.get_ahash(&image_path) + } else { + 0u64 + }; + + let dhash = if args.flag_dhash || flags_get_all_perceptual_hashes(&args) { + lib.get_ahash(&image_path) + } else { + 0u64 + }; + + let phash = if args.flag_phash || flags_get_all_perceptual_hashes(&args) { + lib.get_ahash(&image_path) + } else { + 0u64 + }; + + pihash::hash::PerceptualHashes { + orig_path: image_path.to_str().unwrap(), + ahash: ahash, + dhash: dhash, + phash: phash, + } +}