问题描述
设置
我目前正在尝试使用 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)索贝尔 *= 255/np.max(索贝尔)
然而,这仅适用于 (3x3) sobel 过滤器到我的图像,但我想尝试更大的过滤器.因此,我尝试使用 numpy
和 scipy.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)索贝尔 *= 255/np.max(索贝尔)
如
首先,让我们定义一个 sobel 边缘检测内核:
g = np.asarray([[-1, 0, 1],[-2, 0, 2],[-1, 0, 1]])
现在让我们首先使用 sicpy 的 signal.convolve2d
img_convolved = signal.convolve2d(img, g)plt.imshow(img_convolved, cmap='gray')
...并放大边缘:
plt.imshow(img_convolved[100:150,100:150], cmap='gray')
现在,让我们使用 sicpy 的 signal.correlate2d
img_correlated = signal.correlate2d(img, g)plt.imshow(img_correlated, cmap='gray')
...并放大边缘:
plt.imshow(img_correlated[100:150,100:150], cmap='gray')
最后,让我们将相关结果与使用翻转内核进行卷积时的结果进行比较:
img_convolved_flipped = signal.convolve2d(img, np.fliplr(g))plt.imshow(img_convolved, cmap='gray')
...并放大边缘:
plt.imshow(img_convolved_flipped[100:150,100:150], cmap='gray')
所以,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 滤波器不是必需的,但请记住,这是一个特例.
Setting
I am currently trying to calcualte the image gradients with sobel filters.At first I used scipy.ndimage.sobel
function via
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)
However this only applies (3x3) sobel filter to my image, but I want to try bigger filter. Therefore I tried to calculated the image gradient with numpy
and scipy.signal
. First of all I tried (3x3) filter again.
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)
as suggestet in this post.
Problem
Unfortunately these two approaches are leading to completely different results, which was already mentioned in this question.So I digged a little bit deeper and found that scipy.ndimage.sobel
uses the correlate1d
function instead of convolve2d
or anything similar (source code). Unfortunately it is not possible to look inside the souce code for the correlate1d
function since its functionallty is hidden inside the already compiled _nd_image.pyd
file in the site-packages folder of my conda enviroment. So here comes my question:
Question
Does anyone explicitely know, what exactly is being calculated by correlate1d
and in what way it is comparable to convolve2d
?
Edit
As already mentioned in the answer of Florian Drawitsch one should be able to replace convolution by correlation. But then again, how do these different results appear?!
Judging by the method names correlate1d
and convolve2d
I would strongly suspect that the former computes a correlation, whereas the latter computes a convolution. What's the difference?
Generally speaking, a convolution of a signal f
with a kernel g
involves flipping the kernel before the operation: f*g(-t)
In contrast, a correlation of a signal f
with a kernel g
is carried out without flipping the kernel: f*g(t)
Applying a directional edge detection kernel (like a sobel kernel) using convolution should therefore result in inverted edges compared to the result using correlation. Let's test this in code:
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)
First, let's define a sobel edge detection kernel:
g = np.asarray([[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]])
Now let's first convolve the image with our kernel using sicpy's signal.convolve2d
img_convolved = signal.convolve2d(img, g)
plt.imshow(img_convolved, cmap='gray')
... and zoom in on a edge:
plt.imshow(img_convolved[100:150,100:150], cmap='gray')
Now, let's correlate the image with our kernel using sicpy's signal.correlate2d
img_correlated = signal.correlate2d(img, g)
plt.imshow(img_correlated, cmap='gray')
... and zoom in on a edge:
plt.imshow(img_correlated[100:150,100:150], cmap='gray')
Finally, let's compare the correlation result with what happens if we do a convolution with a flipped kernel:
img_convolved_flipped = signal.convolve2d(img, np.fliplr(g))
plt.imshow(img_convolved, cmap='gray')
... and zoom in on a edge:
plt.imshow(img_convolved_flipped[100:150,100:150], cmap='gray')
So, scipy's signal.correlate2d(img, g)
is equivalent to signal.convolve2d(img, np.fliplr(g))
EDIT (Clarification for the 2D code example):
Please note that in the 2D case a convolution of a signal f
with a kernel g
involves flipping the kernel around both cardinal axes: f*g(-t,-u)
.
Therefore, in my code I should have actually flipped the filter twice: np.flipud(np.fliplr(g))
. I have omitted this since is not necessary for the vertically symmetric sobel filter but keep in mind that this was a special case.
这篇关于scipy 和 numpy sobel 梯度计算的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!