Code Monkey home page Code Monkey logo

looks-same's Introduction

LooksSame

Build Status

Node.js library for comparing images, taking into account human color perception. It is created specially for the needs of visual regression testing for hermione utility, but can be used for other purposes.

Supported image formats

JPEG, PNG, WebP, GIF, AVIF, TIFF and SVG images are supported.

Note: If you want to compare jpeg files, you may encounter random differences due to the jpeg structure if they are not lossless jpeg files.

Comparing images

const looksSame = require('looks-same');

// equal will be true, if images looks the same
const {equal} = await looksSame('image1.png', 'image2.png');

Parameters can be paths to files or buffer with compressed image.

By default, it will detect only noticeable differences. If you wish to detect any difference, use strict options:

const {equal} = await looksSame('image1.png', 'image2.png', {strict: true});

You can also adjust the ΔE value that will be treated as error in non-strict mode:

const {equal} = await looksSame('image1.png', 'image2.png', {tolerance: 5});

Default tolerance in non-strict mode is 2.3 which is enough for the most cases. Setting tolerance to 0 will produce the same result as strict: true, but strict mode is faster. Attempt to set tolerance in strict mode will produce an error.

Some devices can have different proportion between physical and logical screen resolutions also known as pixel ratio. Default value for this proportion is 1. This param also affects the comparison result, so it can be set manually with pixelRatio option.

const {equal} = await looksSame('image1.png', 'image2.png', {pixelRatio: 2});

Comparing images with ignoring caret

Text caret in text input elements it is a pain for visual regression tasks, because it is always blinks. These diffs will be ignored by default. You can use ignoreCaret option with false value to disable ignoring such diffs. In that way text caret will be marked as diffs.

const {equal} = await looksSame('image1.png', 'image2.png', {ignoreCaret: true});

Both strict and ignoreCaret can be set independently of one another.

Comparing images with ignoring antialiasing

Some images has difference while comparing because of antialiasing. These diffs will be ignored by default. You can use ignoreAntialiasing option with false value to disable ignoring such diffs. In that way antialiased pixels will be marked as diffs. Read more about anti-aliasing algorithm.

const {equal} = await looksSame('image1.png', 'image2.png', {ignoreAntialiasing: true});

Sometimes the antialiasing algorithm can work incorrectly due to some features of the browser rendering engine. Use the option antialiasingTolerance to make the algorithm less strict. With this option you can specify the minimum difference in brightness (zero by default) between the darkest/lightest pixel (which is adjacent to the antialiasing pixel) and theirs adjacent pixels.

We recommend that you don't increase this value above 10. If you need to increase more than 10 then this is definitely not antialiasing.

Example:

const {equal} = await looksSame('image1.png', 'image2.png', {ignoreAntialiasing: true, antialiasingTolerance: 3});

Getting diff bounds

Looksame returns information about diff bounds. It returns only first pixel if you passed stopOnFirstFail option with true value. The whole diff area would be returned if stopOnFirstFail option is not passed or it's passed with false value.

Getting diff clusters

Looksame returns diff bounds divided into clusters if option shouldCluster passed with true value. Moreover you can pass clusters size using clustersSize option.

// {
//     equal: false,
//     diffBounds: {left: 10, top: 10, right: 20, bottom: 20}
//     diffClusters: [
//         {left: 10, top: 10, right: 14, bottom: 14},
//         {left: 16, top: 16, right: 20, bottom: 20}
//     ]
// }
const {equal, diffBounds, diffClusters} = await looksSame('image1.png', 'image2.png', {shouldCluster: true, clustersSize: 10});

Building diff image

await looksSame.createDiff({
    reference: '/path/to/reference/image.png',
    current: '/path/to/current/image.png',
    diff: '/path/to/save/diff/to.png',
    highlightColor: '#ff00ff', // color to highlight the differences
    strict: false, // strict comparsion
    tolerance: 2.5,
    antialiasingTolerance: 0,
    ignoreAntialiasing: true, // ignore antialising by default
    ignoreCaret: true // ignore caret by default
});

