首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在JavaScript中计算画布数组中值的有效方法

在JavaScript中计算画布数组中值的有效方法
EN

Stack Overflow用户
提问于 2020-12-09 21:13:36
回答 1查看 428关注 0票数 4

我有一个来自视频N帧的N HTMLCanvasElements数组,我想计算“中间画布”,即每个像素的每个组件(r、g、b、不透明度)都是所有画布中相应组件的中值。

视频帧为1280x720,因此每个画布(用canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height).data获得)的像素数据是长度为3.686.400的Uint8ClampedArray。

计算中位数的天真方法是:

  • 制备长度为3.686.400的结果Uint8ClampedArray
  • 制备长度为N的临时Uint8ClampedArray
  • 循环从0到3.686.399
    • ( a)在N个画布上循环以填充数组
    • ( b)计算数组的中值
    • ( c)将中值存储到结果数组

但它很慢,甚至四张画布也是如此。

是否有一种有效的方法(或现有的代码)来做到这一点?我的问题非常类似于查找图像列表的中值,但我需要使用JavaScript,而不是Python。

注意:对于b),我使用的是D3.中位数(),据我所知,它不适用于类型化数组,因此它意味着转换为数字,然后再转换为Uint8Clamped。

注2:我对GLSL着色器不太了解,但也许使用GPU是获得更快结果的一种方法。但是,这需要将数据从CPU传递到GPU,如果重复的话,这需要时间。

注3:天真的解决方案就在那里:https://observablehq.com/@severo/compute-the-approximate-median-image-of-a-video

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-12-11 16:49:21

你写的

我使用d3.median(),它不适用于类型化数组,…

虽然这并不是完全正确的,但它导致了正确的方向。在内部,d3.median()使用d3.quantile()方法,该方法开头如下:

代码语言:javascript
运行
复制
export default function quantile(values, p, valueof) {
  values = Float64Array.from(numbers(values, valueof));

正如您所看到的,这实际上是使用类型化数组,它不是您的Uint8ClampedArray,而是一个Float64Array。由于浮点算法比其整数对应算法(包括转换本身)要高得多的计算量,这对代码的性能有着巨大的影响。在一个紧密的循环中这样做大约300万次,会降低解决方案的效率。

但是,由于您正在从Uint8ClampedArray检索所有像素值,所以您可以确保始终在处理整数。也就是说,构建一个从d3.median()d3.quantile()派生的自定义d3.quantile()相当容易。

代码语言:javascript
运行
复制
function median(values) {
  // No conversion to floating point values needed.
  if (!(n = values.length)) return;
  if (n < 2) return d3.min(values);
  var n,
      i = (n - 1) * 0.5,
      i0 = Math.floor(i),
      value0 = d3.max(d3.quickselect(values, i0).subarray(0, i0 + 1)),
      value1 = d3.min(values.subarray(i0 + 1));
  return value0 + (value1 - value0) * (i - i0);
}

除了消除第一行存在问题的转换之外,该实现还应用了一些更小的优化,因为在您的情况下,您总是在寻找2分位数(即中位数)。这在一开始似乎并不多,但在一个循环中执行这个数百万次确实会产生不同的效果。

只要对您自己的代码进行最小的更改,您就可以这样称呼它:

代码语言:javascript
运行
复制
// medianImageData.data[i] = d3.median(arr);   Instead of this use line below.
medianImageData.data[i] = median(arr);

看看我的工作分叉你的可观察笔记本。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65224990

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档