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 8 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]
name = "pihash"
version = "0.3.0"
version = "0.3.1"
authors = ["Drew Short <warrick@sothr.com>"]
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/"
@ -22,7 +22,7 @@ bench = []
[dependencies]
docopt = "0.6.78"
rustc-serialize = "0.3.18"
image = "0.7.0"
image = "0.6.1"
complex = "0.8.0"
dft = "0.4.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
*/
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
let mut total = 0u64;
for pixel in self.prepared_image.image.pixels() {
for pixel in image.pixels() {
let channels = pixel.channels();
// println!("Pixel is: {}", channels[0]);
total += channels[0] as u64;
@ -44,7 +46,7 @@ impl<'a> PerceptualHash for AHash<'a> {
// Calculating a hash based on the mean
let mut hash = 0u64;
for pixel in self.prepared_image.image.pixels() {
for pixel in image.pixels() {
let channels = pixel.channels();
let pixel_sum = channels[0] as u64;
if pixel_sum >= mean {
@ -57,7 +59,9 @@ impl<'a> PerceptualHash for AHash<'a> {
hash <<= 1;
}
// println!("Hash for {} is {}", prepared_image.orig_path, 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
*/
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
let mut previous_pixel_val = 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 {
previous_pixel_val = pixel.channels()[0] as u64;
continue;
@ -60,5 +61,8 @@ impl<'a> PerceptualHash for DHash<'a> {
}
hash
},
None => 0u64
}
}
}

36
src/hash/mod.rs

@ -43,7 +43,7 @@ const HAMMING_DISTANCE_SIMILARITY_LIMIT: u64 = 5u64;
*/
pub struct PreparedImage<'a> {
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) => {
PreparedImage {
orig_path: &*image_path,
image: image,
image: Some(image),
}
}
None => {
let image = process_image(&path, size);
let processed_image = process_image(&image_path, size);
// 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(_) => {}
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
*/
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.
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 grey_image = small_image.to_luma();
Some(small_image.to_luma())
},
Err(e) => {
println!("Error Processing Image [{}]: {} ", image_path, e);
None
}
};
PreparedImage {
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
*/
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.
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
// 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) {
Some(matrix) => matrix,
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
match c.put_matrix_in_cache(&Path::new(self.prepared_image.orig_path), width as u32, &matrix) {
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
@ -90,10 +92,13 @@ impl<'a> PerceptualHash for PHash<'a> {
}
// println!("Hash for {} is {}", prepared_image.orig_path, 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();
// Preparing the results
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_y = y as u32;
data_matrix[x]
.push(prepared_image
.image
.push(image
.get_pixel(pos_x, pos_y)
.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>.
// 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 rustc_serialize;
#[cfg(feature = "bench")]

Loading…
Cancel
Save