1use image::{self, FilterType, GenericImageView};
7use std::sync::mpsc;
8use std::thread;
9
10pub struct SimilarChecker {
13 threshold: usize,
14 compressed_w: usize,
15 compressed_h: usize,
16}
17
18impl Default for SimilarChecker {
19 fn default() -> Self {
20 SimilarChecker {
21 threshold: 10,
22 compressed_w: 8,
23 compressed_h: 8,
24 }
25 }
26}
27
28impl SimilarChecker {
29 pub fn new() -> Self {
37 SimilarChecker {
39 threshold: 10,
40 compressed_w: 8,
41 compressed_h: 8,
42 }
43 }
44
45 pub fn compression_size(self, width: usize, height: usize) -> Self {
53 SimilarChecker {
54 compressed_w: width,
55 compressed_h: height,
56 ..self
57 }
58 }
59
60 pub fn threshold(self, threshold: usize) -> Self {
68 SimilarChecker { threshold, ..self }
69 }
70
71 pub fn is_similar(&self, img1: image::DynamicImage, img2: image::DynamicImage) -> bool {
83 let w = self.compressed_w;
84 let h = self.compressed_h;
85
86 let (tx, rx) = mpsc::channel();
87 let tx1 = mpsc::Sender::clone(&tx);
88
89 thread::spawn(move || {
90 let hash1 = get_hash(process(img1, w, h));
91 tx1.send(hash1).unwrap();
92 });
93
94 thread::spawn(move || {
95 let hash2 = get_hash(process(img2, w, h));
96 tx.send(hash2).unwrap();
97 });
98
99 let mut v: Vec<usize> = Vec::new();
100 for received in rx {
101 v.push(received)
102 }
103
104 let distance = get_distance(v[0], v[1], w*h);
105 distance < self.threshold
106 }
107}
108
109fn process(
110 img: image::DynamicImage,
111 compressed_w: usize,
112 compressed_h: usize,
113) -> image::DynamicImage {
114 img.resize_exact(
115 compressed_w as u32,
116 compressed_h as u32,
117 FilterType::Lanczos3,
118 )
119 .grayscale()
120}
121
122pub fn get_hash(img: image::DynamicImage) -> usize {
131 let mut sum_pixels: usize = 0;
132 let mut pixels: Vec<usize> = Vec::new();
133
134 for (_x, _y, pixel) in img.pixels() {
136 let red = pixel[0];
137 sum_pixels += red as usize;
138 pixels.push(red as usize)
139 }
140
141 let (width, height) = img.dimensions();
142 let ave = (sum_pixels as f64) / (f64::from(width) * f64::from(height));
143
144 let mut hash: usize = 0;
145 let mut one: usize = 1;
146
147 for pixel in pixels {
148 if pixel as f64 > ave {
149 hash |= one;
150 }
151 one <<= 1
152 }
153 hash
154}
155
156pub fn get_distance(hash1: usize, hash2: usize, pix_num: usize) -> usize {
164 let mut d = 0;
165 for i in 0..pix_num {
166 let k = 1 << i;
167 if (hash1 & k) != (hash2 & k) {
168 d += 1
169 }
170 }
171 d
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177 #[test]
178 fn get_distance_1() {
179 assert_eq!(get_distance(2247878505465, 2488321179641, 64), 6)
180 }
181
182 #[test]
183 fn get_distance_2() {
184 assert_eq!(get_distance(17431013446337445887, 17431022259610337215, 64), 3)
185 }
186}