Browse Source

Reverted image library. It was no longer processing jpg images. Added some saftey features to opening images. Cleaned up the library calls. Finished the lib conversion of PIHash struct. FFI stuff missing, that will come in another point release to the library.

develop
Drew Short 9 years ago
parent
commit
60f7015937
  1. 4
      Cargo.toml
  2. 12
      src/hash/ahash.rs
  3. 12
      src/hash/dhash.rs
  4. 36
      src/hash/mod.rs
  5. 18
      src/hash/phash.rs
  6. 3
      src/lib.rs

4
Cargo.toml

@ -1,6 +1,6 @@
[package] [package]
name = "pihash" name = "pihash"
version = "0.3.0"
version = "0.3.1"
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/"
@ -22,7 +22,7 @@ bench = []
[dependencies] [dependencies]
docopt = "0.6.78" docopt = "0.6.78"
rustc-serialize = "0.3.18" rustc-serialize = "0.3.18"
image = "0.7.0"
image = "0.6.1"
complex = "0.8.0" complex = "0.8.0"
dft = "0.4.1" dft = "0.4.1"
sha1 = "0.1.1" sha1 = "0.1.1"

12
src/hash/ahash.rs

@ -30,11 +30,13 @@ impl<'a> PerceptualHash for AHash<'a> {
* A u64 representing the value of the hash * A u64 representing the value of the hash
*/ */
fn get_hash(&self, _: &Option<Box<Cache>>) -> u64 { fn get_hash(&self, _: &Option<Box<Cache>>) -> u64 {
let (width, height) = self.prepared_image.image.dimensions();
match self.prepared_image.image {
Some(ref image) => {
let (width, height) = image.dimensions();
// calculating the average pixel value // calculating the average pixel value
let mut total = 0u64; let mut total = 0u64;
for pixel in self.prepared_image.image.pixels() {
for pixel in 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;
@ -44,7 +46,7 @@ impl<'a> PerceptualHash for AHash<'a> {
// Calculating a hash based on the mean // Calculating a hash based on the mean
let mut hash = 0u64; let mut hash = 0u64;
for pixel in self.prepared_image.image.pixels() {
for pixel in image.pixels() {
let channels = pixel.channels(); let channels = pixel.channels();
let pixel_sum = channels[0] as u64; let pixel_sum = channels[0] as u64;
if pixel_sum >= mean { if pixel_sum >= mean {
@ -57,7 +59,9 @@ impl<'a> PerceptualHash for AHash<'a> {
hash <<= 1; hash <<= 1;
} }
// println!("Hash for {} is {}", prepared_image.orig_path, hash); // println!("Hash for {} is {}", prepared_image.orig_path, hash);
hash hash
},
None => 0u64
}
} }
} }

12
src/hash/dhash.rs

@ -30,14 +30,15 @@ impl<'a> PerceptualHash for DHash<'a> {
* Returns a u64 representing the value of the hash * Returns a u64 representing the value of the hash
*/ */
fn get_hash(&self, _: &Option<Box<Cache>>) -> u64 { fn get_hash(&self, _: &Option<Box<Cache>>) -> 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];
match self.prepared_image.image {
Some(ref image) => {
let first_pixel_val = image.pixels().nth(0).unwrap().channels()[0];
let last_pixel_val = image.pixels().last().unwrap().channels()[0];
// Calculate the dhash // Calculate the dhash
let mut previous_pixel_val = 0u64; let mut previous_pixel_val = 0u64;
let mut hash = 0u64; let mut hash = 0u64;
for (index, pixel) in self.prepared_image.image.pixels().enumerate() {
for (index, pixel) in image.pixels().enumerate() {
if index == 0 { if index == 0 {
previous_pixel_val = pixel.channels()[0] as u64; previous_pixel_val = pixel.channels()[0] as u64;
continue; continue;
@ -60,5 +61,8 @@ impl<'a> PerceptualHash for DHash<'a> {
} }
hash hash
},
None => 0u64
}
} }
} }

36
src/hash/mod.rs

@ -43,7 +43,7 @@ const HAMMING_DISTANCE_SIMILARITY_LIMIT: u64 = 5u64;
*/ */
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: Option<image::ImageBuffer<image::Luma<u8>, Vec<u8>>>,
} }
/** /**
@ -143,37 +143,49 @@ pub fn prepare_image<'a>(path: &'a Path,
Some(image) => { Some(image) => {
PreparedImage { PreparedImage {
orig_path: &*image_path, orig_path: &*image_path,
image: image,
image: Some(image),
} }
} }
None => { None => {
let image = process_image(&path, size);
let processed_image = process_image(&image_path, size);
// Oh, and save it in a cache // Oh, and save it in a cache
match c.put_image_in_cache(&path, size, &image.image) {
match processed_image.image {
Some(ref image) => {
match c.put_image_in_cache(&path, size, &image) {
Ok(_) => {} Ok(_) => {}
Err(e) => println!("Unable to store image in cache. {}", e), Err(e) => println!("Unable to store image in cache. {}", e),
}; };
image
},
None => {}
};
processed_image
} }
} }
}, },
None => process_image(&path, size),
None => process_image(&image_path, size),
} }
} }
/** /**
* Turn the image into something we can work with * Turn the image into something we can work with
*/ */
fn process_image(path: &Path,
size: u32) -> PreparedImage{
fn process_image<'a>(image_path: &'a str,
size: u32) -> PreparedImage<'a> {
// Otherwise let's do that work now and store it. // Otherwise let's do that work now and store it.
let image_path = path.to_str().unwrap();
let image = image::open(path).unwrap();
//println!("Path: {}", image_path);
let image = match image::open(Path::new(image_path)) {
Ok(image) => {
let small_image = image.resize_exact(size, size, FilterType::Lanczos3); let small_image = image.resize_exact(size, size, FilterType::Lanczos3);
let grey_image = small_image.to_luma();
Some(small_image.to_luma())
},
Err(e) => {
println!("Error Processing Image [{}]: {} ", image_path, e);
None
}
};
PreparedImage { PreparedImage {
orig_path: &*image_path, orig_path: &*image_path,
image: grey_image,
image: image,
} }
} }

