假设在内核上执行某些函数的通用滑动算法,如图像处理中的均值滤波器(平均滤波器)或绝对差值和算法。当内核滑动到下一个位置时,内存中会有一些冗余读取,因为新内核所包含的数据会与之前的内容重叠。
让我用一个实际的例子解释一下......假设你想在一个内核(窗口)大小为3x3的大型二维矩阵上执行中值滤波。内核的第一个位置(下图中的红色)将以(1,1)为中心,第二个位置(绿色)将以(1,2)为中心。注意黄色区域是如何重叠的,现在需要从内存中重新加载这些值。
meanfilter http://luka.s3.amazonaws.com/meanfilter.png
我的具体问题是3D平均滤波器,因此重叠更大(3D ^ 3 ^ 3-3 ^ 2 = 18 vs 2D ^ 3 = 2-3 = 6)。
我确信这是一个常见的问题......有没有人知道如何有效地实现这样的算法来消除冗余内存查找,或者利用现代架构上CPU缓存的空间和时间局部性(例如双向关联缓存)?
我在3D中的具体问题只取最近的6个邻居(不是对角线)的平均值,并在C中实现如下:
for( i = 0; i <= maxi; i++ ) {
for( j = 0; j <= maxj; j++ ) {
for( k = 0; k <= maxk; k++ ) {
filteredData[ i ][ j ][ k ] =
ONE_SIXTH *
(
data[ i + 1 ][ j ][ k ] +
data[ i - 1 ][ j ][ k ] +
data[ i ][ j + 1 ][ k ] +
data[ i ][ j - 1 ][ k ] +
data[ i ][ j ][ k + 1 ] +
data[ i ][ j ][ k - 1 ]
);
}
}
}
答案 0 :(得分:2)
您正在做的事情称为 Convolution 。您使用相同维数的较小内核对多维数据进行卷积。这是一项非常常见的任务,并且有很多库。
快速解决方案(depending on the kernel size)用于计算频域中的卷积。您计算数据和内核的(多维)FFT,将它们相乘,并计算逆FFT。你会发现优化的库可以做到这一点,例如。对于Python,有scipy.ndimage.filters.convolve和scipy.signal.fftconvolve。
平铺是一种优化低级内存访问的常用图像处理技术。您可以分配适合CPU缓存的方块(或多维数据集)。当您访问相邻像素时,它们将在大多数时间内在内存中靠近。但是,在整个数组上循环会有点棘手。
为了进一步阅读,我推荐了论文Why Modern CPUs Are Starving and What Can Be Done about It,它提到了这种记忆阻塞技术,并指出了实现它的数值库。
最后还有积分图像,它允许您calculate the average任意矩形/长方体,只需极少量的内存访问。
答案 1 :(得分:0)
对于2D均值滤波器的情况,我会保留列总数,然后可以重复使用,以便每次迭代只计算一个新的列总数,然后对列总数求和以得到平均值。例如。对于3x3的意思:
for (i = 1; i < M - 1; ++i)
{
// init first two column sums
col0 = a[i - 1][0] + a[i][0] + a[i + 1][0];
col1 = a[i - 1][1] + a[i][1] + a[i + 1][1];
for (j = 1; j < N - 1; ++j)
{
// calc new col sum
col2 = a[i - 1][j + 1] + a[i][j + 1] + a[i + 1][j + 1];
// calc new mean
mean[i][j] = (col0 + col1 + col2) / 9;
// shuffle col sums
col0 = col1;
col1 = col2;
}
}
这导致每个点只有3个负载,而不是天真情况下的9个负载,但仍然不是最佳。
您可以通过每次迭代处理两行并保持行i和i + 1的重叠列和来进一步优化。