使用FFT进行高斯图像滤波

时间:2017-03-02 16:13:02

标签: python opencv image-processing

对于图像分割,我使用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进行了速度比较:

1 个答案:

答案 0 :(得分:4)

高斯滤波器可以通过级联的盒子(平均)滤波器来近似,如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,并且使用宽度为w1的第二滤波器执行(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

我没有真正处理图像的边缘,但可以通过修改上述功能来调整。这应该更快,特别是对于高斯模糊半径非常高的情况。