Javascript画布:如何有效地计算两个画布的距离

Javascript canvas: how to efficiently compute distance of two canvases

本文关键字:两个 距离 画布 计算 有效地 Javascript      更新时间:2023-09-26

我想计算两个画布中绘制的两个图形之间的距离,实际上我正在做以下操作,迭代画布的数据(画布大小相同):

var computeDifference = function() {
    var imgd1 = bufferCtx.getImageData(0, 0, w, h).data;
    var imgd2 = targetCtx.getImageData(0, 0, w, h).data;
    var diff = 0;
    for(var i=0; i<imgd1.length; i+=4) {
        var d = (imgd1[i]-imgd2[i]);
        var tot = d > 0 ? d : -d;
        diff += tot
    }
    return diff;
}

这不是很有效。

有更好的方法吗?我读过关于复合运算的文章,但我不确定这在这种情况下是否有帮助。

我特意只考虑R频道,因为目前我使用的是黑白图像,但我稍后可能会考虑其他频道。

您可以在单个画布上使用新的difference混合方法,在上次绘制之前设置的模式下绘制两个图像,然后提取位图数据以获得总和。

可以使用相同的属性globalCompositeOperation来设置混合模式。

通过这种方式,您可以让浏览器完成计算每个零部件差异的初始工作,只需对它们进行汇总。您还节省了一个画布,一个对getImageData()的调用,这在硬件加速系统上相对昂贵:

ctx.drawImage(image1, x, y);
ctx.globalCompositeOperation = "difference";  // use composite to set blending...
ctx.drawImage(image2, x, y);
// extract data, and sum -

注意:IE11不支持新的混合模式。对于IE,您需要像最初一样手动进行差异计算。

您可以通过在支持时提供快速方法来检测这一点,在不支持时提供手动方法:

ctx.globalCompositeOperation = "difference";
if (ctx.globalCompositeOperation === "difference") {
    // fast
}
else {
    // manual
}

现场性能测试

测试1将进行手动差异计算,测试2将使用浏览器差异混合模式。在我的设置中,FireFox以超过4倍的优势获胜(Chrome中的差异稍小)。

var canvas1 = document.createElement("canvas"),
    canvas2 = document.createElement("canvas"),
    ctx1 = canvas1.getContext("2d"),
    ctx2 = canvas2.getContext("2d"),
    img1 = new Image, img2 = new Image,
    count = 2,
    startTime1, startTime2, endTime1, endTime2, sum1, sum2;
performance = performance || Date;               // "polyfill" the performance object
img1.crossOrigin = img2.crossOrigin = "";        // we need to extract pixels
img1.onload = img2.onload = loader;
img1.src = "http://i.imgur.com/TJiD5GM.jpg";
img2.src = "http://i.imgur.com/s9ksOb1.jpg";
function loader() {if(!--count) test1()}         // handle async load
function test1(){
  startTime1 = performance.now();
  
  ctx1.drawImage(img1, 0, 0);
  ctx2.drawImage(img2, 0, 0);
  
  var data1 = ctx1.getImageData(0, 0, 500, 500).data,
      data2 = ctx2.getImageData(0, 0, 500, 500).data,
      i = 0, len = data1.length, sum = 0;
  
  // we do all channels except alpha channel (not used in difference calcs.)
  while(i < len) {
    sum += Math.abs(data2[i] - data1[i++]) + 
           Math.abs(data2[i] - data1[i++]) + 
           Math.abs(data2[i] - data1[i++]); 
    i++
  }
  
  sum1 = sum;
  endTime1 = performance.now();
  test2();
}
function test2(){
  startTime2 = performance.now();
  
  ctx1.drawImage(img1, 0, 0);
  ctx1.globalCompositeOperation = "difference";
  if (ctx1.globalCompositeOperation !== "difference")
    alert("Sorry, use Firefox or Chrome");
  ctx1.drawImage(img2, 0, 0);
  
  var data = ctx1.getImageData(0, 0, 500, 500).data,
      i = 0, len = data.length, sum = 0;
  
  // we do all channels except alpha channel
  while(i < len) {
    sum += data[i++];
    sum += data[i++];
    sum += data[i++];
    i++;
  }
  sum2 = sum;      
  endTime2 = performance.now();
  result();
}
function result() {
  var time1 = endTime1 - startTime1,
      time2 = endTime2 - startTime2,
      factor = time1 / time2,
      res = "Manual method: " + time1.toFixed(3) + "ms<br>";
  res += "Blending mode: " + time2.toFixed(3) + "ms<br>";
  res += "Factor: " + factor.toFixed(2) + "x<br>";
  res += "Sum 1 = " + sum1;
  res += "<br>Sum 2 = " + sum2;
  document.querySelector("output").innerHTML = res;
}
<output>Loading images and calculating...</output>