什么是索贝尔算子?
What is the Sobel operator?
我在 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)
根据维基百科,它是 [[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]
如您所见,值的突然增加由第一个内核标记为 negative 值,第二个内核标记为 positive 值。请注意,仅当您需要区分左边缘和右边缘时才相关,当您只想找到垂直边缘时,您可以使用上述两个中的任何一个,然后获得绝对值。
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 轴定义),并且您将能够看到正则化的效果。
我在 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)
根据维基百科,它是 [[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]
如您所见,值的突然增加由第一个内核标记为 negative 值,第二个内核标记为 positive 值。请注意,仅当您需要区分左边缘和右边缘时才相关,当您只想找到垂直边缘时,您可以使用上述两个中的任何一个,然后获得绝对值。
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 轴定义),并且您将能够看到正则化的效果。