比较两张 canvas 图片(参考图片和网站上的当前图片)

Compare two canvas image (reference one and current on the website)

我想比较两张图片。我写了一段代码,结果显示图像是一样的,虽然实际上图像被改变了。我想要实现的是,当我按 'o' 键时,我想每 5 秒刷新一次网站,并将现在网站上的图像与我设置间隔时页面上的图像进行比较(通过按'o' 键)。图像具有相同的大小。

// @name         Nowy123
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  try to take over the world!
// @author       You
// @match        http://*/*
// @grant        none
// @include      *.*
// ==/UserScript==

(function() {
    'use strict';

    // Your code here...
    let intervalNum = localStorage.getItem('intervalNum');
    console.log(intervalNum);
    if(intervalNum){
        clearInterval(intervalNum);
        intervalNum = setInterval(specHero, 5000);
    }

    document.addEventListener("keydown", event => {
       if (event.keyCode === 79) {
           console.log("pressed o");
           console.log("interval started");

           //image
           let img = document.querySelector('.contentContainer').querySelector('.heroImage');
           var canvas = document.createElement('canvas');
           var context = canvas.getContext('2d');
           canvas.width = img.width;
           canvas.height = img.height;
           context.drawImage(img, 0, 0 );
           var refImg = context.getImageData(0, 0, img.width, img.height);
           localStorage.setItem('refImg',refImg);
           console.log(img);


           intervalNum = setInterval(specHero, 5000);
           localStorage.setItem('intervalNum', intervalNum);
       }
    });
    document.addEventListener("keydown", event => {
       if (event.keyCode === 80) {
           console.log("pressed p");
           console.log("interval stopped");
           intervalNum = localStorage.getItem('intervalNum');
           clearInterval(intervalNum);
           window.localStorage.clear();
       }
    });

    function specHero(){
       let img = document.querySelector('.contentContainer').querySelector('.heroImage');
       var canvas = document.createElement('canvas');
       var context = canvas.getContext('2d');
       canvas.width = img.width;
       canvas.height = img.height;
       context.drawImage(img, 0, 0 );
       var imgData = context.getImageData(0, 0, img.width, img.height);
       let refImg = localStorage.getItem('refImg');
       let result = isMatch(imgData, refImg);
       console.log(result);
       if(!result){
        console.log("Found difference");
           clearInterval(intervalNum);
           return;

       }
       location.reload();
    }

    function isMatch(data1,data2){
    for(var i = 0; i<data1.length; i++){
        if(data1[i] != data2[i]) return false;
    }
    return true;
}

})();```

您可能想检查 localStorage 中实际存储的内容。剧透警告:这是字符串 "[object ImageData]".

localStorage.setItem('refImg',refImg) 不存储实际图像数据,它存储字符串 "[object ImageData]"。这是因为 localStorage 只能存储字符串,当您尝试存储 non-string 值时,该值将通过调用 toString 转换为字符串。 toString 图像数据对象 return 字符串 "[object ImageData]".

由于您正在处理图像数据对象(通常是非常大的对象)并且您希望不断比较它们,因此使用 localStorage(使用硬盘驱动器存储数据)可能会导致性能下降,此外,无论如何都没有一种直接的方法可以将图像数据存储在 localStorage 中。我建议您通过使用(半)全局变量来保存参考图像数据对象来将对象存储在内存中,如下所示:

(function() {
  'use strict';

  let refImageData; // this will hold the reference image data object instead of saving it to localStorage

  document.addEventListener("keydown", event => {
    /* ... */
    refImageData = refImg; // instead of localStorage.setItem('refImg',refImg)
    /* ... */
  }

  function specHero() {
    /* ... */
    let refImg = refImageData; // instead of let refImg = localStorage.getItem('refImg'), this is redundant anyway because refImageData is already accessible from the other functions
    /* ... */
  }

注意: 正如@Kaiido 在下面的评论中指出的那样,图像数据对象没有 length 属性,它们有 data 属性 保存实际的像素数据,所以 isMatch 应该是这样的:

function isMatch(data1, data2) {
  for(let i = 0; i < data1.data.length; i++) {
    if(data1.data[i] != data2.data[i]) {
      return false;
    }
  }

  return true;
}

编辑 - 将图像数据对象保存到 localStorage:

如果一定要将图片数据对象保存到本地,需要将图片数据对象(Uint8ClampedArray类型)的data属性转换成常规数组首先使用 Array.from,然后 JSON.stringify/JSON.parse 将该数组转换为字符串并返回数组。为此,我们使用了这两个辅助函数,一个用于保存,一个用于检索图像数据对象的 data 属性:

function saveImageData(key, imageData) {
    localStorage.setItem(key, JSON.stringify(Array.from(imageData.data)));
}

function loadImageData(key) {
    // it's probably a good idea to check if local storage has something saved before executing the following line of code
    return JSON.parse(localStorage.getItem(key));
}

并将它们用于 save/retrieve,如下所示:

saveImageData("refImg", refImg);      // instead of localStorage.setItem('refImg',refImg)

let refImg = loadImageData("refImg"); // instead of let refImg = localStorage.getItem('refImg');

请注意,这将仅保存图像数据对象的 data 数组,而不是图像数据对象本身。所以 isMatch 应该变成:

function isMatch(data1, data2) {
  for(let i = 0; i < data1.length; i++) {
    if(data1[i] != data2[i]) {
      return false;
    }
  }

  return true;
}

当您调用 isMatch 时,您只需传递图像数据对象的 data 数组,如下所示:

isMatch(imgData.data, refImg);
// imgData is an actual image data object so we need to access its 'data' property
// refImg is the 'data' array saved to local storage, no need to access 'data' on it