Giter VIP home page Giter VIP logo

rembrandt's Introduction

TravisCI Status Slack Status

Rembrandt.JS - Client- and server-side image comparison library

Rembrandt

Rembrandt.JS is a image comparison library that works both with the HTML5 Canvas2D API as well as the drop-in Node.JS replacement node-canvas.

We created Rembrandt.JS to have an easy-to-use image comparison library for our internal tests for PhotoEditorSDK. Go check it out. It's really awesome. :)

Installation

Node.JS

Please follow the installation instructions over at node-canvas in order to correctly install all required system libraries. Afterwards, just run:

npm install rembrandt

Browser

Download the latest build from our Releases page, then include it like this:

<script src="/path/to/rembrandt.min.js"></script>

The Rembrandt JavaScript variable is now globally available.

Using module bundlers like Webpack etc.

Install Rembrandt via npm install rembrandt, then require it inside your JavaScript like so:

var Rembrandt = require('rembrandt/build/browser')

Usage

Here is an example (ES6 / ES2015):

import Rembrandt from 'rembrandt'

const rembrandt = new Rembrandt({
  // `imageA` and `imageB` can be either Strings (file path on node.js,
  // public url on Browsers) or Buffers
  imageA: '/path/to/imageA',
  imageB: fs.readFileSync('/path/to/imageB'),

  // Needs to be one of Rembrandt.THRESHOLD_PERCENT or Rembrandt.THRESHOLD_PIXELS
  thresholdType: Rembrandt.THRESHOLD_PERCENT,

  // The maximum threshold (0...1 for THRESHOLD_PERCENT, pixel count for THRESHOLD_PIXELS
  maxThreshold: 0.01,

  // Maximum color delta (0...1):
  maxDelta: 0.02,

  // Maximum surrounding pixel offset
  maxOffset: 0,

  renderComposition: true, // Should Rembrandt render a composition image?
  compositionMaskColor: Rembrandt.Color.RED // Color of unmatched pixels
})

// Run the comparison
rembrandt.compare()
  .then(function (result) {
    console.log('Passed:', result.passed)
    console.log('Pixel Difference:', result.differences, 'Percentage Difference', result.percentageDifference, '%')
    console.log('Composition image buffer:', result.compositionImage)

    // Note that `compositionImage` is an Image when Rembrandt.js is run in the browser environment
  })
  .catch((e) => {
    console.error(e)
  })

License

See LICENSE.md

Authors and Contributors

Copyright (c) 2016 by PhotoEditorSDK.com

rembrandt's People

Contributors

janbussieck avatar keiwando avatar phoenixraw avatar saschagehlich 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

rembrandt's Issues

Trying to install returns error

Why trying to install via npm install rembrandt, I get the below error

`> node-gyp rebuild

gyp ERR! configure error
gyp ERR! stack Error: Command failed: /usr/local/opt/python/libexec/bin/python -c import sys; print "%s.%s.%s" % sys.version_info[:3];
gyp ERR! stack File "", line 1
gyp ERR! stack import sys; print "%s.%s.%s" % sys.version_info[:3];
gyp ERR! stack ^
gyp ERR! stack SyntaxError: invalid syntax
gyp ERR! stack
gyp ERR! stack at ChildProcess.exithandler (child_process.js:294:12)
gyp ERR! stack at ChildProcess.emit (events.js:200:13)
gyp ERR! stack at maybeClose (internal/child_process.js:1021:16)
gyp ERR! stack at Socket. (internal/child_process.js:430:11)
gyp ERR! stack at Socket.emit (events.js:200:13)
gyp ERR! stack at Pipe. (net.js:586:12)
gyp ERR! System Darwin 18.6.0
gyp ERR! command "/Users/tyleroneal/.nvm/versions/node/v12.3.1/bin/node" "/Users/tyleroneal/.nvm/versions/node/v12.3.1/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
gyp ERR! cwd /Users/tyleroneal/repos/TestCafe-Screenshot-Testing/node_modules/canvas
gyp ERR! node -v v12.3.1
gyp ERR! node-gyp -v v3.8.0
gyp ERR! not ok
npm WARN Screenshot_Testing@ No repository field.
npm WARN Screenshot_Testing@ No license field.

npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] install: node-gyp rebuild
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] install script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
`

