You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

439 lines
17 KiB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
  1. // Copyright 2016 Drew Short <drew@sothr.com>.
  2. //
  3. // Licensed under the MIT license<LICENSE-MIT or http://opensource.org/licenses/MIT>.
  4. // This file may not be copied, modified, or distributed except according to those terms.
  5. // Enable nightly features for extra testing behind the bench feature
  6. #![cfg_attr(feature = "bench", feature(test))]
  7. extern crate libc;
  8. extern crate rustc_serialize;
  9. #[cfg(feature = "bench")]
  10. extern crate test;
  11. pub mod hash;
  12. pub mod cache;
  13. use std::path::Path;
  14. use std::ffi::CStr;
  15. use cache::Cache;
  16. #[repr(C)]
  17. pub struct PIHash<'a> {
  18. cache: Option<Cache<'a>>
  19. }
  20. impl<'a> PIHash<'a> {
  21. /**
  22. * Create a new pihash library, and initialize a cache of a path is passed. If none is passed then no cache is initialized or used with the library
  23. */
  24. pub fn new(cache_path: Option<&'a str>) -> PIHash<'a> {
  25. match cache_path {
  26. Some(path) => {
  27. let cache = Cache { cache_dir: path, use_cache: true};
  28. match cache.init() {
  29. Ok(_) => PIHash { cache: Some(cache) },
  30. Err(e) => {
  31. println!("Error creating library with cache: {}", e);
  32. PIHash { cache: None }
  33. }
  34. }
  35. },
  36. None => PIHash { cache: None },
  37. }
  38. }
  39. pub fn get_perceptual_hash(&self, path: &Path, precision: &hash::Precision, hash_type: &hash::HashType) -> u64 {
  40. hash::get_perceptual_hash(&path, &precision, &hash_type, &self.cache)
  41. }
  42. pub fn get_phashes(&self, path: &'a Path) -> hash::PerceptualHashes {
  43. hash::get_perceptual_hashes(path, &hash::Precision::Medium, &self.cache)
  44. }
  45. pub fn get_ahash(&self, path: &Path) -> u64 {
  46. hash::get_perceptual_hash(&path,
  47. &hash::Precision::Medium,
  48. &hash::HashType::AHash,
  49. &self.cache)
  50. }
  51. pub fn get_dhash(&self, path: &Path) -> u64 {
  52. hash::get_perceptual_hash(&path,
  53. &hash::Precision::Medium,
  54. &hash::HashType::DHash,
  55. &self.cache)
  56. }
  57. pub fn get_phash(&self, path: &Path) -> u64 {
  58. hash::get_perceptual_hash(&path,
  59. &hash::Precision::Medium,
  60. &hash::HashType::PHash,
  61. &self.cache)
  62. }
  63. }
  64. /**
  65. * Get the Hamming Distance between two hashes.
  66. * Represents the absolute difference between two numbers.
  67. */
  68. pub fn get_hamming_distance(hash1: u64, hash2: u64) -> u64 {
  69. hash::calculate_hamming_distance(hash1, hash2)
  70. }
  71. // External proxies for the get_*hash methods //
  72. #[no_mangle]
  73. pub extern "C" fn ext_init(cache_path_char: *const libc::c_char) -> *const libc::c_void {
  74. unsafe {
  75. let path_cstr = CStr::from_ptr(cache_path_char);
  76. let path_str = match path_cstr.to_str() {
  77. Ok(path) => Some(path),
  78. Err(_) => None,
  79. };
  80. //println!("Created new lib, with cache at {}", path_str.unwrap());
  81. let lib = Box::new(PIHash::new(path_str));
  82. let ptr = Box::into_raw(lib) as *mut libc::c_void;
  83. ptr
  84. }
  85. }
  86. #[no_mangle]
  87. pub extern "C" fn ext_free(raw_lib: *const libc::c_void) {
  88. unsafe {
  89. drop(Box::from_raw(raw_lib as *mut PIHash));
  90. }
  91. }
  92. #[no_mangle]
  93. pub extern "C" fn ext_get_ahash(lib: &PIHash, path_char: *const libc::c_char) -> libc::uint64_t {
  94. unsafe {
  95. let path_str = CStr::from_ptr(path_char);
  96. let image_path = match path_str.to_str() {
  97. Ok(result) => result,
  98. Err(e) => {
  99. println!("Error: {}. Unable to parse '{}'",
  100. e,
  101. to_hex_string(path_str.to_bytes()));
  102. panic!("Unable to parse path")
  103. }
  104. };
  105. let path = Path::new(&image_path);
  106. lib.get_ahash(&path)
  107. }
  108. }
  109. #[no_mangle]
  110. pub extern "C" fn ext_get_dhash(lib: &PIHash, path_char: *const libc::c_char) -> libc::uint64_t {
  111. unsafe {
  112. let path_str = CStr::from_ptr(path_char);
  113. let image_path = match path_str.to_str() {
  114. Ok(result) => result,
  115. Err(e) => {
  116. println!("Error: {}. Unable to parse '{}'",
  117. e,
  118. to_hex_string(path_str.to_bytes()));
  119. panic!("Unable to parse path")
  120. }
  121. };
  122. let path = Path::new(&image_path);
  123. lib.get_dhash(&path)
  124. }
  125. }
  126. #[no_mangle]
  127. pub extern "C" fn ext_get_phash(lib: &PIHash, path_char: *const libc::c_char) -> libc::uint64_t {
  128. unsafe {
  129. let path_str = CStr::from_ptr(path_char);
  130. let image_path = match path_str.to_str() {
  131. Ok(result) => result,
  132. Err(e) => {
  133. println!("Error: {}. Unable to parse '{}'",
  134. e,
  135. to_hex_string(path_str.to_bytes()));
  136. panic!("Unable to parse path")
  137. }
  138. };
  139. let path = Path::new(&image_path);
  140. lib.get_phash(&path)
  141. }
  142. }
  143. fn to_hex_string(bytes: &[u8]) -> String {
  144. println!("length: {}", bytes.len());
  145. let mut strs: Vec<String> = Vec::new();
  146. for byte in bytes {
  147. // println!("{:02x}", byte);
  148. strs.push(format!("{:02x}", byte));
  149. }
  150. strs.join("\\x")
  151. }
  152. // Module for the tests
  153. //
  154. #[cfg(test)]
  155. mod tests {
  156. use std::fs;
  157. use std::path::Path;
  158. use hash;
  159. use cache;
  160. use super::PIHash;
  161. #[cfg(feature = "bench")]
  162. use super::test::Bencher;
  163. #[test]
  164. fn test_can_get_test_images() {
  165. let paths = fs::read_dir(&Path::new("./test_images")).unwrap();
  166. let mut num_paths = 0;
  167. for path in paths {
  168. let orig_path = path.unwrap().path();
  169. let ext = Path::new(&orig_path).extension();
  170. match ext {
  171. Some(_) => {
  172. if ext.unwrap() == "jpg" {
  173. num_paths += 1;
  174. println!("Is a image {}: {:?}", num_paths, orig_path) ;
  175. }
  176. }
  177. _ => {
  178. println!("Not an image: {:?}", orig_path) ;
  179. continue;
  180. }
  181. }
  182. // println!("Name: {}", path.unwrap().path().display())
  183. }
  184. // Currently 12 images in the test imaages directory
  185. assert!(num_paths == 12);
  186. }
  187. /**
  188. * Updated test function. Assumes 3 images to a set and no hamming distances.
  189. * We don't need to confirm that the hamming distance calculation works in these tests.
  190. */
  191. fn test_imageset_hash(hash_type: hash::HashType,
  192. hash_precision: hash::Precision,
  193. image_paths: [&Path; 3],
  194. image_hashes: [u64; 3],
  195. lib: &PIHash) {
  196. for index in 0..image_paths.len() {
  197. let image_path = image_paths[index];
  198. let calculated_hash = lib.get_perceptual_hash(&image_path,
  199. &hash_precision,
  200. &hash_type);
  201. println!("Image hashes for '{}': expected: {} actual: {}",
  202. image_path.to_str().unwrap(),
  203. image_hashes[index],
  204. calculated_hash);
  205. assert!(calculated_hash == image_hashes[index]);
  206. }
  207. }
  208. #[test]
  209. fn test_confirm_ahash_results() {
  210. // Prep_library
  211. let lib = PIHash::new(Some(cache::DEFAULT_CACHE_DIR));
  212. // Sample_01 tests
  213. let sample_01_images: [&Path; 3] = [&Path::new("./test_images/sample_01_large.jpg"),
  214. &Path::new("./test_images/sample_01_medium.jpg"),
  215. &Path::new("./test_images/sample_01_small.jpg")];
  216. let sample_01_hashes: [u64; 3] = [857051991849750, 857051991849750, 857051991849750];
  217. test_imageset_hash(hash::HashType::AHash,
  218. hash::Precision::Medium,
  219. sample_01_images,
  220. sample_01_hashes,
  221. &lib);
  222. // Sample_02 tests
  223. let sample_02_images: [&Path; 3] = [&Path::new("./test_images/sample_02_large.jpg"),
  224. &Path::new("./test_images/sample_02_medium.jpg"),
  225. &Path::new("./test_images/sample_02_small.jpg")];
  226. let sample_02_hashes: [u64; 3] = [18446744073441116160,
  227. 18446744073441116160,
  228. 18446744073441116160];
  229. test_imageset_hash(hash::HashType::AHash,
  230. hash::Precision::Medium,
  231. sample_02_images,
  232. sample_02_hashes,
  233. &lib);
  234. // Sample_03 tests
  235. let sample_03_images: [&Path; 3] = [&Path::new("./test_images/sample_03_large.jpg"),
  236. &Path::new("./test_images/sample_03_medium.jpg"),
  237. &Path::new("./test_images/sample_03_small.jpg")];
  238. let sample_03_hashes: [u64; 3] = [135670932300497406,
  239. 135670932300497406,
  240. 135670932300497406];
  241. test_imageset_hash(hash::HashType::AHash,
  242. hash::Precision::Medium,
  243. sample_03_images,
  244. sample_03_hashes,
  245. &lib);
  246. // Sample_04 tests
  247. let sample_04_images: [&Path; 3] = [&Path::new("./test_images/sample_04_large.jpg"),
  248. &Path::new("./test_images/sample_04_medium.jpg"),
  249. &Path::new("./test_images/sample_04_small.jpg")];
  250. let sample_04_hashes: [u64; 3] = [18446460933225054208,
  251. 18446460933090836480,
  252. 18446460933090836480];
  253. test_imageset_hash(hash::HashType::AHash,
  254. hash::Precision::Medium,
  255. sample_04_images,
  256. sample_04_hashes,
  257. &lib);
  258. // Clean_Cache
  259. // super::teardown();
  260. }
  261. #[test]
  262. fn test_confirm_dhash_results() {
  263. // Prep_library
  264. let lib = PIHash::new(Some(cache::DEFAULT_CACHE_DIR));
  265. // Sample_01 tests
  266. let sample_01_images: [&Path; 3] = [&Path::new("./test_images/sample_01_large.jpg"),
  267. &Path::new("./test_images/sample_01_medium.jpg"),
  268. &Path::new("./test_images/sample_01_small.jpg")];
  269. let sample_01_hashes: [u64; 3] = [7937395827556495926,
  270. 7937395827556495926,
  271. 7939647627370181174];
  272. test_imageset_hash(hash::HashType::DHash,
  273. hash::Precision::Medium,
  274. sample_01_images,
  275. sample_01_hashes,
  276. &lib);
  277. // Sample_02 tests
  278. let sample_02_images: [&Path; 3] = [&Path::new("./test_images/sample_02_large.jpg"),
  279. &Path::new("./test_images/sample_02_medium.jpg"),
  280. &Path::new("./test_images/sample_02_small.jpg")];
  281. let sample_02_hashes: [u64; 3] = [11009829669713008949,
  282. 11009829670249879861,
  283. 11009829669713008949];
  284. test_imageset_hash(hash::HashType::DHash,
  285. hash::Precision::Medium,
  286. sample_02_images,
  287. sample_02_hashes,
  288. &lib);
  289. // Sample_03 tests
  290. let sample_03_images: [&Path; 3] = [&Path::new("./test_images/sample_03_large.jpg"),
  291. &Path::new("./test_images/sample_03_medium.jpg"),
  292. &Path::new("./test_images/sample_03_small.jpg")];
  293. let sample_03_hashes: [u64; 3] = [225528496439353286,
  294. 225528496439353286,
  295. 226654396346195908];
  296. test_imageset_hash(hash::HashType::DHash,
  297. hash::Precision::Medium,
  298. sample_03_images,
  299. sample_03_hashes,
  300. &lib);
  301. // Sample_04 tests
  302. let sample_04_images: [&Path; 3] = [&Path::new("./test_images/sample_04_large.jpg"),
  303. &Path::new("./test_images/sample_04_medium.jpg"),
  304. &Path::new("./test_images/sample_04_small.jpg")];
  305. let sample_04_hashes: [u64; 3] = [14620651386429567209,
  306. 14620651386429567209,
  307. 14620651386429567209];
  308. test_imageset_hash(hash::HashType::DHash,
  309. hash::Precision::Medium,
  310. sample_04_images,
  311. sample_04_hashes,
  312. &lib);
  313. // Clean_Cache
  314. // super::teardown();
  315. }
  316. #[test]
  317. fn test_confirm_phash_results() {
  318. // Prep_library
  319. let lib = PIHash::new(Some(cache::DEFAULT_CACHE_DIR));
  320. // Sample_01 tests
  321. let sample_01_images: [&Path; 3] = [&Path::new("./test_images/sample_01_large.jpg"),
  322. &Path::new("./test_images/sample_01_medium.jpg"),
  323. &Path::new("./test_images/sample_01_small.jpg")];
  324. let sample_01_hashes: [u64; 3] = [72357778504597504, 72357778504597504, 72357778504597504];
  325. test_imageset_hash(hash::HashType::PHash,
  326. hash::Precision::Medium,
  327. sample_01_images,
  328. sample_01_hashes,
  329. &lib);
  330. // Sample_02 tests
  331. let sample_02_images: [&Path; 3] = [&Path::new("./test_images/sample_02_large.jpg"),
  332. &Path::new("./test_images/sample_02_medium.jpg"),
  333. &Path::new("./test_images/sample_02_small.jpg")];
  334. let sample_02_hashes: [u64; 3] = [5332332327550844928,
  335. 5332332327550844928,
  336. 5332332327550844928];
  337. test_imageset_hash(hash::HashType::PHash,
  338. hash::Precision::Medium,
  339. sample_02_images,
  340. sample_02_hashes,
  341. &lib);
  342. // Sample_03 tests
  343. let sample_03_images: [&Path; 3] = [&Path::new("./test_images/sample_03_large.jpg"),
  344. &Path::new("./test_images/sample_03_medium.jpg"),
  345. &Path::new("./test_images/sample_03_small.jpg")];
  346. let sample_03_hashes: [u64; 3] = [6917529027641081856,
  347. 6917529027641081856,
  348. 6917529027641081856];
  349. test_imageset_hash(hash::HashType::PHash,
  350. hash::Precision::Medium,
  351. sample_03_images,
  352. sample_03_hashes,
  353. &lib);
  354. // Sample_04 tests
  355. let sample_04_images: [&Path; 3] = [&Path::new("./test_images/sample_04_large.jpg"),
  356. &Path::new("./test_images/sample_04_medium.jpg"),
  357. &Path::new("./test_images/sample_04_small.jpg")];
  358. let sample_04_hashes: [u64; 3] = [10997931646002397184,
  359. 10997931646002397184,
  360. 11142046834078253056];
  361. test_imageset_hash(hash::HashType::PHash,
  362. hash::Precision::Medium,
  363. sample_04_images,
  364. sample_04_hashes,
  365. &lib);
  366. // Clean_Cache
  367. // super::teardown();
  368. }
  369. #[cfg(feature = "bench")]
  370. #[bench]
  371. fn bench_with_cache(bench: &mut Bencher) -> () {
  372. // Prep_library
  373. let lib = PIHash::new(Some(cache::DEFAULT_CACHE_DIR));
  374. // Setup the caches to make sure we're good to properly bench
  375. // All phashes so that the matricies are pulled from cache as well
  376. lib.get_perceptual_hash(&Path::new("./test_images/sample_01_large.jpg"),&hash::Precision::Medium, &hash::HashType::PHash);
  377. bench.iter(|| {
  378. // Sample_01 Bench
  379. lib.get_perceptual_hash(&Path::new("./test_images/sample_01_large.jpg"), &hash::Precision::Medium, &hash::HashType::PHash);
  380. })
  381. }
  382. #[cfg(feature = "bench")]
  383. #[bench]
  384. fn bench_without_cache(bench: &mut Bencher) -> () {
  385. // Prep_library
  386. let lib = PIHash::new(None);
  387. bench.iter(|| {
  388. // Sample_01 Bench
  389. lib.get_perceptual_hash(&Path::new("./test_images/sample_01_large.jpg"), &hash::Precision::Medium, &hash::HashType::PHash);
  390. })
  391. }
  392. }