Browse Source

Modularized some code. Changed API to use a PIHash library struct for maintaining the cache information. FFI code currently broken, will bring back next version after I figure out how to pass pointers to Rust structs around.

develop
Drew Short 8 years ago
parent
commit
ebd2fdaa75
  1. 9
      Cargo.toml
  2. 8
      src/cache.rs
  3. 6
      src/hash/ahash.rs
  4. 6
      src/hash/dhash.rs
  5. 79
      src/hash/mod.rs
  6. 73
      src/hash/phash.rs
  7. 383
      src/lib.rs
  8. 16
      src/main.rs

9
Cargo.toml

@ -1,6 +1,6 @@
[package] [package]
name = "pihash" name = "pihash"
version = "0.2.9"
version = "0.3.0"
authors = ["Drew Short <warrick@sothr.com>"] authors = ["Drew Short <warrick@sothr.com>"]
description = "A simple library for generating perceptual hashes for images and comparing images based on their perceptual hashes." description = "A simple library for generating perceptual hashes for images and comparing images based on their perceptual hashes."
repository = "https://github.com/warricksothr/Perceptual-Image-Hashing/" repository = "https://github.com/warricksothr/Perceptual-Image-Hashing/"
@ -12,6 +12,13 @@ exclude = ["test_images/*"]
[lib] [lib]
crate-type = ["dylib", "rlib"] crate-type = ["dylib", "rlib"]
[features]
default = []
# Feature to allow bench testing on nightly
# But still allow the code to build on beta/stable
bench = []
[dependencies] [dependencies]
docopt = "0.6.78" docopt = "0.6.78"
rustc-serialize = "0.3.18" rustc-serialize = "0.3.18"

8
src/cache.rs

