OpenCV filter2d 给出不正确的结果
OpenCV filter2d gives incorrect result
我目前正在尝试使用我自己构建的拉普拉斯核来过滤图像。但是,与 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
相比,结果应该是
这是非常不同的,我不知道如何正确地做到这一点。
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
这里的技巧:g
和 d2g
是一维函数。 g[:,None]
将 1D 函数放在一边,以便乘法产生广播,从而产生 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)
也就是说,我们计算 4 个具有相对较小的 1D 内核的卷积,而不是一个具有大 2D 内核的卷积。请注意,此处的实际顺序无关紧要:
- 应用一维内核
g
并在结果上沿另一个轴应用一维内核 d2g
。这两个操作可以反过来
- 然后重复这个过程,改变应用每个操作的轴。
- 最后将两个结果相加。
(在我写conv
的地方使用cv2.filter2D
是可以的。conv
只是表示任何卷积函数,但是像filter2D
这样的相关函数就可以了,因为内核都是对称的。)
我目前正在尝试使用我自己构建的拉普拉斯核来过滤图像。但是,与 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
相比,结果应该是
这是非常不同的,我不知道如何正确地做到这一点。
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
这里的技巧:g
和 d2g
是一维函数。 g[:,None]
将 1D 函数放在一边,以便乘法产生广播,从而产生 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)
也就是说,我们计算 4 个具有相对较小的 1D 内核的卷积,而不是一个具有大 2D 内核的卷积。请注意,此处的实际顺序无关紧要:
- 应用一维内核
g
并在结果上沿另一个轴应用一维内核d2g
。这两个操作可以反过来 - 然后重复这个过程,改变应用每个操作的轴。
- 最后将两个结果相加。
(在我写conv
的地方使用cv2.filter2D
是可以的。conv
只是表示任何卷积函数,但是像filter2D
这样的相关函数就可以了,因为内核都是对称的。)