Skip to main content

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;