Browse Source

Adding methods to identify similar images in a list of paths

develop
Drew Short 8 years ago
parent
commit
4a1a83cca7
  1. 2
      Cargo.toml
  2. 5
      src/hash/ahash.rs
  3. 5
      src/hash/phash.rs
  4. 79
      src/lib.rs

2
Cargo.toml

@ -1,6 +1,6 @@
[package] [package]
name = "pihash" name = "pihash"
version = "0.3.6"
version = "0.4.0"
authors = ["Drew Short <warrick@sothr.com>"] authors = ["Drew Short <warrick@sothr.com>"]
description = "A simple library for generating perceptual hashes for images and comparing images based on their perceptual hashes." description = "A simple library for generating perceptual hashes for images and comparing images based on their perceptual hashes."
repository = "https://github.com/warricksothr/Perceptual-Image-Hashing/" repository = "https://github.com/warricksothr/Perceptual-Image-Hashing/"

5
src/hash/ahash.rs

@ -38,11 +38,9 @@ impl<'a> PerceptualHash for AHash<'a> {
let mut total = 0u64; let mut total = 0u64;
for pixel in image.pixels() { for pixel in image.pixels() {
let channels = pixel.channels(); let channels = pixel.channels();
// println!("Pixel is: {}", channels[0]);
total += channels[0] as u64; total += channels[0] as u64;
} }
let mean = total / (width * height) as u64; 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;
@ -51,14 +49,11 @@ 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);
} else { } else {
hash |= 0; hash |= 0;
// println!("Pixel {} is < {} therefore {:b}", pixel_sum, mean, hash);
} }
hash <<= 1; hash <<= 1;
} }
// println!("Hash for {} is {}", prepared_image.orig_path, hash);
hash hash
} }
None => 0u64, None => 0u64,

5
src/hash/phash.rs

@ -131,7 +131,6 @@ fn create_data_matrix(width: usize,
// http://calculator.vhex.net/post/calculator-result/2d-discrete-fourier-transform // http://calculator.vhex.net/post/calculator-result/2d-discrete-fourier-transform
// //
fn calculate_2d_dft(data_matrix: &mut Vec<Vec<f64>>) { 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();
@ -145,11 +144,9 @@ fn calculate_2d_dft(data_matrix: &mut Vec<Vec<f64>>) {
} }
// Perform the DCT on this column // Perform the DCT on this 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);
complex_data_matrix.push(complex_column); complex_data_matrix.push(complex_column);
} }
@ -160,10 +157,8 @@ 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);
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);
// Put the row values back // Put the row values back
for x in 0..width { for x in 0..width {

79
src/lib.rs

@ -14,6 +14,8 @@ extern crate test;
pub mod hash; pub mod hash;
pub mod cache; pub mod cache;
use std::collections::{HashMap, LinkedList};
use std::boxed::Box;
use std::path::Path; use std::path::Path;
use std::ffi::CStr; use std::ffi::CStr;
use cache::Cache; use cache::Cache;
@ -80,6 +82,51 @@ impl<'a> PIHash<'a> {
&hash::HashType::PHash, &hash::HashType::PHash,
&self.cache) &self.cache)
} }
pub fn get_all_similar_images(&self, images: &[&Path]) -> Option<HashMap<&Path, &Path>> {
for image in images {
println!("Test Path -> {}", image.to_str().unwrap());
}
None
}
pub fn get_similar_images(&self,
base_image: &Path,
images: &'a [&Path])
-> Option<LinkedList<&Path>> {
//let similar_images = LinkedList::new();
let similar_images = images
.iter()
.filter(|image| self.are_similar_images(&base_image, &image, None))
.map(|&x| x)
.collect::<LinkedList<&Path>>();
Some(similar_images)
}
pub fn are_similar_images(&self,
first: &Path,
second: &Path,
sensitivity: Option<u64>)
-> bool {
let threshold = match sensitivity {
Some(value) => value,
None => 4,
};
let first_pihash = self.get_phashes(first);
let second_pihash = self.get_phashes(second);
if hash::calculate_hamming_distance(first_pihash.ahash, second_pihash.ahash) <= threshold {
if hash::calculate_hamming_distance(first_pihash.dhash, second_pihash.dhash) <=
threshold {
if hash::calculate_hamming_distance(first_pihash.phash, second_pihash.phash) <=
threshold {
return true;
}
}
}
return false;
}
} }
/** /**
@ -255,6 +302,38 @@ mod tests {
} }
} }
#[test]
fn test_confirm_get_similar_images() {
let lib = PIHash::new(Some(cache::DEFAULT_CACHE_DIR));
let sample_01_base_image = &Path::new("./test_images/sample_01_large.jpg");
let sample_01_images: [&Path; 2] = [&Path::new("./test_images/sample_01_medium.jpg"),
&Path::new("./test_images/sample_01_small.jpg")];
let sample_01_results = lib.get_similar_images(&sample_01_base_image, &sample_01_images)
.unwrap();
assert_eq!(sample_01_results.len(), 2);
let sample_02_base_image = &Path::new("./test_images/sample_02_large.jpg");
let sample_02_images: [&Path; 2] = [&Path::new("./test_images/sample_02_medium.jpg"),
&Path::new("./test_images/sample_02_small.jpg")];
let sample_02_results = lib.get_similar_images(&sample_02_base_image, &sample_02_images)
.unwrap();
assert_eq!(sample_02_results.len(), 2);
let sample_03_base_image = &Path::new("./test_images/sample_03_large.jpg");
let sample_03_images: [&Path; 2] = [&Path::new("./test_images/sample_03_medium.jpg"),
&Path::new("./test_images/sample_03_small.jpg")];
let sample_03_results = lib.get_similar_images(&sample_03_base_image, &sample_03_images)
.unwrap();
assert_eq!(sample_03_results.len(), 2);
let sample_04_base_image = &Path::new("./test_images/sample_04_large.jpg");
let sample_04_images: [&Path; 2] = [&Path::new("./test_images/sample_04_medium.jpg"),
&Path::new("./test_images/sample_04_small.jpg")];
let sample_04_results = lib.get_similar_images(&sample_04_base_image, &sample_04_images)
.unwrap();
assert_eq!(sample_04_results.len(), 2);
}
#[test] #[test]
fn test_confirm_ahash_results() { fn test_confirm_ahash_results() {
// Prep_library // Prep_library

Loading…
Cancel
Save