我有一个来自视频N帧的N HTMLCanvasElements数组,我想计算“中间画布”,即每个像素的每个组件(r、g、b、不透明度)都是所有画布中相应组件的中值。
视频帧为1280x720,因此每个画布(用canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height).data获得)的像素数据是长度为3.686.400的Uint8ClampedArray。
计算中位数的天真方法是:
但它很慢,甚至四张画布也是如此。
是否有一种有效的方法(或现有的代码)来做到这一点?我的问题非常类似于查找图像列表的中值,但我需要使用JavaScript,而不是Python。
注意:对于b),我使用的是D3.中位数(),据我所知,它不适用于类型化数组,因此它意味着转换为数字,然后再转换为Uint8Clamped。
注2:我对GLSL着色器不太了解,但也许使用GPU是获得更快结果的一种方法。但是,这需要将数据从CPU传递到GPU,如果重复的话,这需要时间。
注3:天真的解决方案就在那里:https://observablehq.com/@severo/compute-the-approximate-median-image-of-a-video
发布于 2020-12-11 16:49:21
你写的
我使用
d3.median(),它不适用于类型化数组,…
虽然这并不是完全正确的,但它导致了正确的方向。在内部,d3.median()使用d3.quantile()方法,该方法开头如下:
export default function quantile(values, p, valueof) {
  values = Float64Array.from(numbers(values, valueof));正如您所看到的,这实际上是使用类型化数组,它不是您的Uint8ClampedArray,而是一个Float64Array。由于浮点算法比其整数对应算法(包括转换本身)要高得多的计算量,这对代码的性能有着巨大的影响。在一个紧密的循环中这样做大约300万次,会降低解决方案的效率。
但是,由于您正在从Uint8ClampedArray检索所有像素值,所以您可以确保始终在处理整数。也就是说,构建一个从d3.median()和d3.quantile()派生的自定义d3.quantile()相当容易。
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分位数(即中位数)。这在一开始似乎并不多,但在一个循环中执行这个数百万次确实会产生不同的效果。
只要对您自己的代码进行最小的更改,您就可以这样称呼它:
// medianImageData.data[i] = d3.median(arr);   Instead of this use line below.
medianImageData.data[i] = median(arr);看看我的工作分叉你的可观察笔记本。
https://stackoverflow.com/questions/65224990
复制相似问题