diff --git a/.gitignore b/.gitignore index 9973bf5..12c2467 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ # Building a library, so ignore the lock file Cargo.lock + +# Cache Directory +.hash_cache/ diff --git a/Cargo.toml b/Cargo.toml index a31c524..ad0803f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,4 @@ rustc-serialize = "*" image = "*" complex = "*" dft = "*" -rust-crypto = "*" +sha1 = "*" diff --git a/src/cache.rs b/src/cache.rs index 1ee76a5..31f7c08 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -3,28 +3,99 @@ // Licensed under the MIT license. // This file may not be copied, modified, or distributed except according to those terms. -extern image; +extern crate image; +extern crate sha1; use self::image::ImageBuffer; +use self::sha1::Sha1; use std::path::Path; +use std::fs::{File, create_dir_all}; +use std::io::{Read, Error}; +use std::option::Option; +use std::result::Result; + +const CACHE_DIR: &'static str = "./.hash_cache"; +const CACHE_FILE_EXT: &'static str = "png"; + +// Creates the required directories +pub fn prep_cache() -> Result<(), Error> { + create_dir_all(CACHE_DIR) +} /** * Get the hash of the desired file and return it as a hex string */ -fn get_file_hash(path: &Path) -> String { - +fn get_file_hash(path: &Path) -> Result { + let mut source = try!(File::open(&path)); + let mut buf: Vec = Vec::new(); + try!(source.read_to_end(&mut buf)); + let mut sha1 = Sha1::new(); + sha1.update(&buf); + // Return the hex result of the hash + Ok(sha1.hexdigest()) } /** * Put an image buffer in the cache */ -pub fn put_in_cache(path: &Path, image: &ImageBuffer) { - +pub fn put_in_cache(path: &Path, image: &ImageBuffer, Vec>) { + let hash = get_file_hash(&path); + match hash { + Ok(sha1) => { + let cache_path_str = format!("{}/{}.{}",CACHE_DIR, sha1, CACHE_FILE_EXT); + let cached_path = Path::new(&cache_path_str); + // Save the file into the cache + match image.save(cached_path) { + Ok(_) => {}, + Err(e) => println!("Error: {}", e), + } + }, + Err(e) => println!("Error: {}", e), + } } /** * Get an image buffer out of the cache */ -pub fn get_from_cache(path: &Path) -> Some(ImageBuffer) { +pub fn get_from_cache(path: &Path) -> Option, Vec>> { + let hash = get_file_hash(&path); + match hash { + Ok(sha1) => { + // Check if the file exists in the cache + let cache_path_str = format!("{}/{}.{}",CACHE_DIR, sha1, CACHE_FILE_EXT); + let cached_path = Path::new(&cache_path_str); + // Try to open, if it does, then we can read the image in + match File::open(&cached_path) { + Ok(_) => { + let image = image::open(&cached_path).unwrap(); + Some(image.to_luma()) + }, + Err(e) => { + println!("Error: {}", e); + None + }, + } + }, + Err(e) => { + println!("Error: {}", e); + None + }, + } +} +#[test] +fn test_get_file_hash() { + let target = "test_images/sample_01_large.jpg"; + let target_path = Path::new(target); + let hash = get_file_hash(&target_path); + match hash { + Ok(v) => { + println!("Hash: {}", v); + assert!(v == "4beb6f2d852b75a313863916a1803ebad13a3196"); + } + Err(e) => { + println!("Error: {:?}", e); + assert!(false); + } + } } diff --git a/src/hash.rs b/src/hash.rs index a3fb7d6..d20193b 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -16,6 +16,7 @@ use self::image::{ }; use self::dft::real; use self::complex::*; +use cache; /** * Prepared image that can be used to generate hashes @@ -52,13 +53,19 @@ pub struct PerceptualHashes<'a> { pub fn prepare_image(path: &Path, size: u32) -> PreparedImage { let image_path = path.to_str().unwrap(); // Check if we have the already converted image in a cache and use that if possible. - - - // Otherwise let's do that work now and store it. - let image = image::open(path).unwrap(); - let small_image = image.resize_exact(size, size, FilterType::Lanczos3); - let grey_image = small_image.to_luma(); - PreparedImage { orig_path: &*image_path, image: grey_image } + match cache::get_from_cache(&path) { + Some(image) => { + PreparedImage { orig_path: &*image_path, image: image } + }, + None => { + // Otherwise let's do that work now and store it. + let image = image::open(path).unwrap(); + let small_image = image.resize_exact(size, size, FilterType::Lanczos3); + let grey_image = small_image.to_luma(); + cache::put_in_cache(&path, &grey_image); + PreparedImage { orig_path: &*image_path, image: grey_image } + }, + } } /** diff --git a/src/lib.rs b/src/lib.rs index b54aec8..7d988ad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,18 @@ use std::path::Path; mod hash; mod cache; +/** + * Prepare the library for work. + * + * Not performing this step may cause parts to fail. + */ +pub fn init() { + match cache::prep_cache() { + Ok(_) => {}, + Err(e) => println!("Error: {}", e), + } +} + pub fn get_phashes(path: &Path) -> hash::PerceptualHashes { hash::get_perceptual_hashes(path, 8) } diff --git a/src/main.rs b/src/main.rs index 8db5f0e..68d0ff0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -38,6 +38,9 @@ fn main() { let args: Args = Docopt::new(USAGE) .and_then(|d| d.decode()) .unwrap_or_else(|e| e.exit()); + // Init the hashing library + pihash::init(); + //println!("{:?}", args); // All flags set or, no flags set if (args.flag_ahash && args.flag_dhash && args.flag_phash)