@ -21,7 +21,7 @@ use std::option::Option;
use std::result::Result; use std::result::Result;
use super::rustc_serialize::json; use super::rustc_serialize::json;
pub const CACHE_DIR: &'static str = "./.hash_cache";
pub const DEFAULT_CACHE_DIR: &'static str = "./.hash_cache";
const CACHED_IMAGE_EXT: &'static str = "png"; const CACHED_IMAGE_EXT: &'static str = "png";
const CACHED_MATRIX_EXT: &'static str = "dft"; const CACHED_MATRIX_EXT: &'static str = "dft";
// Caching version information // Caching version information
@ -60,7 +60,7 @@ pub struct Cache<'a> {
impl<'a> Default for Cache<'a> { impl<'a> Default for Cache<'a> {
fn default() -> Cache<'a> { fn default() -> Cache<'a> {
Cache { Cache {
cache_dir: CACHE_DIR,
cache_dir: DEFAULT_CACHE_DIR,
use_cache: true, use_cache: true,
} }
} }
@ -154,7 +154,7 @@ impl<'a> Cache<'a> {
match hash { match hash {
Ok(sha1) => { Ok(sha1) => {
let cache_path_str = format!("{}/image/{}x{}/{}.{}", let cache_path_str = format!("{}/image/{}x{}/{}.{}",
CACHE_DIR,
self.cache_dir,
size, size,
size, size,
sha1, sha1,
@ -297,7 +297,7 @@ impl<'a> Cache<'a> {
Ok(sha1) => { Ok(sha1) => {
// Check if the file exists in the cache // Check if the file exists in the cache
let cache_path_str = format!("{}/matrix/{}x{}/{}.{}", let cache_path_str = format!("{}/matrix/{}x{}/{}.{}",
CACHE_DIR,
self.cache_dir,
size, size,
size, size,
sha1, sha1,

6
src/hash/ahash.rs

@ -14,9 +14,9 @@ pub struct AHash<'a> {
} }
impl<'a> AHash<'a> { impl<'a> AHash<'a> {
pub fn new(path: &'a Path, precision: &Precision, cache: &'a Cache) -> Self {
pub fn new(path: &'a Path, precision: &Precision, cache: &Option<Box<Cache>>) -> Self {
AHash { AHash {
prepared_image: Box::new(prepare_image(&path, &HashType::AHash, &precision, &cache)),
prepared_image: Box::new(prepare_image(&path, &HashType::AHash, &precision, cache)),
} }
} }
} }
@ -29,7 +29,7 @@ impl<'a> PerceptualHash for AHash<'a> {
* *
* A u64 representing the value of the hash * A u64 representing the value of the hash
*/ */
fn get_hash(&self) -> u64 {
fn get_hash(&self, _: &Option<Box<Cache>>) -> u64 {
let (width, height) = self.prepared_image.image.dimensions(); let (width, height) = self.prepared_image.image.dimensions();
// calculating the average pixel value // calculating the average pixel value

6
src/hash/dhash.rs

@ -14,9 +14,9 @@ pub struct DHash<'a> {
} }
impl<'a> DHash<'a> { impl<'a> DHash<'a> {
pub fn new(path: &'a Path, precision: &Precision, cache: &'a Cache) -> Self {
pub fn new(path: &'a Path, precision: &Precision, cache: &Option<Box<Cache>>) -> Self {
DHash { DHash {
prepared_image: Box::new(prepare_image(&path, &HashType::DHash, &precision, &cache)),
prepared_image: Box::new(prepare_image(&path, &HashType::DHash, &precision, cache)),
} }
} }
} }
@ -29,7 +29,7 @@ impl<'a> PerceptualHash for DHash<'a> {
* *
* Returns a u64 representing the value of the hash * Returns a u64 representing the value of the hash
*/ */
fn get_hash(&self) -> u64 {
fn get_hash(&self, _: &Option<Box<Cache>>) -> u64 {
// Stored for later // Stored for later
let first_pixel_val = self.prepared_image.image.pixels().nth(0).unwrap().channels()[0]; let first_pixel_val = self.prepared_image.image.pixels().nth(0).unwrap().channels()[0];
let last_pixel_val = self.prepared_image.image.pixels().last().unwrap().channels()[0]; let last_pixel_val = self.prepared_image.image.pixels().last().unwrap().channels()[0];

79
src/hash/mod.rs

@ -44,7 +44,6 @@ const HAMMING_DISTANCE_SIMILARITY_LIMIT: u64 = 5u64;
pub struct PreparedImage<'a> { pub struct PreparedImage<'a> {
orig_path: &'a str, orig_path: &'a str,
image: image::ImageBuffer<image::Luma<u8>, Vec<u8>>, image: image::ImageBuffer<image::Luma<u8>, Vec<u8>>,
cache: &'a Cache<'a>,
} }
/** /**
@ -108,7 +107,7 @@ pub enum HashType {
// Traits // // Traits //
pub trait PerceptualHash { pub trait PerceptualHash {
fn get_hash(&self) -> u64;
fn get_hash(&self, cache: &Option<Box<Cache>>) -> u64;
} }
// Functions // // Functions //
@ -130,7 +129,7 @@ pub trait PerceptualHash {
pub fn prepare_image<'a>(path: &'a Path, pub fn prepare_image<'a>(path: &'a Path,
hash_type: &HashType, hash_type: &HashType,
precision: &Precision, precision: &Precision,
cache: &'a Cache<'a>)
cache: &Option<Box<Cache>>)
-> PreparedImage<'a> { -> PreparedImage<'a> {
let image_path = path.to_str().unwrap(); let image_path = path.to_str().unwrap();
let size: u32 = match *hash_type { let size: u32 = match *hash_type {
@ -138,29 +137,43 @@ pub fn prepare_image<'a>(path: &'a Path,
_ => precision.get_size(), _ => precision.get_size(),
}; };
// Check if we have the already converted image in a cache and use that if possible. // Check if we have the already converted image in a cache and use that if possible.
match cache.get_image_from_cache(&path, size) {
Some(image) => {
PreparedImage {
orig_path: &*image_path,
image: image,
cache: &cache,
match *cache {
Some(ref c) => {
match c.get_image_from_cache(&path, size) {
Some(image) => {
PreparedImage {
orig_path: &*image_path,
image: image,
}
}
None => {
let image = process_image(&path, size);
// Oh, and save it in a cache
match c.put_image_in_cache(&path, size, &image.image) {
Ok(_) => {}
Err(e) => println!("Unable to store image in cache. {}", e),
};
image
}
} }
}
None => {
// Otherwise let's do that work now and store it.
let image = image::open(path).unwrap();
let small_image = image.resize_exact(size, size, FilterType::Lanczos3);
let grey_image = small_image.to_luma();
match cache.put_image_in_cache(&path, size, &grey_image) {
Ok(_) => {}
Err(e) => println!("Unable to store image in cache. {}", e),
};
PreparedImage {
orig_path: &*image_path,
image: grey_image,
cache: &cache,
}
}
},
None => process_image(&path, size),
}
}
/**
* Turn the image into something we can work with
*/
fn process_image(path: &Path,
size: u32) -> PreparedImage{
// Otherwise let's do that work now and store it.
let image_path = path.to_str().unwrap();
let image = image::open(path).unwrap();
let small_image = image.resize_exact(size, size, FilterType::Lanczos3);
let grey_image = small_image.to_luma();
PreparedImage {
orig_path: &*image_path,
image: grey_image,
} }
} }
@ -170,12 +183,12 @@ pub fn prepare_image<'a>(path: &'a Path,
pub fn get_perceptual_hash<'a>(path: &'a Path, pub fn get_perceptual_hash<'a>(path: &'a Path,
precision: &Precision, precision: &Precision,
hash_type: &HashType, hash_type: &HashType,
cache: &Cache)
cache: &Option<Box<Cache>>)
-> u64 { -> u64 {
match *hash_type { match *hash_type {
HashType::AHash => ahash::AHash::new(&path, &precision, &cache).get_hash(),
HashType::DHash => dhash::DHash::new(&path, &precision, &cache).get_hash(),
HashType::PHash => phash::PHash::new(&path, &precision, &cache).get_hash(),
HashType::AHash => ahash::AHash::new(&path, &precision, &cache).get_hash(&cache),
HashType::DHash => dhash::DHash::new(&path, &precision, &cache).get_hash(&cache),
HashType::PHash => phash::PHash::new(&path, &precision, &cache).get_hash(&cache),
} }
} }
@ -184,12 +197,12 @@ pub fn get_perceptual_hash<'a>(path: &'a Path,
*/ */
pub fn get_perceptual_hashes<'a>(path: &'a Path, pub fn get_perceptual_hashes<'a>(path: &'a Path,
precision: &Precision, precision: &Precision,
cache: &Cache)
cache: &Option<Box<Cache>>)
-> PerceptualHashes<'a> { -> PerceptualHashes<'a> {
let image_path = path.to_str().unwrap(); let image_path = path.to_str().unwrap();
let ahash = ahash::AHash::new(&path, &precision, &cache).get_hash();
let dhash = dhash::DHash::new(&path, &precision, &cache).get_hash();
let phash = phash::PHash::new(&path, &precision, &cache).get_hash();
let ahash = ahash::AHash::new(&path, &precision, &cache).get_hash(&cache);
let dhash = dhash::DHash::new(&path, &precision, &cache).get_hash(&cache);
let phash = phash::PHash::new(&path, &precision, &cache).get_hash(&cache);
PerceptualHashes { PerceptualHashes {
orig_path: &*image_path, orig_path: &*image_path,
ahash: ahash, ahash: ahash,

73
src/hash/phash.rs

@ -16,9 +16,9 @@ pub struct PHash<'a> {
} }
impl<'a> PHash<'a> { impl<'a> PHash<'a> {
pub fn new(path: &'a Path, precision: &Precision, cache: &'a Cache) -> Self {
pub fn new(path: &'a Path, precision: &Precision, cache: &Option<Box<Cache>>) -> Self {
PHash { PHash {
prepared_image: Box::new(prepare_image(&path, &HashType::PHash, &precision, &cache)),
prepared_image: Box::new(prepare_image(&path, &HashType::PHash, &precision, cache)),
} }
} }
} }
@ -31,7 +31,7 @@ impl<'a> PerceptualHash for PHash<'a> {
* *
* Returns a u64 representing the value of the hash * Returns a u64 representing the value of the hash
*/ */
fn get_hash(&self) -> u64 {
fn get_hash(&self, cache: &Option<Box<Cache>>) -> u64 {
// Get the image data into a vector to perform the DFT on. // Get the image data into a vector to perform the DFT on.
let width = self.prepared_image.image.width() as usize; let width = self.prepared_image.image.width() as usize;
let height = self.prepared_image.image.height() as usize; let height = self.prepared_image.image.height() as usize;
@ -40,39 +40,23 @@ impl<'a> PerceptualHash for PHash<'a> {
// Either from the cache or calculate it // Either from the cache or calculate it
// Pretty fast already, so caching doesn't make a huge difference // Pretty fast already, so caching doesn't make a huge difference
// Atleast compared to opening and processing the images // Atleast compared to opening and processing the images
let mut data_matrix: Vec<Vec<f64>> = Vec::new();
match self.prepared_image
.cache
.get_matrix_from_cache(&Path::new(self.prepared_image.orig_path), width as u32) {
Some(matrix) => data_matrix = matrix,
None => {
// Preparing the results
for x in 0..width {
data_matrix.push(Vec::new());
for y in 0..height {
let pos_x = x as u32;
let pos_y = y as u32;
data_matrix[x]
.push(self.prepared_image
.image
.get_pixel(pos_x, pos_y)
.channels()[0] as f64);
let data_matrix: Vec<Vec<f64>> = match *cache {
Some(ref c) => {
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, &self.prepared_image);
// Store this DFT in the cache
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
} }
} }
// Perform the 2D DFT operation on our matrix
calculate_2d_dft(&mut data_matrix);
// Store this DFT in the cache
match self.prepared_image
.cache
.put_matrix_in_cache(&Path::new(self.prepared_image.orig_path),
width as u32,
&data_matrix) {
Ok(_) => {}
Err(e) => println!("Unable to store matrix in cache. {}", e),
};
}
}
},
None => create_data_matrix(width, height, &self.prepared_image)
};
// Only need the top left quadrant // Only need the top left quadrant
let target_width = (width / 4) as usize; let target_width = (width / 4) as usize;
@ -109,6 +93,27 @@ impl<'a> PerceptualHash for PHash<'a> {
} }
} }
fn create_data_matrix(width: usize, height: usize, prepared_image: &PreparedImage) -> Vec<Vec<f64>> {
let mut data_matrix: Vec<Vec<f64>> = Vec::new();
// Preparing the results
for x in 0..width {
data_matrix.push(Vec::new());
for y in 0..height {
let pos_x = x as u32;
let pos_y = y as u32;
data_matrix[x]
.push(prepared_image
.image
.get_pixel(pos_x, pos_y)
.channels()[0] as f64);
}
}
// Perform the 2D DFT operation on our matrix
calculate_2d_dft(&mut data_matrix);
data_matrix
}
// Use a 1D DFT to cacluate the 2D DFT. // Use a 1D DFT to cacluate the 2D DFT.
// //
// This is achieved by calculating the DFT for each row, then calculating the // This is achieved by calculating the DFT for each row, then calculating the

383
src/lib.rs

@ -5,138 +5,168 @@
extern crate libc; extern crate libc;
extern crate rustc_serialize; extern crate rustc_serialize;
#[cfg(feature = "bench")]
extern crate test;
pub mod hash; pub mod hash;
mod cache;
pub mod cache;
use std::path::Path; use std::path::Path;
use std::ffi::CStr; use std::ffi::CStr;
use cache::Cache; use cache::Cache;
static LIB_CACHE: Cache<'static> = Cache {
cache_dir: cache::CACHE_DIR,
use_cache: true,
};
pub struct PIHash<'a> {
cache: Option<Box<Cache<'a>>>
}
/**
* Prepare the library for work.
*
* Not performing this step may cause parts to fail.
*/
#[no_mangle]
pub extern "C" fn init() {
match LIB_CACHE.init() {
Ok(_) => {}
Err(e) => println!("Error: {}", e),
}
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
*/
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(Box::new(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::DHash,
&self.cache)
}
} }
/** /**
* Teardown for the library
* Get the Hamming Distance between two hashes.
* Represents the absolute difference between two numbers.
*/ */
#[no_mangle]
pub extern "C" fn teardown() {
match LIB_CACHE.clean() {
Ok(_) => {}
Err(e) => println!("Error: {}", e),
}
}
pub fn get_phashes(path: &Path) -> hash::PerceptualHashes {
hash::get_perceptual_hashes(path, &hash::Precision::Medium, &LIB_CACHE)
}
pub fn get_ahash(path: &Path) -> u64 {
hash::get_perceptual_hash(&path,
&hash::Precision::Medium,
&hash::HashType::AHash,
&LIB_CACHE)
}
pub fn get_dhash(path: &Path) -> u64 {
hash::get_perceptual_hash(&path,
&hash::Precision::Medium,
&hash::HashType::DHash,
&LIB_CACHE)
}
pub fn get_phash(path: &Path) -> u64 {
hash::get_perceptual_hash(&path,
&hash::Precision::Medium,
&hash::HashType::DHash,
&LIB_CACHE)
}
pub fn get_hamming_distance(hash1: u64, hash2: u64) -> u64 { pub fn get_hamming_distance(hash1: u64, hash2: u64) -> u64 {
hash::calculate_hamming_distance(hash1, hash2) hash::calculate_hamming_distance(hash1, hash2)
} }
// External proxies for the get_*hash methods // // External proxies for the get_*hash methods //
#[no_mangle]
pub extern "C" fn ext_get_ahash(path_char: *const libc::c_char) -> libc::uint64_t {
unsafe {
let path_str = CStr::from_ptr(path_char);
let image_path = match path_str.to_str() {
Ok(result) => result,
Err(e) => {
println!("Error: {}. Unable to parse '{}'",
e,
to_hex_string(path_str.to_bytes()));
panic!("Unable to parse path")
}
};
let path = Path::new(&image_path);
get_ahash(&path)
}
}
#[no_mangle]
pub extern "C" fn ext_get_dhash(path_char: *const libc::c_char) -> libc::uint64_t {
unsafe {
let path_str = CStr::from_ptr(path_char);
let image_path = match path_str.to_str() {
Ok(result) => result,
Err(e) => {
println!("Error: {}. Unable to parse '{}'",
e,
to_hex_string(path_str.to_bytes()));
panic!("Unable to parse path")
}
};
let path = Path::new(&image_path);
get_dhash(&path)
}
}
#[no_mangle]
pub extern "C" fn ext_get_phash(path_char: *const libc::c_char) -> libc::uint64_t {
unsafe {
let path_str = CStr::from_ptr(path_char);
let image_path = match path_str.to_str() {
Ok(result) => result,
Err(e) => {
println!("Error: {}. Unable to parse '{}'",
e,
to_hex_string(path_str.to_bytes()));
panic!("Unable to parse path")
}
};
let path = Path::new(&image_path);
get_phash(&path)
}
}
fn to_hex_string(bytes: &[u8]) -> String {
println!("length: {}", bytes.len());
let mut strs: Vec<String> = Vec::new();
for byte in bytes {
// println!("{:02x}", byte);
strs.push(format!("{:02x}", byte));
}
strs.join("\\x")
}
// /**
// * Prepare the library for work.
// *
// * Not performing this step may cause parts to fail.
// */
// #[no_mangle]
// pub extern "C" fn init() {
// match LIB_CACHE.init() {
// Ok(_) => {}
// Err(e) => println!("Error: {}", e),
// }
// }
// /**
// * Teardown for the library
// */
// #[no_mangle]
// pub extern "C" fn teardown() {
// match LIB_CACHE.clean() {
// Ok(_) => {}
// Err(e) => println!("Error: {}", e),
// }
// }
// #[no_mangle]
// pub extern "C" fn ext_get_ahash(path_char: *const libc::c_char) -> libc::uint64_t {
// unsafe {
// let path_str = CStr::from_ptr(path_char);
// let image_path = match path_str.to_str() {
// Ok(result) => result,
// Err(e) => {
// println!("Error: {}. Unable to parse '{}'",
// e,
// to_hex_string(path_str.to_bytes()));
// panic!("Unable to parse path")
// }
// };
// let path = Path::new(&image_path);
// get_ahash(&path)
// }
// }
// #[no_mangle]
// pub extern "C" fn ext_get_dhash(path_char: *const libc::c_char) -> libc::uint64_t {
// unsafe {
// let path_str = CStr::from_ptr(path_char);
// let image_path = match path_str.to_str() {
// Ok(result) => result,
// Err(e) => {
// println!("Error: {}. Unable to parse '{}'",
// e,
// to_hex_string(path_str.to_bytes()));
// panic!("Unable to parse path")
// }
// };
// let path = Path::new(&image_path);
// get_dhash(&path)
// }
// }
// #[no_mangle]
// pub extern "C" fn ext_get_phash(path_char: *const libc::c_char) -> libc::uint64_t {
// unsafe {
// let path_str = CStr::from_ptr(path_char);
// let image_path = match path_str.to_str() {
// Ok(result) => result,
// Err(e) => {
// println!("Error: {}. Unable to parse '{}'",
// e,
// to_hex_string(path_str.to_bytes()));
// panic!("Unable to parse path")
// }
// };
// let path = Path::new(&image_path);
// get_phash(&path)
// }
// }
// fn to_hex_string(bytes: &[u8]) -> String {
// println!("length: {}", bytes.len());
// let mut strs: Vec<String> = Vec::new();
// for byte in bytes {
// // println!("{:02x}", byte);
// strs.push(format!("{:02x}", byte));
// }
// strs.join("\\x")
// }
// Module for the tests // Module for the tests
// //
@ -146,6 +176,10 @@ mod tests {
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
use hash; use hash;
use cache;
use super::PIHash;
#[cfg(feature = "bench")]
use super::test::Bencher;
#[test] #[test]
fn test_can_get_test_images() { fn test_can_get_test_images() {
@ -179,13 +213,13 @@ mod tests {
fn test_imageset_hash(hash_type: hash::HashType, fn test_imageset_hash(hash_type: hash::HashType,
hash_precision: hash::Precision, hash_precision: hash::Precision,
image_paths: [&Path; 3], image_paths: [&Path; 3],
image_hashes: [u64; 3]) {
image_hashes: [u64; 3],
lib: &PIHash) {
for index in 0..image_paths.len() { for index in 0..image_paths.len() {
let image_path = image_paths[index]; let image_path = image_paths[index];
let calculated_hash = hash::get_perceptual_hash(&image_path,
let calculated_hash = lib.get_perceptual_hash(&image_path,
&hash_precision, &hash_precision,
&hash_type,
&super::LIB_CACHE);
&hash_type);
println!("Image hashes for '{}': expected: {} actual: {}", println!("Image hashes for '{}': expected: {} actual: {}",
image_path.to_str().unwrap(), image_path.to_str().unwrap(),
image_hashes[index], image_hashes[index],
@ -196,8 +230,8 @@ mod tests {
#[test] #[test]
fn test_confirm_ahash_results() { fn test_confirm_ahash_results() {
// Prep_Cache
super::init();
// Prep_library
let lib = PIHash::new(Some(cache::DEFAULT_CACHE_DIR));
// Sample_01 tests // Sample_01 tests
let sample_01_images: [&Path; 3] = [&Path::new("./test_images/sample_01_large.jpg"), let sample_01_images: [&Path; 3] = [&Path::new("./test_images/sample_01_large.jpg"),
@ -207,7 +241,8 @@ mod tests {
test_imageset_hash(hash::HashType::AHash, test_imageset_hash(hash::HashType::AHash,
hash::Precision::Medium, hash::Precision::Medium,
sample_01_images, sample_01_images,
sample_01_hashes);
sample_01_hashes,
&lib);
// Sample_02 tests // Sample_02 tests
let sample_02_images: [&Path; 3] = [&Path::new("./test_images/sample_02_large.jpg"), let sample_02_images: [&Path; 3] = [&Path::new("./test_images/sample_02_large.jpg"),
@ -219,7 +254,8 @@ mod tests {
test_imageset_hash(hash::HashType::AHash, test_imageset_hash(hash::HashType::AHash,
hash::Precision::Medium, hash::Precision::Medium,
sample_02_images, sample_02_images,
sample_02_hashes);
sample_02_hashes,
&lib);
// Sample_03 tests // Sample_03 tests
let sample_03_images: [&Path; 3] = [&Path::new("./test_images/sample_03_large.jpg"), let sample_03_images: [&Path; 3] = [&Path::new("./test_images/sample_03_large.jpg"),
@ -231,7 +267,8 @@ mod tests {
test_imageset_hash(hash::HashType::AHash, test_imageset_hash(hash::HashType::AHash,
hash::Precision::Medium, hash::Precision::Medium,
sample_03_images, sample_03_images,
sample_03_hashes);
sample_03_hashes,
&lib);
// Sample_04 tests // Sample_04 tests
let sample_04_images: [&Path; 3] = [&Path::new("./test_images/sample_04_large.jpg"), let sample_04_images: [&Path; 3] = [&Path::new("./test_images/sample_04_large.jpg"),
@ -243,7 +280,8 @@ mod tests {
test_imageset_hash(hash::HashType::AHash, test_imageset_hash(hash::HashType::AHash,
hash::Precision::Medium, hash::Precision::Medium,
sample_04_images, sample_04_images,
sample_04_hashes);
sample_04_hashes,
&lib);
// Clean_Cache // Clean_Cache
// super::teardown(); // super::teardown();
@ -251,8 +289,8 @@ mod tests {
#[test] #[test]
fn test_confirm_dhash_results() { fn test_confirm_dhash_results() {
// Prep_Cache
super::init();
// Prep_library
let lib = PIHash::new(Some(cache::DEFAULT_CACHE_DIR));
// Sample_01 tests // Sample_01 tests
let sample_01_images: [&Path; 3] = [&Path::new("./test_images/sample_01_large.jpg"), let sample_01_images: [&Path; 3] = [&Path::new("./test_images/sample_01_large.jpg"),
@ -264,7 +302,8 @@ mod tests {
test_imageset_hash(hash::HashType::DHash, test_imageset_hash(hash::HashType::DHash,
hash::Precision::Medium, hash::Precision::Medium,
sample_01_images, sample_01_images,
sample_01_hashes);
sample_01_hashes,
&lib);
// Sample_02 tests // Sample_02 tests
let sample_02_images: [&Path; 3] = [&Path::new("./test_images/sample_02_large.jpg"), let sample_02_images: [&Path; 3] = [&Path::new("./test_images/sample_02_large.jpg"),
@ -276,7 +315,8 @@ mod tests {
test_imageset_hash(hash::HashType::DHash, test_imageset_hash(hash::HashType::DHash,
hash::Precision::Medium, hash::Precision::Medium,
sample_02_images, sample_02_images,
sample_02_hashes);
sample_02_hashes,
&lib);
// Sample_03 tests // Sample_03 tests
let sample_03_images: [&Path; 3] = [&Path::new("./test_images/sample_03_large.jpg"), let sample_03_images: [&Path; 3] = [&Path::new("./test_images/sample_03_large.jpg"),
@ -288,7 +328,8 @@ mod tests {
test_imageset_hash(hash::HashType::DHash, test_imageset_hash(hash::HashType::DHash,
hash::Precision::Medium, hash::Precision::Medium,
sample_03_images, sample_03_images,
sample_03_hashes);
sample_03_hashes,
&lib);
// Sample_04 tests // Sample_04 tests
let sample_04_images: [&Path; 3] = [&Path::new("./test_images/sample_04_large.jpg"), let sample_04_images: [&Path; 3] = [&Path::new("./test_images/sample_04_large.jpg"),
@ -300,7 +341,8 @@ mod tests {
test_imageset_hash(hash::HashType::DHash, test_imageset_hash(hash::HashType::DHash,
hash::Precision::Medium, hash::Precision::Medium,
sample_04_images, sample_04_images,
sample_04_hashes);
sample_04_hashes,
&lib);
// Clean_Cache // Clean_Cache
// super::teardown(); // super::teardown();
@ -308,8 +350,8 @@ mod tests {
#[test] #[test]
fn test_confirm_phash_results() { fn test_confirm_phash_results() {
// Prep_Cache
super::init();
// Prep_library
let lib = PIHash::new(Some(cache::DEFAULT_CACHE_DIR));
// Sample_01 tests // Sample_01 tests
let sample_01_images: [&Path; 3] = [&Path::new("./test_images/sample_01_large.jpg"), let sample_01_images: [&Path; 3] = [&Path::new("./test_images/sample_01_large.jpg"),
@ -319,7 +361,8 @@ mod tests {
test_imageset_hash(hash::HashType::PHash, test_imageset_hash(hash::HashType::PHash,
hash::Precision::Medium, hash::Precision::Medium,
sample_01_images, sample_01_images,
sample_01_hashes);
sample_01_hashes,
&lib);
// Sample_02 tests // Sample_02 tests
let sample_02_images: [&Path; 3] = [&Path::new("./test_images/sample_02_large.jpg"), let sample_02_images: [&Path; 3] = [&Path::new("./test_images/sample_02_large.jpg"),
@ -331,7 +374,8 @@ mod tests {
test_imageset_hash(hash::HashType::PHash, test_imageset_hash(hash::HashType::PHash,
hash::Precision::Medium, hash::Precision::Medium,
sample_02_images, sample_02_images,
sample_02_hashes);
sample_02_hashes,
&lib);
// Sample_03 tests // Sample_03 tests
let sample_03_images: [&Path; 3] = [&Path::new("./test_images/sample_03_large.jpg"), let sample_03_images: [&Path; 3] = [&Path::new("./test_images/sample_03_large.jpg"),
@ -343,7 +387,8 @@ mod tests {
test_imageset_hash(hash::HashType::PHash, test_imageset_hash(hash::HashType::PHash,
hash::Precision::Medium, hash::Precision::Medium,
sample_03_images, sample_03_images,
sample_03_hashes);
sample_03_hashes,
&lib);
// Sample_04 tests // Sample_04 tests
let sample_04_images: [&Path; 3] = [&Path::new("./test_images/sample_04_large.jpg"), let sample_04_images: [&Path; 3] = [&Path::new("./test_images/sample_04_large.jpg"),
@ -355,9 +400,75 @@ mod tests {
test_imageset_hash(hash::HashType::PHash, test_imageset_hash(hash::HashType::PHash,
hash::Precision::Medium, hash::Precision::Medium,
sample_04_images, sample_04_images,
sample_04_hashes);
sample_04_hashes,
&lib);
// Clean_Cache // Clean_Cache
// super::teardown(); // super::teardown();
} }
#[cfg(feature = "bench")]
#[bench]
fn bench_with_cache(bench: &mut Bencher) -> () {
// 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_02_large.jpg"),&hash::Precision::Medium, &hash::HashType::PHash);
lib.get_perceptual_hash(&Path::new("./test_images/sample_03_large.jpg"),&hash::Precision::Medium, &hash::HashType::PHash);
lib.get_perceptual_hash(&Path::new("./test_images/sample_04_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::AHash);
lib.get_perceptual_hash(&Path::new("./test_images/sample_01_large.jpg"), &hash::Precision::Medium, &hash::HashType::DHash);
lib.get_perceptual_hash(&Path::new("./test_images/sample_01_large.jpg"), &hash::Precision::Medium, &hash::HashType::PHash);
// Sample_02 Bench
lib.get_perceptual_hash(&Path::new("./test_images/sample_02_large.jpg"), &hash::Precision::Medium, &hash::HashType::AHash);
lib.get_perceptual_hash(&Path::new("./test_images/sample_02_large.jpg"), &hash::Precision::Medium, &hash::HashType::DHash);
lib.get_perceptual_hash(&Path::new("./test_images/sample_02_large.jpg"), &hash::Precision::Medium, &hash::HashType::PHash);
// Sample_03 Bench
lib.get_perceptual_hash(&Path::new("./test_images/sample_02_large.jpg"), &hash::Precision::Medium, &hash::HashType::AHash);
lib.get_perceptual_hash(&Path::new("./test_images/sample_02_large.jpg"), &hash::Precision::Medium, &hash::HashType::DHash);
lib.get_perceptual_hash(&Path::new("./test_images/sample_02_large.jpg"), &hash::Precision::Medium, &hash::HashType::PHash);
// Sample_04 Bench
lib.get_perceptual_hash(&Path::new("./test_images/sample_04_large.jpg"), &hash::Precision::Medium, &hash::HashType::AHash);
lib.get_perceptual_hash(&Path::new("./test_images/sample_04_large.jpg"), &hash::Precision::Medium, &hash::HashType::DHash);
lib.get_perceptual_hash(&Path::new("./test_images/sample_04_large.jpg"), &hash::Precision::Medium, &hash::HashType::PHash);
})
}
#[cfg(feature = "bench")]
#[bench]
fn bench_without_cache(bench: &mut Bencher) -> () {
// 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::AHash);
lib.get_perceptual_hash(&Path::new("./test_images/sample_01_large.jpg"), &hash::Precision::Medium, &hash::HashType::DHash);
lib.get_perceptual_hash(&Path::new("./test_images/sample_01_large.jpg"), &hash::Precision::Medium, &hash::HashType::PHash);
// Sample_02 Bench
lib.get_perceptual_hash(&Path::new("./test_images/sample_02_large.jpg"), &hash::Precision::Medium, &hash::HashType::AHash);
lib.get_perceptual_hash(&Path::new("./test_images/sample_02_large.jpg"), &hash::Precision::Medium, &hash::HashType::DHash);
lib.get_perceptual_hash(&Path::new("./test_images/sample_02_large.jpg"), &hash::Precision::Medium, &hash::HashType::PHash);
// Sample_03 Bench
lib.get_perceptual_hash(&Path::new("./test_images/sample_02_large.jpg"), &hash::Precision::Medium, &hash::HashType::AHash);
lib.get_perceptual_hash(&Path::new("./test_images/sample_02_large.jpg"), &hash::Precision::Medium, &hash::HashType::DHash);
lib.get_perceptual_hash(&Path::new("./test_images/sample_02_large.jpg"), &hash::Precision::Medium, &hash::HashType::PHash);
// Sample_04 Bench
lib.get_perceptual_hash(&Path::new("./test_images/sample_04_large.jpg"), &hash::Precision::Medium, &hash::HashType::AHash);
lib.get_perceptual_hash(&Path::new("./test_images/sample_04_large.jpg"), &hash::Precision::Medium, &hash::HashType::DHash);
lib.get_perceptual_hash(&Path::new("./test_images/sample_04_large.jpg"), &hash::Precision::Medium, &hash::HashType::PHash);
})
}
} }

16
src/main.rs

@ -54,16 +54,16 @@ fn main() {
} }
// Init the hashing library // Init the hashing library
pihash::init();
let lib = pihash::PIHash::new(Some(pihash::cache::DEFAULT_CACHE_DIR));
// println!("{:?}", args); // println!("{:?}", args);
if args.arg_comparison.len() > 0 { if args.arg_comparison.len() > 0 {
let base_image_path = Path::new(&args.arg_path); let base_image_path = Path::new(&args.arg_path);
let base_hash = get_requested_perceptual_hashes(&base_image_path, &args);
let base_hash = get_requested_perceptual_hashes(&lib, &base_image_path, &args);
let mut comparison_hashes: Vec<pihash::hash::PerceptualHashes> = Vec::new(); let mut comparison_hashes: Vec<pihash::hash::PerceptualHashes> = Vec::new();
for index in 0..args.arg_comparison.len() { for index in 0..args.arg_comparison.len() {
comparison_hashes.push(get_requested_perceptual_hashes(&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(); let mut similar_images: Vec<&str> = Vec::new();
@ -82,7 +82,7 @@ fn main() {
} else { } else {
let image_path = Path::new(&args.arg_path); let image_path = Path::new(&args.arg_path);
let hashes = get_requested_perceptual_hashes(&image_path, &args);
let hashes = get_requested_perceptual_hashes(&lib, &image_path, &args);
let hash_result = format!(r#" let hash_result = format!(r#"
file: {} file: {}
ahash: {} ahash: {}
@ -102,21 +102,21 @@ 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)
} }
fn get_requested_perceptual_hashes<'a>(image_path: &'a Path, args: &Args) -> pihash::hash::PerceptualHashes<'a> {
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) { let ahash = if args.flag_ahash || flags_get_all_perceptual_hashes(&args) {
pihash::get_ahash(&image_path)
lib.get_ahash(&image_path)
} else { } else {
0u64 0u64
}; };
let dhash = if args.flag_dhash || flags_get_all_perceptual_hashes(&args) { let dhash = if args.flag_dhash || flags_get_all_perceptual_hashes(&args) {
pihash::get_ahash(&image_path)
lib.get_ahash(&image_path)
} else { } else {
0u64 0u64
}; };
let phash = if args.flag_phash || flags_get_all_perceptual_hashes(&args) { let phash = if args.flag_phash || flags_get_all_perceptual_hashes(&args) {
pihash::get_ahash(&image_path)
lib.get_ahash(&image_path)
} else { } else {
0u64 0u64
}; };

Loading…
Cancel
Save