figif_core/hashers/
blockhash.rs1use crate::traits::FrameHasher;
4use image::RgbaImage;
5use img_hash::{HashAlg, HasherConfig, ImageHash};
6
7#[derive(Debug, Clone)]
30pub struct BlockHasher {
31 hash_width: u32,
32 hash_height: u32,
33}
34
35impl Default for BlockHasher {
36 fn default() -> Self {
37 Self::new()
38 }
39}
40
41impl BlockHasher {
42 pub fn new() -> Self {
44 Self::with_size(8, 8)
45 }
46
47 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::Blockhash)
75 .hash_size(self.hash_width, self.hash_height)
76 .to_hasher()
77 }
78}
79
80impl FrameHasher for BlockHasher {
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 "blockhash"
101 }
102
103 fn suggested_threshold(&self) -> u32 {
104 let base_bits = 64;
106 let actual_bits = self.hash_bits();
107 (6 * actual_bits / base_bits).max(4)
108 }
109}