图像梯度是指图像某像素在x和y两个方向上的变化率(与相邻像素比较),本质上即导数。
Sobel算子
Sobel算子是高斯平滑加微分运算的联合运算,因此它更抗噪声
Sobel算子常用模板
代码:
dst=cv.Sobel(src,ddepth,dx,dy,ksize)
- ddepth: 输出图像的深度,若使用-1,则与原图像深度保持一致
numpy.uint8
,输出图像的深度必须大于等于原图像的深度 - dx和dy分别表示导数方向为水平和竖直方向,0表示此方向不求导
- kszie: Sobel算子的大小,如果ksize = -1,则使用3x3 Scharr滤波器,比3x3 Sobel滤波器具有更好的结果,必须为1,3,5,7
import cv2 as cv
img=cv.imread('test.png',cv.IMREAD_GRAYSCALE)
# 在x方向求导
sobelx=cv.Sobel(img,cv.CV_8U,1,0,ksize=3)
# 取绝对值,转回原来的uint8形式
sobelx=cv.convertScaleAbs(sobelx)
cv.imshow('original',img)
cv.imshow('sobelx',sobelx)
cv.waitKey(0)
cv.destroyAllWindows()
处理结果:

从上面的处理结果我们会发现一个问题 :右边的边缘信息被忽略了.
造成此种情况的原因在于黑色到白色的过渡被视为正斜率(具有正值),而白色到黑色的过渡被视为负斜率(具有负值),此时若设置输出图像的深度为cv.CV_8U
或numpy.uint8
,则所有的负值都会被截断为0(所建立的图像位数不够,会被截断为0),边界将会丢失。
解决方案: 如果要检测两个边缘,更好的选择是将输出数据类型保留为更高的形式,例如cv.CV_16S
,cv.CV_64F
等,取其绝对值,然后转换回cv.CV_8U
。
注意:取绝对值的步骤也同样需要,否则边缘也同样会丢失。
import cv2 as cv
img=cv.imread('test.png',cv.IMREAD_GRAYSCALE)
# CV_64F 表示输出图像的深度
# 在x方向求导
sobelx=cv.Sobel(img,cv.CV_64F,1,0,ksize=3)
sobelx=cv.convertScaleAbs(sobelx)
# 在y方向求导
sobely=cv.Sobel(img,cv.CV_64F,0,1,ksize=3)
sobely=cv.convertScaleAbs(sobely)
# x和y方向按权求和
sobelxy=cv.addWeighted(sobelx,0.5,sobely,0.5,0)
cv.imshow('original',img)
cv.imshow('sobelx',sobelx)
cv.imshow('sobely',sobely)
cv.imshow('sobelxy',sobelxy)
cv.waitKey(0)
cv.destroyAllWindows()
处理结果:
Scharr算子
Scharr是对Sobel算子(使用小的卷积核求解梯度角度时)的优化。
卷积核
代码:
import cv2 as cv
img=cv.imread('test.png',cv.IMREAD_GRAYSCALE)
# x方向求导
scharrx=cv.Scharr(img,cv.CV_64F,1,0,scale=3)
scharrx=cv.convertScaleAbs(scharrx)
# y方向求导
scharry=cv.Scharr(img,cv.CV_64F,0,1,scale=3)
scharry=cv.convertScaleAbs(scharry)
# x和y方向按权求和
scharrxy=cv.addWeighted(scharrx,0.5,scharry,0.5,0)
cv.imshow('x',scharrx)
cv.imshow('Y',scharry)
cv.imshow('xy',scharrxy)
cv.imshow('original',img)
cv.waitKey(0)
cv.destroyAllWindows()
处理结果:
laplacian算子
拉普拉斯算子是一种各向同性的二阶微分算子,在(x,y)处的值定义为:
当ksize=1时,拉普拉斯滤波器使用的卷积核:
其他常用的拉普拉斯模板:
代码:
import cv2 as cv
img=cv.imread('test.png')
laplacian=cv.Laplacian(img,cv.CV_64F)
laplacian=cv.convertScaleAbs(laplacian)
cv.imshow('laplacian',laplacian)
cv.imshow('original',img)
cv.waitKey(0)
cv.destroyAllWindows()
处理结果:
不同算子之间比较
代码:
import cv2 as cv
import numpy as np
img=cv.imread('lena.jpg',cv.IMREAD_GRAYSCALE)
# Sobel算子
sobelx=cv.Sobel(img,cv.CV_64F,1,0,ksize=3)
sobelx=cv.convertScaleAbs(sobelx)
sobely=cv.Sobel(img,cv.CV_64F,0,1,ksize=3)
sobely=cv.convertScaleAbs(sobely)
sobelxy=cv.addWeighted(sobelx,0.5,sobely,0.5,0)
# Scharr算子
scharrx=cv.Scharr(img,cv.CV_64F,1,0,scale=3)
scharrx=cv.convertScaleAbs(scharrx)
scharry=cv.Scharr(img,cv.CV_64F,0,1,scale=3)
scharry=cv.convertScaleAbs(scharry)
scharrxy=cv.addWeighted(scharrx,0.5,scharry,0.5,0)
# Laplacian算子
laplacian=cv.Laplacian(img,cv.CV_64F)
laplacian=cv.convertScaleAbs(laplacian)
res=np.hstack((img,sobelxy,scharrxy,laplacian))
cv.imshow('all',res)
cv.waitKey(0)
cv.destroyAllWindows()
对比:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S5R2E4aj-1635000400560)(https://cdn.jsdelivr.net/gh/code-jie123/code-jie123cdn/202110021507528.png)]