@alttiri/image-hash
Version:
Alt-Image-Hash. An alternative image hashing library.
107 lines (106 loc) • 3.97 kB
JavaScript
import { ImageHash } from "./image-hash.js";
import { getCalculateAverage, getCalculateBT601, getGrayData } from "./grayscale.js";
import { scaleDownLinear, scaleUpNearestNeighbor } from "./resize.js";
import { aHashCore, bHashCore, dHashCore, mHashCore, mHashCoreClassic } from "./hashers-core.js";
const defaultSize = 8;
/** difference hash */
export const dHash = (imageData, opts = {}) => {
const width = opts.width || opts.size || defaultSize;
return hash(dHashCore, imageData, { ...opts, scaleWidth: width + 1 });
};
/** average hash */
export const aHash = (imageData, opts) => hash(aHashCore, imageData, opts);
/** median hash */
export const mHash = (imageData, opts = {}) => {
if (opts.classic) {
return hash(mHashCoreClassic, imageData, opts);
}
return hash(mHashCore, imageData, opts);
};
/**
* block hash
*
* By default, it uses "BT601" for gray-scaling, and uses horizontal bands with 2 pixels height.
*
* With `classic` option — "original-like" block hash that uses "average" for gray-scaling,
* and uses 4 horizontal bands total.
* */
export const bHash = (imageData, opts = {}) => {
if (opts.classic) {
return bHashClassic(imageData, opts);
}
return hash(bHashCore, imageData, opts);
};
const bHashClassic = (imageData, opts = {}) => {
opts.grayScaler = getCalculateAverage;
const bHashCoreWrapped = (gid) => bHashCore(gid, /* bandCount */ 4);
return hash(bHashCoreWrapped, imageData, opts);
};
/**
* When the input is lower than 8x8 (9x8) it uses a simple integer upscaler.
* @see `scaleUpIntegerTwice`
*/
function hash(hash, imageData, opts = {}) {
if (opts.size === 0 || opts.width === 0 || opts.height === 0) {
throw new Error("Wrong hash size (0)");
}
const hashWidth = opts.size || opts.width || defaultSize;
const hashHeight = opts.size || opts.height || defaultSize;
if (hashWidth < 0 || hashHeight < 0) {
throw new Error("Wrong hash size (less than 0)");
}
let scaleWidth = opts.scaleWidth || hashWidth;
let scaleHeight = opts.scaleHeight || hashHeight;
let grayScaler = opts.grayScaler || getCalculateBT601;
const upScale = imageData.width < scaleWidth || imageData.height < scaleHeight;
let origScales;
if (upScale) {
origScales = { scaleWidth, scaleHeight };
scaleWidth = imageData.width;
scaleHeight = imageData.height;
}
let grayData;
let grayDataScaled;
let ready = false;
if ("grayDataScaled" in opts) {
grayDataScaled = opts.grayDataScaled; // ts! // wtf is wrong with ts?
if (grayDataScaled.width === scaleWidth && grayDataScaled.height === scaleHeight) {
ready = true;
}
else {
if (!opts.ignore) {
throw new Error("Wrong grayDataScaled input");
}
}
}
if (!ready) {
if ("grayData" in opts) {
const _grayData = opts.grayData; // ts!
if (_grayData.height !== imageData.height || _grayData.width !== imageData.width) {
if (!opts.ignore) {
throw new Error("Wrong grayData input");
}
grayData = getGrayData(imageData, grayScaler);
}
else {
grayData = _grayData;
}
}
else {
grayData = getGrayData(imageData, grayScaler);
}
grayDataScaled = scaleDownLinear(grayData, {
width: scaleWidth,
height: scaleHeight,
median: opts.median,
ignore: opts.ignore,
});
}
const biImageData = hash(grayDataScaled); // ts! // stupid af
if (upScale && origScales) {
const { scaleWidth, scaleHeight } = origScales;
const hashUpScaled = scaleUpNearestNeighbor(biImageData, scaleWidth, scaleHeight);
return ImageHash.fromMono(hashUpScaled);
}
return ImageHash.fromMono(biImageData);
}