【问题标题】:Implementing a bilateral filter实现双边过滤器
【发布时间】:2020-03-12 08:48:59
【问题描述】:

我正在尝试从Fast Bilateral Filteringfor the Display of High-Dynamic-Range Images 论文中实现双边过滤器。实现双边滤波器的方程(来自论文)如下:

据我了解,

  • f 是一个高斯滤波器
  • g 是一个高斯滤波器
  • p 是给定图像窗口中的一个像素
  • s 是当前像素
  • Ip 是当前像素的强度

有了这个,我编写了实现这些方程式的代码,如下所示:

import cv2
import numpy as np

img = cv2.imread("fish.png")
# image of width 239 and height 200



bl_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

i = cv2.magnitude(
    cv2.Sobel(bl_img, cv2.CV_64F, 1, 0, ksize=3),
    cv2.Sobel(bl_img, cv2.CV_64F, 0, 1, ksize=3)
)

f = cv2.getGaussianKernel(5, 0.1, cv2.CV_64F)
g = cv2.getGaussianKernel(5, 0.1, cv2.CV_64F)


rows, cols, _ = img.shape

filtered = np.zeros(img.shape, dtype=img.dtype)

for r in range(rows):
    for c in range(cols):
        ks = []
        for index in [-2,-1,1,2]:
            if index + c > 0 and index + c < cols-1:
                p = img[r][index + c]
                s = img[r][c]
                i_p = i[index+c]
                i_s = i[c]
                ks.append(
                    (f * (p-s)) * (g * (i_p * i_s)) # EQUATION 7
                )
        ks = np.sum(np.array(ks))

        js = []
        for index in [-2, -1, 1, 2]:
            if index + c > 0 and index + c < cols -1:
                p = img[r][index + c]
                s = img[r][c]
                i_p = i[index+c]
                i_s = i[c]
                js.append((f * (p-s)) * (g * (i_p * i_s)) * i_p) # EQUATION 6

        js = np.sum(np.asarray(js))
        js = js / ks
        filtered[r][c] = js

cv2.imwrite("f.png", filtered)

但是当我运行这段代码时,我收到一条错误消息:

    Traceback (most recent call last):
File "bft.py", line 33, in <module>
    (f * (p-s)) * (g * (i_p * i_s))
ValueError: operands could not be broadcast together with shapes (5,3) (5,239) 

我是否错误地实现了方程式?我错过了什么?

【问题讨论】:

  • 您的矩阵形状不兼容,您不能将一个尺寸为 5,3 的矩阵与另一个尺寸为 5,239 的矩阵相乘
  • @prhmma 你知道对上述等式进行编码的正确方法吗?
  • 更改这两行并检查它是否工作ks.append(np.matmul((f * (p-s)).T, (g * (i_p - i_s))) # EQUATION 7js.append(np.matmul((f * (p-s)).T, (g * (i_p * i_s)) * i_p)) # EQUATION 6
  • @prhmma 不确定这是否是矩阵乘法
  • 您需要索引高斯内核(或者更好的是使用插值)。方程中的 f(p-s) 表示在 p-s 处评估高斯。

标签: python algorithm opencv computer-vision dynamic-programming


【解决方案1】:

您的代码存在各种问题。最重要的是,这个等式被错误地解释了。 f(p-s) 表示在 p-s 处评估函数 ff 是高斯。 g 也是如此。代码部分如下所示:

weight = gaussian(p - s, sigma_f) * gaussian(i_p - i_s, sigma_g)
ks.append(weight)
js.append(weight * i_p)

请注意,这两个循环可以合并,这样可以避免一些重复计算。 gaussian(x, sigma) 将是一个计算 x 处的高斯权重的函数。您需要定义两个 sigma,sigma_fsigma_g,分别是空间和色调 sigma。

第二个问题是在ps 的定义中。这些是像素的坐标,而不是像素处的图像值。 i_pi_s 是这些位置的图像值。 p-s 基本上是(r,c) 处的像素与给定邻居之间的空间距离。

第三个问题是邻域的循环。邻域是gaussian(p - s, sigma_f) 不可忽略的所有像素。所以邻域有多大取决于选择的sigma_f。你至少应该把它当作ceil(2*sigma_f)。假设 sigma_f 为 2,那么您希望邻域从 -4 变为 4(9 像素)。但是这个邻域是二维的,而不是您的代码中的一维。所以你需要两个循环:

for ii in range(-ceil(2*sigma_f), ceil(2*sigma_f)+1):
   if ii + c > 0 and ii + c < cols-1:
   for jj in range(-ceil(2*sigma_f), ceil(2*sigma_f)+1):
      if jj + r > 0 and jj + r < rows-1:
         # compute weight here

请注意,现在p-s 是用math.sqrt(ii**2 + jj**2) 计算的。但还要注意,高斯函数使用x**2,因此您可以通过将x**2 传递到gaussian 函数来跳过平方根的计算。

【讨论】:

  • 感谢您的回答。我正在浏览people.csail.mit.edu/sparis/bf_course/slides/… 的资源,该资源描述了实现 BF 的不同方法。您能否指出一个利用 ceil(2*sigma_f) 为邻居实现 BF 的来源(或论文)?
  • 如果我将邻居的值提供给算法,例如total neighbors != ceil(2*sigma_f),这意味着什么。这意味着什么?
  • @Amanda:提出双边滤波器的原始论文 (Tomasi and Manduchi, 1998) 显示了一个示例,其中截止值接近 2 sigma(sigma 为 5 时为 23 个像素)。那里的方程显示了无限积分(即在整个图像域上求和)。在 2 sigma 处截断的概念是,高斯越远,值就越小(对于普通高斯滤波,我总是使用至少 3 sigma,但双边滤波器不需要那么高的精度)。
  • @Amanda:在图像的边缘,您会发现附近的像素更少。这很好,因为过滤器标准化的方式。您总是除以过滤器权重的总和。如果您的问题与使邻域小于 2 sigma(降低截止值)有关,那么效果是空间高斯形状将被截断,导致非高斯形状。在这种情况下,您不妨使用统一权重。具有统一权重的过滤器具有比具有高斯权重的过滤器更差的特性。
  • @Amanda:在this blog post我讨论了降低高斯滤波器截止的效果。
猜你喜欢
  • 2011-08-07
  • 1970-01-01
  • 2018-09-04
  • 2017-05-15
  • 1970-01-01
  • 1970-01-01
  • 2014-05-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多