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

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

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

相关文章

C++11的部分新特性

目录 1.列表初始化 1.1 { } 初始化 1.2 std::initializer_list 2.声明 2.1 auto 2.2 decltype 2.3 nullptr 3. 范围for 4.STL中的一些变化 5.右值引用与移动语义 5.1 左值引用与右值引用 5.2 左值引用与右值引用的比较 5.3 右值引用使用场景 5.4 完美转发 6.新的…

JavaScript事件处理和常用对象

文章目录 前言一、事件处理程序 1.JavaScript 常用事件2.事件处理程序的调用二、常用对象 1.Window 对象2.String 对象3.Date 对象总结 前言 JavaScript 语言是事件驱动型的。这意味着,该门语言可以通过事件触发来调用某一函数或者一段代码。该文还简单介绍了Window…

Android 11 FileProvider的使用和限制

概述: 从Android 7开始,将不允许在app之间,使用file uri,即file://的方式,传递一个file,否则会抛出异常:FileUriExposedException ,其解决方案,就是使用FileProvider,用c…

有关 Element-ui 的一些思考

本篇文章是基于 element-ui 的 主题样式修改 曾经为了修改组件风格,一个项目用了上百个样式穿透,后来才发现定制一个主题就够了! 第一步,在官网的主题页面,修改背景色、字体颜色及边框颜色 第二步,下载主题 第三步,用下载的css文件替换掉默认的css文件

如何让Windows控制台窗口不接受鼠标点击(禁用鼠标输入)

一、简述 在我们编写控制台应用程序时,默认情况下程序的打印输出会在控制台窗口中进行显示,我们在写服务功能时在窗口中会不断打印消息输出,这个时候如果使用鼠标点击了控制台窗口,会阻塞程序的继续运行,导致我们的程…

SpringBoot集成Thymeleaf模板引擎,为什么使用(详细介绍)

学习本技术第一件事:你为什么要使用,解决什么问题的? 1.为什么使用(使用背景)? 首先应用场景是单体项目,如果是前后端分离就不用关注这个了,因为单体项目你前后端都是写在一个项目…

性能小白终于能看懂Jmeter报告了

对于刚接触性能测试的初学者来说,分析JMeter生成的测试报告无疑是一个巨大的挑战。面对大量的数据信息,如何快速理解响应时间、吞吐量、错误率等关键指标,往往让人感到困惑。今天,让我们一起探讨如何轻松看懂JMeter的性能测试报告…

多线程篇(其它容器- CopyOnWriteArrayList)(持续更新迭代)

一、CopyOnWriteArrayList(一) 1. 简介 并发包中的并发List只有CopyOnWriteArrayList。 CopyOnWriteArrayList是一个线程安全的ArrayList,对其进行的修改操作都是在底层的一个复制的数 组(快照)上进行的&#xff0…

基于STM32C8T6的CubeMX:HAL库点亮LED

三个可能的问题和解决方法: 大家完成之后回来看,每一种改错误都是一种成长,不要畏惧,要快乐,积极面对,要耐心对待 STMCuBeMX新建项目的两种匪夷所思的问题https://mp.csdn.net/mp_blog/creation/editor/1…

【Android 13源码分析】WindowContainer窗口层级-1-初识窗口层级树

在安卓源码的设计中,将将屏幕分为了37层,不同的窗口将在不同的层级中显示。 对这一块的概念以及相关源码做了详细分析,整理出以下几篇。 【Android 13源码分析】WindowContainer窗口层级-1-初识窗口层级树 【Android 13源码分析】WindowCon…

前端技术(七)——less 教程

一、less简介 1. less是什么? less是一种动态样式语言,属于css预处理器的范畴,它扩展了CSS语言,增加了变量、Mixin、函数等特性,使CSS 更易维护和扩展LESS 既可以在 客户端 上运行 ,也可以借助Node.js在服…

Semaphore UI --Ansible webui

1、安装python python下载地址 https://www.python.org/downloads/ 选好版本下载 wget https://www.python.org/ftp/python/3.11.9/Python-3.11.9.tar.xz安装编译工具 sudo dnf groupinstall "Development Tools"安装依赖包 dnf install bzip2-devel ncurses-deve…

IDEA 常用配置和开发插件

件市场中搜索并安装“Git Integration”插件。 一、前言 在本篇文章中我会为大家总结一些我自己常用的配置和开发插件,此外也给大家提供一个建议,可以根据自己的项目需求和个人偏好选择适合的插件。另外,IDEA 也在不断更新,可能会…

哈希表、算法

哈希表 hash: 在编程和数据结构中,"hash" 通常指的是哈希函数,它是一种算法,用于将数据(通常是字符 串)映射到一个固定大小的数字(哈希值)。哈希函数在哈希表中尤为重要…

使用vue2+axios+chart.js画折线图 ,出现 RangeError: Maximum call stack size exceeded 错误

目录 效果图 解决方案 修正要点 效果图 修改前App.vue代码&#xff1a; <template><div id"app"><canvas id"myChart"></canvas></div> </template><script> import axios from axios; import { Chart, regis…

stm32 W25Q数据存储

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、cubemx配置二、keil中文件修改与配置三、几个重要函数的说明四、DMA方式传输&#xff08;待写&#xff09;总结 前言 W25Q128 容量为128位 128/8 16 也就…

Mac 上终端使用 MySql 记录

文章目录 下载安装终端进入 MySql常用操作查看数据库选择一个数据库查看当前选择的数据库Navcat 打开提示报错参考文章 下载安装 先下载社区版的 MySql 安装的过程需要设置 root 的密码&#xff0c;这个是要进入数据库所设定的&#xff0c;所以要记住 终端进入 MySql 首先输…

Linux基础-Makefile的编写、以及编写第一个Linux程序:进度条(模拟在 方便下载的同时,更新图形化界面)

目录 一、Linux项目自动化构建工具-make/Makefile ​编辑 背景&#xff1a; makefile小技巧&#xff1a; 二、Linux第一个小程序&#xff0d;进度条 先导&#xff1a; 1.如何利用/r,fflush(stdout)来实现我们想要的效果&#xff1b; 2.写一个倒计时&#xff1a; 进度条…

智能家居环境监测系统设计(论文+源码)

1. 系统方案 系统由9个部分构成&#xff0c;分别是电源模块、烟雾传感器模块、GSM发送短信模块、报警模块、温度传感器模块、人体红外感应模块、按键设置模块、显示模块、MCU模块。各模块的作用如下&#xff1a;电源模块为系统提供电力&#xff1b;烟雾传感器模块检测烟雾浓度&…

猫狗识别大模型——基于python语言

目录 1.猫狗识别 2.数据集介绍 3.猫狗识别核心原理 4.程序思路 4.1数据文件框架 4.2 训练模型 4.3 模型使用 4.4 识别结果 5.总结 1.猫狗识别 人可以直接分辨出图片里的动物是猫还是狗&#xff0c;但是电脑不可以&#xff0c;要想让电脑也分辨出图片里的动物是猫还是小…