opencv之图像梯度

news2024/11/25 20:30:22

图像梯度

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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

重命名工具 | Advanced Renamer v4.03 绿色版

Advanced Renamer 是一款专为 Windows 平台设计的强大文件批量重命名工具。它提供了多种重命名方法,包括指定新文件名、改变大小写、移动字符、移除字符串、重编文件名序号、替换字符、添加内容、使用列表或列表文件替换文件名、交换字符位置、去除头部或尾部多余空…

百度Apollo打通与ROS的通信,扩展自动驾驶系统生态

技术文档|打通与ROS的通信,扩展自动驾驶系统生态_Apollo开发者社区 (baidu.com)

-顺序表-

一、小概述 二、代码解析 下面利用顺序表实现增删改查的功能&#xff1a; //动态顺序表 #include <iostream> using namespace std; typedef int SLDataType;//适用于多种类型 typedef struct SeqList {SLDataType* a;//数组int size;//表示数组中存储了多少个元素int …

【车载开发系列】ParaSoft单元测试环境配置(一)

【车载开发系列】ParaSoft单元测试环境配置(一) ParaSoft单元测试环境配置 【车载开发系列】ParaSoft单元测试环境配置(一)一. 什么是bdf文件二. bdf文件构成三. 新规做成bdf文件四. 导入bdf文件创建测试工程五. 获取编译器信息六. 新增自定义编译器Step1:打开向导Step2:…

Xcode 16 RC (16A242) 发布下载,正式版下周公布

Xcode 16 RC (16A242) - Apple 平台 IDE IDE for iOS/iPadOS/macOS/watchOS/tvOS/visonOS 请访问原文链接&#xff1a;https://sysin.org/blog/apple-xcode-16/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org Xcode 16 的新功…

【拓扑系列】拓扑排序

【拓扑系列】拓扑排序 前言认识有向无环图认识AOV网&#xff1a;顶点活动图拓扑排序 1. 课程表1.1 题目来源1.2 题目描述1.3 题目解析 2. 课程表 II2.1 题目来源2.2 题目描述2.3 题目解析 3. LCR 114. 火星词典3.1 题目来源3.2 题目描述3.3 题目解析 前言 认识有向无环图 图中…

AI基础 L13 Constraint Satisfaction Problems I约束满足问题

Defining Constraint Satisfaction Problems A constraint satisfaction problem (CSP) consists of three components, X, D, and C: • X is a set of variables, {X1, . . . , Xn}. • D is a set of domains, {D1, . . . , Dn}, one for each variable • C is a set of co…

STM32+ESP8266 WiFi连接机智云平台APP远程控制教程

本文档将介绍如何用STM32ESP8266 WiFi模块从零开始连接上机智云&#xff0c;并通过APP进行远程控制。 机智云官网&#xff1a;机智云|智能物联网操作系统 (gizwits.com) 准备&#xff1a;STM32、ESP8266、手机、可上网的WiFi。 1.创建设备 1.1 注册登陆 请自行注册账号并登陆…

脱离八股文,真实开发中的延时处理需求实现思路(超时订单处理为例)

前言 咱们聊聊那些在开发过程中经常遇到的延时处理需求吧。比如说&#xff0c;网购时那些迟迟不付款的订单&#xff0c;或者是社交软件里那些需要稍后处理的消息&#xff0c;再或者是金融交易中那些需要等待确认的交易。这些都是咱们得搞定的活儿。 不过&#xff0c;很多时候&a…

JS面试真题 part3

JS面试真题 part3 11、bind、call、apply区别&#xff1f;如何实现一个bind12、JavaScript中执行上下文和执行栈是什么13、说说JavaScript中的事件模型14、解释下什么是事件代理&#xff1f;应用场景&#xff1f;15、说说你对闭包的理解&#xff1f;闭包使用场景 11、bind、cal…

make 程序规定的 makefile 文件的书写语法

