JavaScript画布:如何高效计算两个画布的距离

3

我希望计算出在两个画布上绘制的两个图形之间的距离,实际上我正在做以下操作:迭代遍历两个画布的数据(这两个画布具有相同的大小):

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通道,因为我现在使用的是黑白图像,但以后可能会考虑其他通道。

1个回答

1
你可以在单个画布上使用新的“差异”混合方法,在最后一次绘制之前设置模式,然后将两个图像一起绘制,然后提取位图数据以获取总和。
你将使用相同的属性“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>


谢谢,我之前不知道复合操作的区别。这让我的代码稍微加快了一些。使用Chrome进行分析显示,drawImage()和getImageData()现在是瓶颈。 - danielenick89
@danielenick89 是的,不幸的是没有绕过这些的方法。根据您需要结果的准确程度,您可以在获取数据之前将图像预先缩小到较小的尺寸。它们仍然可能成为瓶颈,但至少数据会更快地到达。 - user1693593

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接