感谢您阅读本文,请原谅我糟糕的英语,因为我不是母语人士。
例如Sobel算子: http://en.wikipedia.org/wiki/Sobel_operator
右侧为正,左侧为负(Gx),上方为正(Gy)。
我不是图像处理专家。但我认为大多数人都是从图像的左上角开始计算像素。既然我们在卷积过程中需要“翻转”内核,为什么在定义算子时Gx不采用镜像内核以获得更好的正则性呢?
我知道从技术上讲这不是一个编程问题。但这与编程有关。 例如,python的
scipy.ndimage
提供了sobel
函数。该函数使用左列为正、右列为负的内核。它与我能找到的所有有关图像处理的材料(包括维基百科文章)不同。是否有任何特殊原因导致实际实现与数学定义不同?
首先,让我稍微改一下你的问题:
为什么
scipy.ndimage
的 Sobel 算子版本似乎与维基百科上给出的定义相反?
这里通过并排比较来显示差异。对于输入,我们使用维基百科文章中的自行车图像:http://en.wikipedia.org/wiki/File:Bikesgray.jpg
import matplotlib.pyplot as plt
import numpy as np
import scipy.ndimage as ndimage
# Note that if we leave this as unsigned integers we'll have problems with
# underflow anywhere the gradient is negative. Therefore, we'll cast as floats.
z = ndimage.imread('Bikesgray.jpg').astype(float)
# Wikipedia Definition of the x-direction Sobel operator...
kernel = np.array([[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]], dtype=float)
sobel_wiki = ndimage.convolve(z, kernel)
# Scipy.ndimage version of the x-direction Sobel operator...
sobel_scipy = ndimage.sobel(z, axis=1)
fig, axes = plt.subplots(figsize=(6, 15), nrows=3)
axes[0].set(title='Original')
axes[0].imshow(z, interpolation='none', cmap='gray')
axes[1].set(title='Wikipedia Definition')
axes[1].imshow(sobel_wiki, interpolation='none', cmap='gray')
axes[2].set(title='Scipy.ndimage Definition')
axes[2].imshow(sobel_scipy, interpolation='none', cmap='gray')
plt.show()
请注意,这些值实际上已翻转。
其背后的逻辑是,Sobel 滤波器基本上是一个梯度算子(
numpy.gradient
相当于与 [1, 0, -1]
的卷积,除了边缘处)。维基百科给出的定义给出了梯度数学定义的负数。
例如,
numpy.gradient
给出与 scipy.ndimage
的 Sobel 滤波器类似的结果:
import matplotlib.pyplot as plt
import numpy as np
import scipy.ndimage as ndimage
z = ndimage.imread('/home/jofer/Bikesgray.jpg').astype(float)
sobel = ndimage.sobel(z, 1)
gradient_y, gradient_x = np.gradient(z)
fig, axes = plt.subplots(ncols=2, figsize=(12, 5))
axes[0].imshow(sobel, interpolation='none', cmap='gray')
axes[0].set(title="Scipy's Sobel")
axes[1].imshow(gradient_x, interpolation='none', cmap='gray')
axes[1].set(title="Numpy's Gradient")
plt.show()
因此,
scipy.ndimage
使用的约定与我们对数学梯度的期望一致。
快速旁注:通常所说的“索贝尔滤波器”实际上是由沿不同轴的两个索贝尔滤波器计算出的梯度幅值。在
scipy.ndimage
中,您可以将其计算为:
import matplotlib.pyplot as plt
import scipy.ndimage as ndimage
z = ndimage.imread('/home/jofer/Bikesgray.jpg').astype(float)
sobel = ndimage.generic_gradient_magnitude(z, ndimage.sobel)
fig, ax = plt.subplots()
ax.imshow(sobel, interpolation='none', cmap='gray')
plt.show()
在这种情况下使用哪种约定也并不重要,因为重要的是输入梯度的绝对值。
无论如何,对于大多数用例,具有可调节窗口的更平滑的梯度滤波器(例如
ndimage.gaussian_gradient_magnitude
)是边缘检测的更好选择。
sobel内核的标准形式是标准书籍中定义的,但是不同的库可以根据方便给出不同的结果,结果是否实现并不重要