What do I need to resolve this issue?

browser result.compositionImage base64 encoded incorrectly

The base64 image data returned in the result.compositionImage is incorrect.

If you paste its contents in one of the online base64 image decoders - it will not produce an image.

$('#diffImg').attr('src', 'data:image/png;base64,' + result.compositionImage.toString('base64'));

Surrounding Pixel Algorithm

Hello!

I am trying to learn about image comparison algorithms, and I have a question about the surrounding pixel comparison algorithm in this library.

let newColorA = this._imageA.getColorAt(currentX, currentY)
let newDeltaA = this._calculateColorDelta(colorA, newColorA)
let newColorB = this._imageB.getColorAt(currentX, currentY)
let newDeltaB = this._calculateColorDelta(colorA, newColorB)
if ((Math.abs(newDeltaB - newDeltaA) < maxDelta) && (newDeltaA > maxDelta)) {
return true
}

Particularly, I am very confused why newDeltaB is subtracted by newDeltaA to compare with the maxDelta. I feel that this may result in a false positive even with very different colors since the differences may cancel each other out in unexpected ways (I cannot really explain this well, but it is rooted in math). Also, the algorithm seems to be asymmetrical, meaning that if I switched the order of the images around, so that imageA became imageB and imageB became imageA, the results would differ greatly.

As a solution to this problem, I thought of another algorithm that could possibly work. Why wouldn't this code be a better alternative?

 let newColorA = this._imageA.getColorAt(currentX, currentY) 
 let newDeltaA = this._calculateColorDelta(colorB, newColorA) 
  
 let newColorB = this._imageB.getColorAt(currentX, currentY) 
 let newDeltaB = this._calculateColorDelta(colorA, newColorB) 
  
 if (newDeltaA < maxDelta || newDeltaB < maxDelta) { 
   return true 
 } 

gave it same link and got "no"

`
var rembrandt = new Rembrandt({

  imageA: "https://tr.rbxcdn.com/dbbcad849623da78a1df3930e18d54cd/420/420/Shirt/Png",
  imageB: "https://tr.rbxcdn.com/dbbcad849623da78a1df3930e18d54cd/420/420/Shirt/Png",

  thresholdType: Rembrandt.THRESHOLD_PERCENT,

  // The maximum threshold (0...1 for THRESHOLD_PERCENT, pixel count for THRESHOLD_PIXELS
  maxThreshold: 0,

  // Maximum color delta (0...255):
  maxDelta: 0,

  // Maximum surrounding pixel offset
  maxOffset: 0,

})

// Run the comparison
rembrandt.compare()
  .then(function (result) {

    if(result.passed){
      console.log('Yes');
    } else {
      console.log('No');
    }
})`

Color difference calculation is wrong

Hi there,
the color difference calculation in here:

_calculateColorDelta (colorA, colorB) {
let total = 0
total += Math.pow(colorA.r - colorB.r, 2)
total += Math.pow(colorA.g - colorB.g, 2)
total += Math.pow(colorA.b - colorB.b, 2)
total += Math.pow(colorA.a - colorB.a, 2)
return Math.sqrt(total * 255)
}

is wrong. this page is a valuable resource for implementing it correctly. I guess that you dont want to implement a complicated (yet powerful) color difference algorithm like CIEDE2000, but even the DeltaE algorithm, which you tried to implement is wrong. The problem is, that the RGB values of colorA and colorB are on a scale from 0 to 255 (which implies that the difference for each color dimension is on a scale from 0 to 255 too) but in the end, you are multiplying the color difference by 255 again, which lets the biggest possible difference caluclated by this method be 8.144 (sqrt((255^2+255^2+255^2+255^2) * 255)). The color delta threshold should be specified from 0 to 255 however (and is not squared internally, which you would have to do if you want to keep the current delta calculation even though its broken) which leads to the algorithm beeing WAY too strict.
IMHO, the best way to fix this bug is by removing the * 255 multiplication at the end of the color delta calculation since it doesnt make any sense whatsoever. If you want to, i can provide a PR even though its a fairly small change. If you want to head in a different direction, like changing the way the color delta is handled internally, i could provide a PR too.

