image2/
hash.rs

1use crate::*;
2
3const HASH_SIZE: usize = 16;
4
5/// Hash is used for content-based hashing
6#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
7pub struct Hash(Vec<bool>);
8
9impl std::fmt::Display for Hash {
10    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
11        write!(fmt, "{:x}", self)
12    }
13}
14
15fn to_byte(b: &[bool]) -> u8 {
16    let mut dest = 0;
17    for (i, x) in b.iter().enumerate() {
18        if *x {
19            dest |= 1 << i;
20        }
21    }
22    dest
23}
24
25impl std::fmt::LowerHex for Hash {
26    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
27        for c in self.0.chunks(8).map(to_byte) {
28            write!(fmt, "{:02x}", c)?
29        }
30        Ok(())
31    }
32}
33
34impl Hash {
35    /// Compute difference between two hashes
36    pub fn diff(&self, other: &Hash) -> usize {
37        let mut diff = 0;
38
39        for (i, x) in self.0.iter().enumerate() {
40            if other.0[i] != *x {
41                diff += 1;
42            }
43        }
44
45        diff
46    }
47}
48
49impl From<Hash> for String {
50    fn from(hash: Hash) -> String {
51        format!("{}", hash)
52    }
53}
54
55impl From<Hash> for Vec<bool> {
56    fn from(hash: Hash) -> Vec<bool> {
57        hash.0
58    }
59}
60
61impl<T: Type, C: Color> Image<T, C> {
62    /// Get image hash
63    pub fn hash(&self) -> Hash {
64        let small: Image<T, C> = self.resize((HASH_SIZE, HASH_SIZE));
65        let mut hash = vec![false; HASH_SIZE * HASH_SIZE];
66        let mut index = 0;
67        let mut px = Pixel::new();
68        let mut c = C::CHANNELS;
69        if C::ALPHA.is_some() {
70            c -= 1;
71        }
72
73        let mut sum: f64 = 0.;
74
75        for j in 0..HASH_SIZE {
76            for i in 0..HASH_SIZE {
77                for c in 0..c {
78                    sum += small.get_f((i, j), c);
79                }
80            }
81        }
82
83        let avg = sum / (HASH_SIZE * HASH_SIZE * c) as f64;
84
85        for j in 0..HASH_SIZE {
86            for i in 0..HASH_SIZE {
87                small.pixel_at((i, j), &mut px);
88                if px.iter().sum::<f64>() / c as f64 > avg {
89                    hash[index] = true;
90                }
91                index += 1
92            }
93        }
94        Hash(hash)
95    }
96}