image-hash-native 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/.idea/copilot.data.migration.ask2agent.xml +6 -0
  2. package/README.md +71 -0
  3. package/binding.gyp +50 -0
  4. package/build/Release/img_hash.exp +0 -0
  5. package/build/Release/img_hash.iobj +0 -0
  6. package/build/Release/img_hash.ipdb +0 -0
  7. package/build/Release/img_hash.lib +0 -0
  8. package/build/Release/img_hash.node +0 -0
  9. package/build/Release/img_hash.pdb +0 -0
  10. package/build/Release/nothing.lib +0 -0
  11. package/build/Release/obj/img_hash/img_hash.node.recipe +11 -0
  12. package/build/Release/obj/img_hash/img_hash.tlog/CL.command.1.tlog +0 -0
  13. package/build/Release/obj/img_hash/img_hash.tlog/CL.read.1.tlog +0 -0
  14. package/build/Release/obj/img_hash/img_hash.tlog/CL.write.1.tlog +0 -0
  15. package/build/Release/obj/img_hash/img_hash.tlog/Cl.items.tlog +2 -0
  16. package/build/Release/obj/img_hash/img_hash.tlog/img_hash.lastbuildstate +2 -0
  17. package/build/Release/obj/img_hash/img_hash.tlog/link.command.1.tlog +0 -0
  18. package/build/Release/obj/img_hash/img_hash.tlog/link.read.1.tlog +0 -0
  19. package/build/Release/obj/img_hash/img_hash.tlog/link.secondary.1.tlog +5 -0
  20. package/build/Release/obj/img_hash/img_hash.tlog/link.write.1.tlog +0 -0
  21. package/build/Release/obj/img_hash/src/addon.obj +0 -0
  22. package/build/Release/obj/img_hash/win_delay_load_hook.obj +0 -0
  23. package/build/binding.sln +45 -0
  24. package/build/img_hash.vcxproj +157 -0
  25. package/build/img_hash.vcxproj.filters +58 -0
  26. package/build/node_modules/.pnpm/node-addon-api@8.5.0/node_modules/node-addon-api/Release/obj/nothing/node_modules/.pnpm/node-addon-api@8.5.0/node_modules/node-addon-api/nothing.obj +0 -0
  27. package/build/node_modules/.pnpm/node-addon-api@8.5.0/node_modules/node-addon-api/Release/obj/nothing/nothing.lib.recipe +7 -0
  28. package/build/node_modules/.pnpm/node-addon-api@8.5.0/node_modules/node-addon-api/Release/obj/nothing/nothing.tlog/CL.command.1.tlog +0 -0
  29. package/build/node_modules/.pnpm/node-addon-api@8.5.0/node_modules/node-addon-api/Release/obj/nothing/nothing.tlog/CL.read.1.tlog +0 -0
  30. package/build/node_modules/.pnpm/node-addon-api@8.5.0/node_modules/node-addon-api/Release/obj/nothing/nothing.tlog/CL.write.1.tlog +0 -0
  31. package/build/node_modules/.pnpm/node-addon-api@8.5.0/node_modules/node-addon-api/Release/obj/nothing/nothing.tlog/Cl.items.tlog +2 -0
  32. package/build/node_modules/.pnpm/node-addon-api@8.5.0/node_modules/node-addon-api/Release/obj/nothing/nothing.tlog/Lib-link.read.1.tlog +0 -0
  33. package/build/node_modules/.pnpm/node-addon-api@8.5.0/node_modules/node-addon-api/Release/obj/nothing/nothing.tlog/Lib-link.write.1.tlog +0 -0
  34. package/build/node_modules/.pnpm/node-addon-api@8.5.0/node_modules/node-addon-api/Release/obj/nothing/nothing.tlog/Lib.command.1.tlog +0 -0
  35. package/build/node_modules/.pnpm/node-addon-api@8.5.0/node_modules/node-addon-api/Release/obj/nothing/nothing.tlog/nothing.lastbuildstate +2 -0
  36. package/build/node_modules/.pnpm/node-addon-api@8.5.0/node_modules/node-addon-api/Release/obj/nothing/win_delay_load_hook.obj +0 -0
  37. package/build/node_modules/.pnpm/node-addon-api@8.5.0/node_modules/node-addon-api/node_api.sln +19 -0
  38. package/build/node_modules/.pnpm/node-addon-api@8.5.0/node_modules/node-addon-api/nothing.vcxproj +141 -0
  39. package/build/node_modules/.pnpm/node-addon-api@8.5.0/node_modules/node-addon-api/nothing.vcxproj.filters +115 -0
  40. package/index.d.ts +9 -0
  41. package/index.js +54 -0
  42. package/package.json +22 -0
  43. package/src/addon.cpp +116 -0
  44. package/src/index.hpp +246 -0
  45. package/test.js +20 -0
