Browse Source

Fixed some formatting issues

Looking to do micro-benchmarks with the critereon library
develop
Drew Short 8 years ago
parent
commit
0b28ddeb4c
  1. 3
      Cargo.toml
  2. 2
      src/cache.rs
  3. 4
      src/hash/ahash.rs
  4. 4
      src/hash/dhash.rs
  5. 26
      src/hash/mod.rs
  6. 27
      src/hash/phash.rs
  7. 169
      src/lib.rs
  8. 62
      src/main.rs

3
Cargo.toml

@ -27,4 +27,5 @@ image = "0.9.0"
num = "0.1.32" num = "0.1.32"
docopt = "0.6.80" docopt = "0.6.80"
flate2 = "0.2.13" flate2 = "0.2.13"
sha1 = "0.1.1"
sha1 = "0.1.1"
criterion-stats = "0.1.0"

2
src/cache.rs

@ -161,7 +161,7 @@ impl<'a> Cache<'a> {
sha1, sha1,
CACHED_IMAGE_EXT); CACHED_IMAGE_EXT);
let cache_dir_str = format!("{}/image/{}x{}", self.cache_dir, size, size); 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) { match create_dir_all(cache_dir_str) {
Ok(_) => { Ok(_) => {
let cached_path = Path::new(&cache_path_str); let cached_path = Path::new(&cache_path_str);

4
src/hash/ahash.rs

@ -60,8 +60,8 @@ impl<'a> PerceptualHash for AHash<'a> {
} }
// println!("Hash for {} is {}", prepared_image.orig_path, hash); // println!("Hash for {} is {}", prepared_image.orig_path, hash);
hash hash
},
None => 0u64
}
None => 0u64,
} }
} }
} }

4
src/hash/dhash.rs

@ -61,8 +61,8 @@ impl<'a> PerceptualHash for DHash<'a> {
} }
hash hash
},
None => 0u64
}
None => 0u64,
} }
} }
} }

26
src/hash/mod.rs

@ -59,10 +59,13 @@ pub struct PerceptualHashes<'a> {
impl<'a> PerceptualHashes<'a> { impl<'a> PerceptualHashes<'a> {
pub fn similar(&self, other: &'a PerceptualHashes<'a>) -> bool { pub fn similar(&self, other: &'a PerceptualHashes<'a>) -> bool {
if self.orig_path != other.orig_path && 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 { } else {
false false
} }
@ -146,7 +149,7 @@ pub fn prepare_image<'a>(path: &'a Path,
image: Some(image), image: Some(image),
} }
} }
None => {
None => {
let processed_image = process_image(&image_path, size); let processed_image = process_image(&image_path, size);
// Oh, and save it in a cache // Oh, and save it in a cache
match processed_image.image { match processed_image.image {
@ -155,13 +158,13 @@ pub fn prepare_image<'a>(path: &'a Path,
Ok(_) => {} Ok(_) => {}
Err(e) => println!("Unable to store image in cache. {}", e), Err(e) => println!("Unable to store image in cache. {}", e),
}; };
},
}
None => {} None => {}
}; };
processed_image processed_image
} }
} }
},
}
None => process_image(&image_path, size), 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 * 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. // 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)) { let image = match image::open(Path::new(image_path)) {
Ok(image) => { Ok(image) => {
let small_image = image.resize_exact(size, size, FilterType::Lanczos3); let small_image = image.resize_exact(size, size, FilterType::Lanczos3);
Some(small_image.to_luma()) Some(small_image.to_luma())
},
Err(e) => {
}
Err(e) => {
println!("Error Processing Image [{}]: {} ", image_path, e); println!("Error Processing Image [{}]: {} ", image_path, e);
None None
} }

27
src/hash/phash.rs

