From e0bb7d6592dfe875af968cfe03e57ac84173dabd Mon Sep 17 00:00:00 2001 From: Drew Short Date: Thu, 10 Mar 2016 23:57:36 -0600 Subject: [PATCH] Fixed the FFI implementation. Updated the python FFI example to reflect the new changes. --- Cargo.toml | 2 +- FFI-tests/ffi_test.py | 34 +++++-- src/cache.rs | 2 + src/hash/ahash.rs | 4 +- src/hash/dhash.rs | 4 +- src/hash/mod.rs | 8 +- src/hash/phash.rs | 4 +- src/lib.rs | 216 +++++++++++++++++------------------------- 8 files changed, 129 insertions(+), 145 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 235c4d6..a1d299d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pihash" -version = "0.3.1" +version = "0.3.3" authors = ["Drew Short "] 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/" diff --git a/FFI-tests/ffi_test.py b/FFI-tests/ffi_test.py index c7c9f76..050c24a 100755 --- a/FFI-tests/ffi_test.py +++ b/FFI-tests/ffi_test.py @@ -15,10 +15,19 @@ large_image3_path = "../test_images/sample_03_large.jpg".encode(encoding="utf-8" medium_image3_path = "../test_images/sample_03_medium.jpg".encode(encoding="utf-8") small_image3_path = "../test_images/sample_03_small.jpg".encode(encoding="utf-8") -test_images=[large_image1_path, medium_image1_path, small_image1_path,large_image2_path, medium_image2_path, small_image2_path,large_image3_path, medium_image3_path, small_image3_path] +large_image4_path = "../test_images/sample_04_large.jpg".encode(encoding="utf-8") +medium_image4_path = "../test_images/sample_04_medium.jpg".encode(encoding="utf-8") +small_image4_path = "../test_images/sample_04_small.jpg".encode(encoding="utf-8") + +test_images=[ + large_image1_path, medium_image1_path, small_image1_path, + large_image2_path, medium_image2_path, small_image2_path, + large_image3_path, medium_image3_path, small_image3_path, + large_image4_path, medium_image4_path, small_image4_path +] def unsigned64(number): - return c_ulonglong(number).value + return (number & 0xfffffffffffffffff) print("starting ffi test") @@ -33,12 +42,22 @@ else: # Setting the ctypes return type references for the foreign functions +# returns a pointer to the library that we'll need to pass to all function calls +lib.ext_init.restype = c_void_p +# Returns a longlong hash, takes a pointer and a string lib.ext_get_ahash.restype = c_ulonglong +lib.ext_get_ahash.argtypes = [c_void_p, c_char_p] lib.ext_get_dhash.restype = c_ulonglong +lib.ext_get_dhash.argtypes = [c_void_p, c_char_p] lib.ext_get_phash.restype = c_ulonglong +lib.ext_get_phash.argtypes = [c_void_p, c_char_p] +# Takes a pointer and frees the struct at that memory location +lib.ext_free.argtypes = [c_void_p] #initialize the library -lib.init() +lib_struct = lib.ext_init("./.hash_cache".encode(encoding="utf-8")) + +#print("Pointer to lib_struct: ", lib_struct) # Print the path and the bytes as hex for debugging #print(large_image_path) @@ -46,11 +65,12 @@ lib.init() for image in test_images: print("Requesting hashes for: %s"% image) - print("ahash: %i"% unsigned64(lib.ext_get_ahash(image))) - print("dhash: %i"% unsigned64(lib.ext_get_dhash(image))) - print("phash: %i"% unsigned64(lib.ext_get_phash(image))) + print("ahash: %i"% unsigned64(lib.ext_get_ahash(lib_struct, image))) + print("dhash: %i"% unsigned64(lib.ext_get_dhash(lib_struct, image))) + print("phash: %i"% unsigned64(lib.ext_get_phash(lib_struct, image))) # Do cleanup -#lib.teardown() +# Makes sure that the heap is cleaned up +lib.ext_free(lib_struct) print("ffi test finished") diff --git a/src/cache.rs b/src/cache.rs index 5d1806e..ff01211 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -52,6 +52,7 @@ impl PartialEq for CacheMetadata { /** * Structure to hold implementation of the cache */ +#[repr(C)] pub struct Cache<'a> { pub cache_dir: &'a str, pub use_cache: bool, @@ -160,6 +161,7 @@ impl<'a> Cache<'a> { sha1, CACHED_IMAGE_EXT); let cache_dir_str = format!("{}/image/{}x{}", self.cache_dir, size, size); + //println!("Saving: {}", cache_path_str); match create_dir_all(cache_dir_str) { Ok(_) => { let cached_path = Path::new(&cache_path_str); diff --git a/src/hash/ahash.rs b/src/hash/ahash.rs index 3e5deef..06db899 100644 --- a/src/hash/ahash.rs +++ b/src/hash/ahash.rs @@ -14,7 +14,7 @@ pub struct AHash<'a> { } impl<'a> AHash<'a> { - pub fn new(path: &'a Path, precision: &Precision, cache: &Option>) -> Self { + pub fn new(path: &'a Path, precision: &Precision, cache: &Option) -> Self { AHash { prepared_image: Box::new(prepare_image(&path, &HashType::AHash, &precision, cache)), } @@ -29,7 +29,7 @@ impl<'a> PerceptualHash for AHash<'a> { * * A u64 representing the value of the hash */ - fn get_hash(&self, _: &Option>) -> u64 { + fn get_hash(&self, _: &Option) -> u64 { match self.prepared_image.image { Some(ref image) => { let (width, height) = image.dimensions(); diff --git a/src/hash/dhash.rs b/src/hash/dhash.rs index 415e773..bc73b70 100644 --- a/src/hash/dhash.rs +++ b/src/hash/dhash.rs @@ -14,7 +14,7 @@ pub struct DHash<'a> { } impl<'a> DHash<'a> { - pub fn new(path: &'a Path, precision: &Precision, cache: &Option>) -> Self { + pub fn new(path: &'a Path, precision: &Precision, cache: &Option) -> Self { DHash { prepared_image: Box::new(prepare_image(&path, &HashType::DHash, &precision, cache)), } @@ -29,7 +29,7 @@ impl<'a> PerceptualHash for DHash<'a> { * * Returns a u64 representing the value of the hash */ - fn get_hash(&self, _: &Option>) -> u64 { + fn get_hash(&self, _: &Option) -> u64 { match self.prepared_image.image { Some(ref image) => { let first_pixel_val = image.pixels().nth(0).unwrap().channels()[0]; diff --git a/src/hash/mod.rs b/src/hash/mod.rs index da3caaf..dd9e71e 100644 --- a/src/hash/mod.rs +++ b/src/hash/mod.rs @@ -107,7 +107,7 @@ pub enum HashType { // Traits // pub trait PerceptualHash { - fn get_hash(&self, cache: &Option>) -> u64; + fn get_hash(&self, cache: &Option) -> u64; } // Functions // @@ -129,7 +129,7 @@ pub trait PerceptualHash { pub fn prepare_image<'a>(path: &'a Path, hash_type: &HashType, precision: &Precision, - cache: &Option>) + cache: &Option) -> PreparedImage<'a> { let image_path = path.to_str().unwrap(); let size: u32 = match *hash_type { @@ -195,7 +195,7 @@ fn process_image<'a>(image_path: &'a str, pub fn get_perceptual_hash<'a>(path: &'a Path, precision: &Precision, hash_type: &HashType, - cache: &Option>) + cache: &Option) -> u64 { match *hash_type { HashType::AHash => ahash::AHash::new(&path, &precision, &cache).get_hash(&cache), @@ -209,7 +209,7 @@ pub fn get_perceptual_hash<'a>(path: &'a Path, */ pub fn get_perceptual_hashes<'a>(path: &'a Path, precision: &Precision, - cache: &Option>) + cache: &Option) -> PerceptualHashes<'a> { let image_path = path.to_str().unwrap(); let ahash = ahash::AHash::new(&path, &precision, &cache).get_hash(&cache); diff --git a/src/hash/phash.rs b/src/hash/phash.rs index 19e8f17..5e12c43 100644 --- a/src/hash/phash.rs +++ b/src/hash/phash.rs @@ -16,7 +16,7 @@ pub struct PHash<'a> { } impl<'a> PHash<'a> { - pub fn new(path: &'a Path, precision: &Precision, cache: &Option>) -> Self { + pub fn new(path: &'a Path, precision: &Precision, cache: &Option) -> Self { PHash { prepared_image: Box::new(prepare_image(&path, &HashType::PHash, &precision, cache)), } @@ -31,7 +31,7 @@ impl<'a> PerceptualHash for PHash<'a> { * * Returns a u64 representing the value of the hash */ - fn get_hash(&self, cache: &Option>) -> u64 { + fn get_hash(&self, cache: &Option) -> u64 { match self.prepared_image.image { Some(ref image) => { // Get the image data into a vector to perform the DFT on. diff --git a/src/lib.rs b/src/lib.rs index 723b38f..fd55bcd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,8 +18,9 @@ use std::path::Path; use std::ffi::CStr; use cache::Cache; +#[repr(C)] pub struct PIHash<'a> { - cache: Option>> + cache: Option> } impl<'a> PIHash<'a> { @@ -31,7 +32,7 @@ impl<'a> PIHash<'a> { Some(path) => { let cache = Cache { cache_dir: path, use_cache: true}; match cache.init() { - Ok(_) => PIHash { cache: Some(Box::new(cache)) }, + Ok(_) => PIHash { cache: Some(cache) }, Err(e) => { println!("Error creating library with cache: {}", e); PIHash { cache: None } @@ -68,7 +69,7 @@ impl<'a> PIHash<'a> { pub fn get_phash(&self, path: &Path) -> u64 { hash::get_perceptual_hash(&path, &hash::Precision::Medium, - &hash::HashType::DHash, + &hash::HashType::PHash, &self.cache) } } @@ -83,93 +84,91 @@ pub fn get_hamming_distance(hash1: u64, hash2: u64) -> u64 { // External proxies for the get_*hash methods // -// /** -// * Prepare the library for work. -// * -// * Not performing this step may cause parts to fail. -// */ -// #[no_mangle] -// pub extern "C" fn init() { -// match LIB_CACHE.init() { -// Ok(_) => {} -// Err(e) => println!("Error: {}", e), -// } -// } - -// /** -// * Teardown for the library -// */ -// #[no_mangle] -// pub extern "C" fn teardown() { -// match LIB_CACHE.clean() { -// Ok(_) => {} -// Err(e) => println!("Error: {}", e), -// } -// } - -// #[no_mangle] -// pub extern "C" fn ext_get_ahash(path_char: *const libc::c_char) -> libc::uint64_t { -// unsafe { -// let path_str = CStr::from_ptr(path_char); -// let image_path = match path_str.to_str() { -// Ok(result) => result, -// Err(e) => { -// println!("Error: {}. Unable to parse '{}'", -// e, -// to_hex_string(path_str.to_bytes())); -// panic!("Unable to parse path") -// } -// }; -// let path = Path::new(&image_path); -// get_ahash(&path) -// } -// } - -// #[no_mangle] -// pub extern "C" fn ext_get_dhash(path_char: *const libc::c_char) -> libc::uint64_t { -// unsafe { -// let path_str = CStr::from_ptr(path_char); -// let image_path = match path_str.to_str() { -// Ok(result) => result, -// Err(e) => { -// println!("Error: {}. Unable to parse '{}'", -// e, -// to_hex_string(path_str.to_bytes())); -// panic!("Unable to parse path") -// } -// }; -// let path = Path::new(&image_path); -// get_dhash(&path) -// } -// } - -// #[no_mangle] -// pub extern "C" fn ext_get_phash(path_char: *const libc::c_char) -> libc::uint64_t { -// unsafe { -// let path_str = CStr::from_ptr(path_char); -// let image_path = match path_str.to_str() { -// Ok(result) => result, -// Err(e) => { -// println!("Error: {}. Unable to parse '{}'", -// e, -// to_hex_string(path_str.to_bytes())); -// panic!("Unable to parse path") -// } -// }; -// let path = Path::new(&image_path); -// get_phash(&path) -// } -// } - -// fn to_hex_string(bytes: &[u8]) -> String { -// println!("length: {}", bytes.len()); -// let mut strs: Vec = Vec::new(); -// for byte in bytes { -// // println!("{:02x}", byte); -// strs.push(format!("{:02x}", byte)); -// } -// strs.join("\\x") -// } +#[no_mangle] +pub extern "C" fn ext_init(cache_path_char: *const libc::c_char) -> *const libc::c_void { + unsafe { + let path_cstr = CStr::from_ptr(cache_path_char); + let path_str = match path_cstr.to_str() { + Ok(path) => Some(path), + Err(_) => None, + }; + //println!("Created new lib, with cache at {}", path_str.unwrap()); + let lib = Box::new(PIHash::new(path_str)); + let ptr = Box::into_raw(lib) as *mut libc::c_void; + ptr + } +} + +#[no_mangle] +pub extern "C" fn ext_free(raw_lib: *const libc::c_void) { + unsafe { + drop(Box::from_raw(raw_lib as *mut PIHash)); + } +} + +#[no_mangle] +pub extern "C" fn ext_get_ahash(lib: &PIHash, path_char: *const libc::c_char) -> libc::uint64_t { + unsafe { + let path_str = CStr::from_ptr(path_char); + let image_path = match path_str.to_str() { + Ok(result) => result, + Err(e) => { + println!("Error: {}. Unable to parse '{}'", + e, + to_hex_string(path_str.to_bytes())); + panic!("Unable to parse path") + } + }; + let path = Path::new(&image_path); + lib.get_ahash(&path) + } +} + +#[no_mangle] +pub extern "C" fn ext_get_dhash(lib: &PIHash, path_char: *const libc::c_char) -> libc::uint64_t { + unsafe { + let path_str = CStr::from_ptr(path_char); + let image_path = match path_str.to_str() { + Ok(result) => result, + Err(e) => { + println!("Error: {}. Unable to parse '{}'", + e, + to_hex_string(path_str.to_bytes())); + panic!("Unable to parse path") + } + }; + let path = Path::new(&image_path); + lib.get_dhash(&path) + } +} + +#[no_mangle] +pub extern "C" fn ext_get_phash(lib: &PIHash, path_char: *const libc::c_char) -> libc::uint64_t { + unsafe { + let path_str = CStr::from_ptr(path_char); + let image_path = match path_str.to_str() { + Ok(result) => result, + Err(e) => { + println!("Error: {}. Unable to parse '{}'", + e, + to_hex_string(path_str.to_bytes())); + panic!("Unable to parse path") + } + }; + let path = Path::new(&image_path); + lib.get_phash(&path) + } +} + +fn to_hex_string(bytes: &[u8]) -> String { + println!("length: {}", bytes.len()); + let mut strs: Vec = Vec::new(); + for byte in bytes { + // println!("{:02x}", byte); + strs.push(format!("{:02x}", byte)); + } + strs.join("\\x") +} // Module for the tests // @@ -419,30 +418,10 @@ mod tests { // Setup the caches to make sure we're good to properly bench // All phashes so that the matricies are pulled from cache as well lib.get_perceptual_hash(&Path::new("./test_images/sample_01_large.jpg"),&hash::Precision::Medium, &hash::HashType::PHash); - lib.get_perceptual_hash(&Path::new("./test_images/sample_02_large.jpg"),&hash::Precision::Medium, &hash::HashType::PHash); - lib.get_perceptual_hash(&Path::new("./test_images/sample_03_large.jpg"),&hash::Precision::Medium, &hash::HashType::PHash); - lib.get_perceptual_hash(&Path::new("./test_images/sample_04_large.jpg"),&hash::Precision::Medium, &hash::HashType::PHash); bench.iter(|| { // Sample_01 Bench - lib.get_perceptual_hash(&Path::new("./test_images/sample_01_large.jpg"), &hash::Precision::Medium, &hash::HashType::AHash); - lib.get_perceptual_hash(&Path::new("./test_images/sample_01_large.jpg"), &hash::Precision::Medium, &hash::HashType::DHash); lib.get_perceptual_hash(&Path::new("./test_images/sample_01_large.jpg"), &hash::Precision::Medium, &hash::HashType::PHash); - - // Sample_02 Bench - lib.get_perceptual_hash(&Path::new("./test_images/sample_02_large.jpg"), &hash::Precision::Medium, &hash::HashType::AHash); - lib.get_perceptual_hash(&Path::new("./test_images/sample_02_large.jpg"), &hash::Precision::Medium, &hash::HashType::DHash); - lib.get_perceptual_hash(&Path::new("./test_images/sample_02_large.jpg"), &hash::Precision::Medium, &hash::HashType::PHash); - - // Sample_03 Bench - lib.get_perceptual_hash(&Path::new("./test_images/sample_02_large.jpg"), &hash::Precision::Medium, &hash::HashType::AHash); - lib.get_perceptual_hash(&Path::new("./test_images/sample_02_large.jpg"), &hash::Precision::Medium, &hash::HashType::DHash); - lib.get_perceptual_hash(&Path::new("./test_images/sample_02_large.jpg"), &hash::Precision::Medium, &hash::HashType::PHash); - - // Sample_04 Bench - lib.get_perceptual_hash(&Path::new("./test_images/sample_04_large.jpg"), &hash::Precision::Medium, &hash::HashType::AHash); - lib.get_perceptual_hash(&Path::new("./test_images/sample_04_large.jpg"), &hash::Precision::Medium, &hash::HashType::DHash); - lib.get_perceptual_hash(&Path::new("./test_images/sample_04_large.jpg"), &hash::Precision::Medium, &hash::HashType::PHash); }) } @@ -454,24 +433,7 @@ mod tests { bench.iter(|| { // Sample_01 Bench - lib.get_perceptual_hash(&Path::new("./test_images/sample_01_large.jpg"), &hash::Precision::Medium, &hash::HashType::AHash); - lib.get_perceptual_hash(&Path::new("./test_images/sample_01_large.jpg"), &hash::Precision::Medium, &hash::HashType::DHash); lib.get_perceptual_hash(&Path::new("./test_images/sample_01_large.jpg"), &hash::Precision::Medium, &hash::HashType::PHash); - - // Sample_02 Bench - lib.get_perceptual_hash(&Path::new("./test_images/sample_02_large.jpg"), &hash::Precision::Medium, &hash::HashType::AHash); - lib.get_perceptual_hash(&Path::new("./test_images/sample_02_large.jpg"), &hash::Precision::Medium, &hash::HashType::DHash); - lib.get_perceptual_hash(&Path::new("./test_images/sample_02_large.jpg"), &hash::Precision::Medium, &hash::HashType::PHash); - - // Sample_03 Bench - lib.get_perceptual_hash(&Path::new("./test_images/sample_02_large.jpg"), &hash::Precision::Medium, &hash::HashType::AHash); - lib.get_perceptual_hash(&Path::new("./test_images/sample_02_large.jpg"), &hash::Precision::Medium, &hash::HashType::DHash); - lib.get_perceptual_hash(&Path::new("./test_images/sample_02_large.jpg"), &hash::Precision::Medium, &hash::HashType::PHash); - - // Sample_04 Bench - lib.get_perceptual_hash(&Path::new("./test_images/sample_04_large.jpg"), &hash::Precision::Medium, &hash::HashType::AHash); - lib.get_perceptual_hash(&Path::new("./test_images/sample_04_large.jpg"), &hash::Precision::Medium, &hash::HashType::DHash); - lib.get_perceptual_hash(&Path::new("./test_images/sample_04_large.jpg"), &hash::Precision::Medium, &hash::HashType::PHash); }) } }