Building diff image as a Buffer

If you don't want the diff image to be written on disk, then simply don't pass any diff: path to the createDiff method. The method will then resolve a Buffer containing the diff. You can also specify buffer format with extension key. Default extension is png. List of supported formats: heic, heif, avif, jpeg, jpg, png, raw, tiff, tif, webp, gif, jp2, jpx, j2k, j2c

const buffer = await looksSame.createDiff({
    // exactly same options as above, but with optional extension and without diff
    extension: 'png'
});

Comparing images and creating diff image simultaneously

If you need both co compare images and create diff image, you can pass option createDiffImage: true, it would work faster than two separate function calls:

const {
    equal,
    diffImage,
    differentPixels,
    totalPixels,
    diffBounds,
    diffClusters
} = await looksSame('image1.png', 'image2.png', {createDiffImage: true});

if (!equal) {
    await diffImage.save('diffImage.png');
}

Comparing colors

If you just need to compare two colors you can use colors function:

const equal = looksSame.colors(
    {R: 255, G: 0, B: 0},
    {R: 254, G: 1, B: 1},
    {tolerance: 2.5}
);

looks-same's People

Contributors

catwithapple avatar chrisdeely avatar dudagod avatar j0tunn avatar kuznetsovroman avatar levonet avatar linusu avatar oldskytree avatar rostik404 avatar sipayrt avatar swinx avatar tormozz48 avatar vorobeyko avatar xcatliu 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

looks-same's Issues

diff percent or number of pixel

is there any possibility that the function returns the number of pixels of the difference? or would it be possible to somehow calculate the difference in percentage from the cluster?

The results are undefined

    /**
     * 
     * @param {fs.PathLike} one 
     * @param {fs.PathLike} two 
     * @returns {boolean}
     */
    isSameImg(one, two) {
        let isEqual;
        looksSame(one, two, { strict: false }, function(err, res) {
            if(err) throw err;
            isEqual = res.equal;
        });
        return isEqual;
    };

