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.

463 lines
14 KiB

  1. // Copyright 2015 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. // Pull in the image processing crate
  6. extern crate image;
  7. extern crate dft;
  8. extern crate complex;
  9. use std::path::Path;
  10. use std::f64;
  11. use self::image::{GenericImage, Pixel, FilterType};
  12. use self::dft::Transform;
  13. use cache;
  14. // Used to get ranges for the precision of rounding floats
  15. // Can round to 1 significant factor of precision
  16. const FLOAT_PRECISION_MAX_1: f64 = f64::MAX / 10_f64;
  17. const FLOAT_PRECISION_MIN_1: f64 = f64::MIN / 10_f64;
  18. // Can round to 2 significant factors of precision
  19. const FLOAT_PRECISION_MAX_2: f64 = f64::MAX / 100_f64;
  20. const FLOAT_PRECISION_MIN_2: f64 = f64::MIN / 100_f64;
  21. // Can round to 3 significant factors of precision
  22. const FLOAT_PRECISION_MAX_3: f64 = f64::MAX / 1000_f64;
  23. const FLOAT_PRECISION_MIN_3: f64 = f64::MIN / 1000_f64;
  24. // Can round to 4 significant factors of precision
  25. const FLOAT_PRECISION_MAX_4: f64 = f64::MAX / 10000_f64;
  26. const FLOAT_PRECISION_MIN_4: f64 = f64::MIN / 10000_f64;
  27. // Can round to 5 significant factors of precision
  28. const FLOAT_PRECISION_MAX_5: f64 = f64::MAX / 100000_f64;
  29. const FLOAT_PRECISION_MIN_5: f64 = f64::MIN / 100000_f64;
  30. /**
  31. * Prepared image that can be used to generate hashes
  32. */
  33. pub struct PreparedImage<'a> {
  34. orig_path: &'a str,
  35. image: image::ImageBuffer<image::Luma<u8>, Vec<u8>>,
  36. }
  37. /**
  38. * Wraps the various perceptual hashes
  39. */
  40. pub struct PerceptualHashes<'a> {
  41. pub orig_path: &'a str,
  42. pub ahash: u64,
  43. pub dhash: u64,
  44. pub phash: u64,
  45. }
  46. /**
  47. * All the supported precision types
  48. *
  49. * Low aims for 32 bit precision
  50. * Medium aims for 64 bit precision
  51. * High aims for 128 bit precision
  52. */
  53. pub enum Precision {
  54. Low,
  55. Medium,
  56. High,
  57. }
  58. // Get the size of the required image
  59. //
  60. impl Precision {
  61. fn get_size(&self) -> u32 {
  62. match *self {
  63. Precision::Low => 4,
  64. Precision::Medium => 8,
  65. Precision::High => 16,
  66. }
  67. }
  68. }
  69. /**
  70. * Types of hashes supported
  71. */
  72. pub enum HashType {
  73. Ahash,
  74. Dhash,
  75. Phash,
  76. }
  77. /**
  78. * Resonsible for parsing a path, converting an image and package it to be
  79. * hashed.
  80. *
  81. * # Arguments
  82. *
  83. * * 'path' - The path to the image requested to be hashed
  84. * * 'size' - The size that the image should be resize to, in the form of size x size
  85. *
  86. * # Returns
  87. *
  88. * A PreparedImage struct with the required information for performing hashing
  89. *
  90. */
  91. pub fn prepare_image<'a>(path: &'a Path,
  92. hash_type: &HashType,
  93. precision: &Precision)
  94. -> PreparedImage<'a> {
  95. let image_path = path.to_str().unwrap();
  96. let size: u32 = match *hash_type {
  97. HashType::Phash => precision.get_size() * 4,
  98. _ => precision.get_size(),
  99. };
  100. // Check if we have the already converted image in a cache and use that if possible.
  101. match cache::get_image_from_cache(&path, size) {
  102. Some(image) => {
  103. PreparedImage {
  104. orig_path: &*image_path,
  105. image: image,
  106. }
  107. }
  108. None => {
  109. // Otherwise let's do that work now and store it.
  110. let image = image::open(path).unwrap();
  111. let small_image = image.resize_exact(size, size, FilterType::Lanczos3);
  112. let grey_image = small_image.to_luma();
  113. cache::put_image_in_cache(&path, size, &grey_image);
  114. PreparedImage {
  115. orig_path: &*image_path,
  116. image: grey_image,
  117. }
  118. }
  119. }
  120. }
  121. /**
  122. * Get all perceptual hashes for an image
  123. */
  124. pub fn get_perceptual_hashes<'a>(path: &'a Path, precision: &Precision) -> PerceptualHashes<'a> {
  125. let image_path = path.to_str().unwrap();
  126. let ahash = AHash::new(&path, &precision).get_hash();
  127. let dhash = DHash::new(&path, &precision).get_hash();
  128. let phash = PHash::new(&path, &precision).get_hash();
  129. PerceptualHashes {
  130. orig_path: &*image_path,
  131. ahash: ahash,
  132. dhash: dhash,
  133. phash: phash,
  134. }
  135. }
  136. /**
  137. * Calculate the number of bits different between two hashes
  138. */
  139. pub fn calculate_hamming_distance(hash1: u64, hash2: u64) -> u64 {
  140. // The binary xor of the two hashes should give us a number representing
  141. // the differences between the two hashes. All that's left is to count
  142. // the number of 1's in the difference to determine the hamming distance
  143. let bin_diff = hash1 ^ hash2;
  144. let bin_diff_str = format!("{:b}", bin_diff);
  145. let mut hamming = 0u64;
  146. for bit in bin_diff_str.chars() {
  147. match bit {
  148. '1' => hamming += 1,
  149. _ => continue,
  150. }
  151. }
  152. hamming
  153. }
  154. pub trait PerceptualHash {
  155. fn get_hash(&self) -> u64;
  156. }
  157. pub struct AHash<'a> {
  158. prepared_image: Box<PreparedImage<'a>>,
  159. }
  160. impl<'a> AHash<'a> {
  161. pub fn new(path: &'a Path, precision: &Precision) -> Self {
  162. AHash { prepared_image: Box::new(prepare_image(&path, &HashType::Ahash, &precision)) }
  163. }
  164. }
  165. impl<'a> PerceptualHash for AHash<'a> {
  166. /**
  167. * Calculate the ahash of the provided prepared image.
  168. *
  169. * # Returns
  170. *
  171. * A u64 representing the value of the hash
  172. */
  173. fn get_hash(&self) -> u64 {
  174. let (width, height) = self.prepared_image.image.dimensions();
  175. // calculating the average pixel value
  176. let mut total = 0u64;
  177. for pixel in self.prepared_image.image.pixels() {
  178. let channels = pixel.channels();
  179. // println!("Pixel is: {}", channels[0]);
  180. total += channels[0] as u64;
  181. }
  182. let mean = total / (width * height) as u64;
  183. // println!("Mean for {} is {}", prepared_image.orig_path, mean);
  184. // Calculating a hash based on the mean
  185. let mut hash = 0u64;
  186. for pixel in self.prepared_image.image.pixels() {
  187. let channels = pixel.channels();
  188. let pixel_sum = channels[0] as u64;
  189. if pixel_sum >= mean {
  190. hash |= 1;
  191. // println!("Pixel {} is >= {} therefore {:b}", pixel_sum, mean, hash);
  192. } else {
  193. hash |= 0;
  194. // println!("Pixel {} is < {} therefore {:b}", pixel_sum, mean, hash);
  195. }
  196. hash <<= 1;
  197. }
  198. // println!("Hash for {} is {}", prepared_image.orig_path, hash);
  199. hash
  200. }
  201. }
  202. pub struct DHash<'a> {
  203. prepared_image: Box<PreparedImage<'a>>,
  204. }
  205. impl<'a> DHash<'a> {
  206. pub fn new(path: &'a Path, precision: &Precision) -> Self {
  207. DHash { prepared_image: Box::new(prepare_image(&path, &HashType::Dhash, &precision)) }
  208. }
  209. }
  210. impl<'a> PerceptualHash for DHash<'a> {
  211. /**
  212. * Calculate the dhash of the provided prepared image
  213. *
  214. * # Return
  215. *
  216. * Returns a u64 representing the value of the hash
  217. */
  218. fn get_hash(&self) -> u64 {
  219. // Stored for later
  220. let first_pixel_val = self.prepared_image.image.pixels().nth(0).unwrap().channels()[0];
  221. let last_pixel_val = self.prepared_image.image.pixels().last().unwrap().channels()[0];
  222. // Calculate the dhash
  223. let mut previous_pixel_val = 0u64;
  224. let mut hash = 0u64;
  225. for (index, pixel) in self.prepared_image.image.pixels().enumerate() {
  226. if index == 0 {
  227. previous_pixel_val = pixel.channels()[0] as u64;
  228. continue;
  229. }
  230. let channels = pixel.channels();
  231. let pixel_val = channels[0] as u64;
  232. if pixel_val >= previous_pixel_val {
  233. hash |= 1;
  234. } else {
  235. hash |= 0;
  236. }
  237. hash <<= 1;
  238. previous_pixel_val = channels[0] as u64;
  239. }
  240. if first_pixel_val >= last_pixel_val {
  241. hash |= 1;
  242. } else {
  243. hash |= 0;
  244. }
  245. hash
  246. }
  247. }
  248. pub struct PHash<'a> {
  249. prepared_image: Box<PreparedImage<'a>>,
  250. }
  251. impl<'a> PHash<'a> {
  252. pub fn new(path: &'a Path, precision: &Precision) -> Self {
  253. PHash { prepared_image: Box::new(prepare_image(&path, &HashType::Phash, &precision)) }
  254. }
  255. }
  256. impl<'a> PerceptualHash for PHash<'a> {
  257. /**
  258. * Calculate the phash of the provided prepared image
  259. *
  260. * # Return
  261. *
  262. * Returns a u64 representing the value of the hash
  263. */
  264. fn get_hash(&self) -> u64 {
  265. // Get the image data into a vector to perform the DFT on.
  266. let width = self.prepared_image.image.width() as usize;
  267. let height = self.prepared_image.image.height() as usize;
  268. // Get 2d data to 2d FFT/DFT
  269. let mut data_matrix: Vec<Vec<f64>> = Vec::new();
  270. for x in 0..width {
  271. data_matrix.push(Vec::new());
  272. for y in 0..height {
  273. let pos_x = x as u32;
  274. let pos_y = y as u32;
  275. data_matrix[x]
  276. .push(self.prepared_image.image.get_pixel(pos_x, pos_y).channels()[0] as f64);
  277. }
  278. }
  279. // Perform the 2D DFT operation on our matrix
  280. calculate_2d_dft(&mut data_matrix);
  281. // Store this DFT in the cache
  282. cache::put_matrix_in_cache(&Path::new(self.prepared_image.orig_path),
  283. width as u32,
  284. &"dft",
  285. &data_matrix);
  286. // Only need the top left quadrant
  287. let target_width = (width / 4) as usize;
  288. let target_height = (height / 4) as usize;
  289. let dft_width = (width / 4) as f64;
  290. let dft_height = (height / 4) as f64;
  291. // Calculate the mean
  292. let mut total = 0f64;
  293. for x in 0..target_width {
  294. for y in 0..target_height {
  295. total += data_matrix[x][y];
  296. }
  297. }
  298. let mean = total / (dft_width * dft_height);
  299. // Calculating a hash based on the mean
  300. let mut hash = 0u64;
  301. for x in 0..target_width {
  302. // println!("Mean: {} Values: {:?}",mean,data_matrix[x]);
  303. for y in 0..target_height {
  304. if data_matrix[x][y] >= mean {
  305. hash |= 1;
  306. // println!("Pixel {} is >= {} therefore {:b}", pixel_sum, mean, hash);
  307. } else {
  308. hash |= 0;
  309. // println!("Pixel {} is < {} therefore {:b}", pixel_sum, mean, hash);
  310. }
  311. hash <<= 1;
  312. }
  313. }
  314. // println!("Hash for {} is {}", prepared_image.orig_path, hash);
  315. hash
  316. }
  317. }
  318. // Use a 1D DFT to cacluate the 2D DFT.
  319. //
  320. // This is achieved by calculating the DFT for each row, then calculating the
  321. // DFT for each column of DFT row data. This means that a 32x32 image with have
  322. // 1024 1D DFT operations performed on it. (Slightly caclulation intensive)
  323. //
  324. // This operation is in place on the data in the provided vector
  325. //
  326. // Inspired by:
  327. // http://www.inf.ufsc.br/~visao/khoros/html-dip/c5/s2/front-page.html
  328. //
  329. // Checked with:
  330. // http://calculator.vhex.net/post/calculator-result/2d-discrete-fourier-transform
  331. //
  332. fn calculate_2d_dft(data_matrix: &mut Vec<Vec<f64>>) {
  333. // println!("{:?}", data_matrix);
  334. let width = data_matrix.len();
  335. let height = data_matrix[0].len();
  336. let mut complex_data_matrix = Vec::with_capacity(width);
  337. // Perform DCT on the columns of data
  338. for x in 0..width {
  339. let mut column: Vec<f64> = Vec::with_capacity(height);
  340. for y in 0..height {
  341. column.push(data_matrix[x][y]);
  342. }
  343. // Perform the DCT on this column
  344. // println!("column[{}] before: {:?}", x, column);
  345. let forward_plan = dft::Plan::new(dft::Operation::Forward, column.len());
  346. column.transform(&forward_plan);
  347. let complex_column = dft::unpack(&column);
  348. // println!("column[{}] after: {:?}", x, complex_column);
  349. complex_data_matrix.push(complex_column);
  350. }
  351. // Perform DCT on the rows of data
  352. for y in 0..height {
  353. let mut row = Vec::with_capacity(width);
  354. for x in 0..width {
  355. row.push(complex_data_matrix[x][y]);
  356. }
  357. // Perform DCT on the row
  358. // println!("row[{}] before: {:?}", y, row);
  359. let forward_plan = dft::Plan::new(dft::Operation::Forward, row.len());
  360. row.transform(&forward_plan);
  361. // println!("row[{}] after: {:?}", y, row);
  362. // Put the row values back
  363. for x in 0..width {
  364. data_matrix[x][y] = round_float(row[x].re);
  365. }
  366. }
  367. }
  368. fn round_float(f: f64) -> f64 {
  369. if f >= FLOAT_PRECISION_MAX_1 || f <= FLOAT_PRECISION_MIN_1 {
  370. f
  371. } else if f >= FLOAT_PRECISION_MAX_2 || f <= FLOAT_PRECISION_MIN_2 {
  372. (f * 10_f64).round() / 10_f64
  373. } else if f >= FLOAT_PRECISION_MAX_3 || f <= FLOAT_PRECISION_MIN_3 {
  374. (f * 100_f64).round() / 100_f64
  375. } else if f >= FLOAT_PRECISION_MAX_4 || f <= FLOAT_PRECISION_MIN_4 {
  376. (f * 1000_f64).round() / 1000_f64
  377. } else if f >= FLOAT_PRECISION_MAX_5 || f <= FLOAT_PRECISION_MIN_5 {
  378. (f * 10000_f64).round() / 10000_f64
  379. } else {
  380. (f * 100000_f64).round() / 100000_f64
  381. }
  382. }
  383. #[test]
  384. fn test_2d_dft() {
  385. let mut test_matrix: Vec<Vec<f64>> = Vec::new();
  386. test_matrix.push(vec![1f64, 1f64, 1f64, 3f64]);
  387. test_matrix.push(vec![1f64, 2f64, 2f64, 1f64]);
  388. test_matrix.push(vec![1f64, 2f64, 2f64, 1f64]);
  389. test_matrix.push(vec![3f64, 1f64, 1f64, 1f64]);
  390. println!("{:?}", test_matrix[0]);
  391. println!("{:?}", test_matrix[1]);
  392. println!("{:?}", test_matrix[2]);
  393. println!("{:?}", test_matrix[3]);
  394. println!("Performing 2d DFT");
  395. calculate_2d_dft(&mut test_matrix);
  396. println!("{:?}", test_matrix[0]);
  397. println!("{:?}", test_matrix[1]);
  398. println!("{:?}", test_matrix[2]);
  399. println!("{:?}", test_matrix[3]);
  400. assert!(test_matrix[0][0] == 24_f64);
  401. assert!(test_matrix[0][1] == 0_f64);
  402. assert!(test_matrix[0][2] == 0_f64);
  403. assert!(test_matrix[0][3] == 0_f64);
  404. assert!(test_matrix[1][0] == 0_f64);
  405. assert!(test_matrix[1][1] == 0_f64);
  406. assert!(test_matrix[1][2] == -2_f64);
  407. assert!(test_matrix[1][3] == 2_f64);
  408. assert!(test_matrix[2][0] == 0_f64);
  409. assert!(test_matrix[2][1] == -2_f64);
  410. assert!(test_matrix[2][2] == -4_f64);
  411. assert!(test_matrix[2][3] == -2_f64);
  412. assert!(test_matrix[3][0] == 0_f64);
  413. assert!(test_matrix[3][1] == 2_f64);
  414. assert!(test_matrix[3][2] == -2_f64);
  415. assert!(test_matrix[3][3] == 0_f64);
  416. }