在 JavaScript 中比较两个图像

发布于 2024-11-09 02:13:57 字数 286 浏览 0 评论 0 原文

我试图确定 JavaScript 中的两个图像是否相同(即使源 URL 不同)。

我的具体用例是在 Chrome 扩展程序中(尽管这是一个 Chrome 扩展程序并没有真正考虑到问题)。我可以通过将 img src 设置为:来获取存储在 Chrome 内部数据库中的 favicon png 图像: 'chrome://favicon/'+url 其中“url”是网站的实际 URL。但是,我现在想找到所有独特的图标。鉴于它们都有不同的内部数据库 URL,是否有一种简单的方法可以在 JavaScript 中比较图像?

I am trying to determine if two images are the same in JavaScript (even if the source URLs are different).

My specific use case is within a Chrome extension (though this being a chrome extension doesn't really factor into the question). I can get the image of a favicon png stored within Chrome's internal database by setting the img src to:
'chrome://favicon/'+url where 'url' is the actual URL of the website. However, I now want to find all the unique favicons. Given that they all will have a different URL to the internal database, is there an easy way to compare images in JavaScript?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

遗忘曾经 2024-11-16 02:13:57

不,没有特别简单的方法来做到这一点。 JavaScript 并不是为处理低级操作而设计的,例如直接处理二进制数据以进行图像处理。

您可以使用元素对每个图像进行base64编码< /a>,然后比较生成的 base64 字符串,但这只会告诉您是否图像是相同的。

要使用 getBase64Image 函数(在我链接的答案中定义)来比较两个图像:

var a = new Image(),
    b = new Image();
a.src = 'chrome://favicon/' + url_a;
b.src = 'chrome://favicon/' + url_b;

// might need to wait until a and b have actually loaded, ignoring this for now
var a_base64 = getBase64Image(a),
    b_base64 = getBase64Image(b);

if (a_base64 === b_base64)
{
    // they are identical
}
else
{
    // you can probably guess what this means
}

No, there is no especially easy way to do this. JavaScript was not made for handling low-level operations such as working directly with binary data for, say, image processing.

You could use a <canvas> element to base64 encode each image, and then compare the resulting base64 strings, but this will only tell you whether or not the images are identical.

To use the getBase64Image function (defined in the answer I linked) to compare two images:

var a = new Image(),
    b = new Image();
a.src = 'chrome://favicon/' + url_a;
b.src = 'chrome://favicon/' + url_b;

// might need to wait until a and b have actually loaded, ignoring this for now
var a_base64 = getBase64Image(a),
    b_base64 = getBase64Image(b);

if (a_base64 === b_base64)
{
    // they are identical
}
else
{
    // you can probably guess what this means
}
无可置疑 2024-11-16 02:13:57

我想您可能对这个名为 resemble.js 的 JavaScript 库感兴趣,其中:

使用 HTML5 画布和 JavaScript 分析和比较图像。

Resemble.js 可用于您可能有的任何图像分析和比较要求浏览器。但是,它是为 https://github.com/Huddle/PhantomCSS 强大的视觉回归库 PhantomCSS。 PhantomCSS 需要能够忽略抗锯齿,因为这会导致来自不同机器的屏幕截图之间存在差异。

Resemble.js 使用 HTML5 File API 解析图像数据,并且用于渲染图像差异的画布。

I think you may be interested in this JavaScript library called resemble.js which:

Analyses and compares images with HTML5 canvas and JavaScript.

Resemble.js can be used for any image analysis and comparison requirement you might have in the browser. However, it has been designed and built for use by the https://github.com/Huddle/PhantomCSS powered visual regression library PhantomCSS. PhantomCSS needs to be able to ignore antialiasing as this would cause differences between screenshots derived from different machines.

Resemble.js uses the HTML5 File API to parse image data, and canvas for rendering image diffs.

梦与时光遇 2024-11-16 02:13:57

我们刚刚发布了一个轻量级库 RembrandtJS,它正是这样做的,并且它可以使用 HTML5 Canvas2D API 在浏览器中运行以及在服务器上通过嵌入式 Node.JS 替换节点画布。
它接受 blob 和 url 作为图像源,因此您可以简单地执行此操作:

const rembrandt = new Rembrandt({
  // `imageA` and `imageB` can be either Strings (file path on node.js,
  // public url on Browsers) or Buffers
  imageA: 'chrome://favicon/' + url_a,
  imageB: 'chrome://favicon/' + url_b,

  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){
      // do what you want
    }
  })

