opencv之图像梯度

图像梯度

图像梯度计算的是图像变化的速度。对于图像的边缘部分,其灰度值变化较大,梯度值也较大;相反,对于图像中比较平滑的部分,其灰度值变化较小,相应的梯度值也较小。一般情况下,图像梯度计算的是图像的边缘信息。

严格来讲,图像梯度计算需要求导数,但是图像梯度一般通过计算像素值的差来得到梯度的近似值(我们把这种方法称为求图像梯度)。

例如,图1中的左右两幅图分别描述了图像的水平边界和垂直边界。

图1

针对左图,通过垂直方向的线条A和线条B的位置,可以计算图像水平方向的边界:

  • 对于线条A和线条B,其右侧像素值与左侧像素值的差值不为零,因此是边界。
  • 对于其余列,其右侧像素值与左侧像素值的差值均为零,因此不是边界。

对于其余列,其右侧像素值与左侧像素值的差值均为零,因此不是边界。

  • 对于线条A和线条B,其下侧像素值与上侧像素值的差值不为零,因此是边界。
  • 对于其余行,其下侧像素值与上侧像素值的差值均为零,因此不是边界。

将上述运算关系进一步优化,可以得到更复杂的边缘信息。本章将关注Sobel算子、Scharr算子和Laplacian算子的使用。

Sobel理论基础

Sobel算子是一种离散的微分算子,该算子结合了高斯平滑和微分求导运算。该算子利用局部差分寻找边缘,计算所得的是一个梯度的近似值。

Sobel算子如图2所示。

图2

需要说明的是,滤波器通常是指由一幅图像根据像素点 ( x , y ) (x, y) (x,y)临近的区域计算得到另外一幅新图像的算法。因此,滤波器是由邻域及预定义的操作构成的。滤波器规定了滤波时所采用的形状以及该区域内像素值的组成规律。滤波器也被称为“掩模”、“核”、“模板”、“窗口”、“算子”等。一般信号领域将其称为“滤波器”,数学领域将其称为“核”。本章中出现的滤波器多数为“线性滤波器”,也就是说,滤波的目标像素点的值等于原始像素值及其周围像素值的加权和。这种基于线性核的滤波,就是我们所熟悉的卷积。在本章中,为了方便说明,直接使用“算子”来表示各种算子所使用的滤波器。例如,本章中所说的“Sobel算子”通常是指Sobel滤波器。

假定有原始图像src,下面对Sobel算子的计算进行讨论。

计算水平方向偏导数的近似值

将Sobel算子与原始图像src进行卷积计算,可以计算水平方向上的像素值变化情况。例如,当Sobel算子的大小为3×3时,水平方向偏导数Gx的计算方式为:

上式中,src是原始图像,假设其中有9个像素点,如图3所示:

图3

如果要计算像素点P5的水平方向偏导数 P 5 x P5_x P5x,则需要利用Sobel算子及 P 5 P5 P5邻域点,所使用的公式为:

P 5 x = ( P 3 − P 1 ) + 2 ⋅ ( P 6 − P 4 ) + ( P 9 − P 7 ) P5_x=(P3-P1) + 2·(P6-P4) + (P9-P7) P5x=(P3P1)+2(P6P4)+(P9P7)

即用像素点 P 5 P5 P5右侧像素点的像素值减去其左侧像素点的像素值。其中,中间像素点 ( P 4 和 P 6 ) (P4和P6) P4P6距离像素点 P 5 P5 P5较近,其像素值差值的权重为2;其余差值的权重为1。

计算垂直方向偏导数的近似值

将Sobel算子与原始图像src进行卷积计算,可以计算垂直方向上的变化情况。例如,当Sobel算子的大小为3×3时,垂直方向偏导数 G y G_y Gy的计算方式为:

上式中,src是原始图像,假设其中有9个像素点,如图4所示。

图4

如果要计算像素点P5的垂直方向偏导数 P 5 y P5_y P5y,则需要利用Sobel算子及 P 5 P5 P5邻域点,所使用的公式为:

P 5 y = ( P 7 − P 1 ) + 2 ⋅ ( P 8 − P 2 ) + ( P 9 − P 3 ) P5_y=(P7-P1) + 2·(P8-P2) + (P9-P3) P5y=(P7P1)+2(P8P2)+(P9P3)

式中,使用像素点P5下一行像素点的像素值减去上一行像素点的像素值。其中,中间像素点( P 2 P2 P2 P 8 P8 P8)距离像素点 P 5 P5 P5较近,其像素值差值的权重为2;其余差值的权重为1。

Sobel算子及函数使用

在OpenCV内,使用函数cv2.Sobel()实现Sobel算子运算,其语法形式为:

dst = cv2.Sobel( src, ddepth, dx, dy[, ksize[, scale[, delta[, borderType]]]] )

式中:

  • dst代表目标图像。
  • src代表原始图像。
  • ddepth代表输出图像的深度。其具体对应关系如表1所示。

表1
  • dx代表x方向上的求导阶数。
  • dy代表y方向上的求导阶数。
  • ksize代表Sobel核的大小。该值为-1时,则会使用Scharr算子进行运算。
  • scale代表计算导数值时所采用的缩放因子,默认情况下该值是1,是没有缩放的。
  • delta代表加在目标图像dst上的值,该值是可选的,默认为0。
  • borderType代表边界样式。该参数的具体类型及值如表2所示。

表2
参数ddepth

在函数cv2.Sobel()的语法中规定,可以将函数cv2.Sobel()内ddepth参数的值设置为-1,让处理结果与原始图像保持一致。但是,如果直接将参数ddepth的值设置为-1,在计算时得到的结果可能是错误的。

在实际操作中,计算梯度值可能会出现负数。如果处理的图像是8位图类型,则在ddepth的参数值为-1时,意味着指定运算结果也是8位图类型,那么所有负数会自动截断为0,发生信息丢失。为了避免信息丢失,在计算时要先使用更高的数据类型cv2.CV_64F,再通过取绝对值将其映射为cv2.CV_8U(8位图)类型。所以,通常要将函数cv2.Sobel()内参数ddepth的值设置为“cv2.CV_64F”。

下面对参数ddepth值的设定做一个简要的说明。

例如,图5中的原始图像(左图)是一幅二值图像,图中黑色部分的像素值为0,白色部分的像素值为1。在计算A线条所在位置和B线条所在位置的近似偏导数时:

  • 针对A线条所在列,右侧像素值减去左侧像素值所得近似偏导数的值为-1。
  • 针对B线条所在列,右侧像素值减去左侧像素值所得近似偏导数的值为1。

针对上述偏导数结果进行不同方式的处理,可能会得到不同的结果,例如:

  • 直接计算。此时,A线条位置的值为负数,B线条位置的值为正数。在显示时,由于上述负值不在8位图范围内,因此要做额外处理。将A线条处的负数偏导数处理为0, B线条处的正数偏导数保持不变。在这种情况下,显示结果如图5中间的图所示。

  • 计算绝对值。此时,A线条处的负数偏导数被处理正数,B线条处的正数偏导数保持不变。在显示时,由于上述值在8位图的表示范围内,因此不再对上述值进行处理,此时,显示结果如图5中的右图所示。

图5

上述问题在计算垂直方向的近似偏导数时同样存在。例如,图6中的原始图像(左图)是一幅二值图像,图中黑色部分的像素值为0,白色部分的像素值为1。计算A线条所在位置和B线条所在位置的近似偏导数时:

  • 针对A线条所在行,下方像素值减去上方像素值所得近似偏导数为-1。
  • 针对B线条所在行,下方像素值减去上方像素值所得近似偏导数为1。

针对上述偏导数结果进行不同方式的处理,可能会得到不同的结果,例如:

  • 直接计算。此时,A线条位置的值为负数,B线条位置的值为正数。在显示时,上述值不在8位图的表示范围内,因此需要进行额外处理。将A线条处的负数偏导数处理为0, B线条处的正数偏导数保持不变。此时,显示结果如图6中间的图所示。

  • 计算绝对值。此时,A线条处的负数偏导数被处理正数,B线条处的正数偏导数保持不变。在显示时,由于上述值在8位图的表示范围内,因此不再对上述值进行处理,此时,显示结果如图6中的右图所示。

