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.
- package/.idea/copilot.data.migration.ask2agent.xml +6 -0
- package/README.md +71 -0
- package/binding.gyp +50 -0
- package/build/Release/img_hash.exp +0 -0
- package/build/Release/img_hash.iobj +0 -0
- package/build/Release/img_hash.ipdb +0 -0
- package/build/Release/img_hash.lib +0 -0
- package/build/Release/img_hash.node +0 -0
- package/build/Release/img_hash.pdb +0 -0
- package/build/Release/nothing.lib +0 -0
- package/build/Release/obj/img_hash/img_hash.node.recipe +11 -0
- package/build/Release/obj/img_hash/img_hash.tlog/CL.command.1.tlog +0 -0
- package/build/Release/obj/img_hash/img_hash.tlog/CL.read.1.tlog +0 -0
- package/build/Release/obj/img_hash/img_hash.tlog/CL.write.1.tlog +0 -0
- package/build/Release/obj/img_hash/img_hash.tlog/Cl.items.tlog +2 -0
- package/build/Release/obj/img_hash/img_hash.tlog/img_hash.lastbuildstate +2 -0
- package/build/Release/obj/img_hash/img_hash.tlog/link.command.1.tlog +0 -0
- package/build/Release/obj/img_hash/img_hash.tlog/link.read.1.tlog +0 -0
- package/build/Release/obj/img_hash/img_hash.tlog/link.secondary.1.tlog +5 -0
- package/build/Release/obj/img_hash/img_hash.tlog/link.write.1.tlog +0 -0
- package/build/Release/obj/img_hash/src/addon.obj +0 -0
- package/build/Release/obj/img_hash/win_delay_load_hook.obj +0 -0
- package/build/binding.sln +45 -0
- package/build/img_hash.vcxproj +157 -0
- package/build/img_hash.vcxproj.filters +58 -0
- 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
- package/build/node_modules/.pnpm/node-addon-api@8.5.0/node_modules/node-addon-api/Release/obj/nothing/nothing.lib.recipe +7 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/build/node_modules/.pnpm/node-addon-api@8.5.0/node_modules/node-addon-api/node_api.sln +19 -0
- package/build/node_modules/.pnpm/node-addon-api@8.5.0/node_modules/node-addon-api/nothing.vcxproj +141 -0
- package/build/node_modules/.pnpm/node-addon-api@8.5.0/node_modules/node-addon-api/nothing.vcxproj.filters +115 -0
- package/index.d.ts +9 -0
- package/index.js +54 -0
- package/package.json +22 -0
- package/src/addon.cpp +116 -0
- package/src/index.hpp +246 -0
- 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);
|