this function is in a class I use, when i use the function I get "TypeError: Cannot read properties of undefined (reading 'equal')"
wich means res is undefined, and testing with function(err, { equal } will return me I can't destructure the object cause it's undefined.

any ideas?

Feature: ignore rectangle

Does looks-same support ignoring certain areas (rectangles) during comparison?
I see that gemini, which is using looks-same, allows to ignoreElements. I would expect it (ignoring) is done by means of image-comparison tool?

Looks-same to return the diff as a base64 string

Need

As a developer
I need to have a pluggable application 
So that I can build and manage other application on top of it

It would be nice to have a method that returns the diff as a base64 String, so the developer can manage it how he wants, to store it into a database, to send it to a server etc.

Deliverables

  • implement the method
  • add tests

Solution

add a method to the the prototype of PNGOut:

  /**
  * @param {base64Cb} callback
  */
  createBuffer: function(callback) {
    this._png.pack(); // behaves like a stream

    // collect the data on 'data' events
    // on end event call the callback
    callback(null, buffer);
  }

  /**
  * @callback base64Cb
  * @param error
  * @param {Buffer} buffer
  */

adding another option to the options passed to createDiff function:

  /**
  * @param {Object} options
  * @param {Boolean} [options.save]
  */
  createDiff(options, callback) {}

the callback passed to buildDiffImage, may look like:

function(result) {
  if (opts.save) {
    result.save(opts.path, callback);
  }
  else {
   result.createBase64(callback);
  }
}

TypeScript support

This library looks great! Do you have any plans on adding TypeScript support so us TypeScripters can join in on the fun without having to write the types ourselves?

The problem is me or the lib?

Trying to test the lib, but got some error

Test script:

import looksSame from "looks-same";

async function CompareTwoImages2() {
  looksSame(
    looksSame("cropped.png", "mace.png", function (error, { equal }) {
      console.log(error);
      console.log(equal);
    })
  );
}
CompareTwoImages2();

Error:

(node:5032) UnhandledPromiseRejectionWarning: TypeError: Field "source" does not exist in {}
    at /home/hiwyn/playimg/node_modules/looks-same/lib/validators.js:9:19
    at Array.forEach (<anonymous>)
    at validateRequiredFields (/home/hiwyn/playimg/node_modules/looks-same/lib/validators.js:7:23)
    at /home/hiwyn/playimg/node_modules/looks-same/lib/validators.js:30:9
    at Array.forEach (<anonymous>)
    at Object.exports.validateImages (/home/hiwyn/playimg/node_modules/looks-same/lib/validators.js:25:18)
    at Object.exports.formatImages (/home/hiwyn/playimg/node_modules/looks-same/lib/utils.js:84:16)
    at looksSame (/home/hiwyn/playimg/node_modules/looks-same/index.js:147:30)
    at CompareTwoImages2 (file:///home/hiwyn/playimg/test.js:4:3)
    at file:///home/hiwyn/playimg/test.js:11:1
(node:5032) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:5032) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
null
false

Can there be more tolerance if the patterns are very similar?

image

i m using this for now, but the block is very hard to solve like this image

const { equal } = await looksSame(image1, image2)

what is tolerance value for such scene?

const {equal} = await looksSame('image1.png', 'image2.png', {tolerance: 5});

Make the equal pixels transparent

Need

As a user
I need to see the differences immediately
So that I don't struggle to see the little ones

Deliverables

  • Possibility to make the equal pixels of the diff image transparent
  • tests

test

rect

diff

Solution

Adding another field to the options object:

options = {
  transparency: {Number} between 0 - 255 // default 255
};

Modifying the setPixel method from the prototype of PNGOut, to receive another parameter, that will be set on the alpha channel, i.e this._png.data[idx +3].

Add equal feedback in createDiff.

It would be great to know if the images are equal or not when creating diff.

looksSame.createDiff({ ... }, function (error, equal) { ... });

Single pixel diffs are not ignored

Using looks-same v3.3.0.

Note that the reference image was generated using WebGL on a Macbook Pro 2015, whereas the generated image is generated using a docker image running Ubuntu with osmesa.

looks-same configuration:

await looksSameAsync(actual, expected,
                                           {
                                             tolerance: tolerance,
                                             pixelRatio: self._getDPI(), // set to device DPI
                                             ignoreAntialiasing: true,
                                             ignoreCaret: true
                                           });

await looksSame.createDiffAsync({
        reference: this._getReferenceFilePath(),
        current: this.getActualFilePath(),
        diff: this._getDiffFilePath(),
        highlightColor: '#ff00ff',
        tolerance: tolerance,
        pixelRatio: self._getDPI(),
        ignoreAntialiasing: true,
        ignoreCaret: true
      });

reference image:
single-triangle-colors

generated image:
single-triangle-colors

diff:
single-triangle-colors

The lower left pixel of the triangle is showing up as different, and the test is failing due to this.

Error when diff path contains folders that does not exist

'looks-same' saves the diff file using fs.createWriteStream(), passing that path as a parameter. Now, fs.createWriteStream() fails when the directory the path is pointing to doesn't exist - and that's the problem.

the shown error:

ENOENT: no such file or directory, open '...'

possible solutions:

  1. using fsExtra.outputFile() which creates any missing directories. (best IMO)
  2. Implementing the recursive mkdir ourselves

initially found here

Export TS interface

It would be great if the lib would export its public api interfaces like LooksSameOptions.

Doesn't works with Bun

Hey, When I try to use this lib, i got this error in terminal:

Cannot find module "../build/Release/sharp-linux-x64.node" from "/home/....."

Possible solutions:
- Install with verbose logging and look for errors: "npm install --ignore-scripts=false --foreground-scripts --verbose sharp"
- Install for the current linux-x64 runtime: "npm install --platform=linux --arch=x64 sharp"
- Consult the installation documentation: https://sharp.pixelplumbing.com/install

I'm under WSL2 with Ubuntu on Windows 11 and i'm using the version 1.x of Bun

antialiasingTolerance doesn't work with alpha transparency

I have the following two images to compare. They are full-page screenshots of a web page, captured by Hermione. Taken on Browserstack Windows 10 Edge.

Baseline:
https://user-images.githubusercontent.com/44468637/65691884-eafebc80-e071-11e9-942c-438cfcc667e7.png

Actual:
https://user-images.githubusercontent.com/44468637/65691893-ef2ada00-e071-11e9-84ef-899338b3651b.png

Here is a closeup of some of the differences it produces:

image

To me it looks like antialiasing, but no matter what i set as the antialiasingTolerance, it says these images are different.

I have to increase tolerance to 20 before it starts to pass.

Is it possible we're dealing with a difference in alpha transparency rather than antialiasing? Can looks-same get an option for dealing with alpha?

Inaccurate comparison

Literally have two of the same screenshots and its telling me that the hex color is different and therefore test fails since they're not equal.

error when try CreateDiff

when I run follow code
const looksSame = require('looks-same');

function diff (){
looksSame.createDiff({
reference: './reference.png',
current:'./current.png',
diff: './diff.png',
highlightColor: '#ff00ff', // color to highlight the differences
strict: false, // strict comparsion
tolerance: 2.5,
antialiasingTolerance: 0,
ignoreAntialiasing: true, // ignore antialising by default
ignoreCaret: true // ignore caret by default
}, (error, {equal}) => {
console.log("func block");
console.log(error.name, error.message);
});}

diff();

get an error:

TypeError: Cannot destructure property first of 'undefined' or 'null'.

Could u guys describe me what's going on? Is there issue in my code?

Thank you :)

