visual_hash/
traits.rs

1use image::{imageops, DynamicImage, GenericImageView, GrayImage, ImageBuffer, Pixel};
2
3use std::borrow::Cow;
4use std::ops;
5
6/// Interface for types used for storing hash data.
7///
8/// This is implemented for `Vec<u8>`, `Box<[u8]>` and arrays of any size.
9pub trait HashBytes {
10    /// Construct this type from an iterator of bytes.
11    ///
12    /// If this type has a finite capacity (i.e. an array) then it can ignore extra data
13    /// (the hash API will not create a hash larger than this type can contain). Unused capacity
14    /// **must** be zeroed.
15    fn from_iter<I: Iterator<Item = u8>>(iter: I) -> Self
16    where
17        Self: Sized;
18
19    /// Return the maximum capacity of this type, in bits.
20    ///
21    /// If this type has an arbitrary/theoretically infinite capacity, return `usize::max_value()`.
22    fn max_bits() -> usize;
23
24    /// Get the hash bytes as a slice.
25    fn as_slice(&self) -> &[u8];
26}
27
28impl HashBytes for Box<[u8]> {
29    fn from_iter<I: Iterator<Item = u8>>(iter: I) -> Self {
30        iter.collect()
31    }
32
33    fn max_bits() -> usize {
34        usize::max_value()
35    }
36
37    fn as_slice(&self) -> &[u8] {
38        self
39    }
40}
41
42impl HashBytes for Vec<u8> {
43    fn from_iter<I: Iterator<Item = u8>>(iter: I) -> Self {
44        iter.collect()
45    }
46
47    fn max_bits() -> usize {
48        usize::max_value()
49    }
50
51    fn as_slice(&self) -> &[u8] {
52        self
53    }
54}
55
56impl<const N: usize> HashBytes for [u8; N] {
57    fn from_iter<I: Iterator<Item = u8>>(mut iter: I) -> Self {
58        let mut out = [0; N];
59
60        for (src, dest) in iter.by_ref().zip(out.as_mut()) {
61            *dest = src;
62        }
63
64        out
65    }
66
67    fn max_bits() -> usize {
68        N * 8
69    }
70
71    fn as_slice(&self) -> &[u8] {
72        self
73    }
74}
75
76struct BoolsToBytes<I> {
77    iter: I,
78}
79
80impl<I> Iterator for BoolsToBytes<I>
81where
82    I: Iterator<Item = bool>,
83{
84    type Item = u8;
85
86    fn next(&mut self) -> Option<<Self as Iterator>::Item> {
87        // starts at the LSB and works up
88        self.iter
89            .by_ref()
90            .take(8)
91            .enumerate()
92            .fold(None, |accum, (n, val)| {
93                accum.or(Some(0)).map(|accum| accum | ((val as u8) << n))
94            })
95    }
96
97    fn size_hint(&self) -> (usize, Option<usize>) {
98        let (lower, upper) = self.iter.size_hint();
99        (
100            lower / 8,
101            // if the upper bound doesn't evenly divide by `8` then we will yield an extra item
102            upper.map(|upper| {
103                if upper % 8 == 0 {
104                    upper / 8
105                } else {
106                    upper / 8 + 1
107                }
108            }),
109        )
110    }
111}
112
113pub(crate) trait BitSet: HashBytes {
114    fn from_bools<I: Iterator<Item = bool>>(iter: I) -> Self
115    where
116        Self: Sized,
117    {
118        Self::from_iter(BoolsToBytes { iter })
119    }
120
121    fn hamming(&self, other: &Self) -> u32 {
122        self.as_slice()
123            .iter()
124            .zip(other.as_slice())
125            .map(|(l, r)| (l ^ r).count_ones())
126            .sum()
127    }
128}
129
130impl<T: HashBytes> BitSet for T {}
131
132/// Shorthand trait bound for APIs in this crate.
133///
134/// Currently only implemented for the types provided by `image` with 8-bit channels.
135pub trait Image: GenericImageView + 'static {
136    /// The equivalent `ImageBuffer` type for this container.
137    type Buf: Image + DiffImage;
138
139    /// Grayscale the image, reducing to 8 bit depth and dropping the alpha channel.
140    fn to_grayscale(&self) -> Cow<GrayImage>;
141
142    /// Blur the image with the given `Gaussian` sigma.
143    fn blur(&self, sigma: f32) -> Self::Buf;
144
145    /// Iterate over the image, passing each pixel's coordinates and values in `u8` to the closure.
146    ///
147    /// The iteration order is unspecified but each pixel **must** be visited exactly _once_.
148    ///
149    /// If the pixel's channels are wider than 8 bits then the values should be scaled to
150    /// `[0, 255]`, not truncated.
151    ///
152    /// ### Note
153    /// If the pixel data length is 2 or 4, the last index is assumed to be the alpha channel.
154    /// A pixel data length outside of `[1, 4]` will cause a panic.
155    fn foreach_pixel8<F>(&self, foreach: F)
156    where
157        F: FnMut(u32, u32, &[u8]);
158}
159
160/// Image types that can be diffed.
161pub trait DiffImage {
162    /// Subtract the pixel values of `other` from `self` in-place.
163    fn diff_inplace(&mut self, other: &Self);
164}
165
166#[cfg(not(feature = "nightly"))]
167impl<P: 'static, C: 'static> Image for ImageBuffer<P, C>
168where
169    P: Pixel<Subpixel = u8>,
170    C: ops::Deref<Target = [u8]>,
171{
172    type Buf = ImageBuffer<P, Vec<u8>>;
173
174    fn to_grayscale(&self) -> Cow<GrayImage> {
175        Cow::Owned(imageops::grayscale(self))
176    }
177
178    fn blur(&self, sigma: f32) -> Self::Buf {
179        imageops::blur(self, sigma)
180    }
181
182    fn foreach_pixel8<F>(&self, mut foreach: F)
183    where
184        F: FnMut(u32, u32, &[u8]),
185    {
186        self.enumerate_pixels()
187            .for_each(|(x, y, px)| foreach(x, y, px.channels()))
188    }
189}
190
191#[cfg(feature = "nightly")]
192impl<P: 'static, C: 'static> Image for ImageBuffer<P, C>
193where
194    P: Pixel<Subpixel = u8>,
195    C: ops::Deref<Target = [u8]>,
196{
197    type Buf = ImageBuffer<P, Vec<u8>>;
198
199    default fn to_grayscale(&self) -> Cow<GrayImage> {
200        Cow::Owned(imageops::grayscale(self))
201    }
202
203    default fn blur(&self, sigma: f32) -> Self::Buf {
204        imageops::blur(self, sigma)
205    }
206
207    default fn foreach_pixel8<F>(&self, mut foreach: F)
208    where
209        F: FnMut(u32, u32, &[u8]),
210    {
211        self.enumerate_pixels()
212            .for_each(|(x, y, px)| foreach(x, y, px.channels()))
213    }
214}
215
216impl<P: 'static> DiffImage for ImageBuffer<P, Vec<u8>>
217where
218    P: Pixel<Subpixel = u8>,
219{
220    fn diff_inplace(&mut self, other: &Self) {
221        self.iter_mut().zip(other.iter()).for_each(|(l, r)| *l -= r);
222    }
223}
224
225impl Image for DynamicImage {
226    type Buf = image::RgbaImage;
227
228    fn to_grayscale(&self) -> Cow<GrayImage> {
229        self.as_luma8()
230            .map_or_else(|| Cow::Owned(self.to_luma8()), Cow::Borrowed)
231    }
232
233    fn blur(&self, sigma: f32) -> Self::Buf {
234        imageops::blur(self, sigma)
235    }
236
237    fn foreach_pixel8<F>(&self, mut foreach: F)
238    where
239        F: FnMut(u32, u32, &[u8]),
240    {
241        self.pixels()
242            .for_each(|(x, y, px)| foreach(x, y, px.channels()))
243    }
244}
245
246#[cfg(feature = "nightly")]
247impl Image for GrayImage {
248    // type Buf = GrayImage;
249
250    // Avoids copying
251    fn to_grayscale(&self) -> Cow<GrayImage> {
252        Cow::Borrowed(self)
253    }
254}
255
256#[test]
257fn test_bools_to_bytes() {
258    let bools = (0..16).map(|x| x & 1 == 0);
259    let bytes = Vec::from_bools(bools.clone());
260    assert_eq!(*bytes, [0b01010101; 2]);
261
262    let bools_to_bytes = BoolsToBytes { iter: bools };
263    assert_eq!(bools_to_bytes.size_hint(), (2, Some(2)));
264}