mapbox / pixelmatch Goto Github PK
View Code? Open in Web Editor NEWThe smallest, simplest and fastest JavaScript pixel-level image comparison library
License: ISC License
The smallest, simplest and fastest JavaScript pixel-level image comparison library
License: ISC License
Curious if this has come up before or if it was considered when architecting this project: would it be in scope to convert the module to use C++ to squeeze out some extra performance?
Any bower support for this library?
Thank you
Hi Vladimir !
Nice code, very understandable.
Actualy, pixelmatch return the number of pixel diff.
Shall i suggest a PR with a breaking change so pixelmatch return an object like this (in a first time) ?
{
numDiffPixels:XXXX
misMatchPercentage: '20.33', // i need this ;)
analysisTime: 9227
}
I'd like to go with same output format of compareTo() but without the getDiffImage() function from https://github.com/nikrolls/node-resemble/tree/user-configurable-largeimagethreshold
Are you ok with that ?
Hello
I'm working on a function, based on the code in pixelmatch, that finds an image match within a larger image.
Would it be useful to integrate this in pixelmatch? If so I can create a pull request.
If not I'll just create a separate package (with credits to this one of course :) )
thanks
Dieter
Can we have block-out feature to ignore some-part of image from comparing by specifying start XY co-ordiante followed by Height-Width ?
Congratulations for the great lib. I would like to know if you have an example of usage of the library which features the anti-aliasing algorithm that is being used. I am thinking of one image, with aliasing and without aliasing, applying the library and finding that the aliased and anti-aliased images are identical. That would be helpful for me for an article that I am writing on the subject. Thanks!
Edit: i am also wondering if this is used by mapbox and for what purposes? I understand that fast is important for mobile. I wonder what is the specific application for maps?
It seems like pixelmatch always returns 0 diff when comparing two images that only contain white pixels and transparency. I noticed this when trying to compare two white logo pngs. Pixelmatch doesn't care if i transform, rotate or draw more white on my logo, it still says 0 diff to the original. If I do the same experiment with a colored image it behaves as intended.
It seems like a useful feature to provide the option to set the color for the image diff. For example in my project we are comparing red screens... so red is difficult to distinguish.
The function which uses to color is https://github.com/mapbox/pixelmatch/blob/master/index.js#L147-L152.
has anyone approached this feature before? Do you have strong opinions on how it should be done. I'm willing to take on a PR to help!
hi there,
we are currently using your lib in our pipeline for visual regression tests. to decrease our runtime significantly we added an initial Buffer.equals
to compare if the image buffers are identical (so we don't need to compare pixel by pixel with threshold).
our scenario: taking screenshots via puppeteer and then comparing against a reference set of images
i am happy to file a PR if this library is still maintained
thanks for your work and kind regards
Is there any simple way to scale an image before comparison? resemblejs
gives this option but that library is too heavy. It would be awesome if we can have something like that in pixelmatch
.
For example, i have 2 exactly the same images but one in .png
and other in .jpg
extensions and i want to compare them.
I'm having a problem where my tolerance threshold needs to be set very low to pick up fairly obvious color differences but then it picks up slight differences in text placement that are causing a lot of false positives. You think it is a good idea to be able to set color threshold separately?
Although the documentation states that:
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.
Passing an alpha
value of anything other than the default value of 0.1
doesn't seem to change the diff output.
Minimal test to recreate the issue:
Notes:
img1
below is being passed as both the expected and actual image, so there should be no difference between them.alpha
option is 0
const fs = require('fs');
const PNG = require('pngjs').PNG,
const pixelmatch = require('pixelmatch');
const img1 = PNG.sync.read(fs.readFileSync('test/fixtures/4a.png'));
const {width, height} = img1;
const diff = new PNG({width, height});
pixelmatch(img1.data, img1.data, diff.data, img1.width, img1.height, {threshold: 0.1, alpha: 0});
fs.writeFileSync('diff.png', PNG.sync.write(diff));
Expected:
The diff.png
image should be completely white
Actual:
The diff.png
image is the expected image with a slight opacity.
We use pixelmatch at Chromatic and I've been maintaining our own fork with some extra functionality. I'd much prefer to upstream these changes and wanted to feel you out first to see whether you'd be open to merging them. They are backward compatible out of the box and come with tests. I believe they are aligned with pixelmatch's ethos of remaining small, fast and flexible.
The functionality is behind two new options:
diffMask
- If true
, output will be a mask rather than a complete image. false
by default.drawAA
- If true
, output will contain aaColor
pixels for those detected as anti-aliased otherwise they won't be included in the diff output. true
by default.I can explain the reasoning behind needing these options in case it's unclear.
Is there any option for comparing differently sized images? For example, a way to compare the sizes and add to the canvas size of the smaller size or would that still cause distorted or inaccurate diff results?
/cc @mourner
Hello ๐
I would like to compare two images in memory as opposed to relying on reading and writing from the file system.
I might be going about this the wrong way and if so I'd appreciate some sort of hint/help.
I've created a repo showcasing what I'm seeing:
https://github.com/philmirez/comparing-buffers
examples/diff.png
)examples/prodScreenshot.png
and examples/stagingScreenshot.png
).Side note: One thing that threw me off was when I was reading the pixelmatch/index.js
file, it says...
if (img1.length !== width * height * 4) throw new Error('Image data size does not match width/height.')
I don't understand why width
and height
are being multiplied by 4.
Hello,
here is the code I tested:
import { promises as fs } from 'fs';
import pixelmatch from 'pixelmatch';
import sizeOf from 'buffer-image-size';
const img1 = './public/images/20190605133954-1-atoms.Tables.ATOM-038 - TableCell.png';
const img2 = './public/images/20190605133954-1-atoms.Tables.ATOM-037 - TableHead.png';
const promise1 = fs.readFile(img1);
const promise2 = fs.readFile(img2);
Promise.all([ promise1, promise2 ])
.then(([ buffer1, buffer2 ]) => {
const { width: width1, height: height1 } = sizeOf(buffer1);
const { width: width2, height: height2 } = sizeOf(buffer2);
if (width1 === width2 && height1 === height2) {
const diff = pixelmatch(buffer1, buffer2, null, width1, height1, { threshold: 0 });
}
})
.catch(error => console.error(error));
And I got the following error:
Error: Image sizes do not match.
at pixelmatch (/home/tocab/Projects/Smile/diplopia/diplopia-api/node_modules/pixelmatch/index.js:19:15)
at file:///home/tocab/Projects/Smile/diplopia/diplopia-api/test.mjs:17:20
I used node v12.4.0 with the --experimental-modules
flag.
In your lib you are testing img1.length !== img2.length
and this is false when using Buffers although the images have the same dimensions (800x600).
I am processing 300 DPI gray scale image with resolution of 10800x7200 , and it consumes large memory (> 2GB) and takes long time (> 10 sec).
Is this normal behavior?
Hi there,
Really cool module! Not really an issue, more a question: Can it be used to detect a x number of areas who differ from the original picture in stead of only pixel vs pixels?
When I compare images usually lets say the bottom half of the image does not get returned/compared the top half works perfect . And I don't really know why that happens. https://imgur.com/a/WVwNu
Is it possible to generate a diff for three or four images?
I want to use this script to find a chunk of image data in the canvas, kind of like a hotspot finder. I have a slice of an image which needs to be matched to return the xy coordinates. How would that work?
We've seen quite a few false positives that are due to the anti-aliasing algorithm not quite getting it right when the shape being anti-aliased is quite thin or forms a ring.
The issue comes from the algorithm's reliance on both the darkest and lightest neighbour being "definitely not antialiased" -- which is detected via having at least 3 neighbours of equal color. However, in such cases above, one of the two will not be. Here's an example:
In the image above the orange arrow points at the candidate anti-aliased pixel, and the purple arrow at the darkest neighbour. Notice that because the shape drawn (a dark grey circle in this case) is quite thin (1px wide) we don't end up finding 3 other pixels in the neighbourhood of the dark grey pixel of equal color.
I think an idea to improve the algorithm would be to perhaps relax the constraint on one of the darkest or lightest (with the assumption that the other is a region of flat color and should have plenty of neighbours). Perhaps the relaxed condition could be simply that it has some neighbours of relatively close color? I'm not quite sure. I want to put together a few test cases.
In any case I was interested in your thoughts. Have you thought about this problem before?
A nice demo would increase the tool adoption.
How do we know the images are different with lets say 0.1% or 5% or 10%?
It's that possible to calculate it?
Thank you
Recently I adapted the colorDelta
function from the code in this repo in one of my Observable notebooks, and more or less on autopilot inlined rgb2y
, rgb2i
, and rgb2q
:
// function rgb2y(r, g, b) { return r * 0.29889531 + g * 0.58662247 + b * 0.11448223; }
// function rgb2i(r, g, b) { return r * 0.59597799 - g * 0.27417610 - b * 0.32180189; }
// function rgb2q(r, g, b) { return r * 0.21147017 - g * 0.52261711 + b * 0.31114694; }
// const y = rgb2y(r1, g1, b1) - rgb2y(r2, g2, b2);
// const i = rgb2i(r1, g1, b1) - rgb2i(r2, g2, b2);
// const q = rgb2q(r1, g1, b1) - rgb2q(r2, g2, b2);
const r = r1 - r2;
const g = g1 - g2;
const b = b1 - b2;
const y = r * 0.29889531 + g * 0.58662247 + b * 0.11448223
const i = r * 0.59597799 - g * 0.27417610 - b * 0.32180189
const q = r * 0.21147017 - g * 0.52261711 + b * 0.31114694
return 0.5053 * y * y + 0.299 * i * i + 0.1957 * q * q;
This removes six function calls, six multiplications and three additions. Since this is a hot function I'm sure that the function calls are probably inlined anyway, but the JIT compiler can't perform the other optimization for us ;). Since colorDelta
is the heart of this algorithm I wonder if that small change adds up or not.
Also, now that I'm writing this out it got me thinking: barring rounding errors the following should be equivalent:
0.5053 * y * y + 0.299 * i * i + 0.1957 * q * q
// Math.sqrt(0.5053) === 0.7108445681019163
// Math.sqrt(0.299) === 0.5468089245796927
// Math.sqrt(0.1957) === 0.4423799272118933
// Therefore this can be rewriten as:
0.7108445681019163 * 0.7108445681019163 * y * y +
0.5468089245796927 * 0.5468089245796927 * i * i +
0.4423799272118933 * 0.4423799272118933 *q * q
// or:
(0.7108445681019163 * y)**2 + (0.5468089245796927 * i)**2 + (0.4423799272118933 *q)**2
// Therefore, this should also work: (except for the `yOnly` case obviously)
const y = 0.7108445681019163 * (r * 0.29889531 + g * 0.58662247 + b * 0.11448223)
const i = 0.5468089245796927 * (r * 0.59597799 - g * 0.27417610 - b * 0.32180189)
const q = 0.4423799272118933 * (r * 0.21147017 - g * 0.52261711 + b * 0.31114694)
return y*y + i*i + q*q
// Which can be pre-computed to:
const y = r * 0.2124681075446384 + g * 0.4169973963260294 + b * 0.08137907133969426;
const i = r * 0.3258860837850668 - g * 0.14992193838645426 - b * 0.17596414539861255;
const q = r * 0.0935501584120867 - g * 0.23119531908149002 + b * 0.13764516066940333
return y*y + i*i + q*q
The latter form would save another three multiplications, for a total of nine (going from 21 down to 12).
So I haven't benchmarked any of this, so I don't know the actual impact on performance it has, but technically it should be less work for the CPU at least. I guess we should also check if the results are the same - I did add the change described in this comment to my notebook and it seems to work fine, but that's not a pixel-to-pixel comparison ;)
The actual code would probably look something like:
const r = r1 - r2;
const g = g1 - g2;
const b = b1 - b2;
if (yOnly) return r * 0.29889531 + g * 0.58662247 + b * 0.11448223;
// inlined rgb2y, rgb2i and rgb2q, with strength-reduced constants
const y = r * 0.2124681075446384 + g * 0.4169973963260294 + b * 0.08137907133969426;
const i = r * 0.3258860837850668 - g * 0.14992193838645426 - b * 0.17596414539861255;
const q = r * 0.0935501584120867 - g * 0.23119531908149002 + b * 0.13764516066940333;
return y*y + i*i + q*q;
npm WARN deprecated [email protected]: pngjs2 has now taken over the original pngjs package on npm. Please change to use pngjs dependency, version 2+.
Time to upgrade.
/cc @mourner
Here is what i did
npm install pixelmatch
Navigated to local pixelmatch folder after installation.
/Users/XXXX/node_modules/pixelmatch/bin
ran below command on terminal
pixelmatch 1.png 2.png output.png 0.1
Output
-bash: pixelmatch: command not found
Hi - I'm a newbie to node.js. I would like to use pixelmatch for my project. However, not finding appropriate usage document anywhere. I get the above error. Here's what I did -
I'm unable to proceed further. What I could be possibly missing? Request to respond ASAP. Thanks a ton.
You've great day ahead.
Is there a possibility that different match is returned on Linux and MacOSX? I am facing this, but still running some tests to be sure.
In v5.0.1
there appears to be no support for image Buffers.
Given this code: https://cl.ly/4b2774
5.0.1
: I get this error https://cl.ly/a693b15.0.0
: things work as expected ๐Lines 83 to 85 in 197e8e2
I have being using the pixel match library for a long time now and it is very helpful in comparing two images and returning diffs. Currently where I am struggling is while comparing two text boxes or input boxes images, since I am entering values at times the baselines or the actual images are giving me caret so the comparison is failing for pixel diffs. Is there any way or plan to ignore the carets on input box images?
It is surprisingly hard to find a library for the "real" kind of diffing, where two images are compared based on maximum identical regions, highlighting all the parts that are in both images, even if they moved (example: a webpage with 10px and 100px top padding is the same image. The only difference is 90 pixels of whitespace at the top).
It would be incredibly useful if there was a README.md bit going "if you're looking for a heavier weight image differ, have a look at [...]" (I still haven't found one that can actually do this, despite the fact it would make visual QA infinitely easier!)
Hello! New user of pixelmatch
here -- thanks for making such a nifty library! It's making all my dreams come true! ๐ ๐ฆ โจ ๐
But as a new user, I made a silly mistake when working with data from the Canvas API. Instead of passing in img1.data
as an argument to pixelmatch()
, I passed in img1
directly, which is an ImageData
object.
The problem: I got back a return value of 0
and a blank canvas, which made me think there was something wrong with my canvas data itself or other parts of my codebase!
It would be much more helpful if this resulted in an error.
Cause of the problem:
The pixelmatch()
function tries to access, for example, img1[pos]
, but if img1
is an object it will simply give undefined
because it has no such property pos
. And then pixelmatch()
just goes happily about its usual routine, comparing undefined
vs undefined
many many times and deciding that there are no differences. (Which is technically true, but not the desired behavior.)
Code to replicate the bug:
// First assuming you already have some canvas / context objects
var img1 = ctx.getImageData(0, 0, width, height),
img2 = ctx.getImageData(0, 0, width, height),
diff = ctx.createImageData(width, height);
// Here's the only part different from the example in the README:
// I pass in img1, img2, diff instead of img1.data, img2.data, diff.data
var numPixels = pixelmatch(img1, img2, diff, width, height, {threshold: 0.1});
console.log(numDiffPixels); // returns 0
ctx.putImageData(diff, 0, 0); // draws a blank (white) canvas
I may just make a PR myself to offer a first attempt at fixing this. :)
Thanks!
~ Liz
The current antialiasing detection algorithm that was ported from Blink-diff and it works, but it's not perfect. It's based on absolute color delta comparison; at least we need to take hue into account, and add more metrics to discern between antialiasing of a nearby pixel and just a different pixel.
Currently threshold is relative to the squared YUV distance. It should be relative to plain YUV distance instead so that it scales more predictably, but this would be a breaking change.
To ensure the color distance is correctly compared to the comparison threshold.
The text fixture could be an image with gray background rgb(0.5, 0.5, 0.5)
and an image that contains gray tones with different color offsets (e.g. with rgb(0.6, 0.6, 0.6)
, which should return a color difference of 0.1).
Any chance for bower support for this library?
Thank you
Hello,
First I'd like to thank you for this nice library.
Then I'd like to report a strange behavior: pixelmatch test.png test2.png output.png 0 true
and pixelmatch test2.png test.png output.png 0 true
don't provide the same result. In my test case, test.png is the base image (greyscale) and test2.png is the same image with some white painting upon it. This painting isn't detected during the second test whereas it is during the first one.
Is it the expected behavior when working with greyscale pictures or a bug in the library?
I'm running some WebGL triangles in different environments, and was surprised to see a difference in this test with one triangle:
Scaling up the images in GIMP reveals that the issue is the antialiasing in the triangle (presumably due to differences in the environment):
I've manually checked the value of the alpha channels in the pixels of the diagonal, and it's the same in all: 39.5%.
I've noticed that the antialias-detection algorithm clamps a 3x3 pixel bounding box along the edges, so I assume that the pixels that show as red in my diff image have a 2x2 bounding box, and therefore the calculations differ, ref:
Lines 56 to 60 in 2188ac3
Proposing to add "ignoreRectagles" feature as in node-resemble-js, where options would support an ignoreRectangles property which is as in resemble:
//array of rectangles, each rectangle is defined as (x, y, width, height)
//e.g. [[325, 170, 100, 40]]
Pixels within these rectangles would not be tested. Unsure of best way to represent these in diff image.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.