Please look into this bug since it makes the usage of this library complicated, since now i have to provide a maxDelta of value^2 in my configuration in order to get the correct threshold for the color delta calcuation which is not understandable by someone who did not review the code of this library.

Thanks in advance!

Can I compare image against two images?

I want to do this:

  1. First, compare imageC with imageA (true/false)
  2. if it is false, then compare imageC with imageB (true/false)

Is that possible within this function?

if (jQuery('.portlet-queryRecordSearchResult').length) {
      
      jQuery('.arena-record-cover a img').each(function(){
        var thisImage = jQuery(this),
        thisImageUrl = thisImage.attr('src'),
        sameImage = false,
        rembrandt = new Rembrandt({
          imageA: '/documents/15189/0/portletResources01.jpg',
          imageB: '/documents/15189/0/portletResources02.jpg',
          imageC: thisImageUrl,
          // Needs to be one of Rembrandt.THRESHOLD_PERCENT or Rembrandt.THRESHOLD_PIXELS
          thresholdType: Rembrandt.THRESHOLD_PERCENT,
          // The maximum threshold (0...1 for THRESHOLD_PERCENT, pixel count for THRESHOLD_PIXELS
          maxThreshold: 0,
          // Maximum color delta (0...255):
          maxDelta: 20,
          // Maximum surrounding pixel offset
          maxOffset: 0
        })
        
        function isSameImage(sameImage) {
          if (sameImage) {
            thisImage.css('border', '2px solid red');
          } else {
            console.log('nope');
          }
        }
        
        // Run the comparison
        rembrandt.compare()
          .then(function (result) {
          isSameImage(result.passed)
        })
      });
    }

Results are always the same

I'm creating an application to compare one image to multiples images in a folder. I'm creating this application with Electron and React.

I pass 2 Buffers. As you can see, the Buffers are the same at the first entry, but it gives the same results.
capture d ecran 2017-11-02 a 11 26 40

There's how I use rembrandt:

        let mainImg = fs.readFileSync('/Users/mboutin2/Desktop/12798992_10207498962925066_4475804348577634517_n.jpg');

        data.forEach((file) => {
          smb2Client.readFile('Mike\\' + file, function(err, data){
            if(err) {
              console.log("Error (readdir):\n", err);
              console.log("data", data);
            } else {
              //console.log("File ->", file);

              const rembrandt = new Rembrandt({
                // `imageA` and `imageB` can be either Strings (file path on node.js,
                // public url on Browsers) or Buffers
                imageA: mainImg,
                imageB: data,

                // Needs to be one of Rembrandt.THRESHOLD_PERCENT or Rembrandt.THRESHOLD_PIXELS
                thresholdType: Rembrandt.THRESHOLD_PERCENT,

                // The maximum threshold (0...1 for THRESHOLD_PERCENT, pixel count for THRESHOLD_PIXELS
                maxThreshold: 1,

                // Maximum color delta (0...255):
                maxDelta: 20,

                // Maximum surrounding pixel offset
                maxOffset: 0,

                renderComposition: false, // Should Rembrandt render a composition image?
              });

              rembrandt.compare()
                .then(function (result) {
                  console.log('\n\n\n');
                  console.log(data);
                  console.log(mainImg);
                  console.log('Passed:', result.passed)
                  console.log('Pixel Difference:', result.differences, 'Percentage Difference', result.percentageDifference, '%')
                  console.log('Composition image buffer:', result.compositionImage)

                  // Note that `compositionImage` is an Image when Rembrandt.js is run in the browser environment
                })
                .catch((e) => {
                  console.error(e)
                })
            }
          });
        });

Is there something I do wrong?

No differences found

I am trying to compare two images (in base64 string format) with the following code:

this.images = {
   ref: '<BASE64 STRING>',
   src: '<BASE64 STRING>'
};

async compare() {
    try {
      this.rembrandt = new Rembrandt({
       
        imageA: this.images.ref,
        imageB: this.images.src,
        thresholdType: Rembrandt.THRESHOLD_PERCENT,
        maxThreshold: 0.01,

        // Maximum color delta (0...255):
        maxDelta: 20,

        maxOffset: 0,

        renderComposition: true, 
        compositionMaskColor: Rembrandt.Color.RED 
      });

      this.result = await this.rembrandt.compare();
    } catch (err) {
      console.error(err);
    }
  }

