基于Python的Opencv学习第十天
今天为大家介绍一下图像梯度的相关知识,先来看一下soble算子的内容。
一.sobel算子
1.sobel算子的理论基础
sobel可以理解为计算不同方向的梯度。如图,我们可以采用中间的卷积核与原图像进行相乘,从而可以得到x方向的梯度。
在这里,P5的x方向的梯度值可以用如下算式表示:
P5x=(p3-p1)+2×(p6-p4)+(p9-p7)
这里因为P4和P6两个值距离P5较近,所以提高比例为2。其实计算水平方向梯度就是选取卷积核右边的一列数减去左边的一列数,从而得到水平方向的梯度,也即是水平方向的sobel算子。
至于垂直方向的边界,同水平方向,如图:
在这里,P5的y方向的梯度值可以用如下算式表示:
P5y=(p7-p1)+2×(p8-p2)+(p9-p3)
至于解释和水平方向的解释相同。
分别得到两个方向的梯度值后,我们通过平方开根号运算得到P5点的近似梯度值
但为了方便计算,得到简化版本:
以P5点为例,其sobel算子的梯度值为:
P5sobel=|p5x|+|p5y|
这样就可以得到某一点的梯度值。
2.sobel算子的函数
在opencv中提供写好的soble()函数来进行梯度运算,具体语法如下:
dst=cv2.Sobel(src,ddepth,dx,dy,[ksize])
这里参数解释如下:
ddepth:处理结果的图像深度
通常情况下,将该参数的值设置为-1,让处理结果与原图像保持一致。但在soble算子计算梯度时候需要做一点变化。
如图所示,A和B两条边界左右像素点的值不同(256色位图中,白色点像素值为255,黑色点像素值为0),在求x轴方向的sobel算子时候,其右侧像素值和左侧像素值的差值不为零,是边界。但在其他列中,右侧像素值与左侧像素值的差值均为零,不是边界,这就是sobel的具体解释。
但我们在计算A边界时候,右侧值为0,左侧值为255,差值为负数,在图像深度设为-1时,我们得到结果会把负值取为0,就得不到想要的结果,这时,我们需要填上另外一个更高的数据类型cv2.CV_64F,取绝对值后,再转换为np.uint8(cv2.CV_8U)类型。
来看另一个函数cv2.convertScaleAbs(src,[,alpha[,betal]])这个作用是将原始图像src转换为256色位图(即给负数值取绝对值,从而不会在np.uint8类型中直接截断取0。这个函数还可以直接调整为:
dst=cv2.convertScaleAbs(原始图像)
dx:计算x轴方向的边界
dy:计算y轴方向的边界
计算x方向梯度:【dx=1,dy=0】
计算y方向梯度:【dx=0,dy=1】
在计算sobel结果时候,我们有两种方法:
一般是采用方式2,待会会在代码演示中看出来结果。
利用方式2计算dst时候,我们并不需要直接相加,而是通过cv2.addWeighted(src1,alpha,src2,beta,gamma)来实现计算两幅图像的权重和。具体语法如下:
dst=cv2.addWeighted(src1,alpha,src2,beta,gamma)
参数含义是:
src1:原图像1
alpha:原图像1需要采用的比重
src2:原图像2
beta:原图像2需要采用的比重
gamma:修正值(一般取0,不做修正)
示例:dst=cv2.addWeighted(src1,0.5,src2,0.5,0)
ksize:核大小(一般这个参数不用,opencv默认设置为3(三行三列),如果需要用的话,需要给这个参数设置为奇数)
来看代码和结果:
import cv2
import numpy as np
a=cv2.imread(r"C:\Users\LT010407\Desktop\lena.jpg")
sobelx=cv2.Sobel(a,cv2.CV_64F,1,0)
sobely=cv2.Sobel(a,cv2.CV_64F,0,1)
sobelx=cv2.convertScaleAbs(sobelx)
sobely=cv2.convertScaleAbs(sobely)
sobelxy=cv2.addWeighted(sobelx,0.5,sobely,0.5,0)
sobel11=cv2.Sobel(a,cv2.CV_64F,1,1)
cv2.imshow("a",a)
cv2.imshow("soblex",sobelx)
cv2.imshow("sobley",sobely)
cv2.imshow("soblexy",sobelxy)
cv2.imshow("soble11",sobel11)
这里分别得到了x轴方向和y轴方向的sobel梯度的图像
这是采用两种不同方法得到的结果,可以看出采用方式2得到的才是正确的结果。
二.scharr算子及其函数
在使用33的sobel算子时,可能结果不太准确,所以需要使用scharr算子,这样效果更好。这幅图是scharr算子在计算x轴和y轴方向的梯度时候33卷积核的系数,可以看到这里系数与sobel算子的系数不同,得到的边界的结果更准确。(x轴方向是右边像素值减去左边像素值,y轴方向是下边像素值减去上边像素值)运算和sobel算子相同。
这里我们可以看到两种算子的系数不同。sobel算子和scharr算子的运算强度和运算速度是一样的,但是scharr算子进行的运算结果更准确。
Scharr()函数
在使用scharr算子进行梯度运算时,需要使用scharr()函数。
dst=Scharr(src,ddpeth,dx,dy)
这里相关参数和Sobel()函数一致,不做赘述。
同样在处理图像深度时,还需要用cv2.CV_64F参数:
dst=Scharr(src,cv2.CV_64F,dx,dy)
dst=cv2.convertScaleAbs(dst)
注意:dx,dy需要满足条件:
dx>=0&&dy>=0&&dx+dy==1
这里注意的就是dx与dy的值加起来需要等于1。
示例:dst=Scharr(src,ddpeth,dx=1,dy=0)
dst=Scharr(src,ddpeth,dx=0,dy=1)
不能参与形式为:
dst=Scharr(src,ddpeth,dx=1,dy=1)
分别得到x轴方向和y轴方向两个方向的梯度值之后,我们继续使用addWeighted()函数给两个方向的梯度值加起来。
Scharr是对Sobel算子的改进,所以我们可以在Sobel算子里面修改参数来得到和Scharr算子一致的效果:
import cv2
import numpy as np
a=cv2.imread(r"C:\Users\LT010407\Desktop\lena.jpg")
scharrx=cv2.Scharr(a,cv2.CV_64F,1,0)
scharry=cv2.Scharr(a,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("a",a)
cv2.imshow("scharrx",scharrx)
cv2.imshow("scharry",scharry)
cv2.imshow("scharrxy",scharrxy)
这里可以看到代码和结果。