Browse Source

Rustfmt and code cleanup

develop
Drew Short 7 years ago
parent
commit
487401a45a
  1. 29
      src/cache.rs
  2. 4
      src/hash/ahash.rs
  3. 4
      src/hash/dhash.rs
  4. 24
      src/hash/mod.rs
  5. 29
      src/hash/phash.rs
  6. 198
      src/lib.rs
  7. 66
      src/main.rs

29
src/cache.rs

@ -253,11 +253,10 @@ impl<'a> Cache<'a> {
let mut compressor = ZlibEncoder::new(Vec::new(),
Compression::Default);
for row in file_contents {
let mut row_str = row.iter().fold(String::new(),
|acc, &item| {
acc +
&format!("{},", item)
});
let mut row_str =
row.iter()
.fold(String::new(),
|acc, &item| acc + &format!("{},", item));
// remove the last comma
let desire_len = row_str.len() - 1;
row_str.truncate(desire_len);
@ -319,17 +318,15 @@ impl<'a> Cache<'a> {
}
};
// convert the matrix
let matrix: Vec<Vec<f64>> = matrix_data_str.trim()
.split("\n")
.map(|line| {
line.split(",")
.map(|f| {
f64::from_str(f)
.unwrap()
})
.collect()
})
.collect();
let matrix: Vec<Vec<f64>> = matrix_data_str
.trim()
.split("\n")
.map(|line| {
line.split(",")
.map(|f| f64::from_str(f).unwrap())
.collect()
})
.collect();
Some(matrix)
}

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

24
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);
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
}

29
src/hash/phash.rs

@ -44,20 +44,23 @@ 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
@ -78,27 +81,26 @@ impl<'a> PerceptualHash for PHash<'a> {
// Calculating a hash based on the mean
let mut hash = 0u64;
for x in 0..target_width {
// println!("Mean: {} Values: {:?}",mean,data_matrix[x]);
for y in 0..target_height {
if data_matrix[x][y] >= mean {
hash |= 1;
// println!("Pixel {} is >= {} therefore {:b}", pixel_sum, mean, hash);
} else {
hash |= 0;
// println!("Pixel {} is < {} therefore {:b}", pixel_sum, mean, hash);
}
hash <<= 1;
}
}
// 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 +108,7 @@ 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);
}
}

198
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
*/
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)
}
/**
* 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)
}
}
/**
@ -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());
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]
@ -172,7 +180,7 @@ fn to_hex_string(bytes: &[u8]) -> String {
// Module for the tests
//
#[cfg(test)]
#[cfg(test)]
mod tests {
use std::fs;
@ -194,11 +202,11 @@ mod tests {
Some(_) => {
if ext.unwrap() == "jpg" {
num_paths += 1;
println!("Is a image {}: {:?}", num_paths, orig_path) ;
println!("Is a image {}: {:?}", num_paths, orig_path);
}
}
_ => {
println!("Not an image: {:?}", orig_path) ;
println!("Not an image: {:?}", orig_path);
continue;
}
}
@ -221,31 +229,29 @@ 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],
calculated_hash);
assert_eq!(calculated_hash,image_hashes[index]);
assert_eq!(calculated_hash, image_hashes[index]);
hashes[index] = calculated_hash;
}
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);
}
}
}
}
}
@ -258,9 +264,7 @@ mod tests {
let sample_01_images: [&Path; 3] = [&Path::new("./test_images/sample_01_large.jpg"),
&Path::new("./test_images/sample_01_medium.jpg"),
&Path::new("./test_images/sample_01_small.jpg")];
let sample_01_hashes: [u64; 3] = [857051991849750,
857051991849750,
857051992374038];
let sample_01_hashes: [u64; 3] = [857051991849750, 857051991849750, 857051992374038];
test_imageset_hash(hash::HashType::AHash,
hash::Precision::Medium,
1u64,
@ -286,9 +290,8 @@ mod tests {
let sample_03_images: [&Path; 3] = [&Path::new("./test_images/sample_03_large.jpg"),
&Path::new("./test_images/sample_03_medium.jpg"),
&Path::new("./test_images/sample_03_small.jpg")];
let sample_03_hashes: [u64; 3] = [135670932300497406,
135670932300497406,
135670932300497406];
let sample_03_hashes: [u64; 3] =
[135670932300497406, 135670932300497406, 135670932300497406];
test_imageset_hash(hash::HashType::AHash,
hash::Precision::Medium,
1u64,
@ -351,9 +354,8 @@ mod tests {
let sample_03_images: [&Path; 3] = [&Path::new("./test_images/sample_03_large.jpg"),
&Path::new("./test_images/sample_03_medium.jpg"),
&Path::new("./test_images/sample_03_small.jpg")];
let sample_03_hashes: [u64; 3] = [262683193365159876,
225528496439353284,
225528496435158982];
let sample_03_hashes: [u64; 3] =
[262683193365159876, 225528496439353284, 225528496435158982];
test_imageset_hash(hash::HashType::DHash,
hash::Precision::Medium,
4u64,
@ -388,9 +390,7 @@ mod tests {
let sample_01_images: [&Path; 3] = [&Path::new("./test_images/sample_01_large.jpg"),
&Path::new("./test_images/sample_01_medium.jpg"),
&Path::new("./test_images/sample_01_small.jpg")];
let sample_01_hashes: [u64; 3] = [72357778504597504,
72357778504597504,
72357778504597504];
let sample_01_hashes: [u64; 3] = [72357778504597504, 72357778504597504, 72357778504597504];
test_imageset_hash(hash::HashType::PHash,
hash::Precision::Medium,
0u64,
@ -447,28 +447,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);
})
}
}

66
src/main.rs

@ -44,8 +44,8 @@ struct Args {
fn main() {
let args: Args = Docopt::new(USAGE)
.and_then(|d| d.decode())
.unwrap_or_else(|e| e.exit());
.and_then(|d| d.decode())
.unwrap_or_else(|e| e.exit());
// Print version information and exit
if args.flag_version {
@ -63,7 +63,10 @@ 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 +96,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