图6

经过上述分析可知,为了让偏导数正确地显示出来,需要将值为负数的近似偏导数转换为正数。即,要将偏导数取绝对值,以保证偏导数总能正确地显示出来。

例如,图7描述了如何计算偏导数。

图7

为了得到结果为正数的偏导数,需要对图7中计算的偏导数取绝对值,如下:

∣ P 5 x ∣ = ∣ ( P 3 − P 1 ) + 2 ⋅ ( P 6 − P 4 ) + ( P 9 − P 7 ) ∣ |P5x| = |(P3-P1) + 2·(P6-P4) + (P9-P7)| P5x=(P3P1)+2(P6P4)+(P9P7)

∣ P 5 y ∣ = ∣ ( P 7 − P 1 ) + 2 ⋅ ( P 8 − P 2 ) + ( P 9 − P 3 ) ∣ |P5y| = |(P7-P1) + 2·(P8-P2) + (P9-P3)| P5y=(P7P1)+2(P8P2)+(P9P3)

当然,在需要时,还可以进行如下处理:

P 5 S o b e l = ∣ P 5 x ∣ + ∣ P 5 y ∣ = ∣ ( P 3 − P 1 ) + 2 ⋅ ( P 6 − P 4 ) + ( P 9 − P 7 ) ∣ + ∣ ( P 7 − P 1 ) + 2 ⋅ ( P 8 − P 2 ) + ( P 9 − P 3 ) ∣ P5Sobel = |P5x| + |P5y| = |(P3-P1) +2·(P6-P4) + (P9-P7)| + |(P7-P1) + 2·(P8-P2) + (P9-P3)| P5Sobel=P5x+P5y=(P3P1)+2(P6P4)+(P9P7)+(P7P1)+2(P8P2)+(P9P3)

经过以上分析,我们得知:在实际操作中,计算梯度值可能会出现负数。通常处理的图像是8位图类型,如果结果也是该类型,那么所有负数会自动截断为0,发生信息丢失。所以,为了避免信息丢失,我们在计算时使用更高的数据类型cv2.CV_64F,再通过取绝对值将其映射为cv2.CV_8U(8位图)类型。

在OpenCV中,使用函数cv2.convertScaleAbs()对参数取绝对值,该函数的语法格式为:

dst = cv2.convertScaleAbs( src [, alpha[, beta]] )

上式中:

  • dst代表处理结果。

  • src代表原始图像。

  • alpha代表调节系数,该值是可选值,默认为1。

  • beta代表调节亮度值,该值是默认值,默认为0。

这里,该函数的作用是将原始图像src转换为256色位图,其可以表示为:

dst=saturate(src*alpha+beta)

式中,saturate()表示计算结果的最大值是饱和值,例如当src*alpha+beta的值超过255时,其取值为255。

方向

在函数cv2.Sobel()中,参数dx表示x轴方向的求导阶数,参数dy表示y轴方向的求导阶数。参数dx和dy通常的值为0或者1,最大值为2。如果是0,表示在该方向上没有求导。当然,参数dx和参数dy的值不能同时为0。

参数dx和参数dy可以有多种形式的组合,主要包含:

  • 计算x方向边缘(梯度):dx=1, dy=0。
  • 计算y方向边缘(梯度):dx=0, dy=1。
  • 参数dx与参数dy的值均为1:dx=1, dy=1。
  • 计算x方向和y方向的边缘叠加:通过组合方式实现。

下面分别对上述情况进行简要说明。

计算x方向边缘(梯度):dx=1, dy=0

如果想只计算x方向(水平方向)的边缘,需要将函数cv2.Sobel()的参数dx和dy的值设置为“dx=1, dy=0”。当然,也可以设置为“dx=2,dy=0”。此时,会仅仅获取水平方向的边缘信息,此时的语法格式为:

dst = cv2.Sobel( src , ddepth , 1 , 0 )

