边缘检测
普通边缘检测
opencv提供许多边缘检测滤波函数,这些函数都会将非边缘区域转为黑色,将边缘区域转为白色或其他饱和颜色,但是这些函数容易将噪声错误识别为边缘。缓解这个问题首先对图像进行模糊处理。
python实现方法如下:
import cv2def edges(src,blurKsize=7,edgeKsize=5):
if blurKsize >=3:
src=cv2.imread(src)
cv2.imshow('data',src)
blurredSrc=cv2.medianBlur(src,blurKsize)
graySrc=cv2.cvtColor(blurredSrc,cv2.COLOR_BGR2GRAY)
else:
graySrc=cv2.cvtColor(src,cv2.COLOR_BGR2GRAY)
cv2.Laplacian(graySrc,cv2.CV_8U,graySrc,scale=edgeKsize)
normalizedInverseAlpha=(1.0/255)*(255-graySrc)
channels=cv2.split(src)
for channel in channels:
channel[:]=channel*normalizedInverseAlpha
img=cv2.merge(channels)
cv2.imshow('edge',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
原始图像:
边缘检测图像:
canny边缘检测
1.高斯滤波
滤波的主要目的是降噪,一般的图像处理算法都需要先进行降噪。而高斯滤波主要使图像变得平滑(模糊),同时也有可能增大了边缘的宽度。
高斯函数是一个类似与正态分布的中间大两边小的函数,下面是一个sigma = 1.4,尺寸为3x3的高斯卷积核的例子(需要注意归一化):
若图像中一个3x3的窗口为A,要滤波的像素点为e,则经过高斯滤波之后,像素点e的亮度值为:
2.梯度和方向
图像中的边缘可以指向各个方向,因此Canny算法使用四个算子来检测图像中的水平、垂直和对角边缘。边缘检测的算子(如Roberts,Prewitt,Sobel等)返回水平Gx和垂直Gy方向的一阶导数值,由此便可以确定像素点的梯度G和方向theta
其中G为梯度强度, theta表示梯度方向,arctan为反正切函数。下面以Sobel算子为例讲述如何计算梯度强度和方向。
x和y方向的Sobel算子分别为:
若图像中一个3x3的窗口为A,要计算梯度的像素点为e,则和Sobel算子进行卷积之后,像素点e在x和y方向的梯度值分别为:
3.非极大值抑制
非极大值抑制是一种边缘稀疏技术,非极大值抑制的作用在于“瘦”边。对图像进行梯度计算后,仅仅基于梯度值提取的边缘仍然很模糊。对于标准3,对边缘有且应当只有一个准确的响应。而非极大值抑制则可以帮助将局部最大值之外的所有梯度值抑制为0,对梯度图像中每个像素进行非极大值抑制的算法是:
- 当前像素的梯度强度与沿正负梯度方向上的两个像素进行比较。
- 如果当前像素的梯度强度与另外两个像素相比最大,则该像素点保留为边缘点,否则该像素点将被抑制。
通常为了更加精确的计算,在跨越梯度方向的两个相邻像素之间使用线性插值来得到要比较的像素梯度,现举例如下:
如上图所示,将梯度分为8个方向,分别为E、NE、N、NW、W、SW、S、SE,其中0代表0045o,1代表45090o,2代表-900-45o,3代表-4500o。像素点P的梯度方向为theta,则像素点P1和P2的梯度线性插值为:
因此非极大值抑制的伪代码描写如下:
4.双阈值抑制
在施加非极大值抑制之后,剩余的像素可以更准确地表示图像中的实际边缘。然而,仍然存在由于噪声和颜色变化引起的一些边缘像素。为了解决这些杂散响应,必须用弱梯度值过滤边缘像素,并保留具有高梯度值的边缘像素,可以通过选择高低阈值来实现。如果边缘像素的梯度值高于高阈值,则将其标记为强边缘像素;如果边缘像素的梯度值小于高阈值并且大于低阈值,则将其标记为弱边缘像素;如果边缘像素的梯度值小于低阈值,则会被抑制。阈值的选择取决于给定输入图像的内容。
5.抑制孤立低阈值点
到目前为止,被划分为强边缘的像素点已经被确定为边缘,因为它们是从图像中的真实边缘中提取出来的。然而,对于弱边缘像素,将会有一些争论,因为这些像素可以从真实边缘提取也可以是因噪声或颜色变化引起的。为了获得准确的结果,应该抑制由后者引起的弱边缘。通常,由真实边缘引起的弱边缘像素将连接到强边缘像素,而噪声响应未连接。为了跟踪边缘连接,通过查看弱边缘像素及其8个邻域像素,只要其中一个为强边缘像素,则该弱边缘点就可以保留为真实的边缘。
抑制孤立边缘点的伪代码描述如下:
cv2.imshow("canny",cv2.Canny(img,200,300))
cv2.waitKey(0)
cv2.destroyAllWindows()