【问题标题】:How can I make my JavaScript programs run faster?如何让我的 JavaScript 程序运行得更快?
【发布时间】:2020-02-20 19:09:06
【问题描述】:

好的,所以我正在开发一种检测系统,我将把相机对准屏幕,它必须找到红色物体。我可以用图片成功地做到这一点,但问题是加载需要几秒钟。我希望能够对实时视频执行此操作,因此我需要它来立即找到对象。这是我的代码:

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 循环是否有一些缓慢的地方?我知道我正在循环数千个像素,但这是我能想到的唯一方法。

【问题讨论】:

  • 似乎您想从 getImageData 中获取整个图像数据集,而不是一次获取 1 个像素。
  • 这可行...我会试试看。

标签: javascript performance loops for-loop webcam


【解决方案1】:

如前所述,Javascript 并不是这项任务中性能最好的。但是,我注意到了一些事情,这可能会减慢您的速度。

  1. 您一次抓取一个像素的图像数据。由于此方法可以返回整个帧,因此您可以这样做一次。

  2. 优化您的 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    // /
  1. 您在每个像素之后计算for 循环内的center,但只有在处理整个帧之后才有意义。

  2. 由于视频源来自摄像头,因此您可能不需要查看每个像素。也许每 5 个像素就足够了?这就是下面的示例所做的。调整一下。

包含这些优化的演示

节点:此演示包含对来自this answer 的代码的改编,用于将视频复制到画布上

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>

【讨论】:

  • 谢谢!非常非常彻底。
【解决方案2】:

我会在 webassembly 模块中运行检测算法。既然只是像素数据,那就顺其自然吧。

然后您可以将单个帧传递给 wasm 模块的不同实例。

就直接回答您的问题而言,我会抓取整个帧,而不是一次抓取 1 个像素,或者您可能会从不同帧中采样像素。然后,您可以将该框架提交给工作人员,您甚至可以分割框架并将它们发送给不同的工作人员(或如前所述的 wasm 模块)

此外,由于您有一个数组,您可以使用 Array.map 和 Array.reduce 通过测试相邻像素而不是所有比较来获取红色值以及它们的大小。不确定它是否会更快,但值得一试。

【讨论】:

    【解决方案3】:

    为了速度,你应该考虑你的所有过程:

    • 您的语言越接近机器语言,您的结果就会越好。这么说来,C++ 更适合算法。
    • CPU 速度是你的朋友。在 Atom 处理器或 i7 处理器上启动您的代码就像白天和黑夜一样。此外,某些类型的处理器专用于视觉,例如VPU

    对于您的代码:

    希望对你有帮助:)

    【讨论】:

    • 谢谢,我会调查的。不值得学习一门全新的编程语言。不知道java是否更接近机器语言?
    • @JonathanTheMann 抱歉,java 是最糟糕的之一:-/ (raid6.com.au/~onlyjob/posts/arena)。你可以保留JS,看看opencv的源码,​​翻译成JS。你一定会拥有最好的代码:-)
    猜你喜欢
    • 2017-03-19
    • 2018-08-23
    • 2021-05-11
    • 1970-01-01
    • 2017-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-28
    相关资源
    最近更新 更多