图像梯度
在模糊的图像中,物体的轮廓不明显,轮廓边缘灰度变化不明显,导致层次感不强;
而清晰的图像中,物体的轮廓很清晰,轮廓边缘灰度变换明显,层次感强;
这种变化明显与否可以用 导数或梯度 来衡量,实际上可以用 灰度变化率 来计算;
如下图
如果相邻像素相同,灰度变化率为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