图像梯度
Sobel算子
dst= cv2.Sobel(src, ddepth, dx, dy, ksize)
ddepth:图像深度,都是默认-1,表示输入输出深度一样
dx,dy:分别表示水平和竖直方向, 置1表示计算该方向
ksize:是Sobel算子的大小,表示核的大小
第二个参数,使用 -1 表示输出与输入图像的数据类型一致。如果原图是uint8型,那么在sobel算子计算后,得到的图像可能会有负值,负值会被截断为0或者255。
两种方式:
改变输出图像的数据类型(第二个参数:cv2.CV_64F)
改变原始图像的数据类型,那么第二个参数可以是 -1
如果ksize=-1,默认3x3 的Scharr 滤波器,效果优于 3x3 的Sobel 滤波器
x, y方向的卷积核:
右边 - 左边,差异值作为水平方向
下面 - 上面,差异值作为垂直方向
由于只采用了2个方向的模板,只能检测水平和垂直方向的边缘,因此这种算法对于纹理较为复杂的图像,其边缘检测效果就不是很理想。该算法认为:凡灰度新值大于或等于阈值的像素点时都是边缘点。这会造成边缘点的误判,因为许多噪声点的灰度值也很大。
sobelx = cv.Sobel(img, cv.CV_64F, 1, 0, ksize=3) sobelx = cv.convertScaleAbs(sobelx) # 绝对值转换, 负值取绝对值 sobely = cv.Sobel(img, cv.CV_64F, 0, 1, ksize=3) sobely = cv.convertScaleAbs(sobely) # 建议的做法,是利用 addWeighted 加权 x,y方向的图像,dx=1 && dy=1 效果较差 sobelxy = cv.addWeighted(sobelx, 0.5, sobely, 0.5, 0) res = np.hstack((sobelx, sobely, sobelxy)) show("res", res)
灰图读入,同上用法展示:
Scharr 算子
dst = cv2.Scharr(src,ddepth,dx,dy,scale,delta,borderType)
src: 原图像
ddepth: 输出图像的深度,该值与函数cv2.Sobel()中的参数ddepth的含义相同
dx: x方向上的求导阶数
dy: y方向上的求导阶数
scale: 计算导数时采用的缩放因子,默认为1,是没有缩放的
delta: 加在目标图像dst上的值,默认为0
borderType: 边界样式,默认值为cv2.BORDER_DEFAULT
scharr算子与Sobel的不同点是在平滑部分,这里所用的平滑算子是1/16∗[3,10,3],相比于1/4∗[1,2,1],中心元素占的权重更重,这可能是相对于图像这种随机性较强的信号,邻域相关性不大,所以邻域平滑应该使用相对较小的标准差的高斯函数,也就是更瘦高的模板。对一些细线,更敏感,更能描绘出。
Sobel算子与Scharr算子比较:
Sobel算子的缺点是,当结构较小时,精确度不高,Scharr算子具有更高的精度。
Laplacian算子
dst = cv2.Laplacian(src,ddepth,ksize,scale,delta,borderType)
src: 原图像
ddepth: 输出图像的深度。
ksize: 计算二阶导数的核尺寸大小,必须为正的奇数。
scale: 计算导数时采用的缩放因子,默认为1,是没有缩放的
delta: 加在目标图像dst上的值,默认为0
borderType: 边界样式,默认值为cv2.BORDER_DEFAULT
Laplacian算子是一种二阶导数算子,具有旋转不变性,可以满足不同方向的边缘检测要求。通常其算子的系数之和需要为0。
Canny边缘检测
使用高斯滤波器,以平滑图像,滤除噪声
计算图像中每个像素点的梯度强度和方向
应用非极大值抑制(Non-Maximun Suppression, NMS),以消除边缘检测带来的杂散响应
应用双阈值(Double-Threshold)检测来确定真实的和潜在的边缘
通过抑制孤立的弱边缘最终完成边缘检测
高斯滤波
大小为*(2k + 1) x (2k + 1)*的高斯滤波器核的生成方程式由下式给出:
一般选用 5 × 5 的核,(注意需要归一化)
- 计算梯度强度和方向
- 梯度的方向与边缘的方向总是垂直的,通常取八个不同的方向计算梯度。
- 再用边缘检测算子(Sobel)计算图像的水平,垂直,对角梯度,得到水平Gx和垂直Gy方向的一阶导数值,由此便可以确定像素点的梯度G的大小θ。
角度一般是近似到8个方向上
非极大值抑制(NMS)
非极大值抑制是一种边缘稀疏技术,非极大值抑制的作用在于“瘦”边。非极大值抑制可以将局部最大值之外的所有梯度值抑制为0。
对梯度图像中每个像素进行非极大值抑制的算法是:
将当前像素的梯度强度与沿正负梯度方向上的两个像素进行比较。
如果当前像素的梯度强度与另外两个像素相比最大,则该像素点保留为边缘点,否则该像素点将被抑制。
经过上述处理后,对于同一个方向的若干边缘点,基本上只保留了一个,因此实现了边缘细化的目的。
如下图,A , B , C 三点中,梯度方向上A点的局部梯度值最大,所以保留A点,其余两点被抑制。
用双阈值算法检测和连接边缘
经过上述步骤后,图像内的强边缘已经在当前获取的边缘内,但是,一些虚边缘也在内。
设置两个阈值,高阈值maxVal、低阈值minVal。
根据边缘梯度值和阈值关系,判断边缘的属性:
梯度值 ≥ maxVal, 强边缘
minVal < 梯度值 < maxVal, 虚边缘
虚边缘与强边缘相连时,则保留。
梯度值 ≤ minVal, 抑制当前边缘
实际中maxVal:minVal = 2:1的比例效果比较好
抑制孤立低阈值点
到目前为止,被划分为强边缘的像素点已经被确定为边缘,因为它们是从图像中的真实边缘中提取出来的。然而,对于弱边缘像素,将会有一些争论,因为这些像素可以从真实边缘提取也可以是因噪声或颜色变化引起的。为了获得准确的结果,应该抑制由后者引起的弱边缘。通常,由真实边缘引起的弱边缘像素将连接到强边缘像素,而噪声响应未连接。为了跟踪边缘连接,通过查看弱边缘像素及其8个邻域像素,只要其中一个为强边缘像素,则该弱边缘点就可以保留为真实的边缘。
抑制孤立边缘点的伪代码描述如下:
Canny
cv2.Canny(image,threshold1,threshold2,apertureSize,L2gradient)
image :输入图像,必须为8位图像
threshold1: 第一个阈值
threshold2:第二个阈值
apertureSize: Sobel算子的大小
L2gradient: 计算图像梯度幅度的表示。默认值为False,使用L1范数计算;如果为True,则使用更精确的L2范数计算。
# 灰图读入 img = cv.imread(name + '_3.2.jpg', cv.IMREAD_GRAYSCALE) # 高斯去噪 img = cv.GaussianBlur(img, (3, 3), 0) # Canny检测 canny1 = cv.Canny(img, 128, 200) canny2 = cv.Canny(img, 32, 128) show('origin', img) show('canny1', canny1) show('canny2', canny2)