1 图像边缘
什么是图像边缘:
图像中存在大量边缘线,边缘线的产生是因为图像中存在不同的区域,当整个图像的区域发生灰度值变换的过程中产生边缘。两个区域的交界处所形成的边缘上的点,所组成的线就是边缘线
边缘是一个矢量,不仅有位置还有朝向;穿过边缘的灰度级是不连续的;
边缘产生的原因:
(1)不同颜色的变化;亮度变化;纹理变化、材质变化…
(2)曲面不同的法向量:两个相邻平面的法向量不一致,形成不平整平面产生边缘
(3)不同光照
边缘的分类:
(1)step edge:一阶导数有急剧的峰值
(2)Ramp edge:二阶导数在两个变化区域产生抖动变化(峰值)
(3)Peak edge:一阶导数的变化变会产生两个峰值
2 图像梯度
图像梯度: 用于表达边缘的属性,用梯度表现边缘是什么类型的边缘
图像梯度计算: 对二维图像求二维一阶导数(或更高阶导数)得到一些峰值,当峰值超过一个特定的阈值的时候,则认为该峰值为边缘。
数字图像f(x,y)的梯度被定义为一个向量,对x方向和y方向分别求梯度,整个图像的梯度为两个方向梯度的表达式:
图像的梯度为矢量,包含值和方向,梯度的值为:
梯度的方向为:
对图像求偏导,其实就是求变化率
3 边缘检测算法
3.1 Robert算子
x方向的梯度、y方向的梯度计算方式为:
3.2 Prewitt算法
x方向的梯度、y方向的梯度计算方式为:
3.3 Soble算子
x方向的梯度、y方向的梯度计算方式为:
Sobel算子原理:
(1)假设要处理的图像为I,要在水平方向和垂直方向分别求一阶导数
(2)在图像的每一个点,结合以上两个结果求出:
(3)统计G中极大值所在的位置,就是图像的边缘
Sobel边缘检测流程:
(1)API: Sobel_x_or_y=cv2.Sobel(src,ddepth,dx,dy,dst=None, ksize=None, scale=None, delta=None, borderType=None)
参数:
src:传入图像
ddepth:图像深度
dx和dy:代表是否在这个方向上求导,0代表不求导,1代表求导
ksize:是Sobel算子的大小,既卷积核的大小,必须为奇数。如果ksize=-1,就演变成为3×3的Scharr算子。
scale:缩放导数的比例常数,默认情况为没有伸缩系数
BorderType:图像边界的模式。默认值为cv2.BORDER_DEFAULT
(2) Sobel函数在某方向上求导,求导后可能会有负值,还可能会有大于255的值。而原图像是uint8,既8位无符号数,所以Sobel建立的图像位数不够,会有阶段。因此要使用16位有符号的数据类型,既cv2.CV_16S,处理完图像后,再利用CV2.convertScaleAbs()函数将其转换为uint8格式,否者图像无法显示。
(3) Sobel算子是在两个方向上计算的,最后还需要用cv2.addWeighted()函数将其组合起来。
import cv2
import matplotlib.pyplot as plt
import numpy as np
#1. 读取图像,灰度形式
img=cv2.imread('suanzi.png',1)#灰度值的方式读取图像
#2.计算x方向和y方向的梯度
x=cv2.Sobel(img,cv2.CV_16S,1,0)
y=cv2.Sobel(img,cv2.CV_16S,0,1)
#3.格式转换
abax=cv2.convertScaleAbs(x)
abay=cv2.convertScaleAbs(y)
#4.xy方向加权
res=cv2.addWeighted(abax,0.5,abay,0.5,0)
cv2.imshow('origin',img)
cv2.imshow('sobel',res)
cv2.waitKey(0)
Sobel算子缺点:
当内核大小为3时,Sobel算子内核可能产生比较明显的误差。为解决这一个问题,使用Scharr函数
3.4 Scharr算子
特点:
Scharr函数仅作用于大小为3的内核,该函数的运算与Sobel函数一样快,但是结果却更加准确
Scharr算子原理:
(1)假设要处理的图像为I,要在水平方向和垂直方向分别求一阶导数
(2)在图像的每一个点,结合以上两个结果求出:
(3)统计G中极大值所在的位置,就是图像的边缘。
import cv2
import matplotlib.pyplot as plt
import numpy as np
#1. 读取图像,灰度形式
img=cv2.imread('suanzi.png',1)#灰度值的方式读取图像
#2.计算x方向和y方向的梯度(scharr算子)
x=cv2.Sobel(img,cv2.CV_16S,1,0,ksize=-1)
y=cv2.Sobel(img,cv2.CV_16S,0,1,ksize=-1)
#3.格式转换格式
abax=cv2.convertScaleAbs(x)#转换为uint
abay=cv2.convertScaleAbs(y)
#4.xy方向加权
res=cv2.addWeighted(abax,0.5,abay,0.5,0)
cv2.imshow('origin',img)
cv2.imshow('scharr',res)
cv2.waitKey(0)
3.5 Laplacian算子
原理:
利用二阶导数来检测边缘,因为图像是2维,我们需要在两个方向上进行求导:
不连续函数(图像)的二阶导数:f’’(x)=f’(x+1)-f’(x)=f(x+1)-f(x)-f(x)+f(x-1)=f(x+1)=f(x-1)-2f(x)
使用的卷积核为:
API: cv2.Laplacian(src,ddepth,ksize)
参数:
src:需要处理的图像
Ddepth:图像的深度,-1表示采用的是原图像相同的深度,目标图像的深度必须大于等于源图像的深度
ksize:算子的大小,即卷积核的大小,必须为奇数。
import cv2
import matplotlib.pyplot as plt
import numpy as np
#1. 读取图像,灰度形式
img=cv2.imread('suanzi.png',1)#灰度值的方式读取图像
#2.laplacian边缘检测(算子)
res=cv2.Laplacian(img,cv2.CV_16S)
#3.格式转换格式
res=cv2.convertScaleAbs(res)#转换为uint8
cv2.imshow('origin',img)
cv2.imshow('laplacian',res)
cv2.waitKey(0)
3.6 canny算子
被认为是最优的边缘检测算法
边缘检测流程:
(1)噪声去除——高斯滤波
由于边缘检测很容易受到噪声的影响,所以首先使用5×5的高斯滤波去除噪声。
(2)计算图像梯度
2.1 平滑后的图像使用Sobel算子计算水平和垂直方向的一阶导数(Gx和Gy)
2.2 根据梯度图计算边界的梯度值和梯度方向,公式如下:
如果某个像素点是边缘,则其梯度方向总是垂直于边缘。梯度方向被归为4类:垂直、水平和两个对角线方向。
(3)非极大值抑制
在获得梯度的方向和大小后,对整幅图像进行扫描,去除那些非边界的点。对每一个像素进行检查,看该点梯度值是不是周围具有相同梯度方向的点中是最大的,如果是最大的则保留该点,否则该点被抑制。扫描完整个图像后,结果为具有‘细边’的二进制图像。
(4)滞后阈值
确定真正的边界,设定两个阈值:minVal和maxVal。当图像的灰度梯度高于maxVal时被认为是真正的边界,低于minVal的边界被抛弃;如果灰度梯度值介于两者之间,就要看这个点是否与某个被确定为真正的边界点的点相连,如果是就认为该点是边界点,保留该点,否者抛弃该点。
边缘检测流程:
(1)API
canny=cv2.Canny(image,threshold1,threshold2)
(2)参数:
image:灰度图
threshold1:minVal,较小的阈值,将间断的边缘连接起来
threshold2:maxVal较大的阈值检测图像中明显的边缘。
import cv2
import matplotlib.pyplot as plt
import numpy as np
#1. 读取图像,灰度形式
img=cv2.imread('suanzi.png',1)#灰度值的方式读取图像
#2.canny边缘检测
canny=cv2.Canny(img,0,100)
cv2.imshow('origin',img)
cv2.imshow('laplacian',canny)
cv2.waitKey(0)