Browse Source

Fixed the FFI implementation. Updated the python FFI example to reflect the new changes.

develop
Drew Short 9 years ago
parent
commit
e0bb7d6592
  1. 2
      Cargo.toml
  2. 34
      FFI-tests/ffi_test.py
  3. 2
      src/cache.rs
  4. 4
      src/hash/ahash.rs
  5. 4
      src/hash/dhash.rs
  6. 8
      src/hash/mod.rs
  7. 4
      src/hash/phash.rs
  8. 216
      src/lib.rs

2
Cargo.toml

@ -1,6 +1,6 @@
[package] [package]
name = "pihash" name = "pihash"
version = "0.3.1"
version = "0.3.3"
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/"

34
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") 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") 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): def unsigned64(number):
return c_ulonglong(number).value
return (number & 0xfffffffffffffffff)
print("starting ffi test") print("starting ffi test")
@ -33,12 +42,22 @@ else:
# Setting the ctypes return type references for the foreign functions # 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.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.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.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 #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 the path and the bytes as hex for debugging
#print(large_image_path) #print(large_image_path)
@ -46,11 +65,12 @@ lib.init()
for image in test_images: for image in test_images:
print("Requesting hashes for: %s"% image) 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 # Do cleanup
#lib.teardown()
# Makes sure that the heap is cleaned up
lib.ext_free(lib_struct)
print("ffi test finished") print("ffi test finished")

2
src/cache.rs

