永兴的笔记-OpenCV-6图像梯度和边缘检测

在这里插入图片描述

一、什么是图像梯度:

图像的梯度就是描述图像中灰度的变化,微积分就是求函数的变化率,即导数(梯度)。图像的梯度相当于2个相邻像素之间的差值。
图像梯度可以把图像看成二维离散函数,图像梯度其实就是这个二维离散函数的求导:
在x方向,选取某个像素,假设其像素值是100,沿x方向的相邻像素分别是90,90,90,则根据上面的计算其x方向梯度分别是10,0,0。这里只取变化率的绝对值,表明变化的大小即可。
在这里插入图片描述
灰度值100和90之间亮度相差10,并不是很明显,与一大群90的连续灰度值在一起,轮廓必然是模糊的。我们注意到,如果相邻像素灰度值有变化,那么梯度就有值,如果相邻像素灰度值没有变化,那么梯度就为0。如果我们把梯度值与对应的像素相加,那么灰度值没有变化的,像素值不变,而有梯度值的,灰度值变大了,那么图像的边缘更加明显。
在这里插入图片描述

二、OpenCV中的图像梯度应用(检测边缘):

OpenCV提供三种类型的梯度滤波器或高通滤波器,Sobel,Scharr和Laplacian.
高通滤波器(英语:High-pass filter)是容许高频信号通过、但减弱(或减少)频率低于截止频率信号通过的滤波器。对于不同滤波器而言,每个频率的信号的减弱程度不同。它有时被称为低频剪切滤波器;
Sober 基本数学原理:
在这里插入图片描述
1、Sober 算子
Sober 算子是离散微分算子(discrete differentiation operator),用来计算图像灰度的近似梯度。
cv2.Sobel(src, ddepth, dx, dy, dst=None, ksize=None, scale=None, delta=None, borderType=None)

  • src : 原图像
  • ddepth : 目标图像的深度,填写 -1 则保存与原图像的深度一致;
    图像深度是单个像素点的色彩详细度,往往把像素深度说成是图像深度。表示一个像素的位数越多,它能表达的颜色数目就越多,而它的深度就越深。
    src.depth() 为原图像的深度,则一般对应的ddepth为:
sec.depth() ddepth
CV_8U -1/CV_16S/CV_32F/CV_64F
CV_16U/CV_16S -1/CV_32F/CV_64F
CV_32F -1/CV_32F/CV_64F
CV_64F -1/CV_64F
  • dx:表示在x方向上求导的阶数
  • dy:表示在y方向上求导的阶数
    如果是0则表示在该方向上不求导(不能2个方向都是0)
    一阶导数可以用来描述原函数的增减性。
    二阶导数可以用来判断函数在一段区间上的凹凸性,f’’(x)>0,则是凹的,f’’(x)<0则是凸的。
  • dst : 表示输出图像
  • ksize : 内核大小
    如果不填写或者ksize<0,那么使用scharr内核过滤因子。scharr的内核过滤因子大小为3。
    xorder = dx ,xorder = dy
    在这里插入图片描述
  • scale : scale是缩放导数的比例常数,默认情况下没有伸缩系数;
  • delta :在将目标图像存储进多维数组前,可以将每个像素值增加delta,默认为0;
  • borderType :是判断图像边界的模式。这个参数默认值为cv2.BORDER_DEFAULT。
    在这里插入图片描述
import cv2
img = cv2.imread("first.jpg")
GRAYImg = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
SoberImg = cv2.Sobel(GRAYImg,-1,0,1)
cv2.imshow("img",img)
cv2.imshow("GRAYimg",GRAYImg)
cv2.imshow("Sober",SoberImg)
cv2.waitKey()
cv2.destroyAllWindows()

在这里插入图片描述
Sobel算子算法的优点是计算简单,速度快。但是由于只采用了2个方向的模板,只能检测水平和垂直方向的边缘,因此这种算法对于纹理较为复杂的图像,其边缘检测效果就不是很理想。该算法认为:凡灰度新值大于或等于阈值的像素点时都是边缘点。这种判断有点欠合理,可能会造成边缘点的误判,因为许多噪声点的灰度值也很大。

2、Scharr 算子
cv2.Scharr(src, ddepth, dx, dy, dst=None, scale=None, delta=None, borderType=None)

  • dx 和 dy : dx >= 0 && dy >= 0 && dx+dy == 1
  • 会看到参数和Sobel算子一致,不过,该函数与Sobel的区别在于,Scharr仅作用于大小为3的内核。但结果更为精确
  • scharr算子与Sobel的不同点是在平滑部分,这里所用的平滑算子是1/16∗[3,10,3],相比于1/4∗[1,2,1],中心元素占的权重更重
    在这里插入图片描述
    大小一样,故计算量一样。
    scharr算子临近像素的权重更大,故精确度更高。
    对比两种算子的处理效果。发现scharr算子能计算出更小的梯度变化
    Scharr:
    在这里插入图片描述
