GithubHelp home page GithubHelp logo

mapbox / pixelmatch Goto Github PK

View Code? Open in Web Editor NEW
6.0K 160.0 302.0 2 MB

The smallest, simplest and fastest JavaScript pixel-level image comparison library

License: ISC License

JavaScript 100.00%
image-comparison testing-tools diff

pixelmatch's People

Contributors

ansis avatar ebutleratlassian avatar edwardbetts avatar ivansanchez avatar maio avatar mourner avatar oliver-moran avatar paazmaya avatar tmeasday avatar tristen avatar waldyrious avatar yhuard avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pixelmatch's Issues

[RFC] Convert to C++ for performance gains

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?

Bower

Any bower support for this library?

Thank you

diff stats

@mourner

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 ?

Finding image match within larger image

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

Example of anti-aliasing detection

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?

Detect difference between white and transparent pixels

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.

Use Buffer.equals as initial comparison for performance gain

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

image scaling before comparison?

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.

Finish project setup

  • better readme
  • tests
  • travis
  • coveralls
  • demo
  • screenshots
  • tag version
  • license
  • publish on NPM

JPEG support in CLI

For example, i have 2 exactly the same images but one in .png and other in .jpg extensions and i want to compare them.

Different tolerance for color and placement

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?

Alpha option doesn't seem to be supported

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.
  • The 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.

Would like to upstream some options: diffMask, drawAA

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.

Compare different sized images?

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

Comparing buffers

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

  • The result diff image looks like red static (see examples/diff.png)
  • Both images have the same dimensions 1101 x 2614 (see 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.

Error when comparing two images with the same sizes

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).

Here are the two images I used for my test:
20190605133954-1-atoms Tables ATOM-037 - TableHead
20190605133954-1-atoms Tables ATOM-038 - TableCell

Memory Issue

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?

Detect different Areas

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?

Find image data in canvas?

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?

Improved anti-aliasing

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:

Screenshot 2019-09-26 18 24 19

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?

Image difference in percent

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

Potential inlining/strength reduction optimization in colorDelta

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;

command line not working

Here is what i did

  1. npm install pixelmatch

  2. Navigated to local pixelmatch folder after installation.
    /Users/XXXX/node_modules/pixelmatch/bin

  3. ran below command on terminal
    pixelmatch 1.png 2.png output.png 0.1

Output
-bash: pixelmatch: command not found

'pixelmatch' is not recognized as an internal or external command, operable program or batch file.

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 -

  1. Download https://github.com/mapbox/pixelmatch4.0.1
  2. On command prompt, npm install pixelmatch
  3. Set the environment variables -> Control Panel\System and Security\System >> Environment variables >> System Variables >> Path >>C:\Pixelmatch\pixelmatch-4.0.1\bin; >> Ok >> Ok >> Ok
  4. On command prompt, C:\Pixelmatch\pixelmatch-4.0.1\bin> pixelmatch image1.png image2.png output.png 0.1 as a test command.

I'm unable to proceed further. What I could be possibly missing? Request to respond ASAP. Thanks a ton.

You've great day ahead.

Fails to detect difference

expected.png:

image

actual.png:

image

$ pixelmatch {expected,actual,diff}.png
match: 16ms
different pixels: 0
error: 0%

Is there a way to ignore caret on input boxes while comparing two images?

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?

Add a section to the README.md for people who want the other kind of diffing?

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!)

Should give error when given input data that contains undefined values

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

Better antialiasing detection

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.

Threshold should be linear

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.

Regression test for color distance

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).

Bower

Any chance for bower support for this library?

Thank you

Different results when switching entries

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?

Antialiasing detection fails on image edges

I'm running some WebGL triangles in different environments, and was surprised to see a difference in this test with one triangle:

Firefox:
rgb-triangle browser
headless-gl:
rgb-triangle headless
diff:
rgb-triangle diff

Scaling up the images in GIMP reveals that the issue is the antialiasing in the triangle (presumably due to differences in the environment):

headless-gl:
image
Firefox:
image

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:

pixelmatch/index.js

Lines 56 to 60 in 2188ac3

function antialiased(img, x1, y1, width, height, img2) {
var x0 = Math.max(x1 - 1, 0),
y0 = Math.max(y1 - 1, 0),
x2 = Math.min(x1 + 1, width - 1),
y2 = Math.min(y1 + 1, height - 1),

ignoreRectangles feature

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.