python 无循环均值滤波器

Mean Filter in python without loops

有人要求我在具有给定内核的一维数组上创建一个 mean_filter 函数,假设填充为零。

均值滤波器是一种旨在消除噪声的算法。它需要一个数组,一个内核(比如 K),并用周围 K 值的平均值替换数组的每个值,本身包括在内。

该算法用于图像处理。

我做到了-

def mean_filter(arr, k):
    # applies mean filter to 1-d array with the kernel size 2k+1 . Write your code here
    p=len(arr)
    arr2=np.zeros(2*k+p, dtype=float)
    arr3=np.zeros(2*k+p, dtype=float)
    arr4=np.zeros(p, dtype=float)

    for i in range(p):
        arr2[k+i]=arr[i]

    for i in range(k,k+p):
        sum=0
        for j in range(-k,k+1):
            sum+=arr2[i+j]
        arr3[i]=sum/float(2*k+1)

    for i in range(p):
        arr4[i]=arr3[k+i]

    return arr4

但他们对我的期望是在没有任何循环的情况下做到这一点。

指令是—— "这个任务应该在没有任何循环、理解或函数的情况下完成 比如np.vectorize,等等

不要为此任务使用内置卷积函数"

我完全不知道该怎么做。你能建议点什么吗?线索将不胜感激。

举例:

import numpy as np
k=2
kern=np.ones(2*k+1)/(2*k+1)
arr=np.random.random((10))
out=np.convolve(arr,kern, mode='same')
def meanfilt (x, k):
    """Apply a length-k mean filter to a 1D array x.
    Boundaries are extended by repeating endpoints.
    """
    
    import numpy as np

    assert k % 2 == 1, "Mean filter length must be odd."
    assert x.ndim == 1, "Input must be one-dimensional."
    
    k2 = (k - 1) // 2
    y = np.zeros ((len (x), k), dtype=x.dtype)
    y[:,k2] = x
    for i in range (k2):
        j = k2 - i
        y[j:,i] = x[:-j]
        y[:j,i] = x[0]
        y[:-j,-(i+1)] = x[j:]
        y[-j:,-(i+1)] = x[-1]
    return np.mean (y, axis=1)

注:答案摘自repo

我找到了一个解决方案,该解决方案的灵感来自我在尝试实现均值过滤中没有填充的不同边缘行为时偶然发现的 matlab 函数:https://www.mathworks.com/matlabcentral/fileexchange/23287-smooth2a。 它适用于矩阵乘法,不涉及循环或卷积。 使用一维数组,它会给出如下内容:

import scipy.sparse
import numpy as np

def mean_filter(arr, k):
    p = len(arr)
    diag_offset = np.linspace(-(k//2), k//2, k, dtype=int)
    eL = scipy.sparse.diags(np.ones((k, p)), offsets=diag_offset, shape=(p, p))
    nrmlize = eL @ np.ones_like(arr)
    return (eL @ arr) / nrmlize

这是如何工作的,你创建一个矩阵,其对角线的数量与你的内核大小一样多,然后用你的数组做点积。结果是一个数组,其中每个元素都是内核中原始数组元素的总和。基本上,这相当于一个卷积。最后,您将此总和归一化为内核大小。

需要注意的一件事是,在无法构建完整内核的阵列边缘,使用了适合阵列的大部分内核(这就是为什么您不能事后仅通过标量进行归一化) .

这个解决方案也可以扩展到多维数组上。