Sobel算子是什么?

5

我在Python中尝试了5种不同的Sobel算子实现方式,其中一种是我自己实现的,结果根本不同。

我的问题类似于这个问题,但其他实现中仍然存在我不理解的差异。

是否有关于Sobel算子的约定定义,是否总是等同于“图像梯度”?

甚至Sobel核的定义也随不同来源而异,根据维基百科的说法,它是[[1, 0, -1],[2, 0, -2],[1, 0, -1]],但其他来源则是[[-1, 0, 1],[-2, 0, 2],[-1, 0, 1]]

这是我用不同技巧尝试的代码:

from scipy import ndimage
import numpy as np
import cv2 as cv
from scipy import ndimage
from PIL import Image, ImageFilter

img = np.random.randint(0, 255, [10, 10]).astype(np.uint8)

def sobel_x(img) :
    return ndimage.convolve(img, np.array([[-1, 0, 1],[-2, 0, 2],[-1, 0, 1]]))

my_sobel = sobel_x(img)
_, numpy_sobel = np.gradient(img)
opencv_sobel = cv.Sobel(img, cv.CV_8UC1, 1, 0)
ndimage_sobel = ndimage.sobel(img, axis=0, mode="constant")
pil_sobel = np.array(Image.fromarray(img).filter(ImageFilter.Kernel((3, 3), (-1, 0, 1, -2, 0, 2, -1, 0, 1), 1, 0)))

print(my_sobel)
print(numpy_sobel)
print(opencv_sobel)
print(ndimage_sobel)
print(pil_sobel)

enter image description here


你可以看一下这个链接,了解Sobel算子在OpenCV中的实现。 - Yunus Temurlenk
2个回答

6

Sobel算子用于估计导数。

正确的Sobel算子定义以估计水平导数为:

  | 1  0 -1 |
  | 2  0 -2 | / 8
  | 1  0 -1 |

将图像的导数与8相除是重要的,这样可以得到正确的幅度。人们经常忽略它,因为他们不关心实际的导数,他们关心的是同一图像不同位置的梯度比较。在那里乘以8不会有任何影响,因此省略/8可以使事情变得简单。

您会发现有些地方定义了带有相反符号的卷积核。这些是卷积与相关应用的情况不同(通过反转卷积核区别),例如OpenCV的情况。这也可能是因为人们没有理解就复制代码导致梯度出现错误符号的情况。

但是,Sobel算子主要用于获取梯度幅值(水平和垂直导数平方和的平方根)。在这种情况下,反转符号已经无关紧要了。


请注意,np.gradient(img)可与[1,0,-1]/2进行卷积估计相媲美。这是另一种估计导数的方法。 Sobel在垂直方向上加入了正则化(==平滑)。


如果使用更有意义的测试图像,您将能够更好地理解每个实现。例如,尝试使用中间有白色正方形的黑色图像。您将能够比较估计梯度的强度、它们的方向(我假设某些库使用不同的x和y轴定义),并且您将能够看到正则化的效果。


1
根据维基百科,它是[[1, 0, -1],[2, 0, -2],[1, 0, 1]],但是根据其他来源,它是[[-1, 0, 1],[-2, 0, 2],[-1, 0, 1]]。
两者都用于检测垂直边缘。不同之处在于这些内核如何标记“左”和“右”边缘。
为了简单起见,让我们考虑一个一维的例子,让数组为
[0, 0, 255, 255, 255]
然后如果我们使用填充进行计算,则
内核 [2, 0, -2] 给出 [0, -510, -510, 0, 0]
内核 [-2, 0, 2] 给出 [0, 510, 510, 0, 0]
正如您所看到的,第一个核标记了价值的突然增加,其值为负数,而第二个核标记了价值的突然增加,其值为正数。请注意,这仅在需要区分左侧和右侧边缘时才相关,当您只想查找垂直边缘时,可以使用这两个中的任何一个,然后获取绝对值。

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