45 lines
1.1 KiB
JavaScript
45 lines
1.1 KiB
JavaScript
/**
|
|
* Perceptual hash (pHash) for image dedup using Sharp.
|
|
* Average hashing: resize to 32x32 grayscale, compute mean, create 1024-bit hash.
|
|
*/
|
|
|
|
const sharp = require('sharp');
|
|
|
|
async function imageToPHash(buffer) {
|
|
// Resize to 32x32 grayscale
|
|
const { data, info } = await sharp(buffer)
|
|
.resize(32, 32, { fit: 'fill' })
|
|
.greyscale()
|
|
.raw()
|
|
.toBuffer({ resolveWithObject: true });
|
|
|
|
// Compute average pixel value
|
|
let sum = 0;
|
|
for (let i = 0; i < data.length; i++) {
|
|
sum += data[i];
|
|
}
|
|
const avg = sum / data.length;
|
|
|
|
// Create hash: 1 if pixel > avg, 0 otherwise
|
|
let hash = '';
|
|
for (let i = 0; i < data.length; i++) {
|
|
hash += data[i] > avg ? '1' : '0';
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
function hammingDistance(hash1, hash2) {
|
|
if (hash1.length !== hash2.length) return Infinity;
|
|
let dist = 0;
|
|
for (let i = 0; i < hash1.length; i++) {
|
|
if (hash1[i] !== hash2[i]) dist++;
|
|
}
|
|
return dist;
|
|
}
|
|
|
|
function isSimilar(hash1, hash2, threshold = 150) {
|
|
return hammingDistance(hash1, hash2) <= threshold;
|
|
}
|
|
|
|
module.exports = { imageToPHash, hammingDistance, isSimilar };
|