当前位置: 首页 > 面试题库 >

大步迈向高效的移动平均滤波器

柯星华
2023-03-14
问题内容

我最近在这篇文章的答案中了解了步幅,并且想知道如何使用它们比我在这篇文章中提出的)(使用卷积滤波器)更有效地计算移动平均滤波器。

到目前为止,这就是我所拥有的。它查看原始数组,然后将其滚动所需的量,然后对内核值求和以计算平均值。我知道边缘处理不正确,但是以后我可以解决…是否有更好,更快的方法?目标是过滤大小最大为5000x5000
x 16层的大型浮点数组,这scipy.ndimage.filters.convolve是一个相当慢的任务。

请注意,我正在寻找8邻居连通性,即3x3滤镜将平均9像素(焦点像素周围为8)并将该值分配给新图像中的像素。

import numpy, scipy

filtsize = 3
a = numpy.arange(100).reshape((10,10))
b = numpy.lib.stride_tricks.as_strided(a, shape=(a.size,filtsize), strides=(a.itemsize, a.itemsize))
for i in range(0, filtsize-1):
    if i > 0:
        b += numpy.roll(b, -(pow(filtsize,2)+1)*i, 0)
filtered = (numpy.sum(b, 1) / pow(filtsize,2)).reshape((a.shape[0],a.shape[1]))
scipy.misc.imsave("average.jpg", filtered)

编辑说明我如何看待这一工作:

当前代码:

  1. 使用stride_tricks生成类似于[[0,1,2],[1,2,3],[2,3,4] …]的数组,它对应于过滤器内核的顶行。
  2. 沿垂直轴滚动以获取内核的中间行[[10,11,12],[11,12,13],[13,14,15] …]并将其添加到我得到的数组中1)
  3. 重复以获取内核的下一行[[20,21,22],[21,22,23],[22,23,24] …]。在这一点上,我将每一行的总和除以滤镜中的元素数,从而得到每个像素的平均值(移动1行和1 col,边缘周围有些奇怪,但我可以稍后再处理)。

我希望可以更好地利用stride_tricks来直接获取整个数组的9个值或内核元素的总和,或者有人可以说服我采用另一种更有效的方法…


问题答案:

对于它的价值,这是您将使用“花哨”的大步小技巧来做的事情。我本打算昨天发布这个,但是由于实际工作而分心!:)

@Paul和@eat都使用各种其他方法来实现良好的实现。为了继续前面的问题,我以为我会发布N维等效项。

但是,您将无法显着击败scipy.ndimage1D阵列的功能。(虽然scipy.ndimage.uniform_filter应该击败scipy.ndimage.convolve

而且,如果您试图获得一个多维移动窗口,则每当不经意地复制数组副本时,都有内存使用量爆炸的风险。虽然初始的“滚动”阵列只是原始阵列内存的视图,但是复制该阵列的任何中间步骤都将使复制副本比原始阵列大一个
数量级
(即,假设您正在使用一个100x100的原始数组…对其的视图(过滤器大小为(3,3))将为98x98x3x3,但使用与原始数组相同的内存,但是,任何副本都将使用
完整的 98x98x3x3数组的内存量将!!)

基本上,当您要对ndarray的 单个轴上
的移动窗口操作进行矢量化处理时,使用疯狂的跨步技巧非常有用。它使计算移动标准偏差之类的事情变得非常容易,而开销却很小。当您想沿多个轴开始执行此操作时,这是可能的,但通常情况下,最好使用更专门的功能。(例如scipy.ndimage,等)

无论如何,这是您的操作方法:

import numpy as np

def rolling_window_lastaxis(a, window):
    """Directly taken from Erik Rigtorp's post to numpy-discussion.
    <http://www.mail-archive.com/numpy-discussion@scipy.org/msg29450.html>"""
    if window < 1:
       raise ValueError, "`window` must be at least 1."
    if window > a.shape[-1]:
       raise ValueError, "`window` is too long."
    shape = a.shape[:-1] + (a.shape[-1] - window + 1, window)
    strides = a.strides + (a.strides[-1],)
    return np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)

def rolling_window(a, window):
    if not hasattr(window, '__iter__'):
        return rolling_window_lastaxis(a, window)
    for i, win in enumerate(window):
        if win > 1:
            a = a.swapaxes(i, -1)
            a = rolling_window_lastaxis(a, win)
            a = a.swapaxes(-2, i)
    return a

filtsize = (3, 3)
a = np.zeros((10,10), dtype=np.float)
a[5:7,5] = 1

b = rolling_window(a, filtsize)
blurred = b.mean(axis=-1).mean(axis=-1)

因此,执行操作时得到的b = rolling_window(a, filtsize)是一个8x8x3x3数组,它实际上是与原始10x10数组相同内存的视图。我们可以很容易地沿不同轴使用不同的滤镜大小,或者仅沿N维数组的选定轴进行操作(即filtsize = (0,3,0,3),在4维数组上将为我们提供6维视图)。

然后,我们可以对最后一个轴重复应用任意函数,以有效地计算移动窗口中的事物。

但是,由于我们在meanstd或其他任何步骤)的每个步骤中都存储了比原始数组大得多的临时数组,因此这根本没有提高内存效率!同样,它也不会很快。

等效于ndimage

blurred = scipy.ndimage.uniform_filter(a, filtsize, output=a)

这将处理各种边界条件,在不需要阵列的临时副本的情况下就地进行“模糊处理”,而且速度 非常 快。跨步技巧是将功能沿 一个
轴应用于移动窗口的一种好方法,但通常不是沿多个轴进行操作的一种好方法

无论如何,只要$ 0.02美元…



 类似资料:
  • 我正在尝试优化一个程序,该程序需要在数据流的每个位置(字节)为数据流中的恒定大小窗口计算哈希。在比可用RAM大得多的磁盘文件中查找重复时需要它。目前我为每个窗口计算单独的md5哈希,但它花费了很多时间(窗口大小为几千字节,因此每个数据字节被处理几千次)。我想知道是否有一种方法可以在恒定(与窗口大小无关)时间内计算每个后续哈希(例如移动平均中1个元素的加减)?哈希函数可以是任何东西,只要它不提供长哈

  • Python是否有一个SciPy函数或NumPy函数或模块来计算给定特定窗口的一维数组的运行平均值?

  • 问题内容: 我正在写一个使用numpy中的卷积函数的移动平均函数,它应该等效于(加权移动平均)。当我的权重全部相等时(如简单的算术平均值),它可以正常工作: 给 但是,当我尝试使用加权平均值时 而不是(对于相同的数据)3.667,4.667,5.667,6.667,…我希望,我得到 如果删除“有效”标志,则什至看不到正确的值。我真的很想对WMA和MA使用convolve,因为它可以使代码更整洁(相

  • 问题内容: 我正在为Pyspark中的时间序列编写异常检测算法。我想计算(-3,3)或(-4,4)窗口的加权移动平均值。现在,我正在使用滞后和超前窗口功能,并将它们乘以一组权重。我的窗口当前是(-2,2)。 我想知道是否有另一种方法可以计算Pyspark中的加权移动平均值。 我正在使用的当前代码是: 问题答案: 您可以概括当前的代码: 它可以用作: 注意事项 : 您可能会考虑将滞后缺失的帧的结果标

  • 我正在写一个移动平均函数,它使用numpy中的卷积函数,它应该相当于一个(加权移动平均)。当我的权重都相等时(就像在一个简单的算术平均值中一样),它工作得很好: 给予 对这种行为有什么看法吗?