opencv学习记录之图像梯度

图像的梯度计算的是图像变化的速度,对于边缘部分呢灰度值变换大,梯度值也大,相反则灰度值变化小,梯度值小

图像梯度值严格说应该需要求导数,但是图像梯度一般通过计算像素值的差,来得到梯度的近似值

以下介绍三种算子的使用Sobel算子、Scharr算子和Laplacian算子

Sobel算子是一种离散的微分算子,该算子结合了高斯平滑处理和微分求导运算。 该算子利用局部差寻找边缘

Sobel算子如下所示

-1 0 1
-2 0 2
-1 0 1

                            图一

-1 -2 -1
0 0 0
1 2 1

                            图二    

 将Sobel算子图一和原始图像卷积可以得到水平方向的像素值变化,与图二卷积的到垂直方向的像素值变化

P1 P2 P3
P4 P5 P6
P7 P8 P9

如果要计算P5的水平方向的偏导数,则需要Sobel算子及P5邻域点

公式为:P5x = (P3 - P1 ) + 2 * ( P6 - P4 ) + ( P9 - P7 )

用P5右侧的像素点减左侧的像素点,因为P4和P6离P5较近,所以权值为2,其他为1

垂直方向类似,垂直是下减上

函数形式

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

dst目标图像

src原始图像

ddepth 输出图像的深度,

dx x方向上的求导阶数

dy  y方向上的求导阶数

ksize 代表Sobel核的大小,该值为-1时,会使用Scharr算子进行计算

scale计算导数值时采用的缩放因子,默认为1,没有缩放

delta加在目标图像dst上的值,默认为0

borderType 边界样式 (上篇博客提到过不再重复)

可以将ddepth设置为-1,在计算时可能得到的结果时错误的,

如果处理的图像是8位图类型,且ddepth为-1 意味着指定运算结果也是8位图类型,所有负数都自动处理为0

为了避免信息丢失在计算是要先使用更高的数据类型嗯 cv2.CV_64F,在通过取绝对值将其映射位8位图类型

所以通常要将ddepth设置位cv2.CV_64F

如下代码所示

1 import cv2                                                                       
2 o = cv2.imread("example.bmp" , cv2.IMREAD_GRAYSCALE)
3 Sobelx = cv2.Sobel(o , -1 , 1 , 0) 
4 cv2.imshow("original" , o) 
5 cv2.imshow("x" , Sobelx)
6 cv2.waitKey()
7 cv2.destroyAllWindows()

原图

效果图 

如上图所示在8位灰度图中因为黑色的像素值位0,而白色为255,当使用Sobel算子后,对其水平方向计算近似偏导数

右侧减左侧,右侧边缘得到的是正数所以可以正常显示,而左侧的边缘得到的是负数被处理为0后便显示黑色不能得到准确的结果

将ddepth设置为cv2.CV_64F后 还要取绝对值才能获得所需的图像,则需要绝对值函数 cv2.convertScaleAbs()

dx = 1 , dy = 0

1 import cv2                                                                       
2 o = cv2.imread("example.bmp" , cv2.IMREAD_GRAYSCALE)
3 Sobelx = cv2.Sobel(o , cv2.CV_64F , 1 ,0)
4 Sobelx = cv2.convertScaleAbs(Sobelx)
5 cv2.imshow("original" , o) 
6 cv2.imshow("x" , Sobelx)
7 cv2.waitKey()
8 cv2.destroyAllWindows()

如图   可以获得完整边缘信息

垂直方向的边缘信息 dx = 0 . dy = 1

1 import cv2                                                                       
2 o = cv2.imread("example.bmp" , cv2.IMREAD_UNCHANGED)
3 Sobely = cv2.Sobel(o , cv2.CV_64F , 0 , 1) 
4 Sobely = cv2.convertScaleAbs(Sobely)
5 cv2.imshow("original" , o) 
6 cv2.imshow("y" , Sobely)
7 cv2.waitKey()
8 cv2.destroyAllWindows()

如图

当dx =1 , dy = 1 

1 import cv2                                                                       
2 o = cv2.imread("example.bmp" , cv2.IMREAD_GRAYSCALE)
3 Sobelxy = cv2.Sobel(o , cv2.CV_64F , 1 , 1) 
4 sobelxy = cv2.convertScaleAbs(Sobelxy)
5 cv2.imshow("original" , o) 
6 cv2.imshow("xy" , Sobelxy)
7 cv2.waitKey()
8 cv2.destroyAllWindows()

 没有达到我们想要的结果,而是只留下几个点

若想要x,y方向都显示边缘需要两个方向分别处理,然后向叠加

import cv2                                                                       
o = cv2.imread("example.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()

Scharr算子

在使用3x3的Sobel算子是精度可能不高,Scharr速度与Sobel算子一样 ,但精度更高

Scharr算子通常为

-3 0 3
-10 0 10
-3 0 3
-3 -10 -3
0 0 0
3 10 3

函数形式为

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

与Sobel相似少了Ksize参数, 即当Sobel中ksize = -1 时会使用Scharr算子计算

函数cv2.Scharr()与cv2.Sobel()相似

但有一些约束条件

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

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

 1 import cv2                                                                       
 2 o = cv2.imread("example.bmp" , cv2.IMREAD_GRAYSCALE)
 3 Scharrx = cv2.Scharr(o , cv2.CV_64F , 1 , 0) 
 4 Scharry = cv2.Scharr(o , cv2.CV_64F , 0 , 1) 
 5 Scharrx = cv2.convertScaleAbs(Scharrx)
 6 Scharry = cv2.convertScaleAbs(Scharry)
 7 Scharrxy = cv2.addWeighted(Scharrx , 0.5 , Scharry , 0.5 , 0) 
 8 cv2.imshow("original" , o) 
 9 cv2.imshow("xy" , Scharrxy)
10 cv2.waitKey()
11 cv2.destroyAllWindows()

Sobel和Scharr的比较

原图

 Sobel

 Scharr

 

Laplacian算子

该算子是一个二阶导数算子,具有旋转不变性,可以满足不同方向的图像边缘锐化(边缘检测)的要求,

通常情况下算子的系数和要为0

Laplacian算子 

0 1 0
1 -4 1
0 1 0
p1 p2 p3
p4 p5 p6
p7 p8 p9

P5点的近似导数值

P5lap =( P2 + P4 + P6 + P8 ) - 4* P5

该结果可能是正数也可能是负数,需要对计算结果取绝对值

函数形式

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

dst 目标图像

src原始图像

ddepth目标时图像的深度

ksize二阶导数的核尺寸大小

scale  计算Laplacian值的缩放比例因子

delta 夹道目标图像上的可选值

borderType 边界样式

1 import cv2                                                                       
2 o = cv2.imread("example.bmp" , cv2.IMREAD_GRAYSCALE)
3 Laplacian = cv2.Laplacian(o , cv2.CV_64F)
4 Laplacian = cv2.convertScaleAbs(Laplacian)
5 cv2.imshow("original" , o) 
6 cv2.imshow("Laplacina" , Laplacian)
7 cv2.waitKey()
8 cv2.destroyAllWindows()

猜你喜欢

转载自www.cnblogs.com/miaorn/p/12292908.html