image

Options mutation yields 'Unable to use "strict" and "tolerance" options together' error.

Snippet:

const getImageDiff = async (reference, current) => new Promise((resolve, reject) => {
  const options = {
    highlightColor: '#ff00ff',
    strict: true,
  };

  looksSame(reference, current, options, function(error, equal) {
    if (error) reject(error);
    if (!equal) {
      looksSame.createDiff({reference, current, ...options}, function(error, buffer) {
        if (error) reject(error);
        resolve(buffer);
      });
    }
    resolve();
  });
});

Will throw:

Unable to use "strict" and "tolerance" options together

This happens on looksSame.createDiff() since looksSame() mutates the options to look like this:

{ 
  highlightColor: '#ff00ff',
  strict: true,
  tolerance: 2.3,
  ignoreAntialiasing: true,
  antialiasingTolerance: 0 
}
  • I don't think any operation should mutate the options object since it is owned by the client code.
  • Especially not so if this mutates it into an invalid state.

PS. Would be grand to have equal returned with createDiff: looksSame.createDiff(options, function(error, buffer, equal) would save some of the logic above.

highlightColor undefined issue

Basically highlightColor being undefined is causing this error and it wasn't obvious that it was highlightColor to blame:

TypeError: Cannot read property '0' of undefined
    at parseColorString (/Users/niftylettuce/Public/project/node_modules/looks-same/index.js:170:22)
    at readPair (/Users/niftylettuce/Public/project/node_modules/looks-same/index.js:218:29)
    at readFunc (/Users/niftylettuce/Public/project/node_modules/looks-same/index.js:40:17)
    at png.parse (/Users/niftylettuce/Public/project/node_modules/looks-same/lib/png.js:120:15)
    at exports.PNG.<anonymous> (/Users/niftylettuce/Public/project/node_modules/pngjs/lib/png.js:75:7)
    at Object.onceWrapper (events.js:316:30)
    at emitOne (events.js:115:13)
    at exports.PNG.emit (events.js:210:7)
    at exports.PNG.<anonymous> (/Users/niftylettuce/Public/project/node_modules/pngjs/lib/png.js:37:10)
    at emitOne (events.js:115:13)
    at module.exports.emit (events.js:210:7)
    at module.exports.ParserAsync._complete (/Users/niftylettuce/Public/project/node_modules/pngjs/lib/parser-async.js:145:8)
    at emitOne (events.js:115:13)
    at module.exports.emit (events.js:210:7)
    at module.exports.complete (/Users/niftylettuce/Public/project/node_modules/pngjs/lib/filter-parse-async.js:19:12)
    at module.exports.Filter._reverseFilterLine (/Users/niftylettuce/Public/project/node_modules/pngjs/lib/filter-parse.js:169:10)
    at module.exports.ChunkStream._processRead (/Users/niftylettuce/Public/project/node_modules/pngjs/lib/chunkstream.js:174:13)
    at module.exports.ChunkStream._process (/Users/niftylettuce/Public/project/node_modules/pngjs/lib/chunkstream.js:193:14)
    at module.exports.ChunkStream.write (/Users/niftylettuce/Public/project/node_modules/pngjs/lib/chunkstream.js:61:8)
    at Inflate.<anonymous> (/Users/niftylettuce/Public/project/node_modules/pngjs/lib/parser-async.js:86:9)
    at emitOne (events.js:115:13)
    at Inflate.emit (events.js:210:7)
    at addChunk (_stream_readable.js:252:12)
    at readableAddChunk (_stream_readable.js:239:11)
    at Inflate.Readable.push (_stream_readable.js:197:10)
    at Inflate.Transform.push (_stream_transform.js:147:32)
    at Zlib.callback (zlib.js:473:14)

Compare images as Buffers

Can you add an option to compare two images from buffers instead of files? With my use case I generate the images and it would be nice to not have to save to a temporary file to compare them.

'ERR_BUFFER_OUT_OF_BOUNDS'

I've noticed some inconsistent test behavior when running screenshots.js:60 const diff = await looksSame(testFile, baselineFile, {tolerance: 20})

I receive the following error message on a particular webpage when running my tests:

node:internal/errors:490
    ErrorCaptureStackTrace(err);
    ^

RangeError [ERR_BUFFER_OUT_OF_BOUNDS]: Attempt to access memory outside buffer bounds
    at new NodeError (node:internal/errors:399:5)
    at boundsError (node:internal/buffer:86:11)
    at Buffer.readUInt32BE (node:internal/buffer:311:5)
    at get width [as width] (C:\...\node_modules\looks-same\lib\img-buffer\original-buffer.js:14:29)
    at looksSame (C:\...\node_modules\looks-same\index.js:159:41)
    at async C:\...\screenshots.js:60:30 {
  code: 'ERR_BUFFER_OUT_OF_BOUNDS'
}

Has this issue been previously reported? It's unclear why this occurs for a particular test/webpage and not others?

`looksSame` throws an error when an error is present using the readme code

looksSame throws an error when an error is present, using the readme code

looksSame('image1.png', 'image2.png', function(error, {equal}) {
    // equal will be true, if images looks the same
});

> TypeError: Cannot destructure property `equal` of 'undefined' or 'null'.

Should probably update the docs to set the default in the callback i.e.

looksSame('image1.png', 'image2.png', function(error, {equal} = {}) {
    // equal will be true, if images looks the same
});

Feature: Percentage-based similarity

Right now, looks-same measures each individual pixel, and if even one exceeds the difference tolerance, it returns a failure. It would be nice if there was a mode where we could specify "I expect X% of the pixels to be the same".

In other words, if one or two pixels don't match, then still pass the test, but if a large number of pixels don't match, then it should fail the comparison.

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.