Skip to content

Commit

Permalink
configurable diff output (colors, alpha) (#61)
Browse files Browse the repository at this point in the history
Co-Authored-By: VP- <vp-@users.noreply.github.com>
Co-Authored-By: David Reignier <dreignier@users.noreply.github.com>
  • Loading branch information
3 people committed Jun 7, 2019
1 parent 89f64b6 commit 5ea44fc
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 20 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ Implements ideas from the following papers:

- `threshold` — Matching threshold, ranges from `0` to `1`. Smaller values make the comparison more sensitive. `0.1` by default.
- `includeAA` — If `true`, disables detecting and ignoring anti-aliased pixels. `false` by default.
- `alpha` — Blending factor of unchanged pixels in the diff output. Ranges from `0` for pure white to `1` for original brightness. `0.1` by default.
- `aaColor` — The color of anti-aliased pixels in the diff output. `[255, 255, 0]` by default.
- `diffColor` — The color of differing pixels in the diff output. `[255, 0, 0]` by default.

Compares two images, writes the output diff and returns the number of mismatched pixels.

Expand Down
22 changes: 15 additions & 7 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

module.exports = pixelmatch;

const defaultOptions = {
threshold: 0.1,
includeAA: false,
alpha: 0.1,
aaColor: [255, 255, 0],
diffColor: [255, 0, 0]
};

function pixelmatch(img1, img2, output, width, height, options) {

if (img1.length !== img2.length) throw new Error('Image sizes do not match.');
Expand All @@ -27,14 +35,14 @@ function pixelmatch(img1, img2, output, width, height, options) {
return 0;
}

if (!options) options = {};

const threshold = options.threshold === undefined ? 0.1 : options.threshold;
options = Object.assign({}, defaultOptions, options);

// maximum acceptable square distance between two colors;
// 35215 is the maximum possible value for the YIQ difference metric
const maxDelta = 35215 * threshold * threshold;
const maxDelta = 35215 * options.threshold * options.threshold;
let diff = 0;
const [aaR, aaG, aaB] = options.aaColor;
const [diffR, diffG, diffB] = options.diffColor;

// compare each pixel of one image against the other one
for (let y = 0; y < height; y++) {
Expand All @@ -51,17 +59,17 @@ function pixelmatch(img1, img2, output, width, height, options) {
if (!options.includeAA && (antialiased(img1, x, y, width, height, img2) ||
antialiased(img2, x, y, width, height, img1))) {
// one of the pixels is anti-aliasing; draw as yellow and do not count as difference
if (output) drawPixel(output, pos, 255, 255, 0);
if (output) drawPixel(output, pos, aaR, aaG, aaB);

} else {
// found substantial difference not caused by anti-aliasing; draw it as red
if (output) drawPixel(output, pos, 255, 0, 0);
if (output) drawPixel(output, pos, diffR, diffG, diffB);
diff++;
}

} else if (output) {
// pixels are similar; draw background as grayscale image blended with white
drawGrayPixel(img1, pos, 0.1, output);
drawGrayPixel(img1, pos, options.alpha, output);
}
}
}
Expand Down
Binary file modified test/fixtures/2diff.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
40 changes: 27 additions & 13 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,20 @@ const test = require('tape').test;
const path = require('path');
const match = require('../.');

diffTest('1a', '1b', '1diff', 0.05, false, 143);
diffTest('2a', '2b', '2diff', 0.05, false, 12437);
diffTest('3a', '3b', '3diff', 0.05, false, 212);
diffTest('4a', '4b', '4diff', 0.05, false, 36049);
diffTest('5a', '5b', '5diff', 0.05, false, 0);
diffTest('6a', '6b', '6diff', 0.05, false, 51);
diffTest('6a', '6a', '6empty', 0, false, 0);
const options = {threshold: 0.05};

diffTest('1a', '1b', '1diff', options, 143);
diffTest('2a', '2b', '2diff', {
threshold: 0.05,
alpha: 0.5,
aaColor: [0, 192, 0],
diffColor: [255, 0, 255]
}, 12437);
diffTest('3a', '3b', '3diff', options, 212);
diffTest('4a', '4b', '4diff', options, 36049);
diffTest('5a', '5b', '5diff', options, 0);
diffTest('6a', '6b', '6diff', options, 51);
diffTest('6a', '6a', '6empty', {threshold: 0}, 0);

test('throws error if image sizes do not match', (t) => {
t.throws(() => match([1, 2, 3], [1, 2, 3, 4], null, 2, 1), /Image sizes do not match/);
Expand All @@ -29,20 +36,24 @@ test('throws error if provided wrong image data format', (t) => {
t.end();
});

function diffTest(imgPath1, imgPath2, diffPath, threshold, includeAA, expectedMismatch) {
const name = `comparing ${imgPath1} to ${imgPath2}, threshold: ${threshold}, includeAA: ${includeAA}`;
function diffTest(imgPath1, imgPath2, diffPath, options, expectedMismatch) {
const name = `comparing ${imgPath1} to ${imgPath2}, ${JSON.stringify(options)}`;

test(name, (t) => {
const img1 = readImage(imgPath1);
const img2 = readImage(imgPath2);
const {width, height} = img1;
const expectedDiff = readImage(diffPath);
const diff = new PNG({width, height});

const mismatch = match(img1.data, img2.data, diff.data, width, height, {threshold, includeAA});
const mismatch2 = match(img1.data, img2.data, null, width, height, {threshold, includeAA});
const mismatch = match(img1.data, img2.data, diff.data, width, height, options);
const mismatch2 = match(img1.data, img2.data, null, width, height, options);

t.same(diff.data, expectedDiff.data, 'diff image');
if (process.env.UPDATE) {
writeImage(diffPath, diff);
} else {
const expectedDiff = readImage(diffPath);
t.same(diff.data, expectedDiff.data, 'diff image');
}
t.same(mismatch, expectedMismatch, 'number of mismatched pixels');
t.same(mismatch, mismatch2, 'number of mismatched pixels without diff');

Expand All @@ -53,3 +64,6 @@ function diffTest(imgPath1, imgPath2, diffPath, threshold, includeAA, expectedMi
function readImage(name) {
return PNG.sync.read(fs.readFileSync(path.join(__dirname, `fixtures/${name}.png`)));
}
function writeImage(name, image) {
fs.writeFileSync(path.join(__dirname, `fixtures/${name}.png`), PNG.sync.write(image));
}

0 comments on commit 5ea44fc

Please sign in to comment.