一、图像梯度
梯度简单来说就是求导。OpenCV 提供了三种不同的梯度滤波器,或者说高通滤波器: Sobel,Scharr 和 Laplacian。其中Sobel,Scharr 是求一阶导数。Scharr 是对 Sobel(使用小的卷积核求解求解梯度角度时)的优化,而 Laplacian 是求二阶导数。
1、Sobel算子
原理:
前一个Sobel矩阵与原始图像A进行卷积操作后得到的是右边的像素值减去左边的像素值;后一个Sobel矩阵与原始图像A进行卷积操作后得到的是下边的像素值减去上边的像素值。
dst = cv2.Sobel(src, ddepth, dx, dy, ksize)
- ddepth:图像的深度
- dx和dy分别表示水平和竖直方向
- ksize是Sobel算子的大小
拿一张圆形图片举例:
首先导入图片
img = cv2.imread('pie.png',cv2.IMREAD_GRAYSCALE)
cv2.imshow("img",img)
cv2.waitKey()
cv2.destroyAllWindows()
def cv_show(img,name):
cv2.imshow(name,img)
cv2.waitKey()
cv2.destroyAllWindows()
# x方向取边缘
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
# 白到黑是正数,黑到白就是负数了,所有的负数会被截断成0,所以要取绝对值
sobelx = cv2.convertScaleAbs(sobelx) # 也可以用 sobelx = np.absolute(gradX)
cv_show(sobelx,'sobelx')
# y方向取边缘
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
sobely = cv2.convertScaleAbs(sobely)
cv_show(sobely,'sobely')
但是用sobelxy=cv2.Sobel(img,cv2.CV_64F,1,1,ksize=3)时效果并不好。
所以一般先分别计算x方向和y方向的边缘再求和。
sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0) # 0表示偏置项,一般不加
cv_show(sobelxy,'sobelxy')
结果分别为:
2、Scharr算子
原理:
其实就是将Sobel算子的数增大了,这样对边缘的检测更敏感。
同样用上面的图片举例:
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)
得到结果为:
3、laplacian算子
原理:
由G矩阵的形式可知,拉普拉斯算子只关心离中心点最近的几个边缘点。
用上面的图片举例:
laplacian = cv2.Laplacian(img,cv2.CV_64F)
laplacian = cv2.convertScaleAbs(laplacian)
显示结果为:
从上面的所有结果来看,拉普拉斯算子的效果最好,另外两种算子的结果差不多,下面,用另一张比较复杂的图片做个测试,看看三种算子的效果。
原始图片:
#不同算子的差异
img = cv2.imread('lena.jpg',cv2.IMREAD_GRAYSCALE)
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
sobelx = cv2.convertScaleAbs(sobelx)
sobely = cv2.convertScaleAbs(sobely)
sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0)
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)
laplacian = cv2.Laplacian(img,cv2.CV_64F)
laplacian = cv2.convertScaleAbs(laplacian)
res = np.hstack((sobelxy,scharrxy,laplacian))
cv_show(res,'res')
得到结果:
二、Canny边缘检测
原理:
- 1、使用高斯滤波器,以平滑图像,滤除噪声。
高斯滤波器
- 2、计算图像中每个像素点的梯度强度和方向。
梯度和方向
- 3、应用非极大值(Non-Maximum Suppression)抑制,以消除边缘检测带来的杂散响应。
非极大值抑制
- 4、应用双阈值(Double-Threshold)检测来确定真实的和潜在的边缘。
双阈值检测
- 5、通过抑制孤立的弱边缘最终完成边缘检测。
用代码实现非常简单,导入图片:
img=cv2.imread("car.png",cv2.IMREAD_GRAYSCALE)
v1=cv2.Canny(img,120,250) # 取值越大,对边缘的要求越严格
v2=cv2.Canny(img,50,100)
res = np.hstack((v1,v2))
cv_show(res,'res')
最终得到(左图的边缘明显要比右图的少,这是因为阈值过高,导致很多边缘被舍弃):