如您所见,如果您的域需要在颜色或像素差异方面有一定的余地,Rembrandt 还允许您引入阈值。由于它可以在浏览器和服务器(节点)上运行,因此可以轻松集成到您的测试套件中。

We just released a lightweight library RembrandtJS, that does exactly that and it works both in the browser using HTML5 Canvas2D API as well as on the server via the drop-in Node.JS replacement node-canvas.
It accepts both blobs and urls as image sources so you could simply do this:

const rembrandt = new Rembrandt({
  // `imageA` and `imageB` can be either Strings (file path on node.js,
  // public url on Browsers) or Buffers
  imageA: 'chrome://favicon/' + url_a,
  imageB: 'chrome://favicon/' + url_b,

  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){
      // do what you want
    }
  })

As you can see Rembrandt also allows you to introduce threshold values, if you domain requires some leeway with respect to color or pixel difference. Since it works in both the browser and on the server (node), it makes it easy to integrate into your test suite.

深白境迁sunset 2024-11-16 02:13:57

这可能是一个非常古老的线程,当我需要实现逐像素比较 2 个图像而不是相似性比较的相同目标时,我遇到了它。我发现了一个非常快速且易于使用的 PNG 图像逐像素比较工具 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));

它甚至为您生成一个差异图像。请注意,这不是感性比较。还有其他用于此目的的工具。
以下是 GitHub 上像素匹配的链接:
github 上的像素匹配

This is probably a very old thread and I ran into it when I needed to achieve the same goal of comparing 2 images pixel by pixel instead of similarity comparison. I found a very fast and easy to use tool for PNG image pixel by pixel comparison js library. Here is a simple example provided by the repo:

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

It even generates a diff image for you. Please note this is NOT perceptual comparison. There are other tools for such purpose.
Here is the link on GitHub for pixelmatch:
pixelmatch on github

错々过的事 2024-11-16 02:13:57

这是在浏览器中使用 Pixelmatch 且无需捆绑的完整、可运行的示例。

const prepareImgCtx = img => {
  const canvas = document.createElement("canvas");
  canvas.width = img.width;
  canvas.height = img.height;
  const ctx = canvas.getContext("2d");
  ctx.drawImage(img, 0, 0);
  return ctx;
};

const imgDiff = (imgA, imgB, canvasDiff) => {
  const ctxA = prepareImgCtx(imgA);
  const ctxB = prepareImgCtx(imgB);
  canvasDiff.width = imgA.width;
  canvasDiff.height = imgA.height;
  const diffCtx = canvasDiff.getContext("2d");
  const diff = diffCtx.createImageData(imgA.width, imgA.height);
  const mismatchedPixels = pixelmatch(
    ctxA.getImageData(0, 0, imgA.width, imgA.height).data,
    ctxB.getImageData(0, 0, imgB.width, imgB.height).data,
    diff.data,
    imgA.width,
    imgA.height,
    {threshold: 0.1}
  );
  diffCtx.putImageData(diff, 0, 0);
  return mismatchedPixels;
};

const imageLoaded = async img => {  
  img.crossOrigin = "anonymous";
  img.setAttribute("crossOrigin", "");
  await img.decode();
  return img;
};

(async () => {
  const imgs = [...document.querySelectorAll("img")];
  await Promise.all(imgs.map(imageLoaded));
  const canvasDiff = document.createElement("canvas");
  document.body.prepend(canvasDiff);
  console.log("mismatched pixels:", imgDiff(...imgs, canvasDiff));
})();
<script src="https://bundle.run/[email protected]"></script>
<img src="https://picsum.photos/id/3/200/200">
<img src="https://picsum.photos/id/5/200/200">

Here's a complete, runnable example of using pixelmatch in the browser without bundling.

const prepareImgCtx = img => {
  const canvas = document.createElement("canvas");
  canvas.width = img.width;
  canvas.height = img.height;
  const ctx = canvas.getContext("2d");
  ctx.drawImage(img, 0, 0);
  return ctx;
};

const imgDiff = (imgA, imgB, canvasDiff) => {
  const ctxA = prepareImgCtx(imgA);
  const ctxB = prepareImgCtx(imgB);
  canvasDiff.width = imgA.width;
  canvasDiff.height = imgA.height;
  const diffCtx = canvasDiff.getContext("2d");
  const diff = diffCtx.createImageData(imgA.width, imgA.height);
  const mismatchedPixels = pixelmatch(
    ctxA.getImageData(0, 0, imgA.width, imgA.height).data,
    ctxB.getImageData(0, 0, imgB.width, imgB.height).data,
    diff.data,
    imgA.width,
    imgA.height,
    {threshold: 0.1}
  );
  diffCtx.putImageData(diff, 0, 0);
  return mismatchedPixels;
};