import cv2
img = cv2.imread("first.jpg")
GRAYImg = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
Scharr = cv2.Scharr(GRAYImg,-1,0,1)
cv2.imshow("img",img)
cv2.imshow("GRAYimg",GRAYImg)
cv2.imshow("Scharr",Scharr)
cv2.waitKey()
cv2.destroyAllWindows()

3、Laplacian 算子

Laplacian算子属于二阶微分算子。
二阶微分的边缘定位能力更强,锐化效果更好
在这里插入图片描述
在这里插入图片描述
Laplacian算子进行边缘检测并没有像Sobel的平滑过程,所以它会对噪声产生较大的响应,并且无法分别得到水平方向、垂直方向或者其他固定方向的的边缘。但是它只有一个卷积核,所以计算成本会更低。
在这里插入图片描述
从模板形式容易看出,如果在图像中一个较暗的区域中出现了一个亮点,那么用拉普拉斯运算就会使这个亮点变得更亮。因为图像中的边缘就是那些灰度发生跳变的区域,所以拉普拉斯锐化模板在边缘检测中很有用。一般增强技术对于陡峭的边缘和缓慢变化的边缘很难确定其边缘线的位置。但此算子却可用二次微分正峰和负峰之间的过零点来确定,对孤立点或端点更为敏感,因此特别适用于以突出图像中的孤立点、孤立线或线端点为目的的场合。同梯度算子一样,拉普拉斯算子(Laplacian)也会增强图像中的噪声,有时用拉普拉斯算子进行边缘检测时,可将图像先进行平滑处理。
图像锐化处理的作用是使灰度反差增强,从而使模糊图像变得更加清晰。 图像模糊的实质就是图像受到平均运算或积分运算 。 因此可以对图像进行逆运算,如微分运算能够突出图像细节,使图像变得更为清晰。由于拉普拉斯是一种微分算子,它的应用可增强图像中灰度突变的区域,减弱灰度的缓慢变化区域。

cv2.Laplacian(src, ddepth, dst=None, ksize=None, scale=None, delta=None, borderType=None)

  • src :原图像
  • ddepth:输出图像深度,-1则为保持原图像一致
    src.depth() 为原图像的深度,则一般对应的ddepth为:
深度 取值范围
CV_8U 0–255
CV_8S -128–127
CV_16U 0–65535
CV_16S -32768–32767
CV_32S 0–65535
CV_32F(单精度浮点数) 0.0–1.0
CV_64F(双精度浮点数 ) 0.0–1.0
src.depth() ddepth
CV_8U -1/CV_16S/CV_32F/CV_64F
CV_16S -1/CV_32F/CV_64F
CV_32F -1/CV_32F/CV_64F
CV_64F -1/CV_64F

转换深度后的图像可能需要归一化;

  • dst:处理后的图像
  • ksize:内核大小(大小必须为正奇数)(一般为1、3、5、7。默认为1)
  • scale是缩放导数的比例常数,默认情况下没有伸缩系数;
  • delta是一个可选的增量,将会加到最终的dst中,同样,默认情况下没有额外的值加到dst中;
  • borderType是判断图像边界的模式。这个参数默认值为cv2.BORDER_DEFAULT。
    在这里插入图片描述
import cv2
img = cv2.imread("first.jpg",0) #灰度模式读取图像
LaplacianImg = cv2.Laplacian(img,-1)
cv2.imshow("img",img)
cv2.imshow("Laplacian",LaplacianImg)
cv2.waitKey()
cv2.destroyAllWindows()

在这里插入图片描述

三、边缘检测:

边缘检测是图像处理和计算机视觉中的基本问题,边缘检测的目的是标识数字图像中亮度变化明显的点。图像属性中的显著变化通常反映了属性的重要事件和变化。 这些包括(i)深度上的不连续、(ii)表面方向不连续、(iii)物质属性变化和(iv)场景照明变化。 边缘检测是图像处理和计算机视觉中,尤其是特征提取中的一个研究领域。
图像边缘检测大幅度地减少了数据量,并且剔除了可以认为不相关的信息,保留了图像重要的结构属性。 有许多方法用于边缘检测,它们的绝大部分可以划分为两类:基于查找一类和基于零穿越的一类。基于查找的方法通过寻找图像一阶导数中的最大和最小值来检测边界,通常是将边界定位在梯度最大的方向。基于零穿越的方法通过寻找图像二阶导数零穿越来寻找边界,通常是Laplacian过零点或者非线性差分表示的过零点。
边缘检测的一般步骤:

  • 滤波:边缘检测的算法主要是基于图像强度的一阶和二阶导数,但导数通常对噪声很敏感,因此必须采用滤波器来改善与噪声有关的边缘检测器的性能。常见的滤波方法主要有高斯滤波,即采用离散化的高斯函数产生一组归一化的高斯核(具体见“高斯滤波原理及其编程离散化实现方法”一文),然后基于高斯核函数对图像灰度矩阵的每一点进行加权求和(具体程序实现见下文)。
  • 增强:增强边缘的基础是确定图像各点邻域强度的变化值。增强算法可以将图像灰度点邻域强度值有显著变化的点凸显出来。在具体编程实现时,可通过计算梯度幅值来确定。
  • 检测:经过增强的图像,往往邻域中有很多点的梯度值比较大,而在特定的应用中,这些点并不是我们要找的边缘点,所以应该采用某种方法来对这些点进行取舍。实际工程中,常用的方法是通过阈值化方法来检测。

