looks-same
Version:
Pure node.js library for comparing PNG-images, taking into account human color perception.
113 lines (93 loc) • 3.92 kB
JavaScript
'use strict';
/*
* Anti-aliased pixel detector
* @see http://www.eejournal.ktu.lt/index.php/elt/article/view/10058/5000
*/
const DEFAULT_BRIGHTNESS_TOLERANCE = 0;
module.exports = class AntialiasingComparator {
constructor(baseComparator, img1, img2, {antialiasingTolerance = 0}) {
this._baseComparator = baseComparator;
this._img1 = img1;
this._img2 = img2;
this._brightnessTolerance = antialiasingTolerance; // used only when comparing the darkest and the brightest pixels
}
compare(data) {
return this._baseComparator(data) || this._checkIsAntialiased(data);
}
_checkIsAntialiased(data) {
return this._isAntialiased(this._img2, data.x, data.y, data, this._img1)
|| this._isAntialiased(this._img1, data.x, data.y, data, this._img2);
}
_isAntialiased(img1, x1, y1, data, img2) {
const color1 = img1.getPixel(x1, y1);
const width = data.width;
const height = data.height;
const x0 = Math.max(x1 - 1, 0);
const y0 = Math.max(y1 - 1, 0);
const x2 = Math.min(x1 + 1, width - 1);
const y2 = Math.min(y1 + 1, height - 1);
const checkExtremePixels = !img2;
const brightnessTolerance = checkExtremePixels ? this._brightnessTolerance : DEFAULT_BRIGHTNESS_TOLERANCE;
let zeroes = 0;
let positives = 0;
let negatives = 0;
let min = 0;
let max = 0;
let minX, minY, maxX, maxY;
for (let y = y0; y <= y2; y++) {
for (let x = x0; x <= x2; x++) {
if (x === x1 && y === y1) {
continue;
}
// brightness delta between the center pixel and adjacent one
const delta = this._brightnessDelta(img1.getPixel(x, y), color1);
// count the number of equal, darker and brighter adjacent pixels
if (Math.abs(delta) <= brightnessTolerance) {
zeroes++;
} else if (delta > brightnessTolerance) {
positives++;
} else {
negatives++;
}
// if found more than 2 equal siblings, it's definitely not anti-aliasing
if (zeroes > 2) {
return false;
}
if (checkExtremePixels) {
continue;
}
// remember the darkest pixel
if (delta < min) {
min = delta;
minX = x;
minY = y;
}
// remember the brightest pixel
if (delta > max) {
max = delta;
maxX = x;
maxY = y;
}
}
}
if (checkExtremePixels) {
return true;
}
// if there are no both darker and brighter pixels among siblings, it's not anti-aliasing
if (negatives === 0 || positives === 0) {
return false;
}
// if either the darkest or the brightest pixel has more than 2 equal siblings in both images
// (definitely not anti-aliased), this pixel is anti-aliased
return (!this._isAntialiased(img1, minX, minY, data) && !this._isAntialiased(img2, minX, minY, data)) ||
(!this._isAntialiased(img1, maxX, maxY, data) && !this._isAntialiased(img2, maxX, maxY, data));
}
_brightnessDelta(color1, color2) {
return rgb2y(color1.R, color1.G, color1.B) - rgb2y(color2.R, color2.G, color2.B);
}
};
// gamma-corrected luminance of a color (YIQ NTSC transmission color space)
// see https://www.academia.edu/8200524/DIGITAL_IMAGE_PROCESSING_Digital_Image_Processing_PIKS_Inside_Third_Edition
function rgb2y(r, g, b) {
return r * 0.29889531 + g * 0.58662247 + b * 0.11448223;
}