Browse Source

Same work for DHash and PHash. Also updating testing to the new model.

develop
Drew Short 9 years ago
parent
commit
7ed4752420
  1. 221
      src/hash.rs
  2. 131
      src/lib.rs

221
src/hash.rs

@ -130,13 +130,9 @@ pub fn prepare_image<'a>(path: &'a Path, hash_type: &HashType, precision: &Preci
*/
pub fn get_perceptual_hashes<'a>(path: &'a Path, precision: &Precision) -> PerceptualHashes<'a> {
let image_path = path.to_str().unwrap();
let prepared_image = prepare_image(path, &HashType::Ahash, &precision);
// phash uses a DFT, so it needs an image 4 times larger to work with for
// the same precision of hash. That said, this hash is much more accurate.
let phash_prepared_image = prepare_image(path, &HashType::Phash, &precision);
let ahash = AHash::new(&path, &precision).get_hash();
let dhash = get_dhash(&prepared_image);
let phash = get_phash(&phash_prepared_image);
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 }
}
@ -177,10 +173,6 @@ impl<'a> PerceptualHash for AHash<'a> {
/**
* Calculate the ahash of the provided prepared image.
*
* # Arguments
*
* * 'prepared_image' - The already prepared image for perceptual processing.
*
* # Returns
*
* A u64 representing the value of the hash
@ -218,49 +210,128 @@ impl<'a> PerceptualHash for AHash<'a> {
}
}
pub struct DHash<'a> {
prepared_image: Box<PreparedImage<'a>>,
}
/**
* Calculate the dhash of the provided prepared image
*
* # Arguments
*
* * 'prepared_image' - The already prepared image for perceptual processing
*
* # Return
*
* Returns a u64 representing the value of the hash
*/
pub fn get_dhash(prepared_image: &PreparedImage) -> u64 {
// Stored for later
let first_pixel_val = prepared_image.image.pixels().nth(0).unwrap().channels()[0];
let last_pixel_val = prepared_image.image.pixels().last().unwrap().channels()[0];
// Calculate the dhash
let mut previous_pixel_val = 0u64;
let mut hash = 0u64;
for (index, pixel) in prepared_image.image.pixels().enumerate() {
if index == 0 {
previous_pixel_val = pixel.channels()[0] as u64;
continue;
impl<'a> DHash<'a> {
pub fn new(path: &'a Path, precision: &Precision) -> Self {
DHash { prepared_image: Box::new(prepare_image(&path, &HashType::Dhash, &precision)) }
}
}
impl<'a> PerceptualHash for DHash<'a> {
/**
* Calculate the dhash of the provided prepared image
*
* # Return
*
* Returns a u64 representing the value of the hash
*/
fn get_hash(&self) -> u64 {
// Stored for later
let first_pixel_val = self.prepared_image.image.pixels().nth(0).unwrap().channels()[0];
let last_pixel_val = self.prepared_image.image.pixels().last().unwrap().channels()[0];
// Calculate the dhash
let mut previous_pixel_val = 0u64;
let mut hash = 0u64;
for (index, pixel) in self.prepared_image.image.pixels().enumerate() {
if index == 0 {
previous_pixel_val = pixel.channels()[0] as u64;
continue;
}
let channels = pixel.channels();
let pixel_val = channels[0] as u64;
if pixel_val >= previous_pixel_val {
hash |= 1;
} else {
hash |= 0;
}
hash <<= 1;
previous_pixel_val = channels[0] as u64;
}
let channels = pixel.channels();
let pixel_val = channels[0] as u64;
if pixel_val >= previous_pixel_val {
if first_pixel_val >= last_pixel_val {
hash |= 1;
} else {
hash |= 0;
}
hash <<= 1;
previous_pixel_val = channels[0] as u64;
hash
}
}
if first_pixel_val >= last_pixel_val {
hash |= 1;
} else {
hash |= 0;
pub struct PHash<'a> {
prepared_image: Box<PreparedImage<'a>>,
}
impl<'a> PHash<'a> {
pub fn new(path: &'a Path, precision: &Precision) -> Self {
PHash { prepared_image: Box::new(prepare_image(&path, &HashType::Phash, &precision)) }
}
}
hash
impl<'a> PerceptualHash for PHash<'a> {
/**
* Calculate the phash of the provided prepared image
*
* # Return
*
* Returns a u64 representing the value of the hash
*/
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;
// Get 2d data to 2d FFT/DFT
let mut data_matrix: Vec<Vec<f64>> = Vec::new();
for x in (0..width) {
data_matrix.push(Vec::new());
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);
}
}
// Perform the 2D DFT operation on our matrix
calculate_2d_dft(&mut 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
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);
// 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
}
}
/*
@ -339,70 +410,6 @@ fn round_float(f: f64) -> f64 {
}
}
/**
* Calculate the phash of the provided prepared image
*
* # Arguments
*
* * 'prepared_image' - The already prepared image for perceptual processing
*
* # Return
*
* Returns a u64 representing the value of the hash
*/
pub fn get_phash(prepared_image: &PreparedImage) -> u64 {
// Get the image data into a vector to perform the DFT on.
let width = prepared_image.image.width() as usize;
let height = 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) {
data_matrix.push(Vec::new());
for y in (0..height) {
let pos_x = x as u32;
let pos_y = y as u32;
data_matrix[x].push(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);
// 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
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);
// 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
}
#[test]
fn test_2d_dft() {
let mut test_matrix: Vec<Vec<f64>> = Vec::new();

131
src/lib.rs

@ -30,11 +30,11 @@ pub fn get_ahash(path: &Path) -> u64 {
}
pub fn get_dhash(path: &Path) -> u64 {
hash::get_dhash(&hash::prepare_image(path, &hash::HashType::Dhash, &hash::Precision::Medium))
hash::DHash::new(&path, &hash::Precision::Medium).get_hash()
}
pub fn get_phash(path: &Path) -> u64 {
hash::get_phash(&hash::prepare_image(path, &hash::HashType::Phash, &hash::Precision::Medium))
hash::PHash::new(&path, &hash::Precision::Medium).get_hash()
}
pub fn get_hamming_distance(hash1: u64, hash2: u64) -> u64 {
@ -80,12 +80,9 @@ 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<F: Fn(&hash::PreparedImage) -> u64>(
hash_func: F,
hash_type: hash::HashType,
precision: hash::Precision,
large_path: &str,
medium_path: &str,
small_path: &str,
large_phash: PerceptualHash,
medium_phash: PerceptualHash,
small_phash: PerceptualHash,
expected_large_hash: u64,
expected_medium_hash: u64,
expected_small_hash: u64,
@ -93,13 +90,9 @@ mod tests {
expected_large_small_hamming: u64,
expected_medium_small_hamming: u64) {
let large_prepared_image = hash::prepare_image(path::Path::new(large_path), &hash_type, &precision);
let medium_prepared_image = hash::prepare_image(path::Path::new(medium_path), &hash_type, &precision);
let small_prepared_image = hash::prepare_image(path::Path::new(small_path), &hash_type, &precision);
let actual_large_hash = hash_func(&large_prepared_image);
let actual_medium_hash = hash_func(&medium_prepared_image);
let actual_small_hash = hash_func(&small_prepared_image);
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!("{}: expected: {} actual: {}", large_path, expected_large_hash, actual_large_hash);
@ -129,12 +122,9 @@ mod tests {
fn confirm_ahash_results() {
// Sample_01 tests
test_imageset_hash(
hash::get_ahash,
hash::HashType::Ahash,
hash::Precision::Medium,
"./test_images/sample_01_large.jpg",
"./test_images/sample_01_medium.jpg",
"./test_images/sample_01_small.jpg",
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,
@ -145,12 +135,9 @@ mod tests {
// Sample_02 tests
test_imageset_hash(
hash::get_ahash,
hash::HashType::Ahash,
hash::Precision::Medium,
"./test_images/sample_02_large.jpg",
"./test_images/sample_02_medium.jpg",
"./test_images/sample_02_small.jpg",
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,
@ -160,12 +147,9 @@ mod tests {
);
// Sample_03 tests
test_imageset_hash(
hash::get_ahash,
hash::HashType::Ahash,
hash::Precision::Medium,
"./test_images/sample_03_large.jpg",
"./test_images/sample_03_medium.jpg",
"./test_images/sample_03_small.jpg",
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,
@ -176,12 +160,9 @@ mod tests {
// Sample_04 tests
test_imageset_hash(
hash::get_ahash,
hash::HashType::Ahash,
hash::Precision::Medium,
"./test_images/sample_04_large.jpg",
"./test_images/sample_04_medium.jpg",
"./test_images/sample_04_small.jpg",
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,
@ -195,12 +176,9 @@ mod tests {
fn confirm_dhash_results() {
// Sample_01 tests
test_imageset_hash(
hash::get_dhash,
hash::HashType::Dhash,
hash::Precision::Medium,
"./test_images/sample_01_large.jpg",
"./test_images/sample_01_medium.jpg",
"./test_images/sample_01_small.jpg",
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,
@ -211,12 +189,9 @@ mod tests {
// Sample_02 tests
test_imageset_hash(
hash::get_dhash,
hash::HashType::Dhash,
hash::Precision::Medium,
"./test_images/sample_02_large.jpg",
"./test_images/sample_02_medium.jpg",
"./test_images/sample_02_small.jpg",
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,
@ -226,12 +201,9 @@ mod tests {
);
// Sample_03 tests
test_imageset_hash(
hash::get_dhash,
hash::HashType::Dhash,
hash::Precision::Medium,
"./test_images/sample_03_large.jpg",
"./test_images/sample_03_medium.jpg",
"./test_images/sample_03_small.jpg",
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,
@ -242,12 +214,9 @@ mod tests {
// Sample_04 tests
test_imageset_hash(
hash::get_dhash,
hash::HashType::Dhash,
hash::Precision::Medium,
"./test_images/sample_04_large.jpg",
"./test_images/sample_04_medium.jpg",
"./test_images/sample_04_small.jpg",
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,
@ -261,12 +230,9 @@ mod tests {
fn confirm_phash_results() {
// Sample_01 tests
test_imageset_hash(
hash::get_phash,
hash::HashType::Phash,
hash::Precision::Medium,
"./test_images/sample_01_large.jpg",
"./test_images/sample_01_medium.jpg",
"./test_images/sample_01_small.jpg",
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,
@ -277,12 +243,9 @@ mod tests {
// Sample_02 tests
test_imageset_hash(
hash::get_phash,
hash::HashType::Phash,
hash::Precision::Medium,
"./test_images/sample_02_large.jpg",
"./test_images/sample_02_medium.jpg",
"./test_images/sample_02_small.jpg",
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,
@ -292,12 +255,9 @@ mod tests {
);
// Sample_03 tests
test_imageset_hash(
hash::get_phash,
hash::HashType::Phash,
hash::Precision::Medium,
"./test_images/sample_03_large.jpg",
"./test_images/sample_03_medium.jpg",
"./test_images/sample_03_small.jpg",
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,
@ -308,12 +268,9 @@ mod tests {
// Sample_04 tests
test_imageset_hash(
hash::get_phash,
hash::HashType::Phash,
hash::Precision::Medium,
"./test_images/sample_04_large.jpg",
"./test_images/sample_04_medium.jpg",
"./test_images/sample_04_small.jpg",
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,

Loading…
Cancel
Save