使用该语句获取边缘图的示例如图8所示,其中左图为原始图像,右图为获取的边缘图。

图8

计算y方向边缘(梯度):dx=0, dy=1

如果想只计算y方向(垂直方向)的边缘,需要将函数cv2.Sobel()的参数dx和dy的值设置为“dx=0, dy=1”。当然,也可以设置为“dx=0,dy=2”。此时,会仅仅获取垂直方向的边缘信息,此时的语法格式为:

dst = cv2.Sobel( src , ddepth , 0 , 1 )

使用该语句获取边缘图的示例如图9所示,其中左图为原始图像,右图为获取的边缘图。

图9

参数dx与参数dy的值均为1:dx=1, dy=1

可以将函数cv2.Sobel()的参数dx和dy的值设置为“dx=1, dy=1”,也可以设置为“dx=2, dy=2”,或者两个参数都不为零的其他情况。此时,会获取两个方向的边缘信息,此时的语法格式为:

dst = cv2.Sobel( src , ddepth , 1 , 1 )

使用该语句获取边缘图的示例如图10所示,其中左图为原始图像,右图为获取的边缘图,仔细观察可以看到图中仅有若干个微小白点,每个点的大小为一个像素。

图10

计算x方向和y方向的边缘叠加

如果想获取x方向和y方向的边缘叠加,需要分别获取水平方向、垂直方向两个方向的边缘图,然后将二者相加。此时的语法格式为:

dx= cv2.Sobel( src , ddepth , 1 , 0 )
dy= cv2.Sobel( src , ddepth , 0 , 1 )
dst=cv2.addWeighted( src1 , alpha , src2 , beta , gamma )

使用上述语句获取边缘图的示意图如图11所示,其中左图为原始图像,右图为获取的边缘图。

图11
实例

本节将通过实例来介绍如何使用函数cv2.Sobel()获取图像边缘信息。

【例2】使用函数cv2.Sobel()获取图像水平方向的边缘信息。

在本例中,将参数ddepth的值设置为-1,参数dx和dy的值设置为“dx=1, dy=0”。

根据题目要求及分析,设计程序如下:

import cv2
o = cv2.imread('Sobel4.bmp', cv2.IMREAD_GRAYSCALE)
Sobelx = cv2.Sobel(o, -1,1,0)
cv2.imshow("original", o)
cv2.imshow("x", Sobelx)
cv2.waitKey()
cv2.destroyAllWindows()

运行程序,结果如图12所示。

图12

【分析】

从程序可以看出,当参数ddepth的值为-1时,只得到了图中黑色框的右边界。这是因为,左边界在运算时得到了负值,其在显示时被调整为0,所以没有显示出来。要想获取左边界的值(将其显示出来),必须将参数ddepth的值设置为更大范围的数据结构类型,并将其映射到8位图像内。

【例3】使用函数cv2.Sobel()获取图像水平方向的完整边缘信息。在本例中,将参数ddepth的值设置为cv2.CV_64F,并使用函数cv2.convertScaleAbs()对cv2.Sobel()的计算结果取绝对值。

根据题目要求及分析,设计程序如下:

import cv2
o = cv2.imread('Sobel4.bmp', cv2.IMREAD_GRAYSCALE)
Sobelx = cv2.Sobel(o, cv2.CV_64F,1,0)
Sobelx = cv2.convertScaleAbs(Sobelx)
cv2.imshow("original", o)
cv2.imshow("x", Sobelx)
cv2.waitKey()
cv2.destroyAllWindows()

运行程序,结果如图13所示。

图13

【分析】

从程序可以看出,将函数cv2.Sobel()内参数ddepth的值设置为“cv2.CV_64F”,参数dx和dy的值设置为“dx=1, dy=0”后,执行该函数,再对该函数的结果计算绝对值,可以获取图像在水平方向的完整边缘信息。

【例4】

使用函数cv2.Sobel()获取图像垂直方向的边缘信息。

在本例中,将参数ddepth的值设置为cv2.CV_64F,并使用函数cv2.convertScaleAbs()对cv2.Sobel()的计算结果取绝对值。

