从 cv2 解释 Sobel

Sig*_*gur 4 python sobel cv2

我试图从cv2Python 中理解 Sobel 卷积。

根据文档,Sobel 内核是

-1 0 1
-2 0 2
-1 0 1
Run Code Online (Sandbox Code Playgroud)

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

0 1 0
1 0 1
0 1 0
Run Code Online (Sandbox Code Playgroud)

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

因此,输出中的第一个条目应该是 2. 程序返回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
Run Code Online (Sandbox Code Playgroud)

输出

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]]
Run Code Online (Sandbox Code Playgroud)

And*_*kha 5

阅读文档页面的第二段:

本节中描述的函数和类的另一个共同特点是,与简单的算术函数不同,它们需要外推一些不存在的像素的值。例如,如果您想使用高斯 3x3 滤波器平滑图像,那么在处理每行最左侧的像素时,您需要它们左侧的像素,即图像外部的像素。你可以让这些像素与最左边的图像像素相同(“复制边界”外推法),或者假设所有不存在的像素都为零(“恒定边界”外推法),等等。OpenCV 使您能够指定外推方法。详情请参见下节中参数的功能borderInterpolate()和讨论borderType以及下面的各种功能。

让它按你的预期工作

为了让它按预期工作,您必须明确指定要使用零值插入边框。像这样:

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
Run Code Online (Sandbox Code Playgroud)

输出:

img
[[ 0.  1.  0.]
 [ 1.  0.  1.]
 [ 0.  1.  0.]]
sobelx64f
[[ 2.  0. -2.]
 [ 2.  0. -2.]
 [ 2.  0. -2.]]
Run Code Online (Sandbox Code Playgroud)

默认边框类型

默认值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
Run Code Online (Sandbox Code Playgroud)

输出:

BORDER_DEFAULT == BORDER_DEFAULT
BORDER_DEFAULT == BORDER_REFLECT101
BORDER_DEFAULT == BORDER_REFLECT_101
Run Code Online (Sandbox Code Playgroud)

并且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'
Run Code Online (Sandbox Code Playgroud)

你得到的解释

因此,默认的边界插值类型(即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
Run Code Online (Sandbox Code Playgroud)

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