Browse Source

Style changes

Changed the repo to ignore the rs.bk files produced by rustfmt
Ran RustFmt on the repository to cleanup the code
develop
Drew Short 8 years ago
parent
commit
0d128cb8aa
  1. 3
      .gitignore
  2. 74
      src/cache.rs
  3. 203
      src/hash.rs
  4. 387
      src/lib.rs
  5. 33
      src/main.rs

3
.gitignore

@ -22,3 +22,6 @@ Cargo.lock
# Ignore sublime workspace files
*.sublime-workspace
#Rustfmt backup files
*.rs.bk

74
src/cache.rs

@ -17,7 +17,7 @@ use std::result::Result;
const CACHE_DIR: &'static str = "./.hash_cache";
const CACHE_FILE_EXT: &'static str = "png";
// Creates the required directories
// Creates the required directories
pub fn prep_cache() -> Result<(), Error> {
create_dir_all(CACHE_DIR)
}
@ -42,78 +42,92 @@ fn get_file_hash(path: &Path) -> Result<String, Error> {
/**
* Put an image buffer in the cache
*/
pub fn put_image_in_cache(path: &Path, size: u32, image: &ImageBuffer<image::Luma<u8>, Vec<u8>>) {
pub fn put_image_in_cache(path: &Path, size: u32, image: &ImageBuffer<image::Luma<u8>, Vec<u8>>) {
let hash = get_file_hash(&path);
match hash {
Ok(sha1) => {
let cache_path_str = format!("{}/{}x{}_{}.{}",CACHE_DIR, size, size, sha1, CACHE_FILE_EXT);
let cache_path_str = format!("{}/{}x{}_{}.{}",
CACHE_DIR,
size,
size,
sha1,
CACHE_FILE_EXT);
let cached_path = Path::new(&cache_path_str);
// Save the file into the cache
match image.save(cached_path) {
Ok(_) => {},
Ok(_) => {}
Err(e) => println!("Error: {}", e),
}
},
Err(e) => println!("Error: {}", e),
}
Err(e) => println!("Error: {}", e),
}
}
/**
* Expects a slice of slices that represents lines in the file
*/
pub fn put_matrix_in_cache(path: &Path, size: u32, extension: &str, file_contents: &Vec<Vec<f64>>) {
pub fn put_matrix_in_cache(path: &Path,
size: u32,
extension: &str,
file_contents: &Vec<Vec<f64>>) {
let hash = get_file_hash(&path);
match hash {
Ok(sha1) => {
let cache_path_str = format!("{}/{}x{}_{}.{}",CACHE_DIR, size, size, sha1, extension);
let cache_path_str = format!("{}/{}x{}_{}.{}", CACHE_DIR, size, size, sha1, extension);
let cached_path = Path::new(&cache_path_str);
// Save the file into the cache
match File::create(&cached_path) {
Ok(mut file) => {
for row in file_contents {
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;
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);
row_str.push_str("\n");
file.write(&row_str.into_bytes());
}
file.flush();
},
Err(_) => {},
}
Err(_) => {}
}
},
Err(e) => println!("Error: {}", e),
}
Err(e) => println!("Error: {}", e),
}
}
/**
* Get an image buffer out of the cache
*/
pub fn get_image_from_cache(path: &Path, size: u32) -> Option<ImageBuffer<image::Luma<u8>, Vec<u8>>> {
pub fn get_image_from_cache(path: &Path,
size: u32)
-> Option<ImageBuffer<image::Luma<u8>, Vec<u8>>> {
let hash = get_file_hash(&path);
match hash {
Ok(sha1) => {
// Check if the file exists in the cache
let cache_path_str = format!("{}/{}x{}_{}.{}",CACHE_DIR, size, size, sha1, CACHE_FILE_EXT);
let cache_path_str = format!("{}/{}x{}_{}.{}",
CACHE_DIR,
size,
size,
sha1,
CACHE_FILE_EXT);
let cached_path = Path::new(&cache_path_str);
// Try to open, if it does, then we can read the image in
match File::open(&cached_path) {
Ok(_) => {
let image = image::open(&cached_path).unwrap();
Some(image.to_luma())
},
}
// Don't really care here, it just means an existing cached
// file doesn't exist, or can't be read.
Err(_) => {
None
},
Err(_) => None,
}
},
}
Err(e) => {
println!("Error: {}", e);
None
},
}
}
}
@ -125,7 +139,7 @@ pub fn get_matrix_from_cache(path: &Path, size: u32, extension: &str) -> Option<
match hash {
Ok(sha1) => {
// Check if the file exists in the cache
let cache_path_str = format!("{}/{}x{}_{}.{}",CACHE_DIR, size, size, sha1, extension);
let cache_path_str = format!("{}/{}x{}_{}.{}", CACHE_DIR, size, size, sha1, extension);
let cached_path = Path::new(&cache_path_str);
// Try to open, if it does, then we can read the image in
match File::open(&cached_path) {
@ -134,20 +148,18 @@ pub fn get_matrix_from_cache(path: &Path, size: u32, extension: &str) -> Option<
let mut matrix_data: Vec<u8> = Vec::new();
file.read_to_end(&mut matrix_data);
let matrix_data_str = String::from_utf8(matrix_data);
//convert the matrix
// convert the matrix
Some(matrix)
},
}
// Don't really care here, it just means an existing cached
// file doesn't exist, or can't be read.
Err(_) => {
None
},
Err(_) => None,
}
},
}
Err(e) => {
println!("Error: {}", e);
None
},
}
}
}