When I run this code with rembrandt.js version 0.1.3 (build from the master branch) or installed it via NPM, I get no results (both images are identical, which is not true), but when I try this same code with rembrandt.js code copied from rembrandt.com, I get the same results as when I try the example of that same website. Attached are both libraries. Who can spot the difference?

rembrandt.zip

On load fail handling

Hi,

Now there is no handling if one of specified images was NOT loaded (I tell about browser implementation).
This is bad for frontend applications not to have ability to handle such errors: if URL returns 404, the application just will discontinue working in the silence.

As I see the code:

const browserImage = Utils.createImage()
browserImage.addEventListener('load', () => {
   resolve(Image.fromImage(browserImage))
})
browserImage.crossOrigin = 'Anonymous'
browserImage.src = image

you use DOM API to load image.
So you can at least use this to handle failures:
https://stackoverflow.com/questions/9815762/detect-when-an-image-fails-to-load-in-javascript

But looks like this solution is not cross-browser (see link).
I suppose the best solution is to use File API.
I've tried to do this by myself, but I unable to convert read binary PNG (or any other format) data to BLOB to pass it to FileReader. Maybe you will get luck with this.

NodeJs Version is super slow

Hi,
I have installed the latest version and the comparison between images works but it is very slow. On a i9700k 8 cores with 34gb takes 12 to 15 minutes to compare two images with the default settings.
THe same image comparison on the website is almost istantaneous leading me to believe it's a nodejs issue.
I am running nodejs 13.11 on ubuntu 19.10
this is my code :
'use strict';

/* eslint-disable max-len */
import Rembrandt from 'rembrandt/build/node.js';
....
async performAnalisys(session, files, threshold = 0.01, delta = 0.02, offset = 0, render = false) {
console.log('analisys', threshold, delta, offset, render);

	// this.processImages(session, data);
	const rembrandt = new Rembrandt({
		// `imageA` and `imageB` can be either Strings (file path on node.js, public url on Browsers) or Buffers
		imageA: `./uploads/${session}/${files[0].name}.png`,
		imageB: `./uploads/${session}/${files[1].name}.png`,
		// imageB: fs.readFileSync('/path/to/imageB'),
		thresholdType: Rembrandt.THRESHOLD_PERCENT, // either THRESHOLD_PERCENT or THRESHOLD_PIXELS
		maxThreshold: threshold, //  (0...1 for THRESHOLD_PERCENT, pixel count for THRESHOLD_PIXELS
		maxDelta: delta, // Maximum color delta (0...1):
		maxOffset: offset, // Maximum surrounding pixel offset
		renderComposition: render, // Should Rembrandt render a composition image?
		compositionMaskColor: Rembrandt.Color.RED // Color of unmatched pixels
	});

	const retVal = await rembrandt.compare()
		.then((result) => {
			console.log('Passed:', result.passed);
			console.log('Pixel Difference:', result.differences, 'Percentage Difference', result.percentageDifference, '%');
			console.log('Composition image buffer:', result.compositionImage);
			this.writeOutputFile(result.compositionImage);
			return result;
		})
		.catch((e) => console.error(e));

	return retVal;
}

Any helps or suggestion is appreciated

Compatibility in a React Native app

Hello,

I am trying to use Rembrandt in a React Native app, here is what I did:

npm install rembrandt

then in my App.js file, I added:

import Rembrandt from 'rembrandt'

But I get:

'Rembrandt' is declared but its value is never read.ts(6133)
Could not find a declaration file for module 'rembrandt'. 'c:/Users/aymer/Desktop/CODEUR-CLIENTS_WebDev-TMP/AwesomeProject/node_modules/rembrandt/build/node.js' implicitly has an 'any' type.
  Try `npm i --save-dev @types/rembrandt` if it exists or add a new declaration (.d.ts) file containing `declare module 'rembrandt';`ts(7016)

I tried the suggested solution "npm i --save-dev @types/rembrandt", it did not work.