package/src/index.hpp ADDED
@@ -0,0 +1,246 @@
1
+ #ifndef IMG_HASH_H
2
+ #define IMG_HASH_H
3
+
4
+ #include <algorithm>
5
+ #include <array>
6
+ #include <cmath>
7
+ #include <cstddef>
8
+ #include <cstdint>
9
+ #include <immintrin.h>
10
+ #include <vector>
11
+
12
+ #if defined(_MSC_VER)
13
+ #define FORCE_INLINE __forceinline
14
+ #else
15
+ #define FORCE_INLINE __attribute__((always_inline)) inline
16
+ #endif
17
+
18
+ namespace img_hash {
19
+
20
+ static constexpr uint16_t WR = 77;
21
+ static constexpr uint16_t WG = 150;
22
+ static constexpr uint16_t WB = 29;
23
+
24
+ FORCE_INLINE void accumulate_row_simd(const uint8_t *row, size_t count,
25
+ uint64_t &r, uint64_t &g, uint64_t &b) {
26
+ size_t i = 0;
27
+ uint64_t tr = 0, tg = 0, tb = 0;
28
+
29
+ for (; i + 4 <= count; i += 4) {
30
+ const uint8_t *px = row + i * 3;
31
+ tr += px[0] + px[3] + px[6] + px[9];
32
+ tg += px[1] + px[4] + px[7] + px[10];
33
+ tb += px[2] + px[5] + px[8] + px[11];
34
+ }
35
+
36
+ for (; i < count; ++i) {
37
+ const uint8_t *px = row + i * 3;
38
+ tr += px[0];
39
+ tg += px[1];
40
+ tb += px[2];
41
+ }
42
+ r += tr;
43
+ g += tg;
44
+ b += tb;
45
+ }
46
+
47
+ template <size_t N_WIDTH, size_t N_HEIGHT>
48
+ static void resize_fixed(const uint8_t *src, size_t sw, size_t sh, size_t st,
49
+ uint8_t *dst) {
50
+
51
+ uint32_t x_starts[N_WIDTH];
52
+ uint32_t x_ends[N_WIDTH];
53
+
54
+ for (size_t x = 0; x < N_WIDTH; ++x) {
55
+ x_starts[x] = x * sw / N_WIDTH;
56
+ x_ends[x] = std::min((x + 1) * sw / N_WIDTH, sw);
57
+ }
58
+
59
+ for (size_t y = 0; y < N_HEIGHT; ++y) {
60
+ size_t ys = y * sh / N_HEIGHT;
61
+ size_t ye = std::min((y + 1) * sh / N_HEIGHT, sh);
62
+
63
+ for (size_t x = 0; x < N_WIDTH; ++x) {
64
+ size_t xs = x_starts[x];
65
+ size_t xe = x_ends[x];
66
+
67
+ uint64_t sum_r = 0, sum_g = 0, sum_b = 0;
68
+ size_t count = 0;
69
+
70
+ for (size_t sy = ys; sy < ye; ++sy) {
71
+ const uint8_t *row_ptr = src + (sy * st) + (xs * 3);
72
+ size_t row_width = xe - xs;
73
+ accumulate_row_simd(row_ptr, row_width, sum_r, sum_g, sum_b);
74
+ count += row_width;
75
+ }
76
+
77
+ if (count > 0) {
78
+
79
+ dst[y * N_WIDTH + x] =
80
+ static_cast<uint8_t>((WR * (sum_r / count) + WG * (sum_g / count) +
81
+ WB * (sum_b / count)) >>
82
+ 8);
83
+ } else {
84
+ dst[y * N_WIDTH + x] = 0;
85
+ }
86
+ }
87
+ }
88
+ }
89
+
90
+ static inline uint64_t aHash(const uint8_t *img, size_t w, size_t h, size_t s) {
91
+ constexpr size_t N = 8;
92
+ uint8_t buf[N * N];
93
+ resize_fixed<N, N>(img, w, h, s, buf);
94
+
95
+ uint32_t total = 0;
96
+ for (int i = 0; i < N * N; ++i)
97
+ total += buf[i];
98
+
99
+ uint8_t avg = static_cast<uint8_t>(total / (N * N));
100
+ uint64_t hash = 0;
101
+
102
+ for (int i = 0; i < N * N; ++i) {
103
+ if (buf[i] >= avg)
104
+ hash |= (1ULL << i);
105
+ }
106
+ return hash;
107
+ }
108
+
109
+ static inline uint64_t dHash(const uint8_t *img, size_t w, size_t h, size_t s) {
110
+ constexpr size_t W = 9, H = 8;
111
+ uint8_t buf[W * H];
112
+ resize_fixed<W, H>(img, w, h, s, buf);
113
+
114
+ uint64_t hash = 0;
115
+ size_t bit = 0;
116
+ for (size_t y = 0; y < H; ++y) {
117
+ const uint8_t *row = &buf[y * W];
118
+ for (size_t x = 0; x < H; ++x) {
119
+ if (row[x] > row[x + 1])
120
+ hash |= (1ULL << bit);
121
+ bit++;
122
+ }
123
+ }
124
+ return hash;
125
+ }
126
+
127
+ static inline uint64_t pHash(const uint8_t *img, size_t w, size_t h, size_t s) {
128
+ constexpr size_t S = 32;
129
+ constexpr size_t F = 8;
130
+
131
+ alignas(32) uint8_t buf[S * S];
132
+ resize_fixed<S, S>(img, w, h, s, buf);
133
+
134
+ static const auto dct_mat = []() {
135
+ alignas(32) std::array<float, F * S> mat;
136
+ constexpr float pi = 3.14159265358979323846f;
137
+ constexpr float invS = 1.0f / (2.0f * S);
138
+
139
+ for (size_t u = 0; u < F; ++u) {
140
+ float a = (u == 0) ? (1.0f / std::sqrt(2.0f)) : 1.0f;
141
+ for (size_t x = 0; x < S; ++x)
142
+ mat[u * S + x] = a * std::cos(((2 * x + 1) * u * pi) * invS);
143
+ }
144
+ return mat;
145
+ }();
146
+
147
+ alignas(32) float tmp[F * S];
148
+
149
+ for (size_t y = 0; y < S; ++y) {
150
+ const uint8_t *img_row = &buf[y * S];
151
+
152
+ for (size_t u = 0; u < F; ++u) {
153
+ const float *c = &dct_mat[u * S];
154
+
155
+ __m256 sum_vec = _mm256_setzero_ps();
156
+
157
+ for (size_t x = 0; x < S; x += 8) {
158
+
159
+ __m128i pixels_u8 = _mm_loadl_epi64((const __m128i *)(img_row + x));
160
+
161
+ __m256i pixels_i32 = _mm256_cvtepu8_epi32(pixels_u8);
162
+
163
+ __m256 pixels_f = _mm256_cvtepi32_ps(pixels_i32);
164
+
165
+ __m256 coeffs = _mm256_load_ps(c + x);
166
+
167
+ sum_vec = _mm256_fmadd_ps(pixels_f, coeffs, sum_vec);
168
+ }
169
+
170
+ __m128 lo = _mm256_extractf128_ps(sum_vec, 0);
171
+ __m128 hi = _mm256_extractf128_ps(sum_vec, 1);
172
+ __m128 sum128 = _mm_add_ps(lo, hi);
173
+ sum128 = _mm_hadd_ps(sum128, sum128);
174
+ sum128 = _mm_hadd_ps(sum128, sum128);
175
+
176
+ tmp[u * S + y] = _mm_cvtss_f32(sum128);
177
+ }
178
+ }
179
+
180
+ alignas(32) float dct[F * F];
181
+
182
+ for (size_t u = 0; u < F; ++u) {
183
+ for (size_t v = 0; v < F; ++v) {
184
+ const float *c = &dct_mat[v * S];
185
+ const float *t = &tmp[u * S];
186
+
187
+ __m256 sum_vec = _mm256_setzero_ps();
188
+
189
+ for (size_t k = 0; k < S; k += 8) {
190
+ __m256 t_vals = _mm256_load_ps(t + k);
191
+ __m256 c_vals = _mm256_load_ps(c + k);
192
+ sum_vec = _mm256_fmadd_ps(t_vals, c_vals, sum_vec);
193
+ }
194
+
195
+ __m128 lo = _mm256_extractf128_ps(sum_vec, 0);
196
+ __m128 hi = _mm256_extractf128_ps(sum_vec, 1);
197
+ __m128 sum128 = _mm_add_ps(lo, hi);
198
+ sum128 = _mm_hadd_ps(sum128, sum128);
199
+ sum128 = _mm_hadd_ps(sum128, sum128);
200
+
201
+ dct[u * F + v] = _mm_cvtss_f32(sum128) * 0.25f;
202
+ }
203
+ }
204
+
205
+ float total = 0.0f;
206
+
207
+ for (size_t i = 1; i < F * F; ++i)
208
+ total += dct[i];
209
+
210
+ float avg = total / float(F * F - 1);
211
+
212
+ uint64_t hash = 0;
213
+ size_t bit = 0;
214
+ for (size_t u = 0; u < F; ++u) {
215
+ for (size_t v = 0; v < F; ++v) {
216
+ if (u == 0 && v == 0)
217
+ continue;
218
+ if (dct[u * F + v] >= avg)
219
+ hash |= (1ULL << bit);
220
+ ++bit;
221
+ }
222
+ }
223
+
224
+ return hash;
225
+ }
226
+
227
+ static inline int hamming(uint64_t a, uint64_t b) {
228
+ #if defined(__GNUC__) || defined(__clang__)
229
+ return __builtin_popcountll(a ^ b);
230
+ #elif defined(_MSC_VER)
231
+ return static_cast<int>(__popcnt64(a ^ b));
232
+ #else
233
+
234
+ uint64_t diff = a ^ b;
235
+ int count = 0;
236
+ while (diff) {
237
+ diff &= (diff - 1);
238
+ count++;
239
+ }
240
+ return count;
241
+ #endif
242
+ }
243
+
244
+ } // namespace img_hash
245
+
246
+ #endif
package/test.js ADDED
@@ -0,0 +1,20 @@
1
+ const { aHash, dHash, pHash, hamming } = require('./index.js');
2
+
3
+ const width = 100;
4
+ const height = 100;
5
+ const buffer = Buffer.alloc(width * height * 3);
6
+
7
+ for (let i = 0; i < buffer.length; i++) {
8
+ buffer[i] = Math.floor(Math.random() * 256);
9
+ }
10
+
11
+ const hash1 = aHash(buffer, width, height);
12
+ const hash2 = dHash(buffer, width, height);
13
+ const hash3 = pHash(buffer, width, height);
14
+
15
+ console.log('aHash:', hash1);
16
+ console.log('dHash:', hash2);
17
+ console.log('pHash:', hash3);
18
+
19
+ const distance = hamming(hash1, hash2);
20
+ console.log('Hamming distance:', distance);