快速3D矩阵重新切片

时间:2014-10-16 02:50:19

标签: javascript webgl

我有一个大小为(X,Y,Z)的3D矩阵,它存储在数据结构中作为 Z 矩阵,每个 X x Y < / em>大小。我想重新切片这些矩阵以获得 X 切片,每个 Y x Z 的大小。换句话说,我想重新组合在YZ平面中存储为XY切片的3D矩阵。用例是将轴向CT图像重新定位为矢状图像。我在浏览器环境中工作。

这是我想要实现的一个例子:

http://ieeexplore.ieee.org/ieee_pilot/articles/06/ttg2009061407/assets/img/article_1/fig_2/large.jpg](series)![enter image description here

我在Python中实现了天真(迭代)解决方案,每个切片需要O(Y * Z)。我甚至不打算写出相应的JavaScript实现,因为这种方法太慢了几个数量级。

import glob
import numpy as np
import matplotlib.pyplot as plt
from scipy.misc import imread

height, width, depth = 512, 512, 100

volume = np.zeros((height, width, depth))

s = 0

for filename in glob.iglob('./*.jpg'):
  volume[:,:,s] =  imread(filename)[...,0]/255.0
  s += 1

reslice = np.zeros((depth, height, width))

for s in xrange(0, width):
  current = np.zeros((depth, height))
  for i in xrange(0, height):
    for j in xrange(0, depth):
      current[j,i] = volume[i,s,j]
  reslice[:,:,s] = current

此算法似乎适合并行化。例如,在CUDA中,可以将3D数据加载到全局存储器中,每个像素创建一个线程,然后针对新方向的每个切片进行迭代,并且在每次迭代时请求正确的像素触发以填充当前切片。这将是一个非常简单的内核,每个切片大约为O(1)。但是,我无法在浏览器中访问CUDA。

从CUDA到WebCL的映射相对简单,但由于不存在供应商支持ATM,因此WebCL是不可能的。因此,我认为WebGL是理想的解决方案。

我不太确定如何在&#34; WebGL&#34;范式,但我确信它可以完成,我怀疑它也是相当微不足道的。然而,我无法找到从哪里开始,而使用OpenGL进行通用计算的资源非常稀少。我将如何使用OpenGL加速浏览器中3D矩阵的重新拼接?

1 个答案:

答案 0 :(得分:3)

您没有必要使用webGL足够快。

如果使用3D阵列,JavaScript可能太慢,但使用 flat 数组,重新制作的时间实际上与创建时间相似阵列!

另一个技巧是使用 类型数组 来减少内存使用并提高性能(Uint8Array)。

我创建了一个小类来处理这样的平面数组并对其进行切片。

我认为你想要的最相关的事实是在(x,y)轴或(y,z)轴上获得 视图

由于数组创建速度很慢,因此您希望在固定缓冲区内构建视图。由于您还需要切片视图,因此您还必须为切片视图创建缓冲区和方法。 它的速度很快:为512X512x100示例创建一个视图, 小于5毫秒! (所以事实上,你以后要做的putImageData会花费更多的时间!)

小提琴在这里:http://jsfiddle.net/n38mwh95/1/

这是处理数据的类,您必须更改构造函数,以便接受真实的原始数据:

function Array3D(xSize, ySize, zSize) {
    this.xSize = xSize;
    this.ySize = ySize;
    this.zSize = zSize;
    var xyMultiplier = xSize * ySize;

    this.array = new Uint8Array(xSize * ySize * zSize);

    this.view = new Uint8Array(xSize * ySize);

    this.slicedView = new Uint8Array(ySize * zSize);

    this.valueAt = function (x, y, z) {
        return this.array[x + xSize * (y + z * ySize)];
    };

    this.setValueAt = function (x, y, z, val) {
        return this.array[x + xSize * (y + z * ySize)] = val;
    };

    this.buildView = function (z) {
        var src = this.array;
        var view = this.view;
        for (var x = 0; x < xSize; x++) {
            for (var y = 0; y < ySize; y++) {
                view[x + xSize * y] = src[x + xSize * (y + z * ySize)];
            }
        }
        return view;
    };

    this.buildSlicedView = function (x) {
        var src = this.array;
        var sView = this.slicedView;
        for (var y = 0; y < ySize; y++) {
            for (var z = 0; z < zSize; z++) {
                sView[y + ySize * z] = src[x + xSize * (y + z * ySize)];
            }
        }
        return sView;
    };
}

使用中:

var xSize = 512;
var ySize = 512;
var zSize = 100;

var t1, t2;

t1 = performance.now();
var testArray = new Array3D(xSize, ySize, zSize);
t2 = performance.now();
console.log('created in :' + (t2 - t1));

t1 = performance.now();
var resliced = testArray.buildView(10);
t2 = performance.now();

console.log('building view in :' + (t2 - t1));

var x = 80;
t1 = performance.now();
var resliced = testArray.buildSlicedView(x);
t2 = performance.now();

console.log('building sliced view in :' + (t2 - t1));

结果:

created in :33.92199998779688 (index):73
building view in :2.7559999871300533 (index):79
building sliced view in :5.726000003051013 

在代码的最后,我还添加了一些代码来渲染视图。

不要忘记缓存画布imageData:只创建一次然后重新使用它以获得最佳性能。

您可以轻松实现实时渲染。