18
src/hash/phash.rs

@ -32,9 +32,11 @@ impl<'a> PerceptualHash for PHash<'a> {
* Returns a u64 representing the value of the hash * Returns a u64 representing the value of the hash
*/ */
fn get_hash(&self, cache: &Option<Box<Cache>>) -> u64 { fn get_hash(&self, cache: &Option<Box<Cache>>) -> u64 {
match self.prepared_image.image {
Some(ref image) => {
// 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 = image.width() as usize;
let height = image.height() as usize;
// Get 2d data to 2d FFT/DFT // Get 2d data to 2d FFT/DFT
// Either from the cache or calculate it // Either from the cache or calculate it
@ -45,7 +47,7 @@ impl<'a> PerceptualHash for PHash<'a> {
match c.get_matrix_from_cache(&Path::new(self.prepared_image.orig_path), width as u32) { match c.get_matrix_from_cache(&Path::new(self.prepared_image.orig_path), width as u32) {
Some(matrix) => matrix, Some(matrix) => matrix,
None => { None => {
let matrix = create_data_matrix(width, height, &self.prepared_image);
let matrix = create_data_matrix(width, height, &image);
// Store this DFT in the cache // Store this DFT in the cache
match c.put_matrix_in_cache(&Path::new(self.prepared_image.orig_path), width as u32, &matrix) { match c.put_matrix_in_cache(&Path::new(self.prepared_image.orig_path), width as u32, &matrix) {
Ok(_) => {} Ok(_) => {}
@ -55,7 +57,7 @@ impl<'a> PerceptualHash for PHash<'a> {
} }
} }
}, },
None => create_data_matrix(width, height, &self.prepared_image)
None => create_data_matrix(width, height, &image)
}; };
// Only need the top left quadrant // Only need the top left quadrant
@ -90,10 +92,13 @@ impl<'a> PerceptualHash for PHash<'a> {
} }
// println!("Hash for {} is {}", prepared_image.orig_path, hash); // println!("Hash for {} is {}", prepared_image.orig_path, hash);
hash hash
},
None => 0u64
}
} }
} }
fn create_data_matrix(width: usize, height: usize, prepared_image: &PreparedImage) -> Vec<Vec<f64>> {
fn create_data_matrix(width: usize, height: usize, image: &super::image::ImageBuffer<super::image::Luma<u8>, Vec<u8>>) -> Vec<Vec<f64>> {
let mut data_matrix: Vec<Vec<f64>> = Vec::new(); let mut data_matrix: Vec<Vec<f64>> = Vec::new();
// Preparing the results // Preparing the results
for x in 0..width { for x in 0..width {
@ -102,8 +107,7 @@ fn create_data_matrix(width: usize, height: usize, prepared_image: &PreparedImag
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] data_matrix[x]
.push(prepared_image
.image
.push(image
.get_pixel(pos_x, pos_y) .get_pixel(pos_x, pos_y)
.channels()[0] as f64); .channels()[0] as f64);
} }

3
src/lib.rs

@ -3,6 +3,9 @@
// Licensed under the MIT license<LICENSE-MIT or http://opensource.org/licenses/MIT>. // Licensed under the MIT license<LICENSE-MIT or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed except according to those terms. // This file may not be copied, modified, or distributed except according to those terms.
// Enable nightly features for extra testing behind the bench feature
#![cfg_attr(feature = "bench", feature(test))]
extern crate libc; extern crate libc;
extern crate rustc_serialize; extern crate rustc_serialize;
#[cfg(feature = "bench")] #[cfg(feature = "bench")]

Loading…
Cancel
Save