线性分离高斯滤波器并使用 Numpy 计算

Linearly separating a Gaussian Filter and calculating with Numpy

我有一个 2d numpy array 包含从 0255greyscale 个像素值。我想要做的是从头开始创建一个 gaussian filter 。我已经写了一个函数来生成 normalized 高斯 kernel:

def gaussianKernel(size, sigma):
    kernel =  np.fromfunction(lambda x, y: (1/(2*math.pi*sigma**2)) * math.e ** ((-1*((x-(size-1)/2)**2+(y-(size-1)/2)**2))/(2*sigma**2)), (size, size))
    return kernel / np.sum(kernel)

效果很好:

>>> vision.gaussianKernel(5, 1.5)
array([[ 0.01441882,  0.02808402,  0.0350727 ,  0.02808402,  0.01441882],
       [ 0.02808402,  0.05470021,  0.06831229,  0.05470021,  0.02808402],
       [ 0.0350727 ,  0.06831229,  0.08531173,  0.06831229,  0.0350727 ],
       [ 0.02808402,  0.05470021,  0.06831229,  0.05470021,  0.02808402],
       [ 0.01441882,  0.02808402,  0.0350727 ,  0.02808402,  0.01441882]])

然后我创建了一个基本的 convolution 函数来将此 kernel 应用于每个 pixel 并生成 gaussian 模糊:

def gaussianBlurOld(img, kSize, kSigma):
    kernel = gaussianKernel(kSize, kSigma)
    d = int((kSize-1)/2)
    gaussian = np.zeros((img.shape[0]-2*d, img.shape[1]-2*d))
    for y in range(d, img.shape[0]-d):
        for x in range(d, img.shape[1]-d):
            gaussian[y-d][x-d] = np.sum(np.multiply(img[y-d:y+d+1, x-d:x+d+1], kernel))
    return gaussian

它工作正常并使图像模糊,但是,由于此代码最终将在 raspberry pi 上 运行,我需要它高效且更快。因此,感谢 我昨天提出了一个关于如何加速 Sobel 边缘检测器的问题,我尝试将他给 gaussian 过滤器的相同逻辑应用。然而,由于 function 将接受 kernelvariable 大小参数,它使 Sobel 内核的设置大小稍微复杂化,即 3x3.

如果我理解正确的话,我需要先将内核分成xy组件,这可以通过使用top row和left [=]来完成原始 kernel 的 38=] (显然它们是相同的,但我决定将它们分开,因为我已经计算了 2d 内核)。下面是矩阵分离:

从这些 rowcolumn 向量中,我需要遍历每个值并将数组的 'window' 逐元素乘以它。在每一个之后,将 window 的缩小尺寸沿着数组向右移动。为了更清楚地展示我认为我需要做的事情,这些是我正在谈论的 3 个不同 'windows' 的小图像,kernel 大小为 3x3:

          _______3_______
     _____|_2_______    |
_____|_1__|____|    |   |
|    |    |    |    |   |
|123,|213,|124,|114,|175|
|235,|161,|127,|215,|186|
|128,|215,|111,|141,|221|
|224,|171,|193,|127,|117|
|146,|245,|129,|213,|221|
|152,|131,|150,|112,|171|

因此,对于每个 'window',您乘以内核中 window 的 index,并将其加到总数中。

然后,将应用了 gaussian 内核的 x 组件的 img 应用到 y 组件。

这些是我认为我可以计算 gaussian 模糊的步骤,比使用上面的 nested for-loops 快得多,这是我编写的代码来尝试这样做:

def gaussianBlur(img, kSize, kSigma):
    kernel = gaussianKernel(kSize, kSigma)
    gausX = np.zeros((img.shape[0], img.shape[1] - kSize + 1))
    for i, v in enumerate(kernel[0]):
        gausX += v * img[:, i : img.shape[1] - kSize + i + 1]
    gausY = np.zeros((gausX.shape[0] - kSize + 1, gausX.shape[1]))
    for i, v in enumerate(kernel[:,0]):
        gausY += v * gausX[i : img.shape[0]  - kSize + i + 1]
    return gausY

我的问题是这个函数产生了正确的 'blurring effect',但由于某种原因,输出值都在 03 之间,如 floats。幸运的是,由于某些其他原因,matplotlib 仍然可以正常显示输出,所以我可以检查它是否正确模糊了图像。

问题很简单:为什么输出的像素值在03之间???

我已经调试了几个小时,但找不到原因。我很确定某处只有一点缩放细节,但我就是找不到。任何帮助将不胜感激!

对于任何感兴趣的人,问题出在函数 gaussianKernel 返回 2d kernel normalised 用作 2d kernel。这意味着当我通过取顶部 row 和左侧 column 将其拆分为 rowcolumn 组件时,这些组件是 而不是 normalised.

为了解决这个问题,我只是在 gaussianKernel 函数中添加了一个参数到 select 2 维度或 1 维度(都 normalised 正确) :

def gaussianKernel(size, sigma, twoDimensional=True):
    if twoDimensional:
        kernel = np.fromfunction(lambda x, y: (1/(2*math.pi*sigma**2)) * math.e ** ((-1*((x-(size-1)/2)**2+(y-(size-1)/2)**2))/(2*sigma**2)), (size, size))
    else:
        kernel = np.fromfunction(lambda x: math.e ** ((-1*(x-(size-1)/2)**2) / (2*sigma**2)), (size,))
    return kernel / np.sum(kernel)

所以现在我可以只得到 1d kernelgaussianKernel(size, sigma, False) ,并让它正确地成为 normalised 。这意味着我终于可以在没有 scaled pixel 值的情况下获得正确的模糊效果。