根据题目要求及分析,设计程序如下:

import cv2
o = cv2.imread('Sobel4.bmp', cv2.IMREAD_GRAYSCALE)
Sobely = cv2.Sobel(o, cv2.CV_64F,0,1)
Sobely = cv2.convertScaleAbs(Sobely)
cv2.imshow("original", o)
cv2.imshow("y", Sobely)
cv2.waitKey()
cv2.destroyAllWindows()

运行程序,结果如图14所示。

图14

【分析】

从程序可以看出,将参数ddepth的值设置为“cv2.CV_64F”,参数dx和dy的值设置为“dx=0, dy=1”的情况下,使用函数cv2.convertScaleAbs()对函数cv2.Sobel()的计算结果取绝对值,可以获取图像在垂直方向的完整边缘信息。

根据题目要求,设计程序如下:

import cv2
o = cv2.imread('Sobel4.bmp', cv2.IMREAD_GRAYSCALE)
Sobelxy=cv2.Sobel(o, cv2.CV_64F,1,1)
Sobelxy=cv2.convertScaleAbs(Sobelxy)
cv2.imshow("original", o)
cv2.imshow("xy", Sobelxy)
cv2.waitKey()
cv2.destroyAllWindows()

运行程序,结果如图15所示。

图15

【例6】

计算函数cv2.Sobel()在水平、垂直两个方向叠加的边缘信息。

根据题目要求,设计程序如下:

import cv2
o = cv2.imread('Sobel4.bmp', cv2.IMREAD_GRAYSCALE)
Sobelx = cv2.Sobel(o, cv2.CV_64F,1,0)
Sobely = cv2.Sobel(o, cv2.CV_64F,0,1)
Sobelx = cv2.convertScaleAbs(Sobelx)
Sobely = cv2.convertScaleAbs(Sobely)
Sobelxy =  cv2.addWeighted(Sobelx,0.5, Sobely,0.5,0)
cv2.imshow("original", o)
cv2.imshow("xy", Sobelxy)
cv2.waitKey()
cv2.destroyAllWindows()

运行程序,结果如图16所示。

图16

【分析】

从程序可以看出,本例中首先分别计算x方向的边缘、y方向的边缘,接下来使用函数cv2.addWeighted()对两个方向的边缘进行叠加。在最终的叠加边缘结果中,同时显示两个方向的边缘信息。

【例7】

使用不同方式处理图像在两个方向的边缘信息。在本例中,分别使用两种不同的方式获取边缘信息。

  • 方式1:分别使用“dx=1, dy=0”和“dx=0, dy=1”计算图像在水平方向和垂直方向的边缘信息,然后将二者相加,构成两个方向的边缘信息。
  • 方式2:将参数dx和dy的值设为“dx=1, dy=1”,获取图像在两个方向的梯度。

根据题目要求,设计程序如下:

import cv2
o = cv2.imread('lena.bmp', cv2.IMREAD_GRAYSCALE)
Sobelx = cv2.Sobel(o, cv2.CV_64F,1,0)
Sobely = cv2.Sobel(o, cv2.CV_64F,0,1)
Sobelx = cv2.convertScaleAbs(Sobelx)
Sobely = cv2.convertScaleAbs(Sobely)
Sobelxy =  cv2.addWeighted(Sobelx,0.5, Sobely,0.5,0)
Sobelxy11=cv2.Sobel(o, cv2.CV_64F,1,1)
Sobelxy11=cv2.convertScaleAbs(Sobelxy11)
cv2.imshow("original", o)
cv2.imshow("xy", Sobelxy)
cv2.imshow("xy11", Sobelxy11)
cv2.waitKey()
cv2.destroyAllWindows()

运行程序,结果如图17所示,其中左图为原始图像,中间的图为方式1对应的图像,右图为方式2对应的图像。

图17

Scharr算子及函数使用

在离散的空间上,有很多方法可以用来计算近似导数,在使用3×3的Sobel算子时,可能计算结果并不太精准。OpenCV提供了Scharr算子,该算子具有和Sobel算子同样的速度,且精度更高。可以将Scharr算子看作对Sobel算子的改进,其核通常为:

OpenCV提供了函数cv2.Scharr()来计算Scharr算子,其语法格式如下:

dst = cv2.Scharr( src, ddepth, dx, dy[, scale[, delta[, borderType]]] )

式中:

  • dst代表输出图像。
  • src代表原始图像。
  • ddepth代表输出图像深度。该值与函数cv2.Sobel()中的参数ddepth的含义相同,具体可以参考表1。
  • dx代表x方向上的导数阶数。
  • dy代表y方向上的导数阶数。
  • scale代表计算导数值时的缩放因子,该项是可选项,默认值是1,表示没有缩放。
  • delta代表加到目标图像上的亮度值,该项是可选项,默认值为0。
  • borderType代表边界样式。具体可以参考表2。

在函数cv2.Sobel()中介绍过,如果ksize=-1,则会使用Scharr滤波器。因此,如下语句:

dst=cv2.Scharr(src, ddepth, dx, dy)

dst=cv2.Sobel(src, ddepth, dx, dy, -1)

是等价的。函数cv2.Scharr()和函数cv2.Sobel()的使用方式基本一致。首先,需要注意的是,参数ddepth的值应该设置为“cv2.CV_64F”,并对函数cv2.Scharr()的计算结果取绝对值,才能保证得到正确的处理结果。具体语句为:

dst=Scharr(src, cv2.CV_64F, dx, dy)
dst= cv2.convertScaleAbs(dst)

另外,需要注意的是,在函数cv2.Scharr()中,要求参数dx和dy满足条件:

dx >= 0 && dy >= 0 && dx+dy == 1

因此,参数dx和参数dy的组合形式有:

  • 计算x方向边缘(梯度):dx=1, dy=0。
  • 计算y方向边缘(梯度): dx=0, dy=1。
  • 计算x方向与y方向的边缘叠加:通过组合方式实现。

下面分别对上述情况进行简要说明。

计算x方向边缘(梯度):dx=1, dy=0

此时,使用的语句是:

dst=Scharr(src, ddpeth, dx=1, dy=0)

计算y方向边缘(梯度):dx=0, dy=1

此时,使用的语句是:

dst=Scharr(src, ddpeth, dx=0, dy=1)

计算x方向与y方向的边缘叠加

将两个方向的边缘相加,使用的语句是:

dx=Scharr(src, ddpeth, dx=1, dy=0)
dy=Scharr(src, ddpeth, dx=0, dy=1)
Scharrxy=cv2.addWeighted(dx,0.5, dy,0.5,0)

需要注意的是,参数dx和dy的值不能都为1。例如,如下语句是错误的:

dst=Scharr(src, ddpeth, dx=1, dy=1)

【例8】使用函数cv2.Scharr()获取图像水平方向的边缘信息。

根据题目要求,设计程序如下:

import cv2
o = cv2.imread('Scharr.bmp', cv2.IMREAD_GRAYSCALE)
Scharrx = cv2.Scharr(o, cv2.CV_64F,1,0)
Scharrx = cv2.convertScaleAbs(Scharrx)
cv2.imshow("original", o)
cv2.imshow("x", Scharrx)
cv2.waitKey()
cv2.destroyAllWindows()

【例9】使用函数cv2.Scharr()获取图像垂直方向的边缘信息。

根据题目要求,设计程序如下:

import cv2
o = cv2.imread('Scharr.bmp', cv2.IMREAD_GRAYSCALE)
Scharry = cv2.Scharr(o, cv2.CV_64F,0,1)
Scharry = cv2.convertScaleAbs(Scharry)
cv2.imshow("original", o)
cv2.imshow("y", Scharry)
cv2.waitKey()
cv2.destroyAllWindows()

运行程序,结果如图18所示。

图18

【例10】使用函数cv2.Scharr()实现水平方向和垂直方向边缘叠加的效果。

根据题目要求,设计程序如下