@ -44,20 +44,24 @@ impl<'a> PerceptualHash for PHash<'a> {
// Atleast compared to opening and processing the images // Atleast compared to opening and processing the images
let data_matrix: Vec<Vec<f64>> = match *cache { let data_matrix: Vec<Vec<f64>> = match *cache {
Some(ref c) => { 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, Some(matrix) => matrix,
None => { None => {
let matrix = create_data_matrix(width, height, &image); let matrix = create_data_matrix(width, height, &image);
// Store this DFT in the cache // 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(_) => {} Ok(_) => {}
Err(e) => println!("Unable to store matrix in cache. {}", e), Err(e) => println!("Unable to store matrix in cache. {}", e),
}; };
matrix matrix
} }
} }
},
None => create_data_matrix(width, height, &image)
}
None => create_data_matrix(width, height, &image),
}; };
// Only need the top left quadrant // 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); // println!("Hash for {} is {}", prepared_image.orig_path, hash);
hash hash
},
None => 0u64
}
None => 0u64,
} }
} }
} }
fn create_data_matrix(width: usize, height: usize, image: &super::image::ImageBuffer<super::image::Luma<u8>, Vec<u8>>) -> Vec<Vec<f64>> {
fn create_data_matrix(width: usize,
height: usize,
image: &super::image::ImageBuffer<super::image::Luma<u8>, Vec<u8>>)
-> Vec<Vec<f64>> {
let mut data_matrix: Vec<Vec<f64>> = Vec::new(); let mut data_matrix: Vec<Vec<f64>> = Vec::new();
// Preparing the results // Preparing the results
for x in 0..width { 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 { for y in 0..height {
let pos_x = x as u32; let pos_x = x as u32;
let pos_y = y 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);
} }
} }

169
src/lib.rs

@ -20,58 +20,66 @@ use cache::Cache;
#[repr(C)] #[repr(C)]
pub struct PIHash<'a> { pub struct PIHash<'a> {
cache: Option<Cache<'a>>
cache: Option<Cache<'a>>,
} }
impl<'a> PIHash<'a> { 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] #[no_mangle]
pub extern "C" fn ext_init(cache_path_char: *const libc::c_char) -> *const libc::c_void { 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_cstr = CStr::from_ptr(cache_path_char);
let path_str = match path_cstr.to_str() { 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 lib = Box::new(PIHash::new(path_str));
let ptr = Box::into_raw(lib) as *mut libc::c_void; let ptr = Box::into_raw(lib) as *mut libc::c_void;
ptr ptr
}
}
} }
#[no_mangle] #[no_mangle]
pub extern "C" fn ext_free(raw_lib: *const libc::c_void) { 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] #[no_mangle]
@ -221,9 +229,7 @@ mod tests {
let mut hashes: [u64; 3] = [0; 3]; let mut hashes: [u64; 3] = [0; 3];
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 = 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: [{}]", println!("Image hashes for [{}] expected: [{}] actual: [{}]",
image_path.to_str().unwrap(), image_path.to_str().unwrap(),
image_hashes[index], image_hashes[index],
@ -233,19 +239,20 @@ mod tests {
} }
for index in 0..hashes.len() { 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")] #[cfg(feature = "bench")]
#[bench] #[bench]
fn bench_with_cache(bench: &mut Bencher) -> () { fn bench_with_cache(bench: &mut Bencher) -> () {
// Prep_library
// Prep_library
let lib = PIHash::new(Some(cache::DEFAULT_CACHE_DIR)); let lib = PIHash::new(Some(cache::DEFAULT_CACHE_DIR));
// Setup the caches to make sure we're good to properly bench // Setup the caches to make sure we're good to properly bench
// All phashes so that the matricies are pulled from cache as well // 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(|| { 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")] #[cfg(feature = "bench")]
#[bench] #[bench]
fn bench_without_cache(bench: &mut Bencher) -> () { fn bench_without_cache(bench: &mut Bencher) -> () {
// Prep_library
// Prep_library
let lib = PIHash::new(None); let lib = PIHash::new(None);
bench.iter(|| { 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);
}) })
} }
} }

62
src/main.rs

@ -16,7 +16,7 @@ const VERSION: &'static str = env!("CARGO_PKG_VERSION");
// The usage description // The usage description
const USAGE: &'static str = " const USAGE: &'static str = "
Perceptual Image Hashing (pihash) 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 input file to a set of other images and return a list of the similar
images. images.
@ -63,7 +63,8 @@ fn main() {
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(&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(); let mut similar_images: Vec<&str> = Vec::new();
@ -93,36 +94,41 @@ fn main() {
hashes.ahash, hashes.ahash,
hashes.dhash, hashes.dhash,
hashes.phash); hashes.phash);
println!("{}", hash_result);
println!("{}", hash_result);
} }
} }
fn flags_get_all_perceptual_hashes(args: &Args) -> bool { 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)
(!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}
}
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,
}
}
Loading…
Cancel
Save