Sobel算子算法的优点是计算简单,速度快。但是由于只采用了2个方向的模板,只能检测水平和垂直方向的边缘,因此这种算法对于纹理较为复杂的图像,其边缘检测效果就不是很理想。该算法认为:凡灰度新值大于或等于阈值的像素点时都是边缘点。这种判断欠合理,会造成边缘点的误判,因为许多噪声点的灰度值也很大。
右边-左边,差异值作为水平方向
下面-上面,差异值作为垂直方向
dst= cv2.Sobel(src,ddepth,dx,dy,ksize)
import cv2
import numpy as np
img = cv2.imread('F:/picture/desk/1.jpg')
def cv_imshow(img,name):
cv2.imshow(name,img)
cv2.waitKey()
cv2.destroyAllWindows()
sobelx=cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
# cv2.CV_64F能表示负数的形式,白-黑是正数,黑-白就是负数了,
# 所有的负数会被截断0,所以要取绝对值
# dx=1,dy=0 表示计算水平方向的,不计算竖直方向,谁为1,计算谁
cv_imshow(sobelx,'sobelx')
在滤波函数第二个参数,当我们使用-1表示输出图像与输入图像的数据类型一致时,如果原始图像是uint8型的,那么在经过算子计算以后,得到的图像可能会有负值,如果与原图像数据类型一致,那么负值就会被截断变成0或者255,使得结果错误,那么针对这种问题有两种方式改变(上述程序中都有):一种就是改变输出图像的数据类型(第二个参数cv2.CV_64F),另一种就是改变原始图像的数据类型(此时第二个参数可以为-1,与原始图像一致)。
img = cv2.imread('F:/picture/desk/1.jpg')
cv2.imshow('img',img)
cv2.waitKey()
cv2.destroyAllWindows()
def cv_imshow(img,name):
cv2.imshow(name,img)
cv2.waitKey()
cv2.destroyAllWindows()
sobelx=cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
sobelx = cv2.convertScaleAbs(sobelx) #绝对值转换
cv_imshow(sobelx,'sobelx')
sobely=cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
sobely= cv2.convertScaleAbs(sobely) #绝对值转换
cv_imshow(sobely,'sobely')
#分别为计算x和y,再求和
sobelxy=cv2.addWeighted(sobelx,0.5,sobely,0.5,0) #将x和y融合起来
cv_imshow(sobelxy,'sobelxy')
scharr算子与Sobel的不同点是在平滑部分,这里所用的平滑算子是1/16∗[3,10,3],相比于1/4∗[1,2,1],中心元素占的权重更重,这可能是相对于图像这种随机性较强的信号,邻域相关性不大,所以邻域平滑应该使用相对较小的标准差的高斯函数,也就是更瘦高的模板。对一些细线,更敏感,更能描绘出。
import cv2
import numpy as np
img = cv2.imread('F:/picture/desk/1.jpg')
def cv_imshow(img,name):
cv2.imshow(name,img)
cv2.waitKey()
cv2.destroyAllWindows()
#scharr算子
scharrx=cv2.Scharr(img,cv2.CV_64F,1,0)
scharry=cv2.Scharr(img,cv2.CV_64F,0,1)
scharrx=cv2.convertScaleAbs(scharrx)
scharry=cv2.convertScaleAbs(scharry)
scharrxy=cv2.addWeighted(scharrx,0.5,scharry,0.5,0)
res=np.hstack((img,scharrx,scharry,scharrxy))
cv_imshow(res,'res')
Laplacian 算子是n维欧几里德空间中的一个二阶微分算子,定义为梯度grad的散度div。可使用运算模板来运算这定理定律。不建议单独使用。
laplacian=cv2.Laplacian(img,cv2.CV_64F)
laplacian=cv2.convertScaleAbs(laplacian)
1 高斯平滑滤波
为了尽可能减少噪声对边缘检测结果的影响,所以必须滤除噪声以防止由噪声引起的错误检测。为了平滑图像,使用高斯滤波器与图像进行卷积,该步骤将平滑图像,以减少边缘检测器上明显的噪声影响。大小为(2k+1)x(2k+1)的高斯滤波器核的生成方程式由下式给出:
下面是一个sigma = 1.4,尺寸为3x3的高斯卷积核的例子(需要注意归一化):
若图像中一个3x3的窗口为A,要滤波的像素点为e,则经过高斯滤波之后,像素点e的亮度值为:
其中*为卷积符号,sum表示矩阵中所有元素相加求和。
重要的是需要理解,高斯卷积核大小的选择将影响Canny检测器的性能。尺寸越大,检测器对噪声的敏感度越低,但是边缘检测的定位误差也将略有增加。一般5x5是一个比较不错的trade off。
2 计算梯度强度和方向
图像中的边缘可以指向各个方向,因此Canny算法使用四个算子来检测图像中的水平、垂直和对角边缘。边缘检测的算子(如Roberts,Prewitt,Sobel等)返回水平Gx和垂直Gy方向的一阶导数值,由此便可以确定像素点的梯度G和方向theta 。
其中G为梯度强度, theta表示梯度方向,arctan为反正切函数。下面以Sobel算子为例讲述如何计算梯度强度和方向。
x和y方向的Sobel算子分别为:
其中Sx表示x方向的Sobel算子,用于检测y方向的边缘; Sy表示y方向的Sobel算子,用于检测x方向的边缘(边缘方向和梯度方向垂直)。在直角坐标系中,Sobel算子的方向如下图所示。
图3-1 Sobel算子的方向
若图像中一个3x3的窗口为A,要计算梯度的像素点为e,则和Sobel算子进行卷积之后,像素点e在x和y方向的梯度值分别为:
其中*为卷积符号,sum表示矩阵中所有元素相加求和。根据公式(3-2)便可以计算出像素点e的梯度和方向。
3 非极大值抑制
非极大值抑制是一种边缘稀疏技术,非极大值抑制的作用在于“瘦”边。找最大的,其他的抑制。对图像进行梯度计算后,仅仅基于梯度值提取的边缘仍然很模糊。对于标准3,对边缘有且应当只有一个准确的响应。而非极大值抑制则可以帮助将局部最大值之外的所有梯度值抑制为0,对梯度图像中每个像素进行非极大值抑制的算法是:
1) 将当前像素的梯度强度与沿正负梯度方向上的两个像素进行比较。
2) 如果当前像素的梯度强度与另外两个像素相比最大,则该像素点保留为边缘点,否则该像素点将被抑制。
通常为了更加精确的计算,在跨越梯度方向的两个相邻像素之间使用线性插值来得到要比较的像素梯度,
现举例如下:
线性插值法:设NE处的像素点的梯度幅值为M(NE),E的梯度幅值为M(E),则,p1点可以得到M(P1)=w*M(E)+(1-w)*M(NE)
其中 w=distance(p1,E)/distance(NE,E) distance 表示(NE,E)点的距离,w为权重。
图3-2 梯度方向分割
如图3-2所示,将梯度分为8个方向,分别为E、NE、N、NW、W、SW、S、SE,其中0代表00~45o,1代表450~90o,2代表-900~-45o,3代表-450~0o。像素点P的梯度方向为theta,则像素点P1和P2的梯度线性插值为:
因此非极大值抑制的伪代码描写如下:
需要注意的是,如何标志方向并不重要,重要的是梯度方向的计算要和梯度算子的选取保持一致。
4 双阈值检测
在施加非极大值抑制之后,剩余的像素可以更准确地表示图像中的实际边缘。然而,仍然存在由于噪声和颜色变化引起的一些边缘像素。如果边缘像素的梯度值高于高阈值,则将其标记为强边缘像素;如果边缘像素的梯度值小于高阈值并且大于低阈值,则将其标记为弱边缘像素;如果边缘像素的梯度值小于低阈值,则会被抑制。
梯度值>maxVal:则处理为边界
minVal<梯度值 梯度值 5 抑制孤立低阈值点 到目前为止,被划分为强边缘的像素点已经被确定为边缘,因为它们是从图像中的真实边缘中提取出来的。然而,对于弱边缘像素,将会有一些争论,因为这些像素可以从真实边缘提取也可以是因噪声或颜色变化引起的。为了获得准确的结果,应该抑制由后者引起的弱边缘。通常,由真实边缘引起的弱边缘像素将连接到强边缘像素,而噪声响应未连接。为了跟踪边缘连接,通过查看弱边缘像素及其8个邻域像素,只要其中一个为强边缘像素,则该弱边缘点就可以保留为真实的边缘。 抑制孤立边缘点的伪代码描述如下: import cv2
img = cv2.imread('F:/picture/desk/1.jpg')
v1= cv2.Canny(img,80,150) #minValue取值较小,边界点检测较多,maxValue越大,标准越高,检测点越少
v1= cv2.Canny(img,50,100)
res = np.hstack((v1,v2))
cv_imshow(res,'res')