use super::Bits64;
use crate::definitions::Image;
use image::{Luma, imageops};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct AverageHash(Bits64);
impl AverageHash {
pub fn hamming_distance(self, AverageHash(other): AverageHash) -> u32 {
self.0.hamming_distance(other)
}
}
pub fn average_hash(img: &Image<Luma<f32>>) -> AverageHash {
const HASH_SIZE: u32 = 8;
let resized = imageops::resize(img, HASH_SIZE, HASH_SIZE, imageops::FilterType::Lanczos3);
let num_pixels = (HASH_SIZE * HASH_SIZE) as usize;
debug_assert_eq!(num_pixels, resized.len() as usize);
let sum: f32 = resized.pixels().map(|p| p[0]).sum();
let mean = sum / num_pixels as f32;
let bits = resized.pixels().map(|p| p[0] > mean);
AverageHash(Bits64::new(bits))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_average_hash_self_distance_zero() {
let img = gray_image!(type: f32,
0., 1., 2.;
3., 4., 5.;
6., 7., 8.
);
let hash = average_hash(&img);
assert_eq!(0, hash.hamming_distance(hash));
}
#[test]
fn test_average_hash_distance_non_zero() {
let img1 = gray_image!(type: f32,
0., 1., 2.;
3., 4., 5.;
6., 7., 255.
);
let mut img2 = img1.clone();
*img2.get_pixel_mut(2, 2) = Luma([0.]);
let hash1 = average_hash(&img1);
let hash2 = average_hash(&img2);
assert_eq!(hash1.hamming_distance(hash2), hash2.hamming_distance(hash1));
assert!(hash1.hamming_distance(hash2) > 0);
}
}