官方文档:https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_gradients/py_gradients.html#gradients
OpenCV为我们提供了三个用于梯度检测的高通滤波器:Sobel,Scharr以及Laplacian
Sobel算子是高斯平滑加求导运算的联合运算,因此它的抗噪性更好,在使用时,可以指定求导的方向x / y(水平方向或垂直方向)。还可以通过参数ksize指定内核的大小,如果ksize = -1(不指定),则默认使用大小为3x3的filter(此时,Scharr滤波器比Sobel滤波器的效果更好)。
Python: cv2.Sobel(src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]])
Python: cv2.Scharr(src, ddepth, dx, dy[, dst[, scale[, delta[, borderType]]]])
重要提示:ddepth为输出图像的深度,我们要注意,这里不能用cv2.CV_8U
(8位无符号),因为在计算梯度时:White->Black
这个方向计算得到的梯度是负值,如果使用uint8类型的话,会忽略掉这个方向的梯度,从而导致检测有误。因此最好将输出数据类型保留为更高的形式,例如cv2.CV_16S
,16位的有符号数,或者更高阶的cv2.CV_64F
等等。
Sobel
img = cv2.imread('0003.png',0)
sobelx = cv2.Sobel(img, cv2.CV_16S, 1, 0) # 检测x方向梯度 ksize默认为3x3
sobely = cv2.Sobel(img, cv2.CV_16S, 0, 1) # 检测y方向梯度
abx = cv2.convertScaleAbs(sobelx) # 取sobelx绝对值,并转为uint8类型
aby = cv2.convertScaleAbs(sobely) # 取sobelx绝对值,并转为uint8类型
dst = cv2.addWeighted(abx, 0.5, aby, 0.5, 0) # 融合x、y两方向梯度
plt.subplot(121), plt.imshow(img, cmap='gray'), plt.title('原图')
plt.subplot(122), plt.imshow(abx, cmap='gray'), plt.title('x方向')
plt.show()
plt.subplot(121), plt.imshow(aby, cmap='gray'), plt.title('y方向')
plt.subplot(122), plt.imshow(dst, cmap='gray'), plt.title('边缘检测')
plt.show()
Scharr
代码和上面的差不多,只将Sobel换成对于的Scharr即可。
sobelx = cv2.Scharr(img, cv2.CV_16S, 1, 0) # 检测x方向梯度 ksize默认为3x3
sobely = cv2.Scharr(img, cv2.CV_16S, 0, 1) # 检测y方向梯度
Sobel vs. Scharr
scharr算子实际上是sobel算子的优化,从上述结果可以看出,scharr算子在处理边缘时比sobel精度高一些。在ksize=3x3时,两种算子唯一的区别就是他们的卷积核不同,在时间复杂度都是一样的。但Scharr只应用在ksize=3x3的情况下,而Sobel可以为1, 3, 5, 7,...
图像的拉普拉斯算子由这个关系式计算:
其中每个梯度由Sobel方法求得,当ksize=1时,则使用以下内核进行滤波:
Python: cv2.Laplacian(src, ddepth[, dst[, ksize[, scale[, delta[, borderType]]]]])
laplacian = cv2.Laplacian(img, cv2.CV_16S, ksize=3)
plt.subplot(121), plt.imshow(img, cmap='gray'), plt.title('原图')
plt.subplot(122), plt.imshow(abx, cmap='gray'), plt.title('Laplacian')
plt.show()