从 cv2 解释 Sobel

Interpreting Sobel from cv2

我试图从 Python 中的 cv2 理解 Sobel 卷积。

根据 documentation Sobel 内核是

-1 0 1
-2 0 2
-1 0 1

因此,我尝试将其应用于以下 img(二进制 3x3 数组):

0 1 0
1 0 1
0 1 0

现在,我在解释输出时遇到了问题。我手工计算得到了不同的结果。据我所知,我必须将内核居中于每个像素 (i,j) 并乘以元素并求和。

因此,输出 中的第一个条目应该是 2。程序 returns 0

我错了吗?希望如此。

代码

import cv2
import numpy as np

img = np.array([[0,1,0],[1,0,1],[0,1,0]]).astype(float)

# Output dtype = cv2.CV_8U
sobelx8u = cv2.Sobel(img,cv2.CV_8U,1,0,ksize=3)

# Output dtype = cv2.CV_64F. Then take its absolute and convert to cv2.CV_8U
sobelx64f = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)

abs_sobel64f = np.absolute(sobelx64f)
sobel_8u = np.uint8(abs_sobel64f)

print 'img'
print img

print 'sobelx8u'
print sobelx8u

print 'sobelx64f'
print sobelx64f

print 'abs_sobel64f'
print abs_sobel64f

print 'sobel_8u'
print sobel_8u

输出

img
[[ 0.  1.  0.]
 [ 1.  0.  1.]
 [ 0.  1.  0.]]
sobelx8u
[[0 0 0]
 [0 0 0]
 [0 0 0]]
sobelx64f
[[ 0.  0.  0.]
 [ 0.  0.  0.]
 [ 0.  0.  0.]]
abs_sobel64f
[[ 0.  0.  0.]
 [ 0.  0.  0.]
 [ 0.  0.  0.]]
sobel_8u
[[0 0 0]
 [0 0 0]
 [0 0 0]]

阅读您的 documentation 页面的第二段:

Another common feature of the functions and classes described in this section is that, unlike simple arithmetic functions, they need to extrapolate values of some non-existing pixels. For example, if you want to smooth an image using a Gaussian 3x3 filter, then, when processing the left-most pixels in each row, you need pixels to the left of them, that is, outside of the image. You can let these pixels be the same as the left-most image pixels (“replicated border” extrapolation method), or assume that all the non-existing pixels are zeros (“constant border” extrapolation method), and so on. OpenCV enables you to specify the extrapolation method. For details, see the function borderInterpolate() and discussion of the borderType parameter in the section and various functions below.

让它按预期工作

要使其按您预期的那样工作,您必须明确指定要用零值插入边框。像这样:

import cv2
import numpy as np

img = np.array([[0,1,0],[1,0,1],[0,1,0]]).astype(float)

border = cv2.borderInterpolate(0, 1, cv2.BORDER_CONSTANT)
sobelx64f = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3, borderType=border)

print 'img'
print img

print 'sobelx64f'
print sobelx64f

输出:

img
[[ 0.  1.  0.]
 [ 1.  0.  1.]
 [ 0.  1.  0.]]
sobelx64f
[[ 2.  0. -2.]
 [ 2.  0. -2.]
 [ 2.  0. -2.]]

默认边框类型

borderType 的默认值是 BORDER_DEFAULT,在我的机器上与 BORDER_REFLECT_101 相同。您可以 运行 这个脚本在您的机器上确认它:

import cv2

for var in dir(cv2):
    if not var.startswith('BORDER_'): continue
    if cv2.__dict__[var] == cv2.BORDER_DEFAULT:
        print 'BORDER_DEFAULT ==', var

输出:

BORDER_DEFAULT == BORDER_DEFAULT
BORDER_DEFAULT == BORDER_REFLECT101
BORDER_DEFAULT == BORDER_REFLECT_101

BORDER_REFLECT_101 的工作方式与您的结果完全一致。以下是对不同边框类型的解释:

BORDER_REPLICATE:     aaaaaa|abcdefgh|hhhhhhh
BORDER_REFLECT:       fedcba|abcdefgh|hgfedcb
BORDER_REFLECT_101:   gfedcb|abcdefgh|gfedcba
BORDER_WRAP:          cdefgh|abcdefgh|abcdefg
BORDER_CONSTANT:      iiiiii|abcdefgh|iiiiiii  with some specified 'i'

解释你得到的东西

所以默认的边界插值类型(BORDER_REFLECT_101)使你的数组​​在计算之前看起来像这样:

0 1 0 1 0
1 0 1 0 1
0 1 0 1 0
1 0 1 0 1
0 1 0 1 0

通过简单的算术,您可以确认将 Sobel 内核应用于内部 3x3 像素后的正确值全为零 – 这就是您通过 运行ning 脚本得到的值。