Canny 边缘检测 :
原理:

  • 去噪:先经过高斯平滑

  • 增强:寻找图像强度梯度

  • 非极大抑制:
    这一步的目的是将模糊(blurred)的边界变得清晰(sharp)。通俗的讲,就是保留了每个像素点上梯度强度的极大值,而删掉其他的值。对于每个像素点,进行如下操作:
    将其梯度方向近似为以下值中的一个(0,45,90,135,180,225,270,315)(即上下左右和45度方向)
    在这里插入图片描述
    比较该像素点,和其梯度方向正负方向的像素点的梯度强度
    如果该像素点梯度强度最大则保留,否则抑制(删除,即置为0)
    在这里插入图片描述

  • 双阈值
    经过非极大抑制后图像中仍然有很多噪声点。Canny算法中应用了一种叫双阈值的技术。即设定一个阈值上界和阈值下界(opencv中通常由人为指定的),图像中的像素点如果大于阈值上界则认为必然是边界(称为强边界,strong edge),小于阈值下界则认为必然不是边界,两者之间的则认为是候选项(称为弱边界,weak edge)。

  • 利用滞后的边界跟踪
    思想:和强边界相连的弱边界认为是边界,其他的弱边界则被抑制
    滞后阈值:
    现在需要确定哪些边界是真正的边界,需要两个阈值,minVal和maxVal。图像灰度梯度 高于maxVal被认为是真正的边界,低于minVal的舍弃。两者之间的值要判断是否与真正的边界相连,相连就保留,不相连舍弃。

    • Ⅰ.如果某一像素位置的幅值超过 高 阈值, 该像素被保留为边缘像素。

    • Ⅱ.如果某一像素位置的幅值小于 低 阈值, 该像素被排除。

    • Ⅲ.如果某一像素位置的幅值在两个阈值之间,该像素仅仅在连接到一个高于 高 阈值的像素时被保留。
      在这里插入图片描述

cv2.Canny(image, threshold1, threshold2, edges=None, apertureSize=None, L2gradient=None)

  • image : 原图像
  • threshold1:阈值1
  • threshold2:阈值2
    • 低于阈值1的像素点会被认为不是边缘;
    • 高于阈值2的像素点会被认为是边缘;
    • 在阈值1和阈值2之间的像素点,若与处理后得到的边缘像素点相邻,则被认为是边缘,否则被认为不是边缘。
    • 对于Canny函数的使用,推荐的高低阈值比在2:1到3:1之间。
  • edges:输出边缘映射; 默认:单通道8位图像,与图像大小相同。
  • apertureSize:Sobel算子内核大小 (默认为3)
  • L2gredient:是否采用更精确的方式计算图像梯度(默认为 False)
import cv2
img = cv2.imread("first.jpg")
GRAYImg = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)  #转换为灰度图像
GauBurlGrayImg = cv2.GaussianBlur(GRAYImg,(3,3),1) #进行高斯滤波
imgCanny = cv2.Canny(GauBurlGrayImg,100,200) #边缘检测
cv2.imshow("img",img)
cv2.imshow("Canny",imgCanny)
cv2.waitKey()
cv2.destroyAllWindows()

在这里插入图片描述
Canny 的特点:

  • 1.低错误率: 标识出尽可能多的实际边缘,同时尽可能的减少噪声产生的误报。
  • 2.高定位性: 标识出的边缘要与图像中的实际边缘尽可能接近。
  • 3.最小响应: 图像中的边缘只能标识一次,并且可能存在的图像噪声不因标识为边缘。

练习题6:

将同一图片。转化为灰度图像后,分别使用:Sobor 、 Scharr 、 Laplacian 算子 和 Canny , 并同时显示5副图像。(要求调整4种方式的参数达到最好边缘检测效果)

评论出你的答案。

发布了45 篇原创文章 · 获赞 28 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/m0_43505377/article/details/103772566