use crate::traits::FrameHasher;
use image::RgbaImage;
use img_hash::{HashAlg, HasherConfig, ImageHash};
#[derive(Debug, Clone)]
pub struct PHasher {
hash_width: u32,
hash_height: u32,
}
impl Default for PHasher {
fn default() -> Self {
Self::new()
}
}
impl PHasher {
pub fn new() -> Self {
Self::with_size(8, 8)
}
pub fn with_size(width: u32, height: u32) -> Self {
Self {
hash_width: width,
hash_height: height,
}
}
pub fn hash_width(&self) -> u32 {
self.hash_width
}
pub fn hash_height(&self) -> u32 {
self.hash_height
}
pub fn hash_bits(&self) -> u32 {
self.hash_width * self.hash_height
}
fn build_hasher(&self) -> img_hash::Hasher {
HasherConfig::new()
.hash_alg(HashAlg::DoubleGradient)
.hash_size(self.hash_width, self.hash_height)
.to_hasher()
}
}
impl FrameHasher for PHasher {
type Hash = ImageHash;
fn hash_frame(&self, image: &RgbaImage) -> Self::Hash {
let hasher = self.build_hasher();
let (width, height) = image.dimensions();
let raw = image.as_raw().clone();
let img_hash_image: img_hash::image::RgbaImage =
img_hash::image::ImageBuffer::from_raw(width, height, raw)
.expect("image dimensions should match");
let dynamic = img_hash::image::DynamicImage::ImageRgba8(img_hash_image);
hasher.hash_image(&dynamic)
}
fn distance(&self, a: &Self::Hash, b: &Self::Hash) -> u32 {
a.dist(b)
}
fn name(&self) -> &'static str {
"phash"
}
fn suggested_threshold(&self) -> u32 {
let base_bits = 64;
let actual_bits = self.hash_bits();
(8 * actual_bits / base_bits).max(5)
}
}