是否有任何javascript库使用HTML5画布(或任何其他方法)像素比较图像?

时间:2012-02-03 22:45:06

标签: javascript html5 canvas

想测试2个图像像素是否匹配和/或较小尺寸的图像像素是否与较大图像的相同尺寸部分匹配。

7 个答案:

答案 0 :(得分:9)

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

//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;
}

只要你没有使用非常高分辨率的图像我就不用担心速度了,像这样的一次迭代通常非常快,每台Mega Pixel在带有现代浏览器的不错的计算机上大约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范围为[0,255],如果图像完全匹配则为0,如果一个图像全部为rgba(0,0,0,0)则为255,另一个为rgba(255,255,255,255)。如何阈值可接受的均方根差异取决于您调整(可能大约为1到2)。

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

答案 1 :(得分:4)

答案 2 :(得分:3)

我不知道那里是否有类似的东西,我不确定网络浏览器是否适合这样做...但是我一直在玩一下canvas元素并想分享我的想法。也许你会发现一些有用的东西。

以下代码仅在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);
    }
  );

});

要测试图像是否属于另一张图像,很慢

  • 比较大小:如果小于&gt;那么它就不能成为BIG的一部分。
  • 对于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');
    }

答案 3 :(得分:2)

你可以试试paper.js

它允许您将图像用作栅格

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

paper.js lib绝对可以比比较图像更多。

这是一个有效的简单脚本..

            // 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');
        }

答案 4 :(得分:1)

好吧,我不知道那里有什么东西,所以我会试一试:

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

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
}

现在,这涵盖了问题的第一部分。我确信只需要一点点努力,你就可以在网上找到一个函数来比较2个数组,看看是否有另一个数据包含。

作为最后一点,我知道这些阵列非常庞大,而且这种实现可能不符合成本效益。我知道这个问题可能有更好的解决方案,但这是我能想到的。我相信你可以找到更有效的阵列比较器,也许我的方法在我不知道的其他人附近完全没用。

答案 5 :(得分:1)

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

  

Resemble.js可用于任何图像分析和比较   您可能在浏览器中的要求。但是,它一直都是   设计和构建供PhantomJS驱动的视觉回归使用   库 PhantomCSS 。 PhantomCSS需要能够忽略抗锯齿   因为这会导致从中导出的屏幕截图之间存在差异   不同的机器。

     

Resemble.js使用HTML5 File API来解析图像数据和画布   用于渲染图像差异。

答案 6 :(得分:0)

假设data1&amp; data2是数组。你可以使用canvasPixelArray(s)

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