OpenCV4【11】图像梯度

图像梯度

在模糊的图像中,物体的轮廓不明显,轮廓边缘灰度变化不明显,导致层次感不强;

而清晰的图像中,物体的轮廓很清晰,轮廓边缘灰度变换明显,层次感强;

这种变化明显与否可以用 导数或梯度 来衡量,实际上可以用 灰度变化率 来计算

如下图

如果相邻像素相同,灰度变化率为0,没有梯度;

如果相邻像素不同,灰度变化率不为0,存在梯度;

计算完梯度后,把梯度和原图上对应的像素值相加,那么在存在梯度的位置上像素得到加强,反之像素不变,这样对比度就增强了;

这就是用 梯度 来 增强 图像的原理;

为了减少计算量,可以用 差分 来代替求导

Sobel 算子

Sobel 算子的特点是计算简单,速度快;

主要用来做 边缘检测;

它只采用了两个方向 x、y 上的模板,只能检测 水平和竖直 方向上的边缘,故只能处理 纹理不太复杂 的图片;

对于纹理复杂的图片,其边缘检测效果不是很理想;

def Sobel(src, ddepth, dx, dy, dst=None, ksize=None, scale=None, delta=None, borderType=None)
ddepth:输出图像的深度,要大于等于输入图像的深度;-1代表深度相同,(一般源图像都为CV_8U,为了避免溢出,一般ddepth参数选择CV_32F)
// 解释为什么不用-1
使用-1表示输出图像与输入图像的数据类型一致,即 uint8,
如果原始图像是uint8型的,那么在经过算子计算以后,得到的图像可能会有负值,如果与原图像数据类型一致,那么负值就会被截断变成0或者255,使得结果错误,

那么针对这种问题有两种方式改变:一种就是改变输出图像的数据类型(即第二个参数cv2.CV_64F),另一种就是改变原始图像的数据类型(此时ddepth可以为-1,与原始图像一致)
dx:x方向上的差分阶数,取0或1,1就代表 x 方向上求导
dy:y方向上的差分阶数,取0或1
ksize:卷积核大小,通过卷积计算差分,或者叫求导,只取 1 3 5 7
其他
dst参数表示输出与src相同大小和相同通道数的图像。
scale参数表示缩放导数的比例常数,默认情况下没有伸缩系数。
delta参数表示一个可选的增量,将会加到最终的dst中,同样,默认情况下没有额外的值加到dst中。
borderType表示判断图像边界的模式。这个参数默认值为cv2.BORDER_DEFAULT。

示例

##### example1
img = cv2.imread('imgs/3.png')
img = cv2.resize(img, None, fx=0.5, fy=0.5)

def cv_imshow(img, name):
    cv2.imshow(name, img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

# dx=1, dy=0  表示计算水平方向的,不计算竖直方向,谁为1,计算谁
sobelx1 = cv2.Sobel(img, -1, 1, 0, ksize=3)
print(img.dtype, sobelx1.dtype)     # uint8 uint8
# cv2.CV_64F能表示负数的形式,白-黑是正数,黑-白就是负数了,
# 所有的负数会被截断0,所以要取绝对值
sobelx2 = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3)
print(img.dtype, sobelx2.dtype)     # uint8 float64

print(sobelx2.min())    # -698.0
sobelx2 = cv2.convertScaleAbs(sobelx2)  # 转回原来的uint8形式
print(sobelx2.min())    # 0

hmerge = np.hstack((img, sobelx1, sobelx2))
# cv_imshow(hmerge,'merge')

##### example2
image = cv2.imread('imgs/3.png')
gray_x = cv2.Sobel(image, cv2.CV_32F, 1, 0)  # x方向一阶导数
gray_y = cv2.Sobel(image, cv2.CV_32F, 0, 1)  # y方向一阶导数
gradx = cv2.convertScaleAbs(gray_x)  # 转回原来的uint8形式
grady = cv2.convertScaleAbs(gray_y)
gradxy = cv2.addWeighted(gradx, 0.5, grady, 0.5, 0)  # 图像融合
cv2.imshow("gradient-x", gradx)
cv2.imshow("gradient-y", grady)
cv2.imshow("gradient_xy", gradxy)
# cv2.waitKey(0)

我们看到上面例2中,分别计算了 x方向 和 y方向 的梯度,然后相加,而不是 直接 同时计算  x方向和 y方向  的梯度;

因为经过实验,分别计算再相加效果可能更好一点;

scharr 算子

标准差更小的高斯核,其提取的细节更多;

这个图应该手抖了多写了负号,影响不大

 示例

img = cv2.imread('imgs/3.png')
img = cv2.resize(img, None, fx=0.5, fy=0.5)
scharrx = cv2.Scharr(img, cv2.CV_64F, 1, 0)
scharry = cv2.Scharr(img, cv2.CV_64F, 0, 1)
scharrx = cv2.convertScaleAbs(scharrx)
scharry = cv2.convertScaleAbs(scharry)
scharrxy = cv2.addWeighted(scharrx, 0.5, scharry, 0.5, 0)

res = np.hstack((img, scharrx, scharry, scharrxy))
cv_imshow(res, 'res')
cv2.waitKey(0)

效果图

Laplacian 算子

Laplacian 算子是n维欧几里德空间中的一个二阶微分算子,定义为梯度grad的散度div

 示例

img = cv2.imread('imgs/3.png')
img = cv2.resize(img, None, fx=0.5, fy=0.5)
laplacian = cv2.Laplacian(img, cv2.CV_64F)
laplacian = cv2.convertScaleAbs(laplacian)

res = np.hstack((img, laplacian))
cv_imshow(res, 'reslapls')
cv2.waitKey(0)

效果图

这个算子一般不单独使用 

参考资料:

https://zhuanlan.zhihu.com/p/113397988  CV学习笔记(十三):图像梯度

https://blog.csdn.net/lovetobelove/article/details/86618324

原文地址:https://www.cnblogs.com/yanshw/p/15512239.html