是否有具有这些特征的数据结构?

时间:2010-08-23 17:27:14

标签: c++ data-structures image-processing matrix

我正在寻找一种数据结构,允许我在内存中连续存储M - { - 1}}二维值矩阵,以便任意两点之间的内存距离近似矩阵中这些点之间的欧几里德距离。也就是说,在作为N元素的一维数组的典型行主要表示中,相同行(M * N)中的相邻单元格与相邻行中的相邻单元格之间的存储器距离不同({ {1}})。

我想要一种减少或消除这种差异的数据结构。真的,这样一个结构的名称就足够了 - 我可以自己实现它。如果答案碰巧引用了这类事物的库,那也是可以接受的,但它们应该可以用于C ++。

我有一个应用程序需要执行快速图像卷积而不需要硬件加速,虽然我知道这种事情的常用优化技术,但我觉得有一个专门的数据结构或数据订购可以提高绩效。

10 个答案:

答案 0 :(得分:7)

我猜“不”!如果答案恰好是“是”,那么它几乎肯定是如此不规则,以至于卷积型操作的速度会慢一些。

修改

为了证明我的猜测,举一个例子。假设我们首先存储a[0][0]。我们希望a[k][0]a[0][k]具有相似的距离,并且与k成比例,因此我们可能会选择交错存储第一行和第一列(即a[0][0], a[1][0], a[0][1], a[2][0], a[0][2]等但是我们现在如何做同样的事情a[1][0]?内存中靠近它的所有位置现在都被a[0][0]附近的东西占用。

虽然除了我的例子之外还有其他可能性,但我打赌你总是会遇到这种问题。

修改

如果您的数据稀少,那么可能有一些聪明的做法(重新考虑Cubbi的R树建议)。但是,它仍然需要不规则访问和指针追踪,因此对于任何给定数量的点,它将比直接卷积慢得多。

答案 1 :(得分:7)

鉴于要求您将值连续存储在内存中,我强烈建议您研究space-filling curves,尤其是Hilbert curves

为了给出一些上下文,有时在数据库索引中使用这样的曲线来改善多维范围查询的局部性(例如,“在该矩形中找到具有x / y坐标的所有项目”),从而旨在减少数量访问的不同页面。有点类似于这里已经建议的R树。

无论哪种方式,它看起来你已经绑定到内存中的M * N数组值,所以整个问题是如何排列该数组中的值,我想。 (除非我误解了这个问题。)

事实上,这样的排序可能仍然只会改变距离分布的特征。从矩阵中任意两个随机选择的点的平均距离不应该改变,所以我必须在那里同意Oli。我想,潜在的好处在很大程度上取决于您的具体用例。

答案 2 :(得分:6)

您可以查看空间填充曲线,尤其是Z阶曲线,它(主要)保留空间局部性。然而,查找索引可能在计算上是昂贵的。

如果您正在使用它来尝试提高缓存性能,您可以尝试一种称为“bricking”的技术,这有点像空间填充曲线的一个或两个级别。基本上,您将矩阵细分为nxn tile(其中nxn整齐地适合您的L1缓存)。您还可以存储另一级别的切片以适应更高级别的缓存。这对空间填充曲线的优势在于索引可以相当快地计算。此处的论文中包含一个参考:http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.30.8959

答案 3 :(得分:3)

这听起来像R-tree.或其中一个变种可能有所帮助。在C ++标准库中没有类似的东西,但看起来在候选库Boost.Geometry中有一个R树(还不是boost的一部分)。在写我自己之前,我先看一下。

答案 4 :(得分:3)

不可能将2D结构“线性化”为1D结构并且保持两个方向上的接近关系不变。这是世界上最基本的拓扑属性之一。

有了这一点,当你需要保持接近度(尽可能多)时,通常用于2D数组表示的标准行方式或列式存储顺序确实不是最好的。通过使用分形曲线的各种离散近似(空间填充曲线),您可以获得更好的结果。

Z-order曲线是此应用程序的常用曲线:http://en.wikipedia.org/wiki/Z-order_(curve)

请注意,无论您使用哪种方法,总会有违反距离要求的元素。

答案 5 :(得分:1)

您可以将2D矩阵视为一个大螺旋,从中心开始并向外推进。展开螺旋,按顺序存储数据,地址之间的距离至少模糊近似于它们所代表的点之间的欧几里德距离。虽然它不是很精确,但我很确定你也不能做得更好。与此同时,我认为即使在最好的情况下,它对你的卷积代码的帮助也很小。

答案 6 :(得分:1)

答案是否定的。想想看 - 内存是1D。你的矩阵是2D的。你想压缩额外的维度 - 没有损失?它不会发生。

更重要的是,一旦离开一定距离,加载到缓存需要相同的时间。如果你有一个缓存未命中,那么它是100还是100000并不重要。从根本上说,你不能获得比简单阵列更连续/更好的性能,除非你想为你的阵列获得一个LRU。

答案 7 :(得分:0)

我认为你忘记了计算机内存中的距离不是通过徒步操作的计算机CPU访问的:)所以距离几乎无关紧要。

它是随机存取存储器,所以你必须弄清楚你需要做什么操作,并优化对它的访问。

答案 8 :(得分:0)

您需要将内存空间中的地址重新转换为原始数组空间才能完成此操作。此外,你只强调距离,这可能仍会导致一些问题(没有方向)

如果我有一个R x C数组,以及位于[r,c]和[c,r]位置的两个单元格,那么距离某个任意点的距离,比如[0,0]是相同的。除非你有一台那些花哨的新型量子比特机器,否则你无法让一个内存地址保留两件事。

但是,你可以考虑在一行R x C的主要数组中,每行是C * sizeof(你的数据)字节长。相反,您可以说数组范围内任何内存地址的原始坐标都是

r =(地址/ C) c =(地址%C)

所以

r1 =(地址1 / C)

r2 =(地址2 / C)

c1 =(地址1%C)

c2 =(地址2%C)

dx = r1 - r2

dy = c1 - c2

dist = sqrt(dx ^ 2 + dy ^ 2)

(假设您使用的是基于零的数组) (将所有这些粉碎在一起,使其运行更加优化)

对于更多想法,请查看任何使用名为'stride'的计算值的2D图像处理代码,这基本上是指示它们在内存地址和数组地址之间来回跳转

答案 9 :(得分:0)

这与亲密度并不完全相关,但可能有所帮助。它肯定有助于减少磁盘访问。

获得更好“封闭性”的一种方法是平铺图像。如果你的卷积内核小于一个瓦片的大小,你通常最多会触摸4个瓦片。您可以递归地平铺更大的部分,以便本地化得到改进。类似斯托克斯(至少我认为它的斯托克斯)论证(或一些变化的微积分)可以表明,对于矩形,最好(意味着检查任意子矩形)形状是具有相同纵横比的较小矩形。

快速直觉 - 考虑一个正方形 - 如果你用较小的正方形平铺较大的正方形,则一个正方形包围给定周长的最大面积的事实意味着正方形平铺具有最小的边界长度。当你转换大方块时,我认为你可以告诉你应该以同样的方式转换方块。 (也可能能够进行简单的多变量分化)

经典的例子是放大间谍卫星数据图像并对其进行卷积以进行增强。如果你保持数据并且你回到它,那么额外的平铺计算是非常值得的。

对于不同的压缩方案,例如余弦变换,它也非常值得。 (这就是为什么当你下载一张图像时,它经常出现在较小和较小的正方形中,直到达到最终分辨率。

这个领域有很多书,很有帮助。