figif_core/traits/hasher.rs
1//! Frame hashing trait for duplicate detection.
2
3use image::RgbaImage;
4use std::fmt::Debug;
5use std::hash::Hash;
6
7/// Trait for computing perceptual hashes of frames.
8///
9/// Implementations of this trait provide different algorithms for computing
10/// perceptual hashes that can be compared to detect similar or duplicate frames.
11///
12/// # Example
13///
14/// ```ignore
15/// use figif_core::traits::FrameHasher;
16/// use figif_core::hashers::DHasher;
17///
18/// let hasher = DHasher::new();
19/// let hash1 = hasher.hash_frame(&frame1);
20/// let hash2 = hasher.hash_frame(&frame2);
21/// let distance = hasher.distance(&hash1, &hash2);
22///
23/// if distance < 5 {
24/// println!("Frames are likely duplicates");
25/// }
26/// ```
27pub trait FrameHasher: Send + Sync {
28 /// The hash type produced by this hasher.
29 ///
30 /// Must be comparable via Hamming distance or similar metric.
31 type Hash: Clone + Debug + Send + Sync + Eq + Hash;
32
33 /// Compute the perceptual hash for a single frame.
34 ///
35 /// The image is expected to be in RGBA format. Implementations
36 /// may internally convert or resize as needed.
37 fn hash_frame(&self, image: &RgbaImage) -> Self::Hash;
38
39 /// Compute the distance between two hashes.
40 ///
41 /// Lower values indicate more similar images:
42 /// - 0 = identical
43 /// - 1-5 = likely duplicates
44 /// - 5-10 = possibly similar
45 /// - >10 = different
46 ///
47 /// The exact thresholds depend on the algorithm and use case.
48 fn distance(&self, a: &Self::Hash, b: &Self::Hash) -> u32;
49
50 /// Get the name of this hasher for logging/debugging.
51 fn name(&self) -> &'static str;
52
53 /// Suggested threshold for considering frames as duplicates.
54 ///
55 /// Returns the recommended maximum distance for two frames
56 /// to be considered duplicates. Defaults to 5.
57 fn suggested_threshold(&self) -> u32 {
58 5
59 }
60}
61
62/// Extension trait for batch hashing operations.
63pub trait FrameHasherExt: FrameHasher {
64 /// Hash multiple frames, returning a vector of hashes.
65 fn hash_frames(&self, images: &[RgbaImage]) -> Vec<Self::Hash> {
66 images.iter().map(|img| self.hash_frame(img)).collect()
67 }
68
69 /// Check if two frames are duplicates using the suggested threshold.
70 fn are_duplicates(&self, a: &RgbaImage, b: &RgbaImage) -> bool {
71 let hash_a = self.hash_frame(a);
72 let hash_b = self.hash_frame(b);
73 self.distance(&hash_a, &hash_b) <= self.suggested_threshold()
74 }
75}
76
77// Blanket implementation for all FrameHasher types
78impl<T: FrameHasher> FrameHasherExt for T {}
79
80#[cfg(feature = "parallel")]
81mod parallel {
82 use super::*;
83 use rayon::prelude::*;
84
85 /// Extension trait for parallel hashing operations.
86 pub trait ParallelFrameHasher: FrameHasher {
87 /// Hash multiple frames in parallel.
88 fn hash_frames_parallel(&self, images: &[RgbaImage]) -> Vec<Self::Hash>
89 where
90 Self::Hash: Send,
91 {
92 images.par_iter().map(|img| self.hash_frame(img)).collect()
93 }
94 }
95
96 // Blanket implementation
97 impl<T: FrameHasher> ParallelFrameHasher for T {}
98}
99
100#[cfg(feature = "parallel")]
101pub use parallel::ParallelFrameHasher;