Javascript canvas: 如何有效地计算两个 canvases 的距离
Javascript canvas: how to efficiently compute distance of two canvases
我想计算在两个画布上绘制的两个图形之间的距离,实际上我正在执行以下操作,遍历画布的数据(画布具有相同的大小):
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 通道,因为目前我正在处理黑白图像,但我可能稍后会考虑其他通道。
您可以在单个 canvas 上使用新的 difference
混合 方法,在最后一次绘制之前设置的模式下绘制两个图像,然后提取位图数据以获得总和。
您将使用相同的 属性、globalCompositeOperation
来设置混合模式。
这样您就可以让浏览器完成初始工作,计算每个组件的差异,让您只对它们求和。您还节省了一次 canvas,一次对 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>
我想计算在两个画布上绘制的两个图形之间的距离,实际上我正在执行以下操作,遍历画布的数据(画布具有相同的大小):
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 通道,因为目前我正在处理黑白图像,但我可能稍后会考虑其他通道。
您可以在单个 canvas 上使用新的 difference
混合 方法,在最后一次绘制之前设置的模式下绘制两个图像,然后提取位图数据以获得总和。
您将使用相同的 属性、globalCompositeOperation
来设置混合模式。
这样您就可以让浏览器完成初始工作,计算每个组件的差异,让您只对它们求和。您还节省了一次 canvas,一次对 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>