如何让我的 JavaScript 程序 运行 更快?
How can I make my JavaScript programs run faster?
好的,所以我正在研究一种检测系统,我会将摄像头对准屏幕,它必须找到红色物体。我可以用图片成功地做到这一点,但问题是加载需要几秒钟。我希望能够对实时视频执行此操作,因此我需要它来立即找到对象。这是我的代码:
video.addEventListener('pause', function () {
let reds = [];
for(x=0; x<= canvas.width; x++){
for(y=0; y<= canvas.height; y++){
let data = ctx.getImageData(x, y, 1, 1).data;
let rgb = [ data[0], data[1], data[2] ];
if (rgb[0] >= rgb[1] && rgb[0] >=rgb[2] && !(rgb[0]>100 && rgb[1]>100 && rgb[2]>100) && rgb[1]<100 && rgb[2]<100 && rgb[0]>150){
reds[reds.length] = [x, y]
}
let addedx = 0
let addedy = 0
for(i=0; i<reds.length; i++){
addedx = addedx + reds[i][0]
addedy = addedy + reds[i][1]
}
let center = [addedx/reds.length, addedy/reds.length]
ctx.rect(center[0]-5, center[1]-5, 10, 10)
ctx.stroke()
}, 0);
是的,我知道它很乱。 for 循环有什么慢的地方吗?我知道我正在遍历数千个像素,但这是我能想到的唯一方法。
我会 运行 webassembly 模块中的检测算法。因为它只是像素数据,所以就在它的胡同里。
然后您可以将单个帧传递给 wasm 模块的不同实例。
就直接回答您的问题而言,我会抓取整个帧,而不是一次抓取 1 个像素,否则您可能会从不同的帧中采样像素。然后你可以将该框架提交给一个工作人员,你甚至可以将框架分开并将它们发送给不同的工作人员(或如前所述的 wasm 模块)
此外,由于您有一个数组,因此您可以使用 Arrray.map 和 Array.reduce 通过测试相邻像素而不是所有比较来获取红色值以及它们的大小.不确定它是否会更快但值得一试。
对于速度,你应该考虑你所有的过程:
- 您的语言越接近机器语言,您的结果就会越好。这么说吧,C++更适合算法。
- CPU 速度是你的朋友。在 Atom 处理器或 i7 处理器上启动代码就像白天和黑夜一样。此外,某些类型的处理器专用于视觉,例如 VPU
对于您的代码:
- 您尝试重写已经存在的代码。您可以在出色的 OpenCV 库中找到很好的检测示例:https://www.learnopencv.com/invisibility-cloak-using-color-detection-and-segmentation-with-opencv
希望对你有帮助:)
如前所述,Javascript 不是这项任务的最佳表现。但是,我注意到了一些可能会减慢您速度的事情。
您一次抓取一个像素的图像数据。由于此方法可以return整帧,所以一次即可。
优化您的 isRed 条件:
rgb[0] >= rgb[1] && // \
rgb[0] >= rgb[2] && // >-- This is useless
!(rgb[0] > 100 && rgb[1] > 100 && rgb[2] > 100) && // /
rgb[1] < 100 && // \
rgb[2] < 100 && // >-- These 3 conditions imply the others
rgb[0] > 150 // /
您在每个像素后计算 for
循环中的 center
,但只有在处理整个帧后才有意义。
由于视频源来自相机,也许您不需要查看每个像素。也许每 5 个像素就足够了?这就是下面的例子所做的。调整一下。
包含这些优化的演示
节点:此演示包括对 this answer 中代码的改编,以将视频复制到 canvas。
const video = document.getElementById("video"),
canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");
let width,
height;
// To make this demo work
video.crossOrigin = "Anonymous";
// Set canvas to video size when known
video.addEventListener("loadedmetadata", function() {
width = canvas.width = video.videoWidth;
height = canvas.height = video.videoHeight;
});
video.addEventListener("play", function() {
const $this = this; // Cache
(function loop() {
if (!$this.paused && !$this.ended) {
ctx.drawImage($this, 0, 0);
const reds = [],
data = ctx.getImageData(0, 0, width, height).data,
len = data.length;
for (let i = 0; i < len; i += 5 * 4) { // 4 because data is made of RGBA values
const rgb = data.slice(i, i + 3);
if (rgb[0] > 150 && rgb[1] < 100 && rgb[2] < 100) {
reds.push([i / 4 % width, Math.floor(i / 4 / width)]); // Get [x,y] from i
}
}
if (reds.length) { // Can't divide by 0
const sums = reds.reduce(function (res, point) {
return [res[0] + point[0], res[1] + point[1]];
}, [0,0]);
const center = [
Math.round(sums[0] / reds.length),
Math.round(sums[1] / reds.length)
];
ctx.strokeStyle = "blue";
ctx.lineWidth = 10;
ctx.beginPath();
ctx.rect(center[0] - 5, center[1] - 5, 10, 10);
ctx.stroke();
}
setTimeout(loop, 1000 / 30); // Drawing at 30fps
}
})();
}, 0);
video, canvas { width: 250px; height: 180px; background: #eee; }
<video id="video" src="https://shrt-statics.s3.eu-west-3.amazonaws.com/redball.mp4" controls></video>
<canvas id="canvas"></canvas>
好的,所以我正在研究一种检测系统,我会将摄像头对准屏幕,它必须找到红色物体。我可以用图片成功地做到这一点,但问题是加载需要几秒钟。我希望能够对实时视频执行此操作,因此我需要它来立即找到对象。这是我的代码:
video.addEventListener('pause', function () {
let reds = [];
for(x=0; x<= canvas.width; x++){
for(y=0; y<= canvas.height; y++){
let data = ctx.getImageData(x, y, 1, 1).data;
let rgb = [ data[0], data[1], data[2] ];
if (rgb[0] >= rgb[1] && rgb[0] >=rgb[2] && !(rgb[0]>100 && rgb[1]>100 && rgb[2]>100) && rgb[1]<100 && rgb[2]<100 && rgb[0]>150){
reds[reds.length] = [x, y]
}
let addedx = 0
let addedy = 0
for(i=0; i<reds.length; i++){
addedx = addedx + reds[i][0]
addedy = addedy + reds[i][1]
}
let center = [addedx/reds.length, addedy/reds.length]
ctx.rect(center[0]-5, center[1]-5, 10, 10)
ctx.stroke()
}, 0);
是的,我知道它很乱。 for 循环有什么慢的地方吗?我知道我正在遍历数千个像素,但这是我能想到的唯一方法。
我会 运行 webassembly 模块中的检测算法。因为它只是像素数据,所以就在它的胡同里。
然后您可以将单个帧传递给 wasm 模块的不同实例。
就直接回答您的问题而言,我会抓取整个帧,而不是一次抓取 1 个像素,否则您可能会从不同的帧中采样像素。然后你可以将该框架提交给一个工作人员,你甚至可以将框架分开并将它们发送给不同的工作人员(或如前所述的 wasm 模块)
此外,由于您有一个数组,因此您可以使用 Arrray.map 和 Array.reduce 通过测试相邻像素而不是所有比较来获取红色值以及它们的大小.不确定它是否会更快但值得一试。
对于速度,你应该考虑你所有的过程:
- 您的语言越接近机器语言,您的结果就会越好。这么说吧,C++更适合算法。
- CPU 速度是你的朋友。在 Atom 处理器或 i7 处理器上启动代码就像白天和黑夜一样。此外,某些类型的处理器专用于视觉,例如 VPU
对于您的代码:
- 您尝试重写已经存在的代码。您可以在出色的 OpenCV 库中找到很好的检测示例:https://www.learnopencv.com/invisibility-cloak-using-color-detection-and-segmentation-with-opencv
希望对你有帮助:)
如前所述,Javascript 不是这项任务的最佳表现。但是,我注意到了一些可能会减慢您速度的事情。
您一次抓取一个像素的图像数据。由于此方法可以return整帧,所以一次即可。
优化您的 isRed 条件:
rgb[0] >= rgb[1] && // \
rgb[0] >= rgb[2] && // >-- This is useless
!(rgb[0] > 100 && rgb[1] > 100 && rgb[2] > 100) && // /
rgb[1] < 100 && // \
rgb[2] < 100 && // >-- These 3 conditions imply the others
rgb[0] > 150 // /
您在每个像素后计算
for
循环中的center
,但只有在处理整个帧后才有意义。由于视频源来自相机,也许您不需要查看每个像素。也许每 5 个像素就足够了?这就是下面的例子所做的。调整一下。
包含这些优化的演示
节点:此演示包括对 this answer 中代码的改编,以将视频复制到 canvas。
const video = document.getElementById("video"),
canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");
let width,
height;
// To make this demo work
video.crossOrigin = "Anonymous";
// Set canvas to video size when known
video.addEventListener("loadedmetadata", function() {
width = canvas.width = video.videoWidth;
height = canvas.height = video.videoHeight;
});
video.addEventListener("play", function() {
const $this = this; // Cache
(function loop() {
if (!$this.paused && !$this.ended) {
ctx.drawImage($this, 0, 0);
const reds = [],
data = ctx.getImageData(0, 0, width, height).data,
len = data.length;
for (let i = 0; i < len; i += 5 * 4) { // 4 because data is made of RGBA values
const rgb = data.slice(i, i + 3);
if (rgb[0] > 150 && rgb[1] < 100 && rgb[2] < 100) {
reds.push([i / 4 % width, Math.floor(i / 4 / width)]); // Get [x,y] from i
}
}
if (reds.length) { // Can't divide by 0
const sums = reds.reduce(function (res, point) {
return [res[0] + point[0], res[1] + point[1]];
}, [0,0]);
const center = [
Math.round(sums[0] / reds.length),
Math.round(sums[1] / reds.length)
];
ctx.strokeStyle = "blue";
ctx.lineWidth = 10;
ctx.beginPath();
ctx.rect(center[0] - 5, center[1] - 5, 10, 10);
ctx.stroke();
}
setTimeout(loop, 1000 / 30); // Drawing at 30fps
}
})();
}, 0);
video, canvas { width: 250px; height: 180px; background: #eee; }
<video id="video" src="https://shrt-statics.s3.eu-west-3.amazonaws.com/redball.mp4" controls></video>
<canvas id="canvas"></canvas>