const imageLoaded = async img => {  
  img.crossOrigin = "anonymous";
  img.setAttribute("crossOrigin", "");
  await img.decode();
  return img;
};

(async () => {
  const imgs = [...document.querySelectorAll("img")];
  await Promise.all(imgs.map(imageLoaded));
  const canvasDiff = document.createElement("canvas");
  document.body.prepend(canvasDiff);
  console.log("mismatched pixels:", imgDiff(...imgs, canvasDiff));
})();
<script src="https://bundle.run/[email protected]"></script>
<img src="https://picsum.photos/id/3/200/200">
<img src="https://picsum.photos/id/5/200/200">

别念他 2024-11-16 02:13:57

基于浏览器的文件比较

此答案解决了关于一个已关闭的重复问题 q/73314528/5217142">如何在浏览器中比较两个图像,例如打开或拖放到页面上,不使用画布或base64

高级答案是获取每个文件的数组缓冲区,使用它们创建数据视图或实际类型化数组并逐字节检查它们。

这个简单的示例说明了原理,做出了一些可以通过使用

"use strict";
function checkFiles( event) {
    //console.log(event);
    //console.log(this);
    
    const files = Array.from(event.target.files);
    if( files.length !=2) {
        console.log( "Expected 2 files to compare");
        return;
    }
    Promise.all(files.map(file=>file.arrayBuffer()))
    .then( buffers =>  {          
         const [f1, f2] = buffers.map( buffer => new Uint8Array( buffer));
         if( f1.length!=f2.length) {
              return false;
         }
         if( f1.length == 0) {
              console.warn("Files have zero length");
              return true;
         }
         let i = 0;
         for(; i<f1.length; ++i) {
              if( f1[i] != f2[i]) return false;
         }
         return true;
    })
    .then( same=> {
        console.log( same ? "same" : "different");
    })
    .catch(err=>console.error(err));
}
<input type=file multiple onchange="checkFiles.call(this, event)">

为了提高效率,代码片段的生产版本可以使用 file 对象的 size 属性来检查文件之前是否具有相同的非零长度读取它们以便逐字节比较它们的内容。

Browser based file comparison

This answer addresses a closed as duplicate question about how to compare two images in a browser, say opened or dropped onto the page, without using canvas or base64.

The high level answer is to get array buffers for each of the files, use them to create a data view or actual typed array and check them byte by byte.

This bare bones example illustrates the principles, making some choices that could have been done differently by using

"use strict";
function checkFiles( event) {
    //console.log(event);
    //console.log(this);
    
    const files = Array.from(event.target.files);
    if( files.length !=2) {
        console.log( "Expected 2 files to compare");
        return;
    }
    Promise.all(files.map(file=>file.arrayBuffer()))
    .then( buffers =>  {          
         const [f1, f2] = buffers.map( buffer => new Uint8Array( buffer));
         if( f1.length!=f2.length) {
              return false;
         }
         if( f1.length == 0) {
              console.warn("Files have zero length");
              return true;
         }
         let i = 0;
         for(; i<f1.length; ++i) {
              if( f1[i] != f2[i]) return false;
         }
         return true;
    })
    .then( same=> {
        console.log( same ? "same" : "different");
    })
    .catch(err=>console.error(err));
}
<input type=file multiple onchange="checkFiles.call(this, event)">

For efficiency, a production version of the snippet could use file objects' size attributes to check the files have the same, non-zero length before reading them in order to compare their contents byte by byte.

梦萦几度 2024-11-16 02:13:57

如果其他人像我一样最终来到这里,因为他们试图比较 Node javascript 中的两个图像,我就能够利用 NodeBuffergif 作为 AWS S3 对象转换为 Base64 并对其进行测试。

在 CypressJS 中使用它来测试发送到 S3 的图像,然后在测试中使用 s3.getObject() 返回图像

const imageToBase64 = Buffer .from(image.Body).toString('base64');

If someone else ended up here like I did because they were trying to compare two images in Node javascript, I was able to utilize Node's Buffer to convert a gif as an AWS S3 Object into a Base64 and test against it.

This was used within CypressJS in order to test an image sent to S3 and then in the test using s3.getObject() to return the image

const imageToBase64 = Buffer.from(image.Body).toString('base64');

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文