wonfy_tools/util/dhash/
mod.rs1#[derive(Debug, Clone, Copy)]
4pub struct DHash {
5 pub hash: u64,
6}
7
8impl DHash {
9 pub fn new(bytes: &[u8], width: u32, height: u32, channel_count: u8) -> Self {
10 let width = width as usize;
11 let height = height as usize;
12 let channel_count = channel_count as usize;
13
14 if width * height * channel_count != bytes.len() {
16 panic!(
17 "Invalid image dimensions, expected {} got {}",
18 bytes.len(),
19 width * height * channel_count
20 );
21 }
22
23 let cell_width = width / 9;
24 let cell_height = height / 8;
25
26 let grid = if channel_count >= 3 {
27 grid_from_rgb(bytes, width, cell_width, cell_height, channel_count)
28 } else {
29 grid_from_grayscale(bytes, width, cell_width, cell_height, channel_count)
30 };
31
32 let mut bits = [false; 64];
33
34 for y in 0..8 {
35 for x in 0..8 {
36 bits[y * 8 + x] = grid[y][x] > grid[y][x + 1];
37 }
38 }
39
40 let mut hash: u64 = 0;
41
42 for (i, &bit) in bits.iter().enumerate() {
43 if bit {
44 hash += 1 << i;
45 }
46 }
47
48 Self { hash }
49 }
50
51 pub fn hamming_distance(&self, other: &Self) -> u32 {
52 (self.hash ^ other.hash).count_ones()
53 }
54}
55
56impl PartialEq for DHash {
57 fn eq(&self, other: &Self) -> bool {
58 self.hamming_distance(other) < 11
59 }
60}
61
62fn grid_from_rgb(
63 bytes: &[u8],
64 width: usize,
65 cell_width: usize,
66 cell_height: usize,
67 channel_count: usize,
68) -> [[f64; 9]; 8] {
69 let mut grid = [[0f64; 9]; 8];
70
71 for y in 0..8 {
72 let mut row = [0f64; 9];
73
74 for (x, cell) in row.iter_mut().enumerate() {
75 let from = x * cell_width;
76 let to = from + cell_width;
77
78 let mut rs = 0f64;
79 let mut gs = 0f64;
80 let mut bs = 0f64;
81
82 for image_x in from..to {
83 let from = y * cell_height;
84 let to = from + cell_height;
85
86 for image_y in from..to {
87 let i = (image_y * width + image_x) * channel_count;
88
89 unsafe {
90 rs += *bytes.get_unchecked(i) as f64;
91 gs += *bytes.get_unchecked(i + 1) as f64;
92 bs += *bytes.get_unchecked(i + 2) as f64;
93 }
94 }
95 }
96
97 *cell += rs * 0.299 + gs * 0.587 + bs * 0.114;
98 }
99
100 grid[y] = row
101 }
102
103 grid
104}
105
106fn grid_from_grayscale(
107 bytes: &[u8],
108 width: usize,
109 cell_width: usize,
110 cell_height: usize,
111 channel_count: usize,
112) -> [[f64; 9]; 8] {
113 let mut grid = [[0f64; 9]; 8];
114
115 for y in 0..8 {
116 let mut row = [0f64; 9];
117
118 for (x, cell) in row.iter_mut().enumerate() {
119 let from = x * cell_width;
120 let to = from + cell_width;
121
122 let mut luma = 0f64;
123
124 for image_x in from..to {
125 let from = y * cell_height;
126 let to = from + cell_height;
127
128 for image_y in from..to {
129 let i = (image_y * width + image_x) * channel_count;
130
131 unsafe {
132 luma += *bytes.get_unchecked(i) as f64;
133 }
134 }
135 }
136
137 *cell += luma;
138 }
139
140 grid[y] = row;
141 }
142
143 grid
144}