是否有任何javascript库可以使用HTML5画布(或任何其他方式)对图像进行像素比较

Are there any javascript libs to pixel compare images using HTML5 canvas (or any other means)?

本文关键字:任何 图像 方式 比较 像素 其他 javascript 可以使 是否 画布 HTML5      更新时间:2023-09-26

想要测试两个图像的像素是否匹配和/或较小大小的图像像素是否匹配较大图像的某个相同大小的部分。

第一部分,测试两个图像是否完美匹配,非常直接。

//assuming data1,data2 are canvas data of images of the same size
function isMatch(data1,data2){
    for(var i = 0; i<data1.length; i++){
        if(data1[i] != data2[i]) return false;
    }
    return true;
}

只要你不使用超高分辨率的图像,我就不会担心速度,像这样的画布数据的一次迭代通常非常快,在一台配有现代浏览器的像样电脑上,每兆像素约10ms。请记住,图像的宽度和高度必须相同。对于大多数实际目的来说,这种"是"或"否"匹配不是很有用,编码错误会导致图像发生难以察觉的变化,最好使用度量来测量两个图像之间的距离。一个简单但相当有效的度量是两个图像的RMS:

//assuming data1,data2 are canvas data of images of the same size
function rmsDiff(data1,data2){
    var squares = 0;
    for(var i = 0; i<data1.length; i++){
        squares += (data1[i]-data2[i])*(data1[i]-data2[i]);
    }
    var rms = Math.sqrt(squares/data1.length);
    return rms;
}

这里,如果图像完全匹配,rms的范围将从[0255]到0,如果一个图像都是rgba(0,0,0,0),而另一个图像全是rgba,则rms的范围为255(255255255)。如何设定可接受的均方根差的阈值取决于您的调整(可能在1到2左右)。

对于问题的第二部分,较小尺寸的图像像素是否与较大图像的相同尺寸部分匹配,我假设它们没有被翻译,并且我们知道两者之间的比例差异,否则这将导致一个非常复杂和有趣的问题,请参阅模式识别。然后,根据这个令人震惊的假设,我会使用画布将大图像缩小到与小图像完全相同的大小,并获得它们之间的rms。不要测试精确匹配,因为缩放总是会产生轻微错误,而且永远不会是精确匹配。

我不知道是否有类似的东西,我也不确定网络浏览器是否适合做这件事……然而,我一直在玩画布元素,并想分享我的想法。也许你在里面找到了有用的东西。

以下代码仅在Chrome 20中测试。您必须从同一域加载图像和脚本,否则它将不起作用(我没有找到在jsfiddle上上传图像的方法,所以我没有把它放在那里)

为了测试两个图像是否相同,我加载每个图像并将其绘制在画布元素上,并比较宽度、高度和像素数据。

index.html

<!doctype html>
<html>
  <head>
    <title></title>
    <script type="application/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
    <script type="application/javascript" src="compare.js"></script>
  </head>
  <body>
  </body>
</html>

相同的像素compare.js

$(function(){
  function compare(url_a, url_b, callback) {
    var canvas = $("<canvas>").appendTo(document.body)[0];
    var ctx = canvas.getContext('2d');
    var
      image_a = new Image(),
      image_b = new Image();
    function getData(image) {
      //Set canvas to the same size as the image
      canvas.width = image.width;
      canvas.height = image.height;
      //Draw the image on the canvas
      ctx.drawImage(image, 0, 0);
      //Get the image data from the canvas.
      return ctx.getImageData(0,0,canvas.width,canvas.height);
    }
    function compareIdentical(A,B) {
      // Check sizes
      if (
        A.width != B.width ||
        A.height != B.height ||
        A.data.length != B.data.length
      ) {
        return callback(false,'Not same size');
      }
      var a=A.data; b=B.data;
      //Check image data
      for (var idx=0, len=A.data.length; idx < len; ++idx) {
        if (a[idx] !== b[idx]) {
          return callback(false,'Not same');
        }
      }
      return callback(true,'Identical');
    }
    $(image_a).load(function(){
      console.log('Image A loaded');
      image_a = getData(image_a);
      //Load image b
      image_b.src = url_b;
    });
    $(image_b).load(function(){
      console.log('Image B loaded');
      image_b = getData(image_b);
      canvas.parentNode.removeChild(canvas);
      compareIndentical(image_a, image_b);
      //comparePartOf(image_a, image_b);
    });
    //Load image a
    image_a.src = url_a;
  }
  //IMPORTANT: Images must be loaded from the same domain as the
  //script, or getImageData will not give any data.
  compare(
    'IMG_2195.JPG',
    'IMG_2195.JPG',
    function(result,message){
      console.log(result,message);
    }
  );
});

要测试一个图像是否是另一个图像的一部分,是慢速

  • 比较大小:如果"小">"大",则它不可能是"大"的一部分
  • 对于BIG的每个x和y,比较SMALL是否适合