203
src/hash.rs

@ -10,11 +10,7 @@ extern crate complex;
use std::path::Path;
use std::f64;
use self::image::{
GenericImage,
Pixel,
FilterType
};
use self::image::{GenericImage, Pixel, FilterType};
use self::dft::Transform;
use cache;
@ -40,7 +36,7 @@ const FLOAT_PRECISION_MIN_5: f64 = f64::MIN / 100000_f64;
*/
pub struct PreparedImage<'a> {
orig_path: &'a str,
image: image::ImageBuffer<image::Luma<u8>,Vec<u8>>
image: image::ImageBuffer<image::Luma<u8>, Vec<u8>>,
}
/**
@ -50,7 +46,7 @@ pub struct PerceptualHashes<'a> {
pub orig_path: &'a str,
pub ahash: u64,
pub dhash: u64,
pub phash: u64
pub phash: u64,
}
/**
@ -61,14 +57,13 @@ pub struct PerceptualHashes<'a> {
* High aims for 128 bit precision
*/
pub enum Precision {
Low,
Low,
Medium,
High,
}
/*
* Get the size of the required image
*/
// Get the size of the required image
//
impl Precision {
fn get_size(&self) -> u32 {
match *self {
@ -85,7 +80,7 @@ impl Precision {
pub enum HashType {
Ahash,
Dhash,
Phash
Phash,
}
/**
@ -102,25 +97,34 @@ pub enum HashType {
* A PreparedImage struct with the required information for performing hashing
*
*/
pub fn prepare_image<'a>(path: &'a Path, hash_type: &HashType, precision: &Precision) -> PreparedImage<'a> {
pub fn prepare_image<'a>(path: &'a Path,
hash_type: &HashType,
precision: &Precision)
-> PreparedImage<'a> {
let image_path = path.to_str().unwrap();
let size: u32 = match *hash_type {
HashType::Phash => precision.get_size() * 4,
_ => precision.get_size()
_ => precision.get_size(),
};
// 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 }
},
PreparedImage {
orig_path: &*image_path,
image: 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();
cache::put_image_in_cache(&path, size, &grey_image);
PreparedImage { orig_path: &*image_path, image: grey_image }
},
PreparedImage {
orig_path: &*image_path,
image: grey_image,
}
}
}
}
@ -132,7 +136,12 @@ pub fn get_perceptual_hashes<'a>(path: &'a Path, precision: &Precision) -> Perce
let ahash = AHash::new(&path, &precision).get_hash();
let dhash = DHash::new(&path, &precision).get_hash();
let phash = PHash::new(&path, &precision).get_hash();
PerceptualHashes { orig_path: &*image_path, ahash: ahash, dhash: dhash, phash: phash }
PerceptualHashes {
orig_path: &*image_path,
ahash: ahash,
dhash: dhash,
phash: phash,
}
}
/**
@ -147,8 +156,8 @@ pub fn calculate_hamming_distance(hash1: u64, hash2: u64) -> u64 {
let mut hamming = 0u64;
for bit in bin_diff_str.chars() {
match bit {
'1' => hamming+=1,
_ => continue
'1' => hamming += 1,
_ => continue,
}
}
hamming
@ -170,12 +179,12 @@ impl<'a> AHash<'a> {
impl<'a> PerceptualHash for AHash<'a> {
/**
* Calculate the ahash of the provided prepared image.
*
* # Returns
*
* A u64 representing the value of the hash
*/
* Calculate the ahash of the provided prepared image.
*
* # Returns
*
* A u64 representing the value of the hash
*/
fn get_hash(&self) -> u64 {
let (width, height) = self.prepared_image.image.dimensions();
@ -183,11 +192,11 @@ impl<'a> PerceptualHash for AHash<'a> {
let mut total = 0u64;
for pixel in self.prepared_image.image.pixels() {
let channels = pixel.channels();
//println!("Pixel is: {}", channels[0]);
// println!("Pixel is: {}", channels[0]);
total += channels[0] as u64;
}
let mean = total / (width*height) as u64;
//println!("Mean for {} is {}", prepared_image.orig_path, mean);
let mean = total / (width * height) as u64;
// println!("Mean for {} is {}", prepared_image.orig_path, mean);
// Calculating a hash based on the mean
let mut hash = 0u64;
@ -196,14 +205,14 @@ impl<'a> PerceptualHash for AHash<'a> {
let pixel_sum = channels[0] as u64;
if pixel_sum >= mean {
hash |= 1;
//println!("Pixel {} is >= {} therefore {:b}", pixel_sum, mean, hash);
// println!("Pixel {} is >= {} therefore {:b}", pixel_sum, mean, hash);
} else {
hash |= 0;
//println!("Pixel {} is < {} therefore {:b}", pixel_sum, mean, hash);
// println!("Pixel {} is < {} therefore {:b}", pixel_sum, mean, hash);
}
hash <<= 1;
}
//println!("Hash for {} is {}", prepared_image.orig_path, hash);
// println!("Hash for {} is {}", prepared_image.orig_path, hash);
hash
}
@ -255,7 +264,7 @@ impl<'a> PerceptualHash for DHash<'a> {
hash |= 1;
} else {
hash |= 0;
}
}
hash
}
@ -281,9 +290,9 @@ impl<'a> PerceptualHash for PHash<'a> {
*/
fn get_hash(&self) -> u64 {
// Get the image data into a vector to perform the DFT on.
let width = self.prepared_image.image.width() as usize;
let height = self.prepared_image.image.height() as usize;
let width = self.prepared_image.image.width() as usize;
let height = self.prepared_image.image.height() as usize;
// Get 2d data to 2d FFT/DFT
let mut data_matrix: Vec<Vec<f64>> = Vec::new();
for x in 0..width {
@ -291,67 +300,70 @@ impl<'a> PerceptualHash for PHash<'a> {
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);
data_matrix[x]
.push(self.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);
// Store this DFT in the cache
cache::put_matrix_in_cache(&Path::new(self.prepared_image.orig_path),width as u32,&"dft",&data_matrix);
cache::put_matrix_in_cache(&Path::new(self.prepared_image.orig_path),
width as u32,
&"dft",
&data_matrix);
// Only need the top left quadrant
let target_width = (width / 4) as usize;
let target_height = (height / 4) as usize;
let dft_width = (width / 4) as f64;
let dft_height = (height / 4) as f64;
//Calculate the mean
// Calculate the mean
let mut total = 0f64;
for x in 0..target_width {
for y in 0..target_height {
total += data_matrix[x][y];
}
}
let mean = total / (dft_width * dft_height);
let mean = total / (dft_width * dft_height);
// Calculating a hash based on the mean
let mut hash = 0u64;
for x in 0..target_width {
// println!("Mean: {} Values: {:?}",mean,data_matrix[x]);
// 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);
// println!("Pixel {} is >= {} therefore {:b}", pixel_sum, mean, hash);
} else {
hash |= 0;
//println!("Pixel {} is < {} therefore {:b}", pixel_sum, mean, hash);
// println!("Pixel {} is < {} therefore {:b}", pixel_sum, mean, hash);
}
hash <<= 1;
}
}
//println!("Hash for {} is {}", prepared_image.orig_path, hash);
// println!("Hash for {} is {}", prepared_image.orig_path, hash);
hash
}
}
/*
* Use a 1D DFT to cacluate the 2D DFT.
*
* This is achieved by calculating the DFT for each row, then calculating the
* DFT for each column of DFT row data. This means that a 32x32 image with have
* 1024 1D DFT operations performed on it. (Slightly caclulation intensive)
*
* This operation is in place on the data in the provided vector
*
* Inspired by:
* http://www.inf.ufsc.br/~visao/khoros/html-dip/c5/s2/front-page.html
*
* Checked with:
* http://calculator.vhex.net/post/calculator-result/2d-discrete-fourier-transform
*/
fn calculate_2d_dft(data_matrix: &mut Vec<Vec<f64>>){
//println!("{:?}", data_matrix);
// Use a 1D DFT to cacluate the 2D DFT.
//
// This is achieved by calculating the DFT for each row, then calculating the
// DFT for each column of DFT row data. This means that a 32x32 image with have
// 1024 1D DFT operations performed on it. (Slightly caclulation intensive)
//
// This operation is in place on the data in the provided vector
//
// Inspired by:
// http://www.inf.ufsc.br/~visao/khoros/html-dip/c5/s2/front-page.html
//
// Checked with:
// http://calculator.vhex.net/post/calculator-result/2d-discrete-fourier-transform
//
fn calculate_2d_dft(data_matrix: &mut Vec<Vec<f64>>) {
// println!("{:?}", data_matrix);
let width = data_matrix.len();
let height = data_matrix[0].len();
@ -363,13 +375,13 @@ fn calculate_2d_dft(data_matrix: &mut Vec<Vec<f64>>){
for y in 0..height {
column.push(data_matrix[x][y]);
}
// Perform the DCT on this column
//println!("column[{}] before: {:?}", x, column);
// println!("column[{}] before: {:?}", x, column);
let forward_plan = dft::Plan::new(dft::Operation::Forward, column.len());
column.transform(&forward_plan);
let complex_column = dft::unpack(&column);
//println!("column[{}] after: {:?}", x, complex_column);
// println!("column[{}] after: {:?}", x, complex_column);
complex_data_matrix.push(complex_column);
}
@ -380,10 +392,10 @@ fn calculate_2d_dft(data_matrix: &mut Vec<Vec<f64>>){
row.push(complex_data_matrix[x][y]);
}
// Perform DCT on the row
//println!("row[{}] before: {:?}", y, row);
// println!("row[{}] before: {:?}", y, row);
let forward_plan = dft::Plan::new(dft::Operation::Forward, row.len());
row.transform(&forward_plan);
//println!("row[{}] after: {:?}", y, row);
// println!("row[{}] after: {:?}", y, row);
// Put the row values back
for x in 0..width {
@ -394,45 +406,40 @@ fn calculate_2d_dft(data_matrix: &mut Vec<Vec<f64>>){
fn round_float(f: f64) -> f64 {
if f >= FLOAT_PRECISION_MAX_1 || f <= FLOAT_PRECISION_MIN_1 {
f
}
else if f >= FLOAT_PRECISION_MAX_2 || f <= FLOAT_PRECISION_MIN_2 {
(f * 10_f64).round() / 10_f64
}
else if f >= FLOAT_PRECISION_MAX_3 || f <= FLOAT_PRECISION_MIN_3 {
(f * 100_f64).round() / 100_f64
}
else if f >= FLOAT_PRECISION_MAX_4 || f <= FLOAT_PRECISION_MIN_4 {
(f * 1000_f64).round() / 1000_f64
}
else if f >= FLOAT_PRECISION_MAX_5 || f <= FLOAT_PRECISION_MIN_5 {
(f * 10000_f64).round() / 10000_f64
}
else {
(f * 100000_f64).round() / 100000_f64
f
} else if f >= FLOAT_PRECISION_MAX_2 || f <= FLOAT_PRECISION_MIN_2 {
(f * 10_f64).round() / 10_f64
} else if f >= FLOAT_PRECISION_MAX_3 || f <= FLOAT_PRECISION_MIN_3 {
(f * 100_f64).round() / 100_f64
} else if f >= FLOAT_PRECISION_MAX_4 || f <= FLOAT_PRECISION_MIN_4 {
(f * 1000_f64).round() / 1000_f64
} else if f >= FLOAT_PRECISION_MAX_5 || f <= FLOAT_PRECISION_MIN_5 {
(f * 10000_f64).round() / 10000_f64
} else {
(f * 100000_f64).round() / 100000_f64
}
}
#[test]
fn test_2d_dft() {
let mut test_matrix: Vec<Vec<f64>> = Vec::new();
test_matrix.push(vec![1f64,1f64,1f64,3f64]);
test_matrix.push(vec![1f64,2f64,2f64,1f64]);
test_matrix.push(vec![1f64,2f64,2f64,1f64]);
test_matrix.push(vec![3f64,1f64,1f64,1f64]);
println!("{:?}",test_matrix[0]);
println!("{:?}",test_matrix[1]);
println!("{:?}",test_matrix[2]);
println!("{:?}",test_matrix[3]);
test_matrix.push(vec![1f64, 1f64, 1f64, 3f64]);
test_matrix.push(vec![1f64, 2f64, 2f64, 1f64]);
test_matrix.push(vec![1f64, 2f64, 2f64, 1f64]);
test_matrix.push(vec![3f64, 1f64, 1f64, 1f64]);
println!("{:?}", test_matrix[0]);
println!("{:?}", test_matrix[1]);
println!("{:?}", test_matrix[2]);
println!("{:?}", test_matrix[3]);
println!("Performing 2d DFT");
calculate_2d_dft(&mut test_matrix);
println!("{:?}",test_matrix[0]);
println!("{:?}",test_matrix[1]);
println!("{:?}",test_matrix[2]);
println!("{:?}",test_matrix[3]);
println!("{:?}", test_matrix[0]);
println!("{:?}", test_matrix[1]);
println!("{:?}", test_matrix[2]);
println!("{:?}", test_matrix[3]);
assert!(test_matrix[0][0] == 24_f64);
assert!(test_matrix[0][1] == 0_f64);

387
src/lib.rs

@ -16,7 +16,7 @@ use hash::PerceptualHash;
*/
pub fn init() {
match cache::prep_cache() {
Ok(_) => {},
Ok(_) => {}
Err(e) => println!("Error: {}", e),
}
}
@ -26,7 +26,7 @@ pub fn init() {
*/
pub fn teardown() {
match cache::clear_cache() {
Ok(_) => {},
Ok(_) => {}
Err(e) => println!("Error: {}", e),
}
}
@ -51,16 +51,15 @@ pub fn get_hamming_distance(hash1: u64, hash2: u64) -> u64 {
hash::calculate_hamming_distance(hash1, hash2)
}
/*
* Module for the tests
*/
// Module for the tests
//
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use std::path;
use hash;
use hash;
#[test]
fn test_can_get_test_images() {
@ -70,18 +69,18 @@ mod tests {
let orig_path = path.unwrap().path();
let ext = path::Path::new(&orig_path).extension();
match ext {
Some(_) => {
Some(_) => {
if ext.unwrap() == "jpg" {
num_paths += 1;
println!("Is a image {}: {:?}", num_paths, orig_path) ;
}
},
}
_ => {
println!("Not an image: {:?}", orig_path) ;
continue
continue;
}
}
//println!("Name: {}", path.unwrap().path().display())
// println!("Name: {}", path.unwrap().path().display())
}
// Currently 12 images in the test imaages directory
assert!(num_paths == 12);
@ -89,34 +88,48 @@ mod tests {
// Simple function for the unit tests to succinctly test a set of images
// that are organized in the fashion of large->medium->small
fn test_imageset_hash(
large_phash: &hash::PerceptualHash,
medium_phash: &hash::PerceptualHash,
small_phash: &hash::PerceptualHash,
expected_large_hash: u64,
expected_medium_hash: u64,
expected_small_hash: u64,
expected_large_medium_hamming: u64,
expected_large_small_hamming: u64,
expected_medium_small_hamming: u64) {
fn test_imageset_hash(large_phash: &hash::PerceptualHash,
medium_phash: &hash::PerceptualHash,
small_phash: &hash::PerceptualHash,
expected_large_hash: u64,
expected_medium_hash: u64,
expected_small_hash: u64,
expected_large_medium_hamming: u64,
expected_large_small_hamming: u64,
expected_medium_small_hamming: u64) {
let actual_large_hash = large_phash.get_hash();
let actual_medium_hash = medium_phash.get_hash();
let actual_small_hash = small_phash.get_hash();
// println for the purpose of debugging
println!("Large Image: expected: {} actual: {}", expected_large_hash, actual_large_hash);
println!("Medium Image: expected: {} actual: {}", expected_medium_hash, actual_medium_hash);
println!("Small Image: expected: {} actual: {}", expected_small_hash, actual_small_hash);
let actual_large_medium_hamming = hash::calculate_hamming_distance(actual_large_hash, actual_medium_hash);
let actual_large_small_hamming = hash::calculate_hamming_distance(actual_large_hash, actual_small_hash);
let actual_medium_small_hamming = hash::calculate_hamming_distance(actual_medium_hash, actual_small_hash);
println!("Large-Medium Hamming Distance: expected: {} actual: {}", expected_large_medium_hamming, actual_large_medium_hamming);
println!("Large-Small Hamming Distance: expected: {} actual: {}", expected_large_small_hamming, actual_large_small_hamming);
println!("Medium-Small Hamming Distance: expected: {} actual: {}", expected_medium_small_hamming, actual_medium_small_hamming);
println!("Large Image: expected: {} actual: {}",
expected_large_hash,
actual_large_hash);
println!("Medium Image: expected: {} actual: {}",
expected_medium_hash,
actual_medium_hash);
println!("Small Image: expected: {} actual: {}",
expected_small_hash,
actual_small_hash);
let actual_large_medium_hamming = hash::calculate_hamming_distance(actual_large_hash,
actual_medium_hash);
let actual_large_small_hamming = hash::calculate_hamming_distance(actual_large_hash,
actual_small_hash);
let actual_medium_small_hamming = hash::calculate_hamming_distance(actual_medium_hash,
actual_small_hash);
println!("Large-Medium Hamming Distance: expected: {} actual: {}",
expected_large_medium_hamming,
actual_large_medium_hamming);
println!("Large-Small Hamming Distance: expected: {} actual: {}",
expected_large_small_hamming,
actual_large_small_hamming);
println!("Medium-Small Hamming Distance: expected: {} actual: {}",
expected_medium_small_hamming,
actual_medium_small_hamming);
// Doing that asserts
assert!(actual_large_hash == expected_large_hash);
assert!(actual_medium_hash == expected_medium_hash);
@ -134,58 +147,66 @@ mod tests {
super::init();
// Sample_01 tests
test_imageset_hash(
&hash::AHash::new(path::Path::new("./test_images/sample_01_large.jpg"), &hash::Precision::Medium),
&hash::AHash::new(path::Path::new("./test_images/sample_01_medium.jpg"), &hash::Precision::Medium),
&hash::AHash::new(path::Path::new("./test_images/sample_01_small.jpg"), &hash::Precision::Medium),
857051991849750,
857051991849750,
857051991849750,
0u64,
0u64,
0u64
);
test_imageset_hash(&hash::AHash::new(path::Path::new("./test_images/sample_01_large.jpg"),
&hash::Precision::Medium),
&hash::AHash::new(path::Path::new("./test_images/sample_01_medium.\
jpg"),
&hash::Precision::Medium),
&hash::AHash::new(path::Path::new("./test_images/sample_01_small.jpg"),
&hash::Precision::Medium),
857051991849750,
857051991849750,
857051991849750,
0u64,
0u64,
0u64);
// Sample_02 tests
test_imageset_hash(
&hash::AHash::new(path::Path::new("./test_images/sample_02_large.jpg"), &hash::Precision::Medium),
&hash::AHash::new(path::Path::new("./test_images/sample_02_medium.jpg"), &hash::Precision::Medium),
&hash::AHash::new(path::Path::new("./test_images/sample_02_small.jpg"), &hash::Precision::Medium),
18446744073441116160,
18446744073441116160,
18446744073441116160,
0u64,
0u64,
0u64
);
test_imageset_hash(&hash::AHash::new(path::Path::new("./test_images/sample_02_large.jpg"),
&hash::Precision::Medium),
&hash::AHash::new(path::Path::new("./test_images/sample_02_medium.\
jpg"),
&hash::Precision::Medium),
&hash::AHash::new(path::Path::new("./test_images/sample_02_small.jpg"),
&hash::Precision::Medium),
18446744073441116160,
18446744073441116160,
18446744073441116160,
0u64,
0u64,
0u64);
// Sample_03 tests
test_imageset_hash(
&hash::AHash::new(path::Path::new("./test_images/sample_03_large.jpg"), &hash::Precision::Medium),
&hash::AHash::new(path::Path::new("./test_images/sample_03_medium.jpg"), &hash::Precision::Medium),
&hash::AHash::new(path::Path::new("./test_images/sample_03_small.jpg"), &hash::Precision::Medium),
135670932300497406,
135670932300497406,
135670932300497406,
0u64,
0u64,
0u64
);
test_imageset_hash(&hash::AHash::new(path::Path::new("./test_images/sample_03_large.jpg"),
&hash::Precision::Medium),
&hash::AHash::new(path::Path::new("./test_images/sample_03_medium.\
jpg"),
&hash::Precision::Medium),
&hash::AHash::new(path::Path::new("./test_images/sample_03_small.jpg"),
&hash::Precision::Medium),
135670932300497406,
135670932300497406,
135670932300497406,
0u64,
0u64,
0u64);
// Sample_04 tests
test_imageset_hash(
&hash::AHash::new(path::Path::new("./test_images/sample_04_large.jpg"), &hash::Precision::Medium),
&hash::AHash::new(path::Path::new("./test_images/sample_04_medium.jpg"), &hash::Precision::Medium),
&hash::AHash::new(path::Path::new("./test_images/sample_04_small.jpg"), &hash::Precision::Medium),
18446460933225054208,
18446460933090836480,
18446460933090836480,
1u64,
1u64,
0u64
);
test_imageset_hash(&hash::AHash::new(path::Path::new("./test_images/sample_04_large.jpg"),
&hash::Precision::Medium),
&hash::AHash::new(path::Path::new("./test_images/sample_04_medium.\
jpg"),
&hash::Precision::Medium),
&hash::AHash::new(path::Path::new("./test_images/sample_04_small.jpg"),
&hash::Precision::Medium),
18446460933225054208,
18446460933090836480,
18446460933090836480,
1u64,
1u64,
0u64);
// Clean_Cache
//super::teardown();
// super::teardown();
}
#[test]
@ -194,117 +215,133 @@ mod tests {
super::init();
// Sample_01 tests
test_imageset_hash(
&hash::DHash::new(path::Path::new("./test_images/sample_01_large.jpg"), &hash::Precision::Medium),
&hash::DHash::new(path::Path::new("./test_images/sample_01_medium.jpg"), &hash::Precision::Medium),
&hash::DHash::new(path::Path::new("./test_images/sample_01_small.jpg"), &hash::Precision::Medium),
7937395827556495926,
7937395827556495926,
7939647627370181174,
0u64,
1u64,
1u64
);
test_imageset_hash(&hash::DHash::new(path::Path::new("./test_images/sample_01_large.jpg"),
&hash::Precision::Medium),
&hash::DHash::new(path::Path::new("./test_images/sample_01_medium.\
jpg"),
&hash::Precision::Medium),
&hash::DHash::new(path::Path::new("./test_images/sample_01_small.jpg"),
&hash::Precision::Medium),
7937395827556495926,
7937395827556495926,
7939647627370181174,
0u64,
1u64,
1u64);
// Sample_02 tests
test_imageset_hash(
&hash::DHash::new(path::Path::new("./test_images/sample_02_large.jpg"), &hash::Precision::Medium),
&hash::DHash::new(path::Path::new("./test_images/sample_02_medium.jpg"), &hash::Precision::Medium),
&hash::DHash::new(path::Path::new("./test_images/sample_02_small.jpg"), &hash::Precision::Medium),
11009829669713008949,
11009829670249879861,
11009829669713008949,
1u64,
0u64,
1u64
);
test_imageset_hash(&hash::DHash::new(path::Path::new("./test_images/sample_02_large.jpg"),
&hash::Precision::Medium),
&hash::DHash::new(path::Path::new("./test_images/sample_02_medium.\
jpg"),
&hash::Precision::Medium),
&hash::DHash::new(path::Path::new("./test_images/sample_02_small.jpg"),
&hash::Precision::Medium),
11009829669713008949,
11009829670249879861,
11009829669713008949,
1u64,
0u64,
1u64);
// Sample_03 tests
test_imageset_hash(
&hash::DHash::new(path::Path::new("./test_images/sample_03_large.jpg"), &hash::Precision::Medium),
&hash::DHash::new(path::Path::new("./test_images/sample_03_medium.jpg"), &hash::Precision::Medium),
&hash::DHash::new(path::Path::new("./test_images/sample_03_small.jpg"), &hash::Precision::Medium),
225528496439353286,
225528496439353286,
226654396346195908,
0u64,
2u64,
2u64
);
test_imageset_hash(&hash::DHash::new(path::Path::new("./test_images/sample_03_large.jpg"),
&hash::Precision::Medium),
&hash::DHash::new(path::Path::new("./test_images/sample_03_medium.\
jpg"),
&hash::Precision::Medium),
&hash::DHash::new(path::Path::new("./test_images/sample_03_small.jpg"),
&hash::Precision::Medium),
225528496439353286,
225528496439353286,
226654396346195908,
0u64,
2u64,
2u64);
// Sample_04 tests
test_imageset_hash(
&hash::DHash::new(path::Path::new("./test_images/sample_04_large.jpg"), &hash::Precision::Medium),
&hash::DHash::new(path::Path::new("./test_images/sample_04_medium.jpg"), &hash::Precision::Medium),
&hash::DHash::new(path::Path::new("./test_images/sample_04_small.jpg"), &hash::Precision::Medium),
14620651386429567209,
14620651386429567209,
14620651386429567209,
0u64,
0u64,
0u64
);
test_imageset_hash(&hash::DHash::new(path::Path::new("./test_images/sample_04_large.jpg"),
&hash::Precision::Medium),
&hash::DHash::new(path::Path::new("./test_images/sample_04_medium.\
jpg"),
&hash::Precision::Medium),
&hash::DHash::new(path::Path::new("./test_images/sample_04_small.jpg"),
&hash::Precision::Medium),
14620651386429567209,
14620651386429567209,
14620651386429567209,
0u64,
0u64,
0u64);
// Clean_Cache
//super::teardown();
// super::teardown();
}
#[test]
fn test_confirm_phash_results() {
// Prep_Cache
super::init();
// Sample_01 tests
test_imageset_hash(
&hash::PHash::new(path::Path::new("./test_images/sample_01_large.jpg"), &hash::Precision::Medium),
&hash::PHash::new(path::Path::new("./test_images/sample_01_medium.jpg"), &hash::Precision::Medium),
&hash::PHash::new(path::Path::new("./test_images/sample_01_small.jpg"), &hash::Precision::Medium),
72357778504597504,
72357778504597504,
72357778504597504,
0u64,
0u64,
0u64
);
test_imageset_hash(&hash::PHash::new(path::Path::new("./test_images/sample_01_large.jpg"),
&hash::Precision::Medium),
&hash::PHash::new(path::Path::new("./test_images/sample_01_medium.\
jpg"),
&hash::Precision::Medium),
&hash::PHash::new(path::Path::new("./test_images/sample_01_small.jpg"),
&hash::Precision::Medium),
72357778504597504,
72357778504597504,
72357778504597504,
0u64,
0u64,
0u64);
// Sample_02 tests
test_imageset_hash(
&hash::PHash::new(path::Path::new("./test_images/sample_02_large.jpg"), &hash::Precision::Medium),
&hash::PHash::new(path::Path::new("./test_images/sample_02_medium.jpg"), &hash::Precision::Medium),
&hash::PHash::new(path::Path::new("./test_images/sample_02_small.jpg"), &hash::Precision::Medium),
5332332327550844928,
5332332327550844928,
5332332327550844928,
0u64,
0u64,
0u64
);
test_imageset_hash(&hash::PHash::new(path::Path::new("./test_images/sample_02_large.jpg"),
&hash::Precision::Medium),
&hash::PHash::new(path::Path::new("./test_images/sample_02_medium.\
jpg"),
&hash::Precision::Medium),
&hash::PHash::new(path::Path::new("./test_images/sample_02_small.jpg"),
&hash::Precision::Medium),
5332332327550844928,
5332332327550844928,
5332332327550844928,
0u64,
0u64,
0u64);
// Sample_03 tests
test_imageset_hash(
&hash::PHash::new(path::Path::new("./test_images/sample_03_large.jpg"), &hash::Precision::Medium),
&hash::PHash::new(path::Path::new("./test_images/sample_03_medium.jpg"), &hash::Precision::Medium),
&hash::PHash::new(path::Path::new("./test_images/sample_03_small.jpg"), &hash::Precision::Medium),
6917529027641081856,
6917529027641081856,
6917529027641081856,
0u64,
0u64,
0u64
);
test_imageset_hash(&hash::PHash::new(path::Path::new("./test_images/sample_03_large.jpg"),
&hash::Precision::Medium),
&hash::PHash::new(path::Path::new("./test_images/sample_03_medium.\
jpg"),
&hash::Precision::Medium),
&hash::PHash::new(path::Path::new("./test_images/sample_03_small.jpg"),
&hash::Precision::Medium),
6917529027641081856,
6917529027641081856,
6917529027641081856,
0u64,
0u64,
0u64);
// Sample_04 tests
test_imageset_hash(
&hash::PHash::new(path::Path::new("./test_images/sample_04_large.jpg"), &hash::Precision::Medium),
&hash::PHash::new(path::Path::new("./test_images/sample_04_medium.jpg"), &hash::Precision::Medium),
&hash::PHash::new(path::Path::new("./test_images/sample_04_small.jpg"), &hash::Precision::Medium),
10997931646002397184,
10997931646002397184,
11142046834078253056,
0u64,
1u64,
1u64
);
test_imageset_hash(&hash::PHash::new(path::Path::new("./test_images/sample_04_large.jpg"),
&hash::Precision::Medium),
&hash::PHash::new(path::Path::new("./test_images/sample_04_medium.\
jpg"),
&hash::Precision::Medium),
&hash::PHash::new(path::Path::new("./test_images/sample_04_small.jpg"),
&hash::Precision::Medium),
10997931646002397184,
10997931646002397184,
11142046834078253056,
0u64,
1u64,
1u64);
// Clean_Cache
//super::teardown();
// super::teardown();
}
}

33
src/main.rs

@ -15,15 +15,20 @@ const USAGE: &'static str = "
Perceptual Image Hashing (pihash)
Usage:
pihash [options] <path>...
pihash [options] \
<path>...
pihash (--help | --version)
Options:
-h, --help Show this screen.
-h, --help \
Show this screen.
-V, --version Print version.
-a, --ahash Include an ahash calculation.
-d, --dhash Include an dhash calculation.
-p, --phash Include an phash calculation.
-a, \
--ahash Include an ahash calculation.
-d, --dhash \
Include an dhash calculation.
-p, --phash Include an phash \
calculation.
";
#[derive(Debug, RustcDecodable)]
@ -36,15 +41,15 @@ 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());
// Init the hashing library
pihash::init();
//println!("{:?}", args);
// println!("{:?}", args);
// All flags set or, no flags set
if (args.flag_ahash && args.flag_dhash && args.flag_phash)
|| (!args.flag_ahash && !args.flag_dhash && !args.flag_phash) {
if (args.flag_ahash && args.flag_dhash && args.flag_phash) ||
(!args.flag_ahash && !args.flag_dhash && !args.flag_phash) {
for path in args.arg_path {
let image_path = Path::new(&path);
let hashes = pihash::get_phashes(&image_path);
@ -53,10 +58,14 @@ fn main() {
ahash: {}
dhash: {}
phash: {}
"#, hashes.orig_path, hashes.ahash, hashes.dhash, hashes.phash);
"#,
hashes.orig_path,
hashes.ahash,
hashes.dhash,
hashes.phash);
println!("{}", hash_result);
}
//Otherwise process only specific hashes
// Otherwise process only specific hashes
} else {
for path in args.arg_path {
println!("file: {}", path);

Loading…
Cancel
Save