import cv2
o = cv2.imread('scharr.bmp', cv2.IMREAD_GRAYSCALE)
scharrx = cv2.Scharr(o, cv2.CV_64F,1,0)
scharry = cv2.Scharr(o, cv2.CV_64F,0,1)
scharrx = cv2.convertScaleAbs(scharrx)
scharry = cv2.convertScaleAbs(scharry)
scharrxy =  cv2.addWeighted(scharrx,0.5, scharry,0.5,0)
cv2.imshow("original", o)
cv2.imshow("xy", scharrxy)
cv2.waitKey()
cv2.destroyAllWindows()

运行程序,结果如图19所示。

图19

【例11】观察将函数cv2.Scharr()的参数dx、dy同时设置为1时,程序的运行情况。

根据题目要求,设计程序如下:

import cv2
import numpy as np
o = cv2.imread('image\\Scharr.bmp', cv2.IMREAD_GRAYSCALE)
Scharrxy11=cv2.Scharr(o, cv2.CV_64F,1,1)
cv2.imshow("original", o)
cv2.imshow("xy11", Scharrxy11)
cv2.waitKey()
cv2.destroyAllWindows()

运行程序,报错如下:

【分析】

需要注意,不允许将函数cv2.Scharr()的参数dx和dy的值同时设置为1。因此,本例中将这两个参数的值都设置为1后,程序会报错。

【例12】

使用函数cv2.Sobel()完成Scharr算子的运算。当函数cv2.Sobel()中ksize的参数值为-1时,就会使用Scharr算子进行运算。因此,

dst=cv2.Sobel(src, ddpeth, dx, dy, -1)

等价于

dst=cv2.Scharr(src, ddpeth, dx, dy)

根据题目要求及分析,设计程序如下:

import cv2
o = cv2.imread('Sobel4.bmp', cv2.IMREAD_GRAYSCALE)
Scharrx = cv2.Sobel(o, cv2.CV_64F,1,0, -1)
Scharry = cv2.Sobel(o, cv2.CV_64F,0,1, -1)
Scharrx = cv2.convertScaleAbs(Scharrx)
Scharry = cv2.convertScaleAbs(Scharry)
cv2.imshow("original", o)
cv2.imshow("x", Scharrx)
cv2.imshow("y", Scharry)
cv2.waitKey()
cv2.destroyAllWindows()

运行程序,结果如图20所示,其中左图为原始图像,中间的图为水平边缘图像,右图为垂直边缘图像。

图20

Sobel算子和Scharr算子的比较

Sobel算子的缺点是,当其核结构较小时,精确度不高,而Scharr算子具有更高的精度。Sobel算子和Scharr算子的核结构如图21所示。

图21

【例13】分别使用Sobel算子和Scharr算子计算一幅图像的水平边缘和垂直边缘的叠加信息。根据题目要求,设计程序如下:

import cv2
o = cv2.imread('lena.bmp', cv2.IMREAD_GRAYSCALE)
Sobelx = cv2.Sobel(o, cv2.CV_64F,1,0, ksize=3)
Sobely = cv2.Sobel(o, cv2.CV_64F,0,1, ksize=3)
Sobelx = cv2.convertScaleAbs(Sobelx)
Sobely = cv2.convertScaleAbs(Sobely)
Sobelxy =  cv2.addWeighted(Sobelx,0.5, Sobely,0.5,0)
Scharrx = cv2.Scharr(o, cv2.CV_64F,1,0)
Scharry = cv2.Scharr(o, cv2.CV_64F,0,1)
Scharrx = cv2.convertScaleAbs(Scharrx)
Scharry = cv2.convertScaleAbs(Scharry)
Scharrxy =  cv2.addWeighted(Scharrx,0.5, Scharry,0.5,0)
cv2.imshow("original", o)
cv2.imshow("Sobelxy", Sobelxy)
cv2.imshow("Scharrxy", Scharrxy)
cv2.waitKey()
cv2.destroyAllWindows()

运行程序,结果如图22所示。
在这里插入图片描述

图22

Laplacian算子及函数使用

Laplacian(拉普拉斯)算子是一种二阶导数算子,其具有旋转不变性,可以满足不同方向的图像边缘锐化(边缘检测)的要求。通常情况下,其算子的系数之和需要为零。例如,一个3×3大小的Laplacian算子如图23所示。

