scipy和numpy中的Sobel梯度计算有什么区别?

4

设置

我目前正在尝试使用Sobel滤波器计算图像梯度。 起初,我使用了scipy.ndimage.sobel函数,

sx = ndimage.sobel(im, axis=0,mode="constant")
sy = ndimage.sobel(im, axis=1,mode="constant")
sobel = np.hypot(sx,sy)
sobel *= 255 / np.max(sobel)

然而,这仅适用于我的图像中的(3x3) Sobel滤波器,但我想尝试更大的滤波器。因此,我尝试使用numpyscipy.signal计算图像梯度。首先,我再次尝试了(3x3)滤波器。

filter_x = np.array([[-1,0,1],[-2,0,2],[-1,0,1]], dtype=np.float)
filter_y = np.array([[1,2,1], [0,0,0], [-1,-2,-1]], dtype = np.float)
sx = signal.convolve2d(im,filter_x,mode="same",boundary="symm", fillvalue=0)
sy = signal.convolve2d(im,filter_y,mode="same",boundary="symm", fillvalue=0)
sobel = np.hypot(sx,sy)
sobel *= 255 / np.max(sobel)

此帖中所建议的。

问题

不幸的是,这两种方法会导致完全不同的结果,这已经在问题中提到过。 因此,我深入挖掘后发现scipy.ndimage.sobel使用的是correlate1d函数,而不是convolve2d或类似的任何东西(源代码)。不幸的是,由于其功能已被隐藏在已编译的_nd_image.pyd文件中,并位于我的conda环境中的site-packages文件夹中,因此无法查看correlate1d函数的内部源代码。因此,这里出现了我的问题:

问题

有人明确知道correlate1d正在计算什么以及它如何与convolve2d相比吗?

编辑

如Florian Drawitsch的答案中已经提到的,人们应该能够通过相关性替换卷积。但是,这些不同的结果是如何出现的?!


1
你可以在 github 上查看源代码。而它调用的 NI_Correlate1D 函数位于同一文件夹中的 ni_filters.c 文件中。 - Reti43
1
谢谢,我会查看一下! - pafi
1个回答

7
根据方法名correlate1dconvolve2d,我强烈怀疑前者计算相关性,而后者计算卷积。它们有什么区别?
一般来说,用核g对信号f进行卷积需要在操作之前翻转核:f*g(-t) 相反,用核g对信号f进行相关操作时,不需要翻转核:f*g(t) 使用卷积应用方向边缘检测核(如sobel核)因此应该会导致与使用相关性的结果相比较的反转边缘。让我们在代码中测试这一点:
import numpy as np
from scipy import signal
from PIL import Image
from matplotlib import pyplot as plt

img = Image.open('lena.png')
plt.imshow(img)

enter image description here

首先,让我们定义一个Sobel边缘检测卷积核:

g = np.asarray([[-1, 0, 1],
                [-2, 0, 2],
                [-1, 0, 1]])

现在让我们首先使用scipy的signal.convolve2d将图像与我们的卷积核进行卷积。
img_convolved = signal.convolve2d(img, g)
plt.imshow(img_convolved, cmap='gray')

enter image description here

...并放大到边缘:

plt.imshow(img_convolved[100:150,100:150], cmap='gray')

enter image description here

现在,让我们使用sicpy的signal.correlate2d将图像与我们的内核相关联。
img_correlated = signal.correlate2d(img, g)
plt.imshow(img_correlated, cmap='gray')

enter image description here

...并缩放到边缘:

plt.imshow(img_correlated[100:150,100:150], cmap='gray')

enter image description here

最后,让我们将相关结果与使用翻转的卷积核进行卷积的结果进行比较:
img_convolved_flipped = signal.convolve2d(img, np.fliplr(g))
plt.imshow(img_convolved, cmap='gray')

enter image description here

...并放大边缘:

plt.imshow(img_convolved_flipped[100:150,100:150], cmap='gray')

enter image description here

因此,scipy的signal.correlate2d(img, g)等同于signal.convolve2d(img, np.fliplr(g)) 编辑(有关2D代码示例的澄清):
请注意,在2D情况下,将信号f与内核g卷积涉及将内核沿着两个基准轴翻转:f*g(-t,-u)
因此,在我的代码中,我实际上应该将滤波器翻转两次:np.flipud(np.fliplr(g))。 我省略了这一点,因为对于垂直对称的Sobel滤波器而言不是必要的,但请记住这是一个特殊情况。

非常感谢您提供如此详细的答案。也许您需要补充说明,这些属性仅适用于对称或反对称的核函数,而不适用于任意核函数。 - pafi
我看到了你的编辑。你是对的。我想到的是一般可积函数的卷积和相关性,这有点误导人,当谈论矩阵的卷积时。对此我感到抱歉,再次感谢你! :) - pafi
我不确定为什么这会有所区别。在连续情况下,卷积和相关性的属性与离散情况是完全类似的。相比于翻转离散矩阵,您需要沿主轴翻转连续函数。 - Florian Drawitsch
我认为我明白你的困惑来自哪里,这可能是我造成的,因为我使用了非常松散的符号表示内核翻转。在我的回答中,我试图通过写g(-x)来表示内核翻转,这当然看起来像信号(或图像)被翻转了。我应该在这里使用不同的索引,比如g(-t),因为信号根本没有翻转,只是内核的值的顺序被索引(或在连续情况下进行积分方向)。我将更新我的回答,使其更加清晰。 - Florian Drawitsch
如果 g(x)=g(-x),则 g 对称;如果 g(x)=-g(-x),则 g 反对称。如果我们有这样的属性,我们将得到 (f*g)(-x)=(f**g)(x),如果 g 是对称的,或者 (f*g)(-x)=-(f**g)(x),如果 g 是反对称的。那就是我所说的,如果不清楚的话。 - pafi
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接