(将此添加到上面的代码中,并在加载图像B后更改调用)

    function comparePartOf(bigimg, smallimg) {
      if (
        bigimg.width < smallimg.width ||
        bigimg.height < smallimg.height ||
        bigimg.data.length < smallimg.data.length
      ) {
        return callback(false,'bigimg smaller than smallimg');
      }
      var
        big=bigimg.data,
        small=smallimg.data,
        idx,
        len=small.length/4,
        big_x,
        big_y,
        big_width = bigimg.width,
        big_height = bigimg.height,
        small_x,
        small_y,
        small_width = smallimg.width,
        small_height = smallimg.height,
        lenw = big_width - small_width,
        lenh = big_height - small_height,
        big_offset,
        small_offset,
        result=false;
      for(big_x=0; big_x < lenw; ++big_x) {
        for(big_y=0; big_y < lenh; ++big_y) {
          result = true;
          for (idx=0; idx < len; ++idx) {
            small_x = idx % small_width;
            small_y = Math.floor(idx / small_width);
            big_offset = (
              (big_x + small_x) + ((big_y + small_y) * big_width)
            )<<2;
            small_offset = idx << 2;
            if (
              big[big_offset++] != small[small_offset++] ||
              big[big_offset++] != small[small_offset++] ||
              big[big_offset++] != small[small_offset++] ||
              big[big_offset] != small[small_offset]
            ) {
              result = false;
              break;
            }
          }
          if (result) return callback(true,'Found at '+big_x+' '+big_y);
        }
      }
      return callback(false,'not found');
    }

您可以尝试paper.js

它允许您将图像用作光栅

http://paperjs.org/tutorials/images/using-pixel-colors/http://paperjs.org/tutorials/images/working-with-rasters/

paper.js库绝对可以做的不仅仅是比较图像。。

这里有一个简单的脚本。。

            // rasterize both images
        var im_a = new Raster('image_a');
        var im_b = new Raster('image_b');
        var ratio = Math.round(im_a.width / 100);
        // downsize the images so we don't have to loop through all the pixels.
        im_a.size = new Size(im_a.width/ratio, im_a.height/ratio);
        im_b.size = new Size(im_b.width/ratio, im_b.height/ratio);
        //hide the images, so they don't display on the canvas
        im_a.visible = false;
        im_b.visible = false;
        var different = false;
        // check the image dimensions
        if((im_a.width == im_b.width) && (im_a.height == im_b.height)){
            // loop through the pixels
            for(x = 0 ; x < im_a.width ; x++){
                for(y = 0; y < im_a.height; y++){
                    if(im_a.getPixel(x,y) != im_b.getPixel(x,y) ){
                        different = true;
                        break;
                    }
                }
            }

        }else{
            alert('not the same size');
        }

好吧,我不知道是否有什么原因,所以我试试:

首先,你需要一个函数来比较两个数组,你会在网上的任何地方找到一个;类似:

function arrayCompare(){
   if (x === y)
      return true;            
   if (x.length != y.length)
      return false;
   for (key in x) {
      if (x[key] !== y[key]) {
         return false;
      }
   }
   return true;
}

然后创建一个函数,返回图像的imageData:

function getImgData(imgUrl){
   var img = new Image();   // Create new img element
   img.src = '../img/logo.png'; // params are urls for the images
   var canvas = document.createElement('canvas');
   canvas.width = img.width;
   canvas.height = img.height;
   var ctx = canvas.getContext('2d');
   ctx.drawImage(img1,0,0);
   var imageData = ctx.getImageData(0,0,canvas.width, canvas.height);
   //returns an array, see documentation for * info
   return imageData.data;
}

然后你可以做一些类似的事情:

var imgData1 = getImgData('../img/image1');
var imgData2 = getImgData('../img/image2');
if(arrayCompare(imgData1, imgData2)){
   //images are the same;
} else {
   // images are different
}

现在,这涵盖了问题的第一部分。我相信,只要付出一点努力,你就可以在网上找到一个函数来比较两个数组,找出其中一个数组是否包含在另一个数组中。

最后要指出的是,我知道这些阵列非常非常大,而且这种实现可能不具有成本效益。我知道这个问题可能有更好的解决方案,但这是我能想到的。我相信你可以在周围找到更有效的数组比较器,也许我的方法在我不知道的其他方法附近是完全无用的。

Resemble.js是一个用于图像比较的JavaScript库。来自GitHub描述:

类似的.js可以用于任何图像分析和比较浏览器中可能存在的要求。然而为PhantomJS驱动的视觉回归而设计和构建库PhantomCSS。PhantomCSS需要能够忽略抗锯齿因为这会导致源于不同的机器。

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

假设数据1&data2是数组。可以使用canvasPixelArray

var same = [];
same = data1 && data2;
if (same.length < data1.length) {
   //your code here
}