图23

Laplacian算子类似二阶Sobel导数,需要计算两个方向的梯度值。例如,在图24中:

图24
  • 左图是Laplacian算子。
  • 右图是一个简单图像,其中有9个像素点。

计算像素点 P 5 P5 P5的近似导数值,如下:

P 5 l a p = ( P 2 + P 4 + P 6 + P 8 ) − 4 ⋅ P 5 P5_{lap}=(P2 + P4 + P6 + P8) -4·P5 P5lap=(P2+P4+P6+P8)4P5

图25展示了像素点与周围点的一些实例,其中:

图25
  • 在左图中,像素点P5与周围像素点的值相差较小,得到的计算结果值较小,边缘不明显。
  • 在中间的图中,像素点P5与周围像素点的值相差较大,得到的计算结果值较大,边缘较明显。
  • 在右图中,像素点P5与周围像素点的值相差较大,得到的计算结果值较大,边缘较明显。

需要注意,在上述图像中,计算结果的值可能为正数,也可能为负数。所以,需要对计算结果取绝对值,以保证后续运算和显示都是正确的。在OpenCV内使用函数cv2.Laplacian()实现Laplacian算子的计算,该函数的语法格式为:

dst = cv2.Laplacian( src, ddepth[, ksize[, scale[, delta[, borderType]]]] )

式中:

  • dst代表目标图像。
  • src代表原始图像。
  • ddepth代表目标图像的深度。
  • ksize代表用于计算二阶导数的核尺寸大小。该值必须是正的奇数。
  • scale代表计算Laplacian值的缩放比例因子,该参数是可选的。默认情况下,该值为1,表示不进行缩放。
  • delta代表加到目标图像上的可选值,默认为0。
  • borderType代表边界样式。

该函数分别对x、y方向进行二次求导,具体为:

在这里插入图片描述

上式是当ksize的值大于1时的情况。当ksize的值为1时,Laplacian算子计算时采用的3×3的核如下:

通过从图像内减去它的Laplacian图像,可以增强图像的对比度,此时其算子如图26所示。

图26

【例14】使用函数cv2.Laplacian()计算图像的边缘信息。根据题目要求,设计程序如下:

import cv2
o = cv2.imread('Laplacian.bmp', cv2.IMREAD_GRAYSCALE)
Laplacian = cv2.Laplacian(o, cv2.CV_64F)
Laplacian = cv2.convertScaleAbs(Laplacian)
cv2.imshow("original", o)
cv2.imshow("Laplacian", Laplacian)
cv2.waitKey()
cv2.destroyAllWindows()

运行程序,结果如图27所示,其中左图为原始图像,右图为得到的边缘信息。

图27

算子总结

Sobel算子、Scharr算子、Laplacian算子都可以用作边缘检测,它们的核如图28所示。
在这里插入图片描述

图28

Sobel算子和Scharr算子计算的都是一阶近似导数的值。通常情况下,可以将它们表示为:

S o b e l 算子 = ∣ 左 − 右 ∣ / ∣ 下 − 上 ∣ Sobel算子=|左-右| / |下-上| Sobel算子=∣/∣

S c h a r r 算子 = ∣ 左 − 右 ∣ / ∣ 下 − 上 ∣ Scharr算子=|左-右| / |下-上| Scharr算子=∣/∣

式中|左-右|表示左侧像素值减右侧像素值的结果的绝对值,|下-上|表示下方像素值减上方像素值的结果的绝对值。

Laplacian算子计算的是二阶近似导数值,可以将它表示为:

L a p l a c i a n 算子 = ∣ 左 − 右 ∣ + ∣ 左 − 右 ∣ + ∣ 下 − 上 ∣ + ∣ 下 − 上 ∣ Laplacian算子=|左-右| + |左-右| + |下-上| + |下-上| Laplacian算子=+++

通过公式可以发现,Sobel算子和Scharr算子各计算了一次|左-右||下-上|的值,而Laplacian算子分别计算了两次|左-右||下-上|的值。

  • 16
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hola173841439

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值