figif_core/hashers/
phash.rs1use crate::traits::FrameHasher;
4use image::RgbaImage;
5use img_hash::{HashAlg, HasherConfig, ImageHash};
6
7#[derive(Debug, Clone)]
31pub struct PHasher {
32 hash_width: u32,
33 hash_height: u32,
34}
35
36impl Default for PHasher {
37 fn default() -> Self {
38 Self::new()
39 }
40}
41
42impl PHasher {
43 pub fn new() -> Self {
45 Self::with_size(8, 8)
46 }
47
48 pub fn with_size(width: u32, height: u32) -> Self {
53 Self {
54 hash_width: width,
55 hash_height: height,
56 }
57 }
58
59 pub fn hash_width(&self) -> u32 {
61 self.hash_width
62 }
63
64 pub fn hash_height(&self) -> u32 {
66 self.hash_height
67 }
68
69 pub fn hash_bits(&self) -> u32 {
71 self.hash_width * self.hash_height
72 }
73
74 fn build_hasher(&self) -> img_hash::Hasher {
75 HasherConfig::new()
76 .hash_alg(HashAlg::DoubleGradient)
77 .hash_size(self.hash_width, self.hash_height)
78 .to_hasher()
79 }
80}
81
82impl FrameHasher for PHasher {
83 type Hash = ImageHash;
84
85 fn hash_frame(&self, image: &RgbaImage) -> Self::Hash {
86 let hasher = self.build_hasher();
87 let (width, height) = image.dimensions();
89 let raw = image.as_raw().clone();
90 let img_hash_image: img_hash::image::RgbaImage =
91 img_hash::image::ImageBuffer::from_raw(width, height, raw)
92 .expect("image dimensions should match");
93 let dynamic = img_hash::image::DynamicImage::ImageRgba8(img_hash_image);
94 hasher.hash_image(&dynamic)
95 }
96
97 fn distance(&self, a: &Self::Hash, b: &Self::Hash) -> u32 {
98 a.dist(b)
99 }
100
101 fn name(&self) -> &'static str {
102 "phash"
103 }
104
105 fn suggested_threshold(&self) -> u32 {
106 let base_bits = 64;
108 let actual_bits = self.hash_bits();
109 (8 * actual_bits / base_bits).max(5)
110 }
111}