Sobel算子
- 如何理解梯度?
我们可以把梯度理解为一个图像的边界点。 - 如何计算梯度,我们可以之前定义kernel函数,对图像中的某一个点进行计算。同时我们还需要计算要从两个方向入手,1:水平计算;2:垂直计算。Sobel算子的计算公式如下如所示:
- 通过代码及展现形式可以更深入地了解用法:
#dst = cv2.Sobel(src, ddepth, dx, dy, ksize)
#src:图像
#ddepth:图像的深度
#dx和dy分别表示水平和竖直方向
#ksize是Sobel算子的大小
def cv_show(img,name):
cv2.imshow(name,img)
cv2.waitKey()
cv2.destroyAllWindows()
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
cv_show(sobelx,'sobelx')
- 为什么只显示了半边的边界呢?因为白到黑是正数,黑到白就是负数了,所有的负数会被截断成0,所以要取绝对值。优化代码如下:
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
sobelx = cv2.convertScaleAbs(sobelx)
cv_show(sobelx,'sobelx')
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
sobely = cv2.convertScaleAbs(sobely)
cv_show(sobely,'sobely')
- 计算完x,y之后在进行相加操作:
#这里的0.5表示的是图像的权重,0表示表示的是偏置项,默认为0
sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0)
cv_show(sobelxy,'sobelxy')
- 这里不建议直接计算,这可能导致最后得到的梯度图像效果非常差,如下图所示:
sobelxy=cv2.Sobel(img,cv2.CV_64F,1,1,ksize=3)
sobelxy = cv2.convertScaleAbs(sobelxy)
cv_show(sobelxy,'sobelxy')
- 实战一下,我继续使用Lena这个图像进行操作。
img = cv2.imread('lena.jpg',cv2.IMREAD_GRAYSCALE)
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
sobelx = cv2.convertScaleAbs(sobelx)
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
sobely = cv2.convertScaleAbs(sobely)
sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0)
cv_show(sobelxy,'sobelxy')
Scharr算子
- Scharr算子公式如下图所示:
- 相比较Sobel,Scharr算子距离点近的值很大,而距离较远的值相对较小,整体数值要比Sobel大,最终对计算结果的差异性会更明显。
- 使用的函数为cv2.Scharr,具体实现与Sobel类似。
Laplacian算子
- Sobel算子是模拟一阶求导,导数越大的地方说明变换越明显,越有可能是边缘,而Laplacian使用到了二阶导(一阶导的变化率),虽然对于变化更敏感,但是同时会对噪音点也会更加敏感,反而影响了最终结果。所以需要配合其他工具一起使用才能得到更好的效果。
- Laplacian矩阵公式如下图所示
- 使用的函数为cv2.Laplacian,这个函数无需考虑到X,Y,用上去更简单了。
不同算子直接的比较
- 我们继续使用Lena这个图片来进行操作
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')
- 从上面的图片我们可以到,相比于Sobel算子使用Scharr算子可以把本不是特别明显的边界也能展示出来。而拉普拉斯算子就好像有点拉胯了,当然它是需要配合其他工具一起使用的,效果才能更好。