图像梯度计算的是图像变化的速度。对于图像的边缘部分,其灰度值变化较大,梯度值也较大,相反,对于图像中较平缓的部分,其灰度值变化较小,相应的梯度值也较小。一般情况下,图像梯度计算的是图像的边缘信息,(在此图像梯度并不是纯数学意义上的梯度(需要求倒数),图像梯度一般通过计算像素值的差值来得到梯度的近似值(近似导数值))
一:sobel理论基础
sobel算子是一种离散的微分算子,该算子结合了高斯平滑和微分求导运算,该算子利用局部差分寻找边缘,计算所得的是梯度的近似值。
1、计算水平方向(x方向)偏导数的近似值
(由于图像的非边缘地区的相邻像素点的值相隔很近,水平相减后,该类区域基本为接近0的数(黑色),只有水平方向相差较大的被凸显出来,所以整张图看起来就像一列一列组合起来的图像)
如上图所示,A为3*3像素矩阵,如要要计算A矩阵中心像素点的水平偏导数,即用右侧像素点的加权和减去左侧像素点的加权和。
2、计算垂直方向(y方向)偏导数的近似值
如上图所示,A为3*3像素矩阵,如要要计算A矩阵中心像素点的垂直偏导数,即用上侧像素点的加权和减去下侧像素点的加权和。
3、sobel算子及函数使用
dst=cv2.Sobel(src, ddepth, dx, dy, dst=None, ksize=None, scale=None, delta=None, borderType=None)
(1)、ddepth代表输出图像的深度
(2)、dx代表x方向的求导阶数
(3)、dy代表y方向的求导阶数
dx、dy通常的值为0、1或者2,0表示在该方向没有求导。dx、dy的取值可以任意组合,
但不能同时为0。
(4)、ksize代表sobel算子的大小,为-1时会使用scharr算子计算
(5)、scale代表缩放大小,默认1(不放缩)。
(6)、delta代表加在目标图像dst上的值,该值是可选的,默认为0。
(7)、borderType代表边界样式,一般默认即可。
注意:在梯度计算中可能会存在负值,如果直接输出8位图类型,则负值会自动截断为0,从而发生错误。故我们在使用时,经常需要先使用更高的数据类型cv2.CV_64F(即将ddepth设置为cv2.CV_64F),再通过取绝对值将其映射为cv2.CV_8F(8位图)。
(1)、在opencv中,使用函数cv2.convertScaleAbs()对参数取绝对值,该函数的作用是将原始图像src转化为256色位图,可以将其表示为:dst=saturate(srcalpha+beta),saturate表示计算结果的最大值是饱和值,即当srcalpha+beta范围超出255时,其值取255。
st=cv2.convertScaleAbs(src, dst=None, alpha=None, beta=None)
alpha代表调节系数,该值是可选值,默认为1
beta代表调节亮度值,默认为0
sobel算子的缺点:当核结构较小时,其精度不高。
import cv2
import numpy as np
img=cv2.resize(cv2.imread("F:/my_project/opencv/img/3.jpg"),(300,300))
#x方向
sobel_x=cv2.Sobel(img,cv2.CV_64F,1,0)
sobel_x=cv2.convertScaleAbs(sobel_x)
#y方向
sobel_y=cv2.Sobel(img,cv2.CV_64F,0,1)
sobel_y=cv2.convertScaleAbs(sobel_y)
#x,y方向
sobel_xy=cv2.Sobel(img,cv2.CV_64F,1,1)
sobel_xy=cv2.convertScaleAbs(sobel_xy)
#x,y方向叠加
sobel_x_y=cv2.addWeighted(sobel_x,0.5,sobel_y,0.5,0)
cv2.imshow("img",img)
cv2.imshow("sobel_x",sobel_x)
cv2.imshow("sobel_y",sobel_y)
cv2.imshow("sobel_xy",sobel_xy)
cv2.imshow("sobel_x_y",sobel_x_y)
cv2.waitKey()
该图像的轮廓不是特别明显,所以在sobel算子下,水平或垂直方向均看不到明显的条纹感,但可以看出,在x,y方向同时求梯度(sobel_xy)与分别求梯度再叠加(sobel_x_y)的结果是不一样的。
二:scharr算子及函数使用
1、scharr算子具有和sobel算子相同的速度,且有更高的精度,可以将其看作sobel算子的改进。其在x、y方向的操作核通常如下所示:
2、dst=cv2.Scharr(src, ddepth, dx, dy, dst=None, scale=None, delta=None, borderType=None)
其各个参数的意义与sobel算子函数基本一致。但需要注意的是在cv2.Scharr()中: dx>=0 & dy>=0 & dx+dy=1。
import cv2
import numpy as np
img=cv2.resize(cv2.imread("F:/my_project/opencv/img/3.jpg"),(300,300))
#x方向
scharr_x=cv2.Scharr(img,cv2.CV_64F,1,0)
scharr_x=cv2.convertScaleAbs(scharr_x)
#sobel算子(ksize=-时相当于scharr)
sobel_x=cv2.Sobel(img,cv2.CV_64F,1,0,ksize=-1)
sobel_x=cv2.convertScaleAbs(sobel_x)
#sobel算子作为对比
_sobel_x=cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
_sobel_x=cv2.convertScaleAbs(_sobel_x)
#y方向
scharr_y=cv2.Scharr(img,cv2.CV_64F,0,1)
scharr_y=cv2.convertScaleAbs(scharr_y)
#x,y方向叠加
scharr_x_y=cv2.addWeighted(scharr_x,0.5,scharr_y,0.5,0)
cv2.imshow("img",img)
cv2.imshow("scharr_x",scharr_x)
cv2.imshow("sobel_x",sobel_x)
cv2.imshow("_sobel_x",_sobel_x)
cv2.imshow("scharr_y",scharr_y)
cv2.imshow("scharr_x_y",scharr_x_y)
cv2.waitKey()
对比scharr_x与sobel_x图可知,当cv.sobel()中ksize=-1时,两者是一样的,都调用的scharr算子。对比图scharr_x与_sobel_x可知,当轮廓较模糊的部分,采用scharr算子会更加准确(模糊部分也能凸显出来)。
三:Laplacian算子及函数使用
1、laplacian(拉普拉斯)算子是一种二阶导数算子,具有旋转不变性,可以满足不同方向的图像边缘锐化(边缘检测)的要求。通常情况下,其算子的系数之和需要为0。如下图为一个常见的3*3的Laplacian算子。
2、dst=cv2.Laplacian(src, ddepth, dst=None, ksize=None, scale=None, delta=None, borderType=None)
(1)、ksize代表用于计算二阶导数的核尺寸大小,该值必须为正的奇数。当ksize=1时,laplacian算子采用如上图所示的3*3算子(该处不太理解,结果和ksize=3时不一致)。
(2)、其余参数与sobel算子一致
在使用laplacian算子进行计算时,其结果可能为负,也需要对计算结果取绝对值,保证后续运算和显示都是正确的。
import cv2
import numpy as np
img = cv2.resize(cv2.imread("img/1.jpg"), (300, 300))
#ksize=5
dst=cv2.Laplacian(img, cv2.CV_64F,ksize=5)
dst=cv2.convertScaleAbs(dst)
#ksize=1
dst_1=cv2.Laplacian(img,cv2.CV_64F,ksize=1)
dst_1=cv2.convertScaleAbs(dst_1)
cv2.imshow("img",img)
cv2.imshow("dst",dst)
cv2.imshow("dst_1",dst_1)
cv2.waitKey()
cv2.destroyAllWindows()
四:三种算子的总结
1、scharr算子和sobel算子使用的方法差不多,也分为x方向和y方向,区别就是scharr算子的核数值相对较大,使得周围像素对其影响会变大,边缘会更多,最终效果图会更明显。也是取绝对值。
2、laplacian算子是二阶的,对边缘更敏感,但是对噪声也敏感,所以一般会和其他算法一起使用;他不分为xy,相当于周围4个像素和对4倍的目标像素相减,也就是周围4个像素的均值与该像素比较,,若此处有边缘,会差值较大,取的也是差的绝对值。