zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    FileSystemWatcher用法详解【转】
    关于TransactionScope事务的TransactionScopeOption的实例
    TransactionScope IsolationLevel 事务隔离级别
    C#中TransactionScope的使用方法和原理
    关于Qt 静态成员函数调用信号
    【Qt编程】基于QWT的曲线绘制及图例显示操作——有样点的实现功能
    使用qwt作曲线图——有网格线背景的画法
    Qt程序app添加图标复制到其它电脑后不显示的解决方法
    QUrl的使用,特别是对含特殊字符的字符串进行 URL 格式化编码
    QDateTime 本地时间和UTC时间转换问题
  • 原文地址:https://www.cnblogs.com/yanshw/p/15512239.html
Copyright © 2011-2022 走看看