@ -52,6 +52,7 @@ impl PartialEq<CacheMetadata> for CacheMetadata {
/** /**
* Structure to hold implementation of the cache * Structure to hold implementation of the cache
*/ */
#[repr(C)]
pub struct Cache<'a> { pub struct Cache<'a> {
pub cache_dir: &'a str, pub cache_dir: &'a str,
pub use_cache: bool, pub use_cache: bool,
@ -160,6 +161,7 @@ impl<'a> Cache<'a> {
sha1, sha1,
CACHED_IMAGE_EXT); CACHED_IMAGE_EXT);
let cache_dir_str = format!("{}/image/{}x{}", self.cache_dir, size, size); let cache_dir_str = format!("{}/image/{}x{}", self.cache_dir, size, size);
//println!("Saving: {}", cache_path_str);
match create_dir_all(cache_dir_str) { match create_dir_all(cache_dir_str) {
Ok(_) => { Ok(_) => {
let cached_path = Path::new(&cache_path_str); let cached_path = Path::new(&cache_path_str);

4
src/hash/ahash.rs

@ -14,7 +14,7 @@ pub struct AHash<'a> {
} }
impl<'a> AHash<'a> { impl<'a> AHash<'a> {
pub fn new(path: &'a Path, precision: &Precision, cache: &Option<Box<Cache>>) -> Self {
pub fn new(path: &'a Path, precision: &Precision, cache: &Option<Cache>) -> Self {
AHash { AHash {
prepared_image: Box::new(prepare_image(&path, &HashType::AHash, &precision, cache)), 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 * A u64 representing the value of the hash
*/ */
fn get_hash(&self, _: &Option<Box<Cache>>) -> u64 {
fn get_hash(&self, _: &Option<Cache>) -> u64 {
match self.prepared_image.image { match self.prepared_image.image {
Some(ref image) => { Some(ref image) => {
let (width, height) = image.dimensions(); let (width, height) = image.dimensions();

4
src/hash/dhash.rs

@ -14,7 +14,7 @@ pub struct DHash<'a> {
} }
impl<'a> DHash<'a> { impl<'a> DHash<'a> {
pub fn new(path: &'a Path, precision: &Precision, cache: &Option<Box<Cache>>) -> Self {
pub fn new(path: &'a Path, precision: &Precision, cache: &Option<Cache>) -> Self {
DHash { DHash {
prepared_image: Box::new(prepare_image(&path, &HashType::DHash, &precision, cache)), 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 * Returns a u64 representing the value of the hash
*/ */
fn get_hash(&self, _: &Option<Box<Cache>>) -> u64 {
fn get_hash(&self, _: &Option<Cache>) -> u64 {
match self.prepared_image.image { match self.prepared_image.image {
Some(ref image) => { Some(ref image) => {
let first_pixel_val = image.pixels().nth(0).unwrap().channels()[0]; let first_pixel_val = image.pixels().nth(0).unwrap().channels()[0];

8
src/hash/mod.rs

@ -107,7 +107,7 @@ pub enum HashType {
// Traits // // Traits //
pub trait PerceptualHash { pub trait PerceptualHash {
fn get_hash(&self, cache: &Option<Box<Cache>>) -> u64;
fn get_hash(&self, cache: &Option<Cache>) -> u64;
} }
// Functions // // Functions //
@ -129,7 +129,7 @@ pub trait PerceptualHash {
pub fn prepare_image<'a>(path: &'a Path, pub fn prepare_image<'a>(path: &'a Path,
hash_type: &HashType, hash_type: &HashType,
precision: &Precision, precision: &Precision,
cache: &Option<Box<Cache>>)
cache: &Option<Cache>)
-> PreparedImage<'a> { -> PreparedImage<'a> {
let image_path = path.to_str().unwrap(); let image_path = path.to_str().unwrap();
let size: u32 = match *hash_type { 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, pub fn get_perceptual_hash<'a>(path: &'a Path,
precision: &Precision, precision: &Precision,
hash_type: &HashType, hash_type: &HashType,
cache: &Option<Box<Cache>>)
cache: &Option<Cache>)
-> u64 { -> u64 {
match *hash_type { match *hash_type {
HashType::AHash => ahash::AHash::new(&path, &precision, &cache).get_hash(&cache), 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, pub fn get_perceptual_hashes<'a>(path: &'a Path,
precision: &Precision, precision: &Precision,
cache: &Option<Box<Cache>>)
cache: &Option<Cache>)
-> PerceptualHashes<'a> { -> PerceptualHashes<'a> {
let image_path = path.to_str().unwrap(); let image_path = path.to_str().unwrap();
let ahash = ahash::AHash::new(&path, &precision, &cache).get_hash(&cache); let ahash = ahash::AHash::new(&path, &precision, &cache).get_hash(&cache);

4
src/hash/phash.rs

@ -16,7 +16,7 @@ pub struct PHash<'a> {
} }
impl<'a> PHash<'a> { impl<'a> PHash<'a> {
pub fn new(path: &'a Path, precision: &Precision, cache: &Option<Box<Cache>>) -> Self {
pub fn new(path: &'a Path, precision: &Precision, cache: &Option<Cache>) -> Self {
PHash { PHash {
prepared_image: Box::new(prepare_image(&path, &HashType::PHash, &precision, cache)), 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 * Returns a u64 representing the value of the hash
*/ */
fn get_hash(&self, cache: &Option<Box<Cache>>) -> u64 {
fn get_hash(&self, cache: &Option<Cache>) -> u64 {
match self.prepared_image.image { match self.prepared_image.image {
Some(ref 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.

216
src/lib.rs

@ -18,8 +18,9 @@ use std::path::Path;
use std::ffi::CStr; use std::ffi::CStr;
use cache::Cache; use cache::Cache;
#[repr(C)]
pub struct PIHash<'a> { pub struct PIHash<'a> {
cache: Option<Box<Cache<'a>>>
cache: Option<Cache<'a>>
} }
impl<'a> PIHash<'a> { impl<'a> PIHash<'a> {
@ -31,7 +32,7 @@ impl<'a> PIHash<'a> {
Some(path) => { Some(path) => {
let cache = Cache { cache_dir: path, use_cache: true}; let cache = Cache { cache_dir: path, use_cache: true};
match cache.init() { match cache.init() {
Ok(_) => PIHash { cache: Some(Box::new(cache)) },
Ok(_) => PIHash { cache: Some(cache) },
Err(e) => { Err(e) => {
println!("Error creating library with cache: {}", e); println!("Error creating library with cache: {}", e);
PIHash { cache: None } PIHash { cache: None }
@ -68,7 +69,7 @@ impl<'a> PIHash<'a> {
pub fn get_phash(&self, path: &Path) -> u64 { pub fn get_phash(&self, path: &Path) -> u64 {
hash::get_perceptual_hash(&path, hash::get_perceptual_hash(&path,
&hash::Precision::Medium, &hash::Precision::Medium,
&hash::HashType::DHash,
&hash::HashType::PHash,
&self.cache) &self.cache)
} }
} }
@ -83,93 +84,91 @@ pub fn get_hamming_distance(hash1: u64, hash2: u64) -> u64 {
// External proxies for the get_*hash methods // // 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<String> = 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<String> = Vec::new();
for byte in bytes {
// println!("{:02x}", byte);
strs.push(format!("{:02x}", byte));
}
strs.join("\\x")
}
// Module for the tests // Module for the tests
// //
@ -419,30 +418,10 @@ mod tests {
// Setup the caches to make sure we're good to properly bench // Setup the caches to make sure we're good to properly bench
// All phashes so that the matricies are pulled from cache as well // 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_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(|| { bench.iter(|| {
// Sample_01 Bench // 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); 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(|| { bench.iter(|| {
// Sample_01 Bench // 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); 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);
}) })
} }
} }
Loading…
Cancel
Save