Browse Source

Fixed some formatting issues

Looking to do micro-benchmarks with the critereon library
develop
Drew Short 9 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"
docopt = "0.6.80"
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,
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);

4
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,
}
}
}

4
src/hash/dhash.rs

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

26
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
}

27
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<Vec<f64>> = 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<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();
// 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);
}
}

169
src/lib.rs

@ -20,58 +20,66 @@ use cache::Cache;
#[repr(C)]
pub struct PIHash<'a> {
cache: Option<Cache<'a>>
cache: Option<Cache<'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]
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);
})
}
}

62
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<pihash::hash::PerceptualHashes> = 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}
}
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