计算机视觉基础-图像处理-边缘检测
简介
1.什么是边缘?
边缘是图像强度函数快速变化的地方。
2.如何检测边缘?
为了检测边缘,我们需要检测图像中的不连续性,可以使用导数来检测不连续性。但是,导数也会受到噪声的影响,因此建议在求导数之前先对图像进行平滑处理。 然后我们可以使用遮罩使用卷积来检测边缘。
Sobel边缘检测
sobel算子是一个离散差分算子.它计算图像像素点亮度值的近似梯度。图像是二维的,即沿着宽度/高度两个方向,我们使用两个卷积核对原图像进行处理:
1.水平方向
原始像素灰度值–>(右边像素值-左边像素值),反映了水平方向的变化情况。
2.垂直方向
这样的话,我们就得到了两个新的矩阵,分别反映了每一点像素在水平方向上的亮度变化情况和在垂直方向上的亮度变换情况。
综合考虑这两个方向的变化,我们使用:
反映某个像素的梯度变化情况.
有时候为了简单起见,也直接用绝对值相加替代:
3.基于OpenCV的实现
"""
cv2.Sobel(src, #参数是需要处理的图像;
ddepth, #图像的深度,-1表示采用的是与原图像相同的深度。目标图像的深度必须大于等于原图像的深度
dx, #dx和dy表示的是求导的阶数,0表示这个方向上没有求导,一般为0、1、2。
dy[,
dst[, #输出图片
ksize[,#Sobel算子的大小,必须为1、3、5、7。
scale[, #缩放导数的比例常数,默认情况下没有伸缩系数;
delta[, #可选的增量,将会加到最终的dst中,同样,默认情况下没有额外的值加到dst中;
borderType #判断图像边界的模式。这个参数默认值为cv2.BORDER_DEFAULT。
]]]]])
"""
import cv2
from matplotlib import pyplot as plt
#读图
img = cv2.imread('C:\\Users\\Administrator\\Pictures\\icecream.jpg',0)
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=5)
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=5)
#画图
plt.subplot(2,1,2),plt.imshow(img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,1),plt.imshow(sobelx,cmap = 'gray')
plt.title('Sobel X'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,2),plt.imshow(sobely,cmap = 'gray')
plt.title('Sobel Y'), plt.xticks([]), plt.yticks([])
plt.show()
Canny边缘检测
分为5步:
- 使用高斯滤波器,以平滑图像,滤除噪声。
- 计算图像中每个像素点的梯度强度和方向。
- 应用非极大值(Non-Maximum Suppression)抑制,以消除边缘检测带来的杂散响应。
- 应用双阈值(Double-Threshold)检测来确定真实的和潜在的边缘。
- 通过抑制孤立的弱边缘最终完成边缘检测。
1.高斯模糊
在实际的图片中,都会包含噪声。但有时候,图片中的噪声会导致图片中边缘信息的消失。对此的解决方案就是使用高斯平滑来减少噪声,即进行高斯模糊操作。该操作是一种滤波操作,与高斯分布有关,下面是一个二维的高斯函数,其中 (x, y) 为坐标,σ 为标准差:
进行高斯滤波之前,需要先得到一个高斯滤波器(kernel)。如何得到一个高斯滤波器?其实就是将高斯函数离散化,将滤波器中对应的横纵坐标索引代入高斯函数,即可得到对应的值。不同尺寸的滤波器,得到的值也不同,下面是 (2k+1)x(2k+1) 滤波器的计算公式 :
常用尺寸为 5x5,σ=1.4 的高斯滤波器。
2.梯度检测
边缘检测就是梯度检测,这里要知道梯度的大小和方向。由于图片是离散的,可以用有限导数来近似图片的梯度:
图片梯度幅值为:
梯度方向为:
3.非极大值抑制
在每一点上,邻域中心与沿着其对应的梯度方向的两个像素相比,若中心像素为最大值,则保留,否则中心置0,这样可以抑制非极大值,保留局部梯度最大的点,以得到细化的边缘。
对图像进行梯度计算后,仅仅基于梯度值提取的边缘仍然很模糊。对边缘有且应当只有一个准确的响应。而非极大值抑制则可以帮助将局部最大值之外的所有梯度值抑制为0。非极大值抑制是一种边缘稀疏技术,非极大值抑制的作用在于“瘦”边。直观上地看,对第二步得到的图片,边缘由粗变细了。
4.双阀值检测
一般的边缘检测算法用一个阀值来滤除噪声或颜色变化引起的小的梯度值,而保留大的梯度值。Canny算法应用双阀值,即一个高阀值和一个低阀值来区分边缘像素。
如果边缘像素点梯度值>高阀值,则被认为是强边缘点。
如果低阀值<边缘梯度值<高阀值,则标记为弱边缘点。
如果边缘像素点梯度值<低阀值,则被抑制掉。
5.抑制孤立低阈值点
此时,强边缘被划分为边缘,小于低阈值的被抑制,剩下弱边缘等待被分类,弱边缘有可能是真实的边缘,也有可能是噪声或者颜色变化引起的。
通常,由真实边缘引起的弱边缘像素将连接到强边缘像素,而噪声响应未连接。为了跟踪边缘连接,通过查看弱边缘像素及其8个邻域像素,只要其中一个为强边缘像素,则该弱边缘点就可以保留为真实的边缘。
6.基于OpenCV的实现
"""
cv2.Canny(image, # 输入原图(必须为单通道图)
threshold1,
threshold2, # 较大的阈值2用于检测图像中明显的边缘
[, edges[,
apertureSize[, # apertureSize:Sobel算子的大小
L2gradient ]]]) # 参数(布尔值):
true: 使用更精确的L2范数进行计算(即两个方向的倒数的平方和再开放),
false:使用L1范数(直接将两个方向导数的绝对值相加)。
"""
import cv2
import numpy as np
from matplotlib import pyplot as plt
original_img = cv2.imread("C:\\Users\\Administrator\\Pictures\\icecream.jpg",0)
# canny边缘检测
img1 = cv2.GaussianBlur(original_img,(3,3),0)
canny = cv2.Canny(img1, 50, 150)
# 画图
plt.subplot(1,2,1),plt.imshow(original_img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(1,2,2),plt.imshow(canny,cmap = 'gray')
plt.title('Canny'), plt.xticks([]), plt.yticks([])
plt.show()