&#xff08;1&#xff09; 常用的 gcc 选项&#xff1a; &#xff08;2&#xff09; make 的作用&#xff1a; &#xff08;3&#xff09; 搭建 make 的实验环境 &#xff0c; linux 的很简单&#xff0c; windows 的复杂一点&#xff1a; windows 上 make 环境的搭建&#…

python爬虫基础:了解html

编辑器vscode <!DOCTYPE html> <html><head><title>第一个网页</title></head><body><h1>字体</h1><h2>字体</h2><h3>字体</h3><p>Lorem, ipsum dolor sit amet consectetur adipisicing…

电商平台如何实现自动监控订单签收状态,加快资金划拨进程?

资金划拨作为交易流程的核心环节之一&#xff0c;直接关系到商家资金回笼的速度、消费者购物体验的满意度以及平台自身的信誉与稳定性。 区别于自营电商&#xff0c;电商平台入驻了许多第三方商家&#xff0c;为了保障交易安全和控制风险&#xff0c;在交易未完成之前&#xff…

Java 入门指南:Java 并发编程 —— 同步工具类 Semephore(信号量)

文章目录 同步工具类Semephore核心功能限制并发访问量公平与非公平策略灵活性与适应性 常用方法使用示例 同步工具类 JUC&#xff08;Java.util.concurrent&#xff09;是 Java 提供的用于并发编程的工具类库&#xff0c;其中包含了一些通信工具类&#xff0c;用于在多个线程之…

Spring和MyBatis常见面试题总结

文章目录 1 Spring 基础1.1 说一下你对 Spring 的理解&#x1f525;1.2 Spring,Spring MVC,Spring Boot 之间什么关系?1.3 Spring 框架中用到了哪些设计模式&#xff1f;&#x1f525;1.4 说说自己对于 Spring MVC 了解?1.5 Spring MVC 的核心组件有哪些&#xff1f;1.6 Spri…

flutter开发实战-GoRouter路由go与push区别实践

flutter开发实战-GoRouter路由go与push区别实践 GoRouter是一个flutter的声明性路由包&#xff0c;使用路由器API提供一个方便的、基于url的API&#xff0c;用于在不同屏幕之间导航。可以定义URL模式、使用URL导航、处理深度链接以及许多其他与导航相关的场景。 之前使用了Go…

Vue3封装table表格右键菜单功能

1) 效果&#xff0c;右键单击单元格&#xff0c;打开菜单弹窗: 点击菜单选项&#xff0c;可选择只读/编辑&#xff0c;可在只读/编辑方法中&#xff0c;拿到该行列表格的数据&#xff0c;进行相关操作 2) 思路 1、右键菜单组件 出现的时机&#xff0c;是右键单击table表格row-…

移动安全需求分析与安全保护工程

移动应用安全威胁与需求分析 移动应用系统组成&#xff1a; 移动应用&#xff1a;简称App 通信网络&#xff1a;无线网络&#xff0c;移动通信网络及互联网 应用服务端&#xff1a;由相关服务器构成&#xff0c;负责处理来自App相关信息或数据 移动应用安全分析 Android系统…

【0基础】制作HTML网页小游戏——贪吃蛇(附详细解析)

我在昨天的文章&#xff08;贪吃蛇HTML源码&#xff09;里面分享了网页版贪吃蛇小游戏的源码&#xff0c;今天就来给大家详细讲解一下每部分代码是如何运作的&#xff0c;以及以后要如何美化贪吃蛇的UI界面&#xff0c;在哪里修改等。 目录 一、代码运作 1、HTML结构: 2、C…

数组的常用算法

数组是同类型数据的集合。便于整体处理数据&#xff0c;数组操作的主要算法有&#xff1a; 1求极值 2查找 3排序 2查找 cprimer plus第11.1节278--279页 4数组和指针的区别&#xff1a;数组表示法和指针表示法 数组表示法1 int a[4]{2,4,1,5}; for(int i0;i<4;i)cou…