UNPKG

image-hash

Version:
179 lines 6.47 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const median = (data) => { const mdarr = data.slice(0).sort((a, b) => a - b); if (mdarr.length % 2 === 0) { return (mdarr[mdarr.length / 2] + mdarr[mdarr.length / 2 + 1]) / 2.0; } return mdarr[Math.floor(mdarr.length / 2)]; }; const translateBlocksToBits = (blocks, pixelsPerBlock) => { const newblocks = blocks; const halfBlockValue = (pixelsPerBlock * 256 * 3) / 2; const bandsize = blocks.length / 4; // Compare medians across four hor zontal bands for (let i = 0; i < 4; i += 1) { const m = median(blocks.slice(i * bandsize, (i + 1) * bandsize)); for (let j = i * bandsize; j < (i + 1) * bandsize; j += 1) { const v = blocks[j]; // Output a 1 if the block is brighter than the median. // With images dominated by black or white, the median may // end up being 0 or the max value, and thus having a lot // of blocks of value equal to the median. To avoid // generating hashes of all zeros or ones, in that case output // 0 if the median is in the lower value space, 1 otherwise newblocks[j] = Number(v > m || (Math.abs(v - m) < 1 && m > halfBlockValue)); } } }; const bitsToHexhash = (bitsArray) => { const hex = []; for (let i = 0; i < bitsArray.length; i += 4) { const nibble = bitsArray.slice(i, i + 4); hex.push(parseInt(nibble.join(''), 2).toString(16)); } return hex.join(''); }; const bmvbhashEven = (data, bits) => { const blocksizeX = Math.floor(data.width / bits); const blocksizeY = Math.floor(data.height / bits); const result = []; for (let y = 0; y < bits; y += 1) { for (let x = 0; x < bits; x += 1) { let total = 0; for (let iy = 0; iy < blocksizeY; iy += 1) { for (let ix = 0; ix < blocksizeX; ix += 1) { const cx = x * blocksizeX + ix; const cy = y * blocksizeY + iy; const ii = (cy * data.width + cx) * 4; const alpha = data.data[ii + 3]; if (alpha === 0) { total += 765; } else { total += data.data[ii] + data.data[ii + 1] + data.data[ii + 2]; } } } result.push(total); } } translateBlocksToBits(result, blocksizeX * blocksizeY); return bitsToHexhash(result); }; const bmvbhash = (data, bits) => { const result = []; let weightTop; let weightBottom; let weightLeft; let weightRight; let blockTop; let blockBottom; let blockLeft; let blockRight; let yMod; let yFrac; let yInt; let xMod; let xFrac; let xInt; const blocks = []; const evenX = data.width % bits === 0; const evenY = data.height % bits === 0; if (evenX && evenY) { return bmvbhashEven(data, bits); } // initialize blocks array with 0s for (let i = 0; i < bits; i += 1) { blocks.push([]); for (let j = 0; j < bits; j += 1) { blocks[i].push(0); } } const blockWidth = data.width / bits; const blockHeight = data.height / bits; for (let y = 0; y < data.height; y += 1) { if (evenY) { // don't bother dividing y, if the size evenly divides by bits blockBottom = Math.floor(y / blockHeight); blockTop = blockBottom; weightTop = 1; weightBottom = 0; } else { yMod = (y + 1) % blockHeight; yFrac = yMod - Math.floor(yMod); yInt = yMod - yFrac; weightTop = 1 - yFrac; weightBottom = yFrac; // y_int will be 0 on bottom/right borders and on block boundaries if (yInt > 0 || y + 1 === data.height) { blockBottom = Math.floor(y / blockHeight); blockTop = blockBottom; } else { blockTop = Math.floor(y / blockHeight); blockBottom = Math.ceil(y / blockHeight); } } for (let x = 0; x < data.width; x += 1) { let avgvalue; const ii = (y * data.width + x) * 4; const alpha = data.data[ii + 3]; if (alpha === 0) { avgvalue = 765; } else { avgvalue = data.data[ii] + data.data[ii + 1] + data.data[ii + 2]; } if (evenX) { blockRight = Math.floor(x / blockWidth); blockLeft = blockRight; weightLeft = 1; weightRight = 0; } else { xMod = (x + 1) % blockWidth; xFrac = xMod - Math.floor(xMod); xInt = xMod - xFrac; weightLeft = 1 - xFrac; weightRight = xFrac; // x_int will be 0 on bottom/right borders and on block boundaries if (xInt > 0 || x + 1 === data.width) { blockRight = Math.floor(x / blockWidth); blockLeft = blockRight; } else { blockLeft = Math.floor(x / blockWidth); blockRight = Math.ceil(x / blockWidth); } } // add weighted pixel value to relevant blocks blocks[blockTop][blockLeft] += avgvalue * weightTop * weightLeft; blocks[blockTop][blockRight] += avgvalue * weightTop * weightRight; blocks[blockBottom][blockLeft] += avgvalue * weightBottom * weightLeft; blocks[blockBottom][blockRight] += avgvalue * weightBottom * weightRight; } } for (let i = 0; i < bits; i += 1) { for (let j = 0; j < bits; j += 1) { result.push(blocks[i][j]); } } translateBlocksToBits(result, blockWidth * blockHeight); return bitsToHexhash(result); }; exports.default = (imgData, bits, method) => { let hash; if (method === 1) { hash = bmvbhashEven(imgData, bits); } else if (method === 2) { hash = bmvbhash(imgData, bits); } else { throw new Error('Bad hashing method'); } return hash; }; //# sourceMappingURL=block-hash.js.map