And I don't know how to "add a new declaration (.d.ts) file containing `declare module rembrandt"

Any idea ?

Thank you

Issue with global variable

screenshot 2018-10-25 at 17 22 57

In browser this code write in global variable Buffer empty function. In out project used [Amplitude JS SDK](https://amplitude.zendesk.com/hc/en-us/articles/115002889587-JavaScript-SDK-Reference) where overwrite with this empty function Buffer and get error.

screenshot 2018-10-25 at 17 30 35

ReferenceError: window is not defined (even after npm install canvas)

I tried the Rembrandt sample usage in a node js hello world project after running:
npm install canvas

but I still get the error below:

ReferenceError: window is not defined
    at Function.createImage ([myProject]\node_modules\rembrandt\build\browser.js:358:10)
    at [myProject]\node_modules\rembrandt\build\browser.js:229:47
    at new Promise (<anonymous>)
    at Rembrandt._loadImage ([myProject]\node_modules\rembrandt\build\browser.js:219:15)
    at Rembrandt._loadImages ([myProject]\node_modules\rembrandt\build\browser.js:201:20)
    at Rembrandt.compare ([myProject]\node_modules\rembrandt\build\browser.js:136:20)

Here's the example:

const Rembrandt = require('rembrandt/build/browser');
const http = require('http');
const hostname = '127.0.0.1';
const port = 3000;


const server = http.createServer((req, res) => {

    const rembrandt = new Rembrandt({
        // `imageA` and `imageB` can be either Strings (file path on node.js,
        // public url on Browsers) or Buffers
        imageA: './label1.png',
        imageB: './label2.png',
      
        // Needs to be one of Rembrandt.THRESHOLD_PERCENT or Rembrandt.THRESHOLD_PIXELS
        thresholdType: Rembrandt.THRESHOLD_PERCENT,
      
        // The maximum threshold (0...1 for THRESHOLD_PERCENT, pixel count for THRESHOLD_PIXELS
        maxThreshold: 0.01,
      
        // Maximum color delta (0...1):
        maxDelta: 0.02,
      
        // Maximum surrounding pixel offset
        maxOffset: 0,
      
        renderComposition: true, // Should Rembrandt render a composition image?
        compositionMaskColor: Rembrandt.Color.RED // Color of unmatched pixels
      })


        // Run the comparison
        rembrandt.compare()
        .then(function (result) {
            console.log('Passed:', result.passed)
            console.log('Pixel Difference:', result.differences, 'Percentage Difference', result.percentageDifference, '%')
            console.log('Composition image buffer:', result.compositionImage)

            // Note that `compositionImage` is an Image when Rembrandt.js is run in the browser environment
        })

    .catch((e) => {
        console.error(e)
    })

    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/plain');
    res.end('Hello World\n');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});


Can not change global var

Hello,

I want to use this in a loop of images. So I created an each loop.

I set sameImage as a global var. Within the compare I try to set the value to true or false on sameImage. The problem is that it is always false. I can not get the value outside the compare function. I get the correct true / false within the compare though.

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
  <script src="rembrandt.min.js"></script>
  <script>
    if (jQuery('.portlet-queryRecordSearchResult').length) {
      jQuery('.arena-record-cover a img').each(function(){
        var thisImage = jQuery(this).attr('src');
        var sameImage = false;
        
        var rembrandt = new Rembrandt({
          imageA: '/documents/15189/0/portletResources.jpg',
          imageB: thisImage,

          // Needs to be one of Rembrandt.THRESHOLD_PERCENT or Rembrandt.THRESHOLD_PIXELS
          thresholdType: Rembrandt.THRESHOLD_PERCENT,

          // The maximum threshold (0...1 for THRESHOLD_PERCENT, pixel count for THRESHOLD_PIXELS
          maxThreshold: 0,

          // Maximum color delta (0...255):
          maxDelta: 20,

          // Maximum surrounding pixel offset
          maxOffset: 0
        })
        
        // Run the comparison
        rembrandt.compare()
          .then(function (result) {
          sameImage = result.passed;
          console.log('Inside compare: ' + sameImage);
        })
        
        console.log('Outside compare: ' + sameImage);
      });
    }
  </script>

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.