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