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算子分别计算了两次|左-右||下-上|的值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/874334.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

首批通过!华为云CodeArts Snap智能开发助手通过可信AI智能编码工具评估,获当前最高等级

近日,华为云CodeArts Snap智能开发助手在中国信通院组织的智能编码工具首轮评估中,最终获得4级评级, 成为国内首批通过该项评估并获得当前最高评级的企业之一。 此次评估以《智能化软件工程技术和应用要求 第2部分:智能开发能力》为依据&…

ubuntu 20.04 一直卡在登录界面,即使密码正确也无法登录(失败记录)

ubuntu 20.04 一直卡在登录界面,即使密码正确也无法登录 这次是装实体机,一次失败的尝试。。。 名称型号CPUIntel Xeon E5-2673 V3GPURTX 3060 mobile 安装的时候不要选install third-party software for graphics and Wi-fi hardware and additional …

Leetcode面试经典150题-55.跳跃游戏

解法都在代码里,不懂就留言或者私信 class Solution {public boolean canJump(int[] nums) {/**如果就一个位置,你本来就在这,肯定可以跳到*/if(nums.length 1) {return true;}/**这个题的解题思路是遍历数组,如果当前位置不在之…

每日OJ_牛客_数组中出现次数超过一半的数字

目录 牛客_数组中出现次数超过一半的数字 解析代码1 解析代码2 牛客_数组中出现次数超过一半的数字 数组中出现次数超过一半的数字__牛客网 给一个长度为 n 的数组,数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为…

RP2040 C SDK clocks时钟源配置使用

RP2040 C SDK clocks时钟源配置使用 🌿RP2040时钟源API函数文档:https://www.raspberrypi.com/documentation/pico-sdk/hardware.html#group_hardware_clocks 🍁RP2040时钟树: 系统时钟源可以来自外部时钟输入(exte…

程序员如何写笔记并整理资料?

整理笔记 word。没错,我也看了网上一大堆软件,还有git管理等等。个人认为如果笔记只是记录个人的经验积累,一个word就够了,那些notepad,laTex个人觉得不够简练。word。 1.word可以插入任何文件附件(目前最大的word 20…

9.9(QT Day 2)

将day1做的登录界面升级优化【资源文件的添加】 在登录界面的登录取消按钮进行以下设置: 使用手动连接,将登录框中的取消按钮使用第2种方式的连接到自定义的槽函数中,在自定义的槽函数中调用关闭函数 将登录按钮使用qt4版本的连接到自定义…

面试题复习(0902-0909)

1. 完全背包问题 和01背包唯一的区别是&#xff0c;每件物品都有无限个&#xff08;也就是可以放入背包多次&#xff09; 代码和01唯一的区别在于j的循环是从小到大&#xff0c;不是从大到小。ij谁在外谁在内层区别不大。 #include <bits/stdc.h> using namespace std…

国产化数据库挑战及发展趋势

非国产数据库如Oracle、MySQL和MSSQL等在某些领域占据重要地位&#xff0c;但国产数据库的市场份额正在逐步提升&#xff0c;特别是在政策支持和市场需求的双重推动下&#xff0c;国产数据库的替代进程正在加速。 一、国产数据库市场规模 2024年中国数据库市场规模预计为543.1亿…

【树和二叉树的相关定义】概念

1.回顾与概览 2.什么是树型结构 3.树的&#xff08;递归&#xff09;定义与基本术语 3.1树的定义 注意&#xff1a;除了根结点以外&#xff0c;任何一个结点都有且仅有一个前驱 3.2树的其他表示方式 3.3树的基本术语 结点&#xff1a;数据元素以及指向子树的分支根结点:非空…

AI基础 L16 Logic Agents I

What is an Agent? • The main point about agents is they are autonomous: capable of acting independently, exhibiting control over their internal state • Thus: an agent is a computer system capable of autonomous action in some environment in order to mee…

JavaFX应用更新检测功能(在线自动更新方案)

JavaFX开发的桌面应用属于C端&#xff0c;一般来说需要版本检测和自动更新功能&#xff0c;这里记录一下一种版本检测和自动更新的方法。 1. 整体方案 JavaFX.应用版本检测、自动更新主要涉及一下步骤&#xff1a; 读取本地应用版本拉取远程版本并比较两个版本如果需要升级&…

手机TF卡格式化后数据恢复:方法、挑战与预防措施

在现代生活中&#xff0c;‌手机已经成为我们不可或缺的一部分&#xff0c;‌而TF卡&#xff08;‌即MicroSD卡&#xff09;‌作为手机存储的扩展&#xff0c;‌更是承载了我们大量的重要数据。‌然而&#xff0c;‌不慎的格式化操作往往导致数据丢失&#xff0c;‌给用户带来不…

【重学 MySQL】五、MySQL 的卸载

【重学 MySQL】五、MySQL 的卸载 停止MySQL服务卸载MySQL程序删除残余文件清理注册表删除环境变量配置重启电脑 MySQL的卸载过程需要仔细操作&#xff0c;以确保彻底卸载并清理所有相关文件和配置。 停止MySQL服务 打开任务管理器&#xff1a;右键点击任务栏空白处&#xff0…

C++笔记---list

1. list的介绍 list其实就是就是我们所熟知的链表&#xff08;双向循环带头结点&#xff09;&#xff0c;但其是作为STL中的一个类模板而存在。 也就是说&#xff0c;list是可以用来存储任意类型数据的顺序表&#xff0c;既可以是内置类型&#xff0c;也可以是自定义类型&…

单词排序C++实现

代码如下&#xff1a; #include<iostream> #include<string> #include<fstream> #include<map> #include<iomanip> #include<algorithm> #include<vector>int read_file(std::map<std::string,int> &map_words) {std::st…

大牛直播SDK最经典的一句

搜索引擎搜大牛直播SDK&#xff0c;居然提示我搜“大牛直播SDK最经典的一句”&#xff0c;闲来无事&#xff0c;点开看看&#xff0c;AI智能问答&#xff0c;给出了答案&#xff1a; ‌大牛直播SDK最经典的一句是&#xff1a;"我们只做最擅长的部分,我们不做的,提供对接接…

[进阶]面向对象之多态(二)

文章目录 多态调用成员的特点多态的优势和弊端 多态调用成员的特点 变量调用:编译看左边,运行也看左边方法调用:编译看左边,运行看右边 多态的优势和弊端 优势&#xff1a; 在多态形式下&#xff0c;右边对象可以实现解耦合&#xff0c;便于扩展和维护定义方法的时候&…

mysql高级sql

文章目录 一&#xff0c;查询1.按关键字排序1.1按关键字排序操作(1)按分数排序查询&#xff08;不加asc默认为升序&#xff09;(2)按分数降序查询&#xff08;DESC&#xff09;(3)使用where进行条件查询(4)使用ORDER BY语句对多个字段排序 1.2使用区间判断查询&#xff08;and/…

ESP32 TCP通信交换数据Mixly Arduino编程

TCP通信交换数据 在ESP32与ESP32或其它局域网络内主机间传输数据时&#xff0c;TCP是很方便的&#xff0c;特别当我们连接互联网后ESPnow不能用&#xff0c;MQTT又不稳定发送大量的数据&#xff0c;同时蓝牙有其它用途时&#xff0c;那么学会TCP通信协议就变得十分重要。 一、…