OpenCV filter2d 给出不正确的结果

Posted

技术标签:

【中文标题】OpenCV filter2d 给出不正确的结果【英文标题】:OpenCV filter2d gives incorrect result 【发布时间】:2019-02-19 12:49:12 【问题描述】:

我目前正在尝试使用我自己构建的拉普拉斯内核过滤图像。但是,当使用此内核过滤输入图像时,与 SciPy 中的实现相比,它会产生意想不到的结果。

我构建的拉普拉斯核应该通过下图验证

过滤图片的代码:

im = cv2.imread("test.png",0)
im = im.astype(np.float32)

def lkern(t=1.):
    ax = np.arange(np.round(-5*np.sqrt(t),0),np.round(5*np.sqrt(t),0)+1)
    xx, yy = np.meshgrid(ax, ax)

    kernel = -1/(np.sqrt(2*np.pi*t)*t)*np.exp(-(xx**2+yy**2)/(2*t))+
        (xx**2+yy**2)/(np.sqrt(2*np.pi*t)*t**2)*np.exp(-(xx**2+yy**2)/(2*t))


    return kernel.astype(np.float)

t = 25**2/2
l = lkern(t)

L = cv2.filter2D(im/255,-1,l)

plt.figure()
plt.imshow(L,cmap="gray")
plt.show()

导致

对比 SciPy 的ndimage.gaussian_laplace,结果应该是

这是非常不同的,我不知道如何正确地做到这一点。

【问题讨论】:

im/255im 仍然是np.uint8 ...如果我没记错的话,那是整数除法。顺便说一句,您能否提供原始输入图像,以便我们重现该问题? @DanMašek 我用ìm = im.astype(np.float32) 编辑了这个问题,应该避免整数除法。结果相同。 【参考方案1】:

OP 中的代码似乎采用了一维拉普拉斯高斯方程,并使用它来构造一个二维径向对称函数。也就是说,沿着内核的任何直径,该函数看起来都像一维拉普拉斯高斯函数。这不是创建二维拉普拉斯高斯的正确方法。

高斯拉普拉斯is defined 是沿每个轴的高斯核的二阶导数之和。也就是说,

LoG = d²/dx² G + d²/dy² G

使用G 高斯核。

使用 Numpy,您可以按如下方式构建此内核。我正在使用高斯的可分离性来降低计算复杂度。

s = 5;
x = np.arange(np.floor(-4*s),np.ceil(4*s)+1)
g = 1/(np.sqrt(2*np.pi)*s)*np.exp(-x**2/(2*s**2))
d2g = (x**2 - s**2)/(s**4) * g
log = g * d2g[:,None] + g[:,None] * d2g

这里的技巧:gd2g 是一维函数。 g[:,None] 将 1D 函数翻转到一边,因此乘法会导致广播,从而导致 2D 输出。

我以这种方式编写内核,而不是一次性表达完整的 2D 方程,因为这会提高代码的效率:图像f 与内核log 的卷积可以写为:

conv(f, log) = conv(f, g * d2g[:,None] + g[:,None] * d2g)
             = conv(conv(f, g), d2g[:,None]) + conv(conv(f, g[:,None]), d2g)

也就是说,我们使用相对较小的 1D 内核计算 4 个卷积,而不是一个具有大 2D 内核的卷积。注意这里的实际顺序无关紧要:

一个应用一维内核g 并在结果上沿另一个轴应用一维内核d2g。这两个操作可以颠倒。 然后重复此过程,更改应用每个操作的轴。 最后一个将两个结果相加。

(可以在我写conv的地方使用cv2.filter2Dconv只是表示任何卷积函数,但是像filter2D这样的相关函数很好,因为内核都是对称的。)

【讨论】:

非常详细和好的答案!非常感谢你。只想要高斯然后是g = 1/(np.sqrt(2*np.pi)*s)*np.exp(-x**2/(2*s**2)),然后是G = g + g[:,None] 得到2D?将其显示为图像看起来不像我想象的高斯。 @Jesper:您需要将这两者相乘以形成二维内核。这相当于与列向量进行卷积,然后将结果与行向量进行卷积。 我明白了。非常感谢!

以上是关于OpenCV filter2d 给出不正确的结果的主要内容,如果未能解决你的问题,请参考以下文章

手撕OpenCV源码之filter2D

OpenCV:filter2D函数的计算效率

opencv filter2D()函数(卷积)计算原理

cv2.filter2D:如果内核大小太大,则没有结果

标量的OpenCV双矩阵除法产生不正确的结果

卷积处理过程模拟:用Python实现OpenCV函数filter2D等效的卷积功能