1

我面临的问题是分割大型数据集(高达 2048x2048x40x10000 x,y,z,t == 解压后的几 TB,给予或接受)。从好的方面来说,这个数据集中的特征相当小。最大 20x20x20x20 左右。

据我所知,没有开箱即用的解决方案(如果我错了,请纠正我)。我有一些如何解决这个问题的计划,但我希望得到您的反馈。

一个时间片最大约为 600mb;在典型情况下较少;我可以在我的 4gb 内存中保存一堆连续的此类切片。

鉴于我的特征尺寸很小,我的直觉告诉我最好避开所有形式的分割技巧,而只需对我的标签进行局部迭代的类似洪水填充的更新;如果你的邻居有更高的标签,复制它;迭代直到收敛。迭代次数应受任何维度中的最大集群大小的限制,这又应该很小。

CUDA 天生偏爱 3D,所以我可以分两步进行;迭代所有尚未收敛的 3d 体积切片。然后简单地对所有连续的时间片进行元素循环,并执行相同的填充逻辑。

我可以用一个简单的递增唯一计数器来初始化迭代,或者先找到局部最大值,然后在那里种子标签。后者是首选,所以我可以保留一个按标签索引的数组来存储所有区域的 x、y、z、t 最小/最大范围(也可以作为后处理)。如果一个区域没有扩展到最新的时间片,它会从数据中删除,并将其位置写入数据库。如果尾随时间片以这种方式完全耗尽(总和为零),请将其从内存中删除。(或者如果内存溢出,也删除最新的;必须忍受这样的近似值)

看起来应该可以。考虑到 z 维度的大小有限,您认为是启动 x,y,z 线程块,还是启动 x,y 块并让每个线程在 z 维度上循环更好?这是一种“试试看”的事情,还是有一个答案?

我刚刚想到的另一个优化;如果我将一个 x,y,z 块加载到共享内存中,那么在我获得内存的同时执行几次洪水填充更新会不会更快?也许最好让本地内存迭代到收敛,然后继续……我想这与上述问题有关。单个邻居最大查找可能是次优的计算强度,因此在 z 上循环或迭代多次应该可以抵消这一点。我觉得我更喜欢后者。

另一个问题; 似乎不存在类似的东西,但是包含执行类似操作的模板代码的项目的链接将受到高度赞赏(优化的 3d 洪水填充代码?),因为我的 CUDA 知识仍然参差不齐。

提前感谢您的想法和反馈!

4

3 回答 3

1

作为记录; 我得到了一个令我满意的可行解决方案;当它更成熟时,可能会在某个地方上线。这是一个描述:

我现在做的是:

  • 找到最大值。
  • 使用原子增量标记最大值
  • 创建每个像素指向自身的指针图像
  • 指针图像上的填充:如果邻居指向更大的值,则复制他的指针

这给出了一个分水岭指针图像,每个指针指向一个唯一的标签。

然后我做一个合并步骤;如果相邻像素指向不同的标签,并且它们的值超过阈值,我会合并它们的标签,以便“充分连接”的最大值不会形成它们自己的区域。

然后我取消引用指针,我有一个漂亮的标签图像。因为我只在最大值上播种标签,所以最大标签是一个小数字;我分配了一个该大小的 6xN 数组,并使用原子计算每个特征的最小/最大范围。

目前没有使用任何共享内存,但它已经相当快了。我意外地做了一些半聪明的事情;因为我首先做一个分水岭,然后使用单次扫描合并相邻的分水岭区域,所以我实际上是在做一个多层次的算法。性能主要由少量的洪水填充调用和两到三个合并扫描主导。正确使用共享内存可以将我的内存带宽需求减少 5 倍,但要等到它明确显示为瓶颈。我已经得到了一个比我可以从 scipy 争吵的更接近我想要的分割,它更快,并且使用更少的内存,所以我现在很高兴。

于 2012-08-10T09:27:41.653 回答
0

I would suggest a pyramidal processing scheme: you can quickly compute mipmap-like levels of detail where you store the maximum/minimin value of the combined voxels. This should result in something similar to an octree. Then you can start your segmentation only on the interesting areas, which sould give a considerable speedup. This should work pretty well if you have only few and very small segments.

I guess you could also use level sets (which can be implemented on the GPU), but I recon these are a bit harder to implement (google for "CUDA level set").

Do you have to keep track of your segments over time? Do you have any idea how to do that?

于 2012-08-07T09:37:49.730 回答
0

最简单的方法是使用 1D 存储并在其上覆盖 4D 索引模式。

地址 = x + y * 宽度 + z * 高度 * 宽度 + t * 长度 * 高度 * 宽度;

这将意味着

data( 0 ,0, 0, 0 ) 和 data( 1, 0, 0, 0 ) 在连续的地址,但是

data( 0 ,0, 0, 0 ) 和 data( 0 ,0, 0, 1 ) 是宽度 * 高度 * 长度地址部分。

但是,如果您的访问模式正在处理最近的邻居,那么您需要空间局部性来进行索引。

这可以使用索引的 Morton Z (Peano Key) 排序来实现。它将最近的邻居放置在紧密的线性内存地址中。这个 Z 阶线性地址是通过交错 x,y,z,t 索引的交替位获得的。有关 2D 示例,请参见

http://graphics.stanford.edu/~seander/bithacks.html#InterleaveTableObvious

于 2012-08-06T21:42:14.543 回答