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

Overview

pixelmatch

Build Status

The smallest, simplest and fastest JavaScript pixel-level image comparison library, originally created to compare screenshots in tests.

Features accurate anti-aliased pixels detection and perceptual color difference metrics.

Inspired by Resemble.js and Blink-diff. Unlike these libraries, pixelmatch is around 150 lines of code, has no dependencies, and works on raw typed arrays of image data, so it's blazing fast and can be used in any environment (Node or browsers).

const numDiffPixels = pixelmatch(img1, img2, diff, 800, 600, {threshold: 0.1});

Implements ideas from the following papers:

Demo

Example output

expected actual diff
1diff
1diff
1diff

API

pixelmatch(img1, img2, output, width, height[, options])

  • img1, img2 — Image data of the images to compare (Buffer, Uint8Array or Uint8ClampedArray). Note: image dimensions must be equal.
  • output — Image data to write the diff to, or null if don't need a diff image.
  • width, height — Width and height of the images. Note that all three images need to have the same dimensions.

options is an object literal with the following properties:

  • 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 in [R, G, B] format. [255, 255, 0] by default.
  • diffColor — The color of differing pixels in the diff output in [R, G, B] format. [255, 0, 0] by default.
  • diffColorAlt — An alternative color to use for dark on light differences to differentiate between "added" and "removed" parts. If not provided, all differing pixels use the color specified by diffColor. null by default.
  • diffMask — Draw the diff over a transparent background (a mask), rather than over the original image. Will not draw anti-aliased pixels (if detected).

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

Command line

Pixelmatch comes with a binary that works with PNG images:

pixelmatch image1.png image2.png output.png 0.1

Example usage

Node.js

const fs = require('fs');
const PNG = require('pngjs').PNG;
const pixelmatch = require('pixelmatch');

const img1 = PNG.sync.read(fs.readFileSync('img1.png'));
const img2 = PNG.sync.read(fs.readFileSync('img2.png'));
const {width, height} = img1;
const diff = new PNG({width, height});

pixelmatch(img1.data, img2.data, diff.data, width, height, {threshold: 0.1});

fs.writeFileSync('diff.png', PNG.sync.write(diff));

Browsers

const img1 = img1Context.getImageData(0, 0, width, height);
const img2 = img2Context.getImageData(0, 0, width, height);
const diff = diffContext.createImageData(width, height);

pixelmatch(img1.data, img2.data, diff.data, width, height, {threshold: 0.1});

diffContext.putImageData(diff, 0, 0);

Install

Install with NPM:

npm install pixelmatch

Use in the browser from a CDN:

<script src="https://bundle.run/pixelmatch"></script>

Changelog

Comments
  • Regression test for color distance

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

    tests 
    opened by hastebrot 21
  • Different results when switching entries

    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?

    bug 
    opened by yhuard 9
  • ignoreRectangles feature

    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.

    enhancement question 
    opened by curtw 7
  • add .npmignore

    add .npmignore

    Hi 👋

    I’ve been doing a little research for a conference talk on how npm package size relates to their content, and your package was one in the several ones that were flagged by my scripts. It has an outstanding weekly download count on npm and relatively large content that is not related directly to the package functionality.

    I've added some files and folders to an .npmignore file, so they won't get packaged next time you release it on npm.

    Thanks, and have a great day!

    opened by necccc 6
  • Produce a warning if two images' dimensions don't match

    Produce a warning if two images' dimensions don't match

    I'm having difficulty getting a usable diff image from this lib, I've provided output from blink-diff as a comparison for the same images.

    Baseline:

    chrome-50 0 2661 86-linux

    Comparison:

    chrome-50 0 2661 86-linux

    pixelmatch diff image:

    chrome-50 0 2661 86-linux-diff

    blink-diff diff image:

    chrome-50 0 2661 86-linux-diff

    opened by nathanmarks 6
  • Update PNG library

    Update PNG library

    The pngjs2 project has been merged (back?) into pngjs. Consequently, pngjs2 is now deprecated and pngjs is the latest version. Confused? I was too.

    Consequently, PixelMatch is currently giving the following warning when being installed:

    npm WARN deprecated [email protected]: pngjs2 has now taken over the original pngjs package on npm. Please change to use pngjs dependency, version 2+.
    

    This pull request will update to the latest library and cure this warning. No API changes are needed on your part.

    And thanks for the very hot library, which I have included here: https://github.com/oliver-moran/jimp

    opened by oliver-moran 6
  • I cant get a correct Matching Image.

    I cant get a correct Matching Image.

    So im trying to compare these images but its not getting the correct one.

    async function compareHashTest(type, image, sku) {
      try {
        const path1 = path.resolve(__dirname, `../images/sku/${type}${sku}`);
        const path2 = path.resolve(__dirname, `../images/temp/${type}${image}`);
        const pngPath1 = path.resolve(
          __dirname,
          `../images/sku/${type}${sku.split(".jpg")[0]}.png`
        );
        const pngPath2 = path.resolve(
          __dirname,
          `../images/temp/${type}${image.split(".jpg")[0]}.png`
        );
    
        let image1 = await sharp(path1).toFile(pngPath1);
    
        const img1 = PNG.sync.read(fs.readFileSync(pngPath1));
    
        const { width, height } = img1;
    
        let image2 = await sharp(path2).resize({ width, height }).toFile(pngPath2);
    
        const img2 = PNG.sync.read(fs.readFileSync(pngPath2));
    
        let diff = new PNG({ width, height });
    
        let num = pixelmatch(img1.data, img2.data, diff.data, width, height, {
          threshold: 0.5,
        });
        console.log("num", num);
        diff = null;
        return num;
      } catch (e) {
        console.log("error", e);
        return 1;
      }
    }
    

    How im comparing and these are the images getting lowest number

    Find Match Image : https://images.accessgrantedonline.com/Cardid_DHG_1_220693.jpg - image using to match

    Test Images: https://c1.scryfall.com/file/scryfall-cards/normal/front/e/9/e95756ee-e76b-44e9-a13f-6125ee097ce7.jpg?1580561471 https://c1.scryfall.com/file/scryfall-cards/normal/front/7/d/7d24b200-5cab-406a-a0e6-71a4d33b26fb.jpg?1592765261 https://c1.scryfall.com/file/scryfall-cards/normal/front/5/3/53d2260a-e001-4d03-a108-759591e4d233.jpg?1543676327

    Image matched was https://c1.scryfall.com/file/scryfall-cards/normal/front/5/3/53d2260a-e001-4d03-a108-759591e4d233.jpg?1543676327

    Expected Match https://c1.scryfall.com/file/scryfall-cards/normal/front/e/9/e95756ee-e76b-44e9-a13f-6125ee097ce7.jpg?1580561471

    question 
    opened by CaiIsProgrammer 5
  • Error when comparing two images with the same sizes

    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

    opened by tonai 5
  • YIQ-based color metric

    YIQ-based color metric

    Implements the formula in this paper, which is much closer to CIEDE2000 in quality unlike YUV distance which was non-uniform.

    Also fixes RGBA to RGB conversion to blend with white rather than black.

    • [x] update tests
    • [ ] try to optimize some more
    • [x] cleanup and code comments
    • [x] update readme

    cc @hastebrot

    opened by mourner 5
  • Uncaught (in promise) Error: Image data: Uint8Array, Uint8ClampedArray or Buffer expected.

    Uncaught (in promise) Error: Image data: Uint8Array, Uint8ClampedArray or Buffer expected.

    I must be doing something errant. (Javascript Browser method)

    import pixelmatch from "pixelmatch"
    
    export default async function TextFromImage(imageElement, frameData, lastImage) {
      
      const scaleFactor = 4
      
      //create an offscreen canvas and crop the image
      var crop = document.createElement('canvas');
      crop.width = frameData.width * scaleFactor
      crop.height = frameData.height * scaleFactor
      var crop_x = crop.getContext('2d');
      
      const imageProcessing = crop_x.getImageData(0, 0, crop.width, crop.height);
      
      const origImg = imageProcessing
      console.log("origImg: ", imageProcessing)
      
      PreProcessFunctions(imageProcessing.data, frameData.name, destWidth, destHeight, grey, blur, dilate, invert, thresh, replace, threshLevel, rLevelMIN, rLevelMAX, gLevelMIN, gLevelMAX, bLevelMIN, bLevelMAX)
      
      const newImg = imageProcessing
      console.log("newImg: ", imageProcessing)
      
      const numDiffPixels = pixelmatch(origImg, newImg, {threshold: 0.1});
    }
    

    Console.log:

    ImageData { width: 393, height: 120, data: Uint8ClampedArray(188640) }
    ​data: Uint8ClampedArray(188640) [ 255, 255, 255, … ]
    ​height: 120
    ​width: 393
    ​<prototype>: ImageDataPrototype { width: Getter, height: Getter, data: Getter, … }
    
    ImageData { width: 393, height: 120, data: Uint8ClampedArray(188640) }
    ​data: Uint8ClampedArray(188640) [ 255, 255, 255, … ]
    ​height: 120
    ​width: 393
    ​<prototype>: ImageDataPrototype { width: Getter, height: Getter, data: Getter, … }
    

    Error: Uncaught (in promise) Error: Image data: Uint8Array, Uint8ClampedArray or Buffer expected.

    The images are the same size. I also tried added in diff using:

    const diff = new Image()
    const numDiffPixels = pixelmatch(origImg, newImg, diff, 393, 120, {threshold: 0.1});
    

    Eventually I would like to use lastImage, but for now this function basically compares the original image before tesseract pre-processing to a new image after pre-processing. The images (orig and new) should be very different from each other but will still be the same size.

    opened by github-jeff 4
  • Publish the latest changes to npm?

    Publish the latest changes to npm?

    It looks like the latest version of pixelmatch was published 2 years ago https://www.npmjs.com/package/pixelmatch

    However, there are some changes made to this repo 8 months ago.

    Could someone with the correct permission publish these changes to npm?

    Perhaps @mourner. Thanks!

    opened by styfle 4
  • Avoid generating diff images with no differences in it

    Avoid generating diff images with no differences in it

    Hi,

    not sure if this is already a feature and I just didn't find it in the documentation and generated diff object properties after the function analysis, but it would be good to have the information if the software has found differences between the two images. If there are no differences, I would like to avoid saving these images.

    Maybe something like

    var diffImg = pixelmatch(img1, img2, diff, 800, 600, {threshold: 0.1, GenerateNoDiff: true});
    // if "GenerateNoDiff" is set to false and no diff was found, the function returns null. If differences were found, it returns the regular obj
    // if "GenerateNoDiff" is set to to true, the function always returns the obj
    

    or maybe as part of the generated obj

    var diffImg = pixelmatch(img1, img2, diff, 800, 600, {threshold: 0.1});
    // diffImg.diff will be true if differences were found or false, if no differences were found
    

    Thanks :)

    opened by abbrechen 0
  • Improve anti-aliasing algorithm

    Improve anti-aliasing algorithm

    this fixes the issue w false positives and also a bug for returning true when false should have been returned. In addition, I added conditional handling for the "edge" cases where you variably can accept one or two zeros depending on the line they are on.

    opened by josieoharrow 3
  • Prevent diff to be generated if threashold not reached

    Prevent diff to be generated if threashold not reached

    Hi,

    I use pixelmatch to compare the whole bunch of screenshots i've been taking for each version of my product (approx 300 screenshots) I use the binary to test each new screenshots against the previous one. But even if there is no differences (or if the diff threshold ins't reach), I still have a diff file generated which make the tester job quite difficult since he has to go through 300 files, even if there is no différence at all.

    Can't you prevent the diff file to be writen if the threshold is not reached ?

    question 
    opened by bluepioupiou 2
  • Handle Chrome font rendering weirdness with

    Handle Chrome font rendering weirdness with "pixelShift" comparison

    Hi @mourner ,

    I can only imagine what your life is like right now in the midst of invasion! This is certainly not urgent, but I just wanted to document here...

    I wanted to pose a question and propose a possible solution to the issue where Chrome and other browsers are often inconsistent in their anti-aliased font rendering like below output from latest pixelmatch: image

    A human can see "New Client" is shifted a few pixels up and over between the two screenshots, and might consider it within the threshold of "close enough - PASS". Anti-aliasing parameters don't seem to get it to filter out the false positive. So I forked your repo and made this pull request: https://github.com/mapbox/pixelmatch/pull/106

    enhancement 
    opened by tybrd916 0
  • Horizontal/Vertical shift threshold

    Horizontal/Vertical shift threshold

    Proposed addition of two parameters to popular pixelmatch library:

    • horizontalShiftPixels
    • verticalShiftPixels

    Setting these parameters > 0 makes pixelmatch do additional checking for neighboring pixels within plus-or-minus horizontal/vertical "shift pixels" to avoid false-positives when the browser misaligns text by a few pixes.

    • Only execute this more expensive operation if the original pixel position mis-matches

    Useful for jest-puppeteer testing screenshot matching where Chrome/Firefox text rendering is slightly shifted even on the same machine like this difference with original pixelmatch: image

    opened by tybrd916 8
Releases(v5.3.0)
  • v5.3.0(Apr 25, 2022)

  • v5.2.0(Apr 17, 2020)

    • Added a diffColorAlt option for coloring pixels that got darker differently (for differentiating between "added" and "removed" parts). #87 (h/t @tristen)
    • Dropped Node v4/v6 support in the binary.
    Source code(tar.gz)
    Source code(zip)
  • v5.1.0(Sep 23, 2019)

  • v5.0.2(Jun 10, 2019)

  • v5.0.1(Jun 7, 2019)

  • v5.0.0(Jun 7, 2019)

    • ⚠️ Breaking: update to ES6 syntax, dropping Node <6.4 and IE11 support.
    • 🎨 Added configurable diff output (options alpha, aaColor, diffColor) (@dreignier @VP-)
    • ⚡️ Improve matching performance by ~35%.
    • ⚡️ Bypass detailed comparison when image data is identical, significantly improving performance when running tests on CI #60 (thanks to @bontscho).
    • Fix anti-aliasing detection on image edges #51 (@IvanSanchez).
    • Produce a meaningful error when image data is in the wrong format #53 (thanks to @LearningNerd)
    • CLI: return non-zero exit code when images differ #54 (@maio).
    • CLI: make diff output optional.
    • Make the published NPM module smaller.
    Source code(tar.gz)
    Source code(zip)
  • v4.0.2(Aug 8, 2016)

  • v4.0.1(Dec 2, 2015)

  • v4.0.0(Oct 23, 2015)

    • Breaking: switched to a much more accurate color difference metric based on recent research. As a side effect, the threshold scale changed — bump your old value by about ~70%.
    • Changed processing of alpha-channel (blending to white instead of black when comparing images)
    • Added an option to skip diff image creation (by passing null as the output argument)
    Source code(tar.gz)
    Source code(zip)
  • v3.0.0(Oct 20, 2015)

    • Breaking: changed the API to accept options object literal (with threshold and includeAA properties) rather than arguments.
    • Breaking: changed threshold to be relative to YUV distance (not squared); this means you'll have to change the threshold values you're currently using to sqrt(threshold).
    • Added usage examples for Node & browsers in the readme.
    Source code(tar.gz)
    Source code(zip)
  • v2.0.2(Oct 18, 2015)

  • v2.0.1(Oct 17, 2015)

  • v2.0.0(Oct 17, 2015)

    • Much more accurate anti-aliased pixels detection algorithm #2
    • Display transparent pixels as white rather than gray in diff

    Note a breaking API change: antialiasing argument (number, 1 by default) is replaced with includeAA (boolean, false by default). Anti-aliasing detection is still enabled by default but always happens with 1 pixel radius.

    Source code(tar.gz)
    Source code(zip)
  • v1.1.1(Oct 17, 2015)

  • v1.1.0(Oct 16, 2015)

  • v1.0.0(Oct 14, 2015)

Owner
Mapbox
Mapbox is the location data platform for mobile and web applications. We're changing the way people move around cities and explore our world.
Mapbox
Super Low-Level Raster Reprojection and Resampling Library

geowarp Super Low-Level Raster Reprojection and Resampling Library install npm install -S geowarp usage const geowarp = require("geowarp"); const proj

Daniel J. Dufour 27 Nov 9, 2022
A library that makes Image Map Area responsive

A library that makes Image Map Area responsive

elenh 1 Jan 21, 2022
Medium's Image Zoom for jQuery

ZOOM.JS A simple jQuery plugin for image zooming; as seen on Medium. Demo https://fat.github.io/zoom.js How Link the zoom.js and zoom.css files to you

fat 4.1k Jan 2, 2023
🏸 A simple plugin for image zooming without dependencies ~1.65KB gzip

ZOOOM.JS A simple plugin for image zoooming without dependencies. Only pure javascipt. Installation CDN JavaScript <script src="https://cdn.jsdelivr.n

Grzegorz Tomicki 13 Aug 30, 2022
The NASA WorldWind Javascript SDK (WebWW) includes the library and examples for creating geo-browser web applications and for embedding a 3D globe in HTML5 web pages.

Web WorldWind New versions of WorldWind released Web WorldWind 0.10.0 and WorldWind Java 2.2.0 are now available on GitHub. The new version of Web Wor

NASA WorldWind 770 Jan 1, 2023
geotiff.js is a small library to parse TIFF files for visualization or analysis. It is written in pure JavaScript, and is usable in both the browser and node.js applications.

geotiff.js Read (geospatial) metadata and raw array data from a wide variety of different (Geo)TIFF files types. Features Currently available function

geotiff.js 649 Dec 21, 2022
An open-source JavaScript library for world-class 3D globes and maps :earth_americas:

CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin. It uses WebGL for hardware-accelerated graphics

Cesium 9.7k Dec 26, 2022
An open-source JavaScript library for world-class 3D globes and maps :earth_americas:

CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin. It uses WebGL for hardware-accelerated graphics

Cesium 9.7k Jan 3, 2023
:leaves: JavaScript library for mobile-friendly interactive maps

Leaflet is the leading open-source JavaScript library for mobile-friendly interactive maps. Weighing just about 39 KB of gzipped JS plus 4 KB of gzipp

Leaflet 36.5k Jan 1, 2023
Polymaps is a free JavaScript library for making dynamic, interactive maps in modern web browsers.

Polymaps Polymaps is a free JavaScript library for making dynamic, interactive maps in modern web browsers. See http://polymaps.org for more details.

Urban Airship 1.6k Dec 23, 2022
JavaScript library to transform coordinates from one coordinate system to another, including datum transformations

PROJ4JS Proj4js is a JavaScript library to transform point coordinates from one coordinate system to another, including datum transformations. Origina

null 1.7k Dec 28, 2022
A very fast geospatial point clustering library for browsers and Node.

supercluster A very fast JavaScript library for geospatial point clustering for browsers and Node. <script src="https://unpkg.com/[email protected]/d

Mapbox 1.6k Jan 7, 2023
A Node.js map tile library for PostGIS and torque.js, with CartoCSS styling

Windshaft A Node.js map tile library for PostGIS and torque.js, with CartoCSS styling. Can render arbitrary SQL queries Generates image and UTFGrid in

CARTO 306 Dec 22, 2022
jQuery Vector Map Library

This project is a heavily modified version of jVectorMap as it was in April of 2012. I chose to start fresh rather than fork their project as my inten

10 Best Design 1.8k Dec 28, 2022
jQuery Vector Map Library

This project is a heavily modified version of jVectorMap as it was in April of 2012. I chose to start fresh rather than fork their project as my inten

10 Best Design 1.8k Dec 28, 2022
AngularJS directive to embed an interact with maps managed by Leaflet library

Angular Leaflet Why the fork? While we are grateful for all the original work at tombatossals/angular-leaflet-directive. We need to be able to operate

AngularUI 313 Nov 10, 2022
Waypoints is a library that makes it easy to execute a function whenever you scroll to an element.

Waypoints Waypoints is a library that makes it easy to execute a function whenever you scroll to an element. var waypoint = new Waypoint({ element:

Caleb Troughton 10.3k Jan 4, 2023
Geokit - is a command-line interface (CLI) tool written in javascript, that contains all the basic functionalities for measurements, conversions and operations of geojson files.

Geokit Geokit is a command-line interface (CLI) tool written in javascript, that contains all the basic functionalities for measurements, conversions

Development Seed 31 Nov 17, 2022
UNMAINTAINED Open source JavaScript renderer for Kartograph SVG maps

This project is not maintained anymore. Here are a few reasons why I stopped working on kartograph.js: there's no need to support non-SVG browsers any

null 1.5k Dec 11, 2022