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 9 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 # Ignore sublime workspace files
*.sublime-workspace *.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_DIR: &'static str = "./.hash_cache";
const CACHE_FILE_EXT: &'static str = "png"; const CACHE_FILE_EXT: &'static str = "png";
// Creates the required directories
// Creates the required directories
pub fn prep_cache() -> Result<(), Error> { pub fn prep_cache() -> Result<(), Error> {
create_dir_all(CACHE_DIR) 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 * 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); let hash = get_file_hash(&path);
match hash { match hash {
Ok(sha1) => { 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); let cached_path = Path::new(&cache_path_str);
// Save the file into the cache // Save the file into the cache
match image.save(cached_path) { match image.save(cached_path) {
Ok(_) => {},
Ok(_) => {}
Err(e) => println!("Error: {}", e), 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 * 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); let hash = get_file_hash(&path);
match hash { match hash {
Ok(sha1) => { 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); let cached_path = Path::new(&cache_path_str);
// Save the file into the cache // Save the file into the cache
match File::create(&cached_path) { match File::create(&cached_path) {
Ok(mut file) => { Ok(mut file) => {
for row in file_contents { 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.truncate(desire_len);
row_str.push_str("\n"); row_str.push_str("\n");
file.write(&row_str.into_bytes()); file.write(&row_str.into_bytes());
} }
file.flush(); file.flush();
},
Err(_) => {},
}
Err(_) => {}
} }
},
Err(e) => println!("Error: {}", e),
}
Err(e) => println!("Error: {}", e),
} }
} }
/** /**
* Get an image buffer out of the cache * 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); let hash = get_file_hash(&path);
match hash { match hash {
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!("{}/{}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); let cached_path = Path::new(&cache_path_str);
// Try to open, if it does, then we can read the image in // Try to open, if it does, then we can read the image in
match File::open(&cached_path) { match File::open(&cached_path) {
Ok(_) => { Ok(_) => {
let image = image::open(&cached_path).unwrap(); let image = image::open(&cached_path).unwrap();
Some(image.to_luma()) Some(image.to_luma())
},
}
// Don't really care here, it just means an existing cached // Don't really care here, it just means an existing cached
// file doesn't exist, or can't be read. // file doesn't exist, or can't be read.
Err(_) => {
None
},
Err(_) => None,
} }
},
}
Err(e) => { Err(e) => {
println!("Error: {}", e); println!("Error: {}", e);
None None
},
}
} }
} }
@ -125,7 +139,7 @@ pub fn get_matrix_from_cache(path: &Path, size: u32, extension: &str) -> Option<
match hash { match hash {
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!("{}/{}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); let cached_path = Path::new(&cache_path_str);
// Try to open, if it does, then we can read the image in // Try to open, if it does, then we can read the image in
match File::open(&cached_path) { 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(); let mut matrix_data: Vec<u8> = Vec::new();
file.read_to_end(&mut matrix_data); file.read_to_end(&mut matrix_data);
let matrix_data_str = String::from_utf8(matrix_data); let matrix_data_str = String::from_utf8(matrix_data);
//convert the matrix
// convert the matrix
Some(matrix) Some(matrix)
},
}
// Don't really care here, it just means an existing cached // Don't really care here, it just means an existing cached
// file doesn't exist, or can't be read. // file doesn't exist, or can't be read.
Err(_) => {
None
},
Err(_) => None,
} }
},
}
Err(e) => { Err(e) => {
println!("Error: {}", e); println!("Error: {}", e);
None None
},
}
} }
} }

203
src/hash.rs

@ -10,11 +10,7 @@ extern crate complex;
use std::path::Path; use std::path::Path;
use std::f64; use std::f64;
use self::image::{
GenericImage,
Pixel,
FilterType
};
use self::image::{GenericImage, Pixel, FilterType};
use self::dft::Transform; use self::dft::Transform;
use cache; use cache;
@ -40,7 +36,7 @@ const FLOAT_PRECISION_MIN_5: f64 = f64::MIN / 100000_f64;
*/ */
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>>,
} }
/** /**
@ -50,7 +46,7 @@ pub struct PerceptualHashes<'a> {
pub orig_path: &'a str, pub orig_path: &'a str,
pub ahash: u64, pub ahash: u64,
pub dhash: u64, pub dhash: u64,
pub phash: u64
pub phash: u64,
} }
/** /**
@ -61,14 +57,13 @@ pub struct PerceptualHashes<'a> {
* High aims for 128 bit precision * High aims for 128 bit precision
*/ */
pub enum Precision { pub enum Precision {
Low,
Low,
Medium, Medium,
High, High,
} }
/*
* Get the size of the required image
*/
// Get the size of the required image
//
impl Precision { impl Precision {
fn get_size(&self) -> u32 { fn get_size(&self) -> u32 {
match *self { match *self {
@ -85,7 +80,7 @@ impl Precision {
pub enum HashType { pub enum HashType {
Ahash, Ahash,
Dhash, Dhash,
Phash
Phash,
} }
/** /**
@ -102,25 +97,34 @@ pub enum HashType {
* A PreparedImage struct with the required information for performing hashing * 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 image_path = path.to_str().unwrap();
let size: u32 = match *hash_type { let size: u32 = match *hash_type {
HashType::Phash => precision.get_size() * 4, 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. // Check if we have the already converted image in a cache and use that if possible.
match cache::get_image_from_cache(&path, size) { match cache::get_image_from_cache(&path, size) {
Some(image) => { Some(image) => {
PreparedImage { orig_path: &*image_path, image: image }
},
PreparedImage {
orig_path: &*image_path,
image: image,
}
}
None => { None => {
// Otherwise let's do that work now and store it. // Otherwise let's do that work now and store it.
let image = image::open(path).unwrap(); let image = image::open(path).unwrap();
let small_image = image.resize_exact(size, size, FilterType::Lanczos3); let small_image = image.resize_exact(size, size, FilterType::Lanczos3);
let grey_image = small_image.to_luma(); let grey_image = small_image.to_luma();
cache::put_image_in_cache(&path, size, &grey_image); 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 ahash = AHash::new(&path, &precision).get_hash();
let dhash = DHash::new(&path, &precision).get_hash(); let dhash = DHash::new(&path, &precision).get_hash();
let phash = PHash::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; let mut hamming = 0u64;
for bit in bin_diff_str.chars() { for bit in bin_diff_str.chars() {
match bit { match bit {
'1' => hamming+=1,
_ => continue
'1' => hamming += 1,
_ => continue,
} }
} }
hamming hamming
@ -170,12 +179,12 @@ impl<'a> AHash<'a> {
impl<'a> PerceptualHash for 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 { fn get_hash(&self) -> u64 {
let (width, height) = self.prepared_image.image.dimensions(); let (width, height) = self.prepared_image.image.dimensions();
@ -183,11 +192,11 @@ impl<'a> PerceptualHash for AHash<'a> {
let mut total = 0u64; let mut total = 0u64;
for pixel in self.prepared_image.image.pixels() { for pixel in self.prepared_image.image.pixels() {
let channels = pixel.channels(); let channels = pixel.channels();
//println!("Pixel is: {}", channels[0]);
// println!("Pixel is: {}", channels[0]);
total += channels[0] as u64; 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 // Calculating a hash based on the mean
let mut hash = 0u64; let mut hash = 0u64;
@ -196,14 +205,14 @@ impl<'a> PerceptualHash for AHash<'a> {
let pixel_sum = channels[0] as u64; let pixel_sum = channels[0] as u64;
if pixel_sum >= mean { if pixel_sum >= mean {
hash |= 1; hash |= 1;
//println!("Pixel {} is >= {} therefore {:b}", pixel_sum, mean, hash);
// println!("Pixel {} is >= {} therefore {:b}", pixel_sum, mean, hash);
} else { } else {
hash |= 0; hash |= 0;
//println!("Pixel {} is < {} therefore {:b}", pixel_sum, mean, hash);
// println!("Pixel {} is < {} therefore {:b}", pixel_sum, mean, hash);
} }
hash <<= 1; hash <<= 1;
} }
//println!("Hash for {} is {}", prepared_image.orig_path, hash);
// println!("Hash for {} is {}", prepared_image.orig_path, hash);
hash hash
} }
@ -255,7 +264,7 @@ impl<'a> PerceptualHash for DHash<'a> {
hash |= 1; hash |= 1;
} else { } else {
hash |= 0; hash |= 0;
}
}
hash hash
} }
@ -281,9 +290,9 @@ impl<'a> PerceptualHash for PHash<'a> {
*/ */
fn get_hash(&self) -> u64 { fn get_hash(&self) -> 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 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 // Get 2d data to 2d FFT/DFT
let mut data_matrix: Vec<Vec<f64>> = Vec::new(); let mut data_matrix: Vec<Vec<f64>> = Vec::new();
for x in 0..width { for x in 0..width {
@ -291,67 +300,70 @@ impl<'a> PerceptualHash for PHash<'a> {
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(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 // Perform the 2D DFT operation on our matrix
calculate_2d_dft(&mut data_matrix); calculate_2d_dft(&mut data_matrix);
// Store this DFT in the cache // 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 // Only need the top left quadrant
let target_width = (width / 4) as usize; let target_width = (width / 4) as usize;
let target_height = (height / 4) as usize; let target_height = (height / 4) as usize;
let dft_width = (width / 4) as f64; let dft_width = (width / 4) as f64;
let dft_height = (height / 4) as f64; let dft_height = (height / 4) as f64;
//Calculate the mean
// Calculate the mean
let mut total = 0f64; let mut total = 0f64;
for x in 0..target_width { for x in 0..target_width {
for y in 0..target_height { for y in 0..target_height {
total += data_matrix[x][y]; 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 // Calculating a hash based on the mean
let mut hash = 0u64; let mut hash = 0u64;
for x in 0..target_width { 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 { for y in 0..target_height {
if data_matrix[x][y] >= mean { if data_matrix[x][y] >= mean {
hash |= 1; hash |= 1;
//println!("Pixel {} is >= {} therefore {:b}", pixel_sum, mean, hash);
// println!("Pixel {} is >= {} therefore {:b}", pixel_sum, mean, hash);
} else { } else {
hash |= 0; hash |= 0;
//println!("Pixel {} is < {} therefore {:b}", pixel_sum, mean, hash);
// println!("Pixel {} is < {} therefore {:b}", pixel_sum, mean, hash);
} }
hash <<= 1; hash <<= 1;
} }
} }
//println!("Hash for {} is {}", prepared_image.orig_path, hash);
// println!("Hash for {} is {}", prepared_image.orig_path, hash);
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 width = data_matrix.len();
let height = data_matrix[0].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 { for y in 0..height {
column.push(data_matrix[x][y]); column.push(data_matrix[x][y]);
} }
// Perform the DCT on this column // 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()); let forward_plan = dft::Plan::new(dft::Operation::Forward, column.len());
column.transform(&forward_plan); column.transform(&forward_plan);
let complex_column = dft::unpack(&column); let complex_column = dft::unpack(&column);
//println!("column[{}] after: {:?}", x, complex_column);
// println!("column[{}] after: {:?}", x, complex_column);
complex_data_matrix.push(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]); row.push(complex_data_matrix[x][y]);
} }
// Perform DCT on the row // 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()); let forward_plan = dft::Plan::new(dft::Operation::Forward, row.len());
row.transform(&forward_plan); row.transform(&forward_plan);
//println!("row[{}] after: {:?}", y, row);
// println!("row[{}] after: {:?}", y, row);
// Put the row values back // Put the row values back
for x in 0..width { 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 { fn round_float(f: f64) -> f64 {
if f >= FLOAT_PRECISION_MAX_1 || f <= FLOAT_PRECISION_MIN_1 { 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] #[test]
fn test_2d_dft() { fn test_2d_dft() {
let mut test_matrix: Vec<Vec<f64>> = Vec::new(); 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"); println!("Performing 2d DFT");
calculate_2d_dft(&mut test_matrix); 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][0] == 24_f64);
assert!(test_matrix[0][1] == 0_f64); assert!(test_matrix[0][1] == 0_f64);

387
src/lib.rs

@ -16,7 +16,7 @@ use hash::PerceptualHash;
*/ */
pub fn init() { pub fn init() {
match cache::prep_cache() { match cache::prep_cache() {
Ok(_) => {},
Ok(_) => {}
Err(e) => println!("Error: {}", e), Err(e) => println!("Error: {}", e),
} }
} }
@ -26,7 +26,7 @@ pub fn init() {
*/ */
pub fn teardown() { pub fn teardown() {
match cache::clear_cache() { match cache::clear_cache() {
Ok(_) => {},
Ok(_) => {}
Err(e) => println!("Error: {}", e), 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) hash::calculate_hamming_distance(hash1, hash2)
} }
/*
* Module for the tests
*/
// Module for the tests
//
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use std::fs; use std::fs;
use std::path; use std::path;
use hash;
use hash;
#[test] #[test]
fn test_can_get_test_images() { fn test_can_get_test_images() {
@ -70,18 +69,18 @@ mod tests {
let orig_path = path.unwrap().path(); let orig_path = path.unwrap().path();
let ext = path::Path::new(&orig_path).extension(); let ext = path::Path::new(&orig_path).extension();
match ext { match ext {
Some(_) => {
Some(_) => {
if ext.unwrap() == "jpg" { if ext.unwrap() == "jpg" {
num_paths += 1; 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
continue;
} }
} }
//println!("Name: {}", path.unwrap().path().display())
// println!("Name: {}", path.unwrap().path().display())
} }
// Currently 12 images in the test imaages directory // Currently 12 images in the test imaages directory
assert!(num_paths == 12); assert!(num_paths == 12);
@ -89,34 +88,48 @@ mod tests {
// Simple function for the unit tests to succinctly test a set of images // Simple function for the unit tests to succinctly test a set of images
// that are organized in the fashion of large->medium->small // 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_large_hash = large_phash.get_hash();
let actual_medium_hash = medium_phash.get_hash(); let actual_medium_hash = medium_phash.get_hash();
let actual_small_hash = small_phash.get_hash(); let actual_small_hash = small_phash.get_hash();
// println for the purpose of debugging // 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 // Doing that asserts
assert!(actual_large_hash == expected_large_hash); assert!(actual_large_hash == expected_large_hash);
assert!(actual_medium_hash == expected_medium_hash); assert!(actual_medium_hash == expected_medium_hash);
@ -134,58 +147,66 @@ mod tests {
super::init(); super::init();
// Sample_01 tests // 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 // 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 // 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 // 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 // Clean_Cache
//super::teardown();
// super::teardown();
} }
#[test] #[test]
@ -194,117 +215,133 @@ mod tests {
super::init(); super::init();
// Sample_01 tests // 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 // 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 // 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 // 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 // Clean_Cache
//super::teardown();
// super::teardown();
} }
#[test] #[test]
fn test_confirm_phash_results() { fn test_confirm_phash_results() {
// Prep_Cache // Prep_Cache
super::init(); super::init();
// Sample_01 tests // 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 // 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 // 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 // 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 // Clean_Cache
//super::teardown();
// super::teardown();
} }
} }

33
src/main.rs

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

Loading…
Cancel
Save