【问题标题】:Gaussian Image filtering using FFT使用 FFT 进行高斯图像滤波
【发布时间】:2017-03-02 16:13:01
【问题描述】:

对于图像分割,我使用 OpenCV 的 GaussianBlur 的高斯差分特征(范围从 0.8 到 8.43,指数步长为 1.4)。我的图像尺寸为 4096 x 2160,因此这需要相当长的时间(在一个核心上 8 秒,这在处理视频时相当长)。

你能给我一些关于如何加快速度的建议吗?目前我正在尝试在 FFT 中实现高斯滤波。到目前为止,我有以下代码:

ftimage = np.fft.fft2(image)
ftimage = np.fft.fftshift(ftimage)
kernel = cv2.getGaussianKernel(11, 3)
kernel = kernel * kernel.T
ftkernel = np.fft.fftshift(np.fft.fft2(kernel, (h, w)))
ftimagep = ftimage * gmask
imagep = np.fft.ifft2(ftimagep)
imageq = cv2.GaussianBlur(image, (11,11), 3))

这里的问题是imagepimageq 是相互转换的版本。 其次,由于高斯的傅立叶也是高斯,我如何才能以直接的方式计算ftkernel


下面的答案: 我已经实现了近似过滤:

 def approx_g(image, sigma_g, n=5):
      w = np.sqrt(12*sigma_g**2/n + 1)
      wu = np.ceil(w) if np.ceil(w) % 2 == 1 else np.ceil(w)+1
      wl = np.floor(w) if np.floor(w) % 2 == 1 else np.floor(w)-1
      if w == w//1:
          wl -= 2
          wu += 2
      m = round((12*sigma_g**2 - n*wl**2 - 4*n*wl - 3*n) / (-4*wl - 4))
      wl = int(wl)
      wu = int(wu)
      for num in range(0,int(m)):
          image = cv2.blur(image, (wl, wl))
      for num in range(0,int(n-m)):
          image = cv2.blur(image, (wu, wu))
      return image

对于 n=4,L2 像素差异看起来相当不错:

我也做了不同sigma的速度比较:

【问题讨论】:

  • 为什么不能在较小的图像尺寸上运行分割?
  • 我不确定 OpenCV 中 GaussianBlur 的特定实现,但是 filter2D()(可以很容易地用于实现相同的效果)已经使用基于 DFT 的算法来处理大于 ~11x11 像素的内核.考虑一下:docs.opencv.org/3.0-beta/modules/imgproc/doc/…

标签: python opencv image-processing


【解决方案1】:

Fast Almost-Gaussian Filtering 的第 II 节所述,可以通过级联的盒(平均)滤波器来近似高斯滤波器。此方法需要使用Integral Image,并允许更快地应用(近)高斯滤波,尤其是对于高度模糊的情况。

下面的代码演示了如何使用上面链接的论文中的步骤来做到这一点。

让过滤器半径为问题中的 8.43。

sigma_g = 8.43

盒式滤波器的连续应用次数决定了逼近的程度。在本例中,我将其设置为 5:

n = 5

首先,使用公式 3 找到盒式过滤器的理想宽度:

w = np.sqrt(12*sigma_g**2/n + 1)

正如论文中所讨论的,使用两个不同大小的盒子过滤器效果更好。为了对称,滤波器需要具有奇数长度,长度相差 2。下面的代码采用 w 并找到最接近的奇数。 (可能写得更好):

wu = np.ceil(w) if np.ceil(w) % 2 == 1 else np.ceil(w)+1
wl = np.floor(w) if np.floor(w) % 2 == 1 else np.floor(w)-1
if w == w//1:
    wl -= 2
    wu += 2

如果需要 n 个连续的应用程序,则使用宽度为 wu 的第一个过滤器执行 m,并使用宽度为 wl 的第二个过滤器执行 (n-m)。公式 5 显示了如何计算 m:

m = round((12*sigma_g**2 - n*wl**2 - 4*n*wl - 3*n) / (-4*wl - 4))

接下来,计算水平和垂直积分图像的函数:

def integral_image_1d_hor(image):
    ''' Calculated the 1d horizontal integral
    image of an image.'''
    n1, n2 = np.shape(image)
    int_im = np.zeros((n1, n2))
    for row in range(0,n1):
        int_im[row,0] = image[row,0]

    for row in range(0,n1):
        for col in range(1,n2):
            int_im[row,col] = image[row,col] + int_im[row,col-1]

    return int_im


def integral_image_1d_ver(image):
    ''' Calculated the 1d vertical integral
        image of an image.'''
    n1, n2 = np.shape(image)
    int_im = np.zeros((n1, n2))
    for col in range(0,n2):
        int_im[0,col] = image[0,col]

    for col in range(0,n2):
        for row in range(1,n1):
            int_im[row,col] = image[row,col] + int_im[row-1,col]

    return int_im

要使用积分图像进行过滤,我有以下功能:

def box_1d_filter_hor(int_im_1d, width):
    w = int((width-1)/2)
    fil_im = np.zeros(np.shape(int_im_1d))
    pad = w
    int_im_1d = np.pad(int_im_1d, pad, 'constant')
    n1 = np.shape(int_im_1d)[0]
    n2 = np.shape(int_im_1d)[1]
    for row in range(pad, n1-pad):
        for col in range(pad, n2-pad):
            fil_im[row-pad,col-pad] = (int_im_1d[row,col+w]
                                    - int_im_1d[row,col-w-1])/width
    return fil_im


def box_1d_filter_ver(int_im_1d, width):
    w = int((width-1)/2)
    fil_im = np.zeros(np.shape(int_im_1d))
    pad = w
    int_im_1d = np.pad(int_im_1d, pad, 'constant')
    n1 = np.shape(int_im_1d)[0]
    n2 = np.shape(int_im_1d)[1]
    for col in range(pad, n2-pad):
        for row in range(pad, n1-pad):
            fil_im[row-pad,col-pad] = (int_im_1d[row+w,col]
                                    - int_im_1d[row-w-1,col])/width
    return fil_im

然后我定义了另外两个函数,用于处理水平和垂直方向的图像:

def process_hor(image, w):
    int_im = integral_image_1d_hor(image)
    fil_im = box_1d_filter_hor(int_im, w)
    return fil_im

def process_ver(image, w):
    int_im = integral_image_1d_ver(image)
    fil_im2 = box_1d_filter_ver(int_im, w)
    return fil_im2

最后,使用所有这些先前的函数,使用以下函数来近似高斯滤波:

def approximate_gaussian(image, wl, wu, m, n):
    for num in range(0,int(m)):
        image = process_hor(image, wl)
        image = process_ver(image, wl)
    for num in range(0,int(n-m)):
        image = process_hor(image, wu)
        image = process_ver(image, wu)
    return image

我并没有真正处理图像的边缘,但可以通过修改上面的函数来调整。这应该会更快,尤其是对于高斯模糊半径非常高的情况。

【讨论】:

  • 我已经用 OpenCV 的 cv2.blur 函数试过了。效果不错!
  • box_1d_filter_ver 中到int_im_1d 的索引应该增加1,因为如果row == pad,第一个轴的结果是索引'-1'
猜你喜欢
  • 2013-09-12
  • 1970-01-01
  • 2021-05-22
  • 2020-11-17
  • 1970-01-01
  • 2014-12-31
  • 1970-01-01
  • 2023-03-16
  • 2017-12-28
相关资源
最近更新 更多