Sobel算子
sobel算子计算的是图像的一阶梯度。灰度值变化越快的地方,梯度越大,如下图所示。
dst = cv2.Sobel(img, ddepth, dx, dy, k_size)
# ddepth: dst的图像深度,一般使用 cv2.CV_64F
# dx = 1, dy = 0 表示x方向上的梯度
# dx = 0, dy = 1 表示y方向上的梯度
# k_size 默认是3, 当k_size设为-1时,会使用3x3的scharr算子,这个算子的实现精度比Sobel更高
使用Sobel算子进行边缘检测的一般步骤:
- 计算x方向的一阶梯度Gx
- 计算y方向的一阶梯度Gy
- 计算G = |Gx|+|Gy|, 当G大于一个阈值时,判定为边缘点
其实可以直接传dx = 1, dy = 1。和上面步骤的结果不太一样,进过实验,分三步的效果更好,见本文最后实例部分。
拉普拉斯算子
拉普拉斯算子是图像的二阶导数,如下图所示,一阶导数的极值位置,二阶导数为0。所以我们也可以用这个特点来作为检测图像边缘的方法。 但是, 二阶导数的0值不仅仅出现在边缘(它们也可能出现在无意义的位置),但是可以过滤掉这些点。
对于二维离散信号:
则:
对应的mask:
当考虑对角线元素时,mask也可以是:
cv2.Laplacian(img, cv2.CV_64F)
相对于Sobel算子,拉普拉斯算子做的是团块检测(周边高、低于中心点)、像素值快速变化的区域。
Sobel和Laplacian实例
import cv2
from matplotlib import pyplot as plt
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('../laugh.jpg', 0)
# 先降噪
img = cv2.bilateralFilter(img, 21, 75, 75)
laplacian = cv2.Laplacian(img, cv2.CV_64F)
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3)
sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3)
sobelxy = cv2.Sobel(img, cv2.CV_64F, 1, 1, ksize=3)
sobelxy5 = cv2.Sobel(img, cv2.CV_64F, 1, 1, ksize=5)
sobel = cv2.addWeighted(sobelx, 0.5, sobely, 0.5, 0)
sherr = cv2.addWeighted(cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=-1), 0.5, cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=-1), 0.5, 0)
# 把所有结果映射回uint8
laplacian = cv2.convertScaleAbs(laplacian)
sobelxy = cv2.convertScaleAbs(sobelxy)
sobelxy5 = cv2.convertScaleAbs(sobelxy5)
sobelx = cv2.convertScaleAbs(sobelx)
sobely = cv2.convertScaleAbs(sobely)
sobel = cv2.convertScaleAbs(sobel)
sherr = cv2.convertScaleAbs(sherr)
titles = ['Original', 'Laplacian', 'Sobel dx=1 ksize=3', 'Sobel dy=1 ksize=3', 'Sobel ksize=3', 'Sobel dx=dy=1 ksize=3',
'Sherr', 'Sobel dx=dy=1 ksize=5']
imgs = [img, laplacian, sobelx, sobely, sobel, sobelxy, sherr, sobelxy5]
for i in range(8):
plt.subplot(3, 3, i + 1), plt.imshow(imgs[i], cmap='gray')
plt.title(titles[i]), plt.xticks([]), plt.yticks([])
plt.show()
Canny边缘检测
cv2.Canny(img, low_thresh, high_thresh)
算法
- 高斯滤波平滑图像
- 用Sobel算子计算出一阶偏导,以及各个像素点的梯度方向
- 对梯度值进行非最大值抑制(non-maximum suppression),只保留那些比其梯度方向上前后两个像素的梯度都大的像素。直观的表现是瘦化了边缘
- 滞后阈值处理
一般的边缘检测算法用一个阈值来滤除噪声或颜色变化引起的小的梯度值,而保留大的梯度值。Canny算法应用双阈值,即一个高阈值和一个低阈值来区分边缘像素。如果边缘像素点梯度值大于高阈值,则被认为是强边缘点。如果边缘梯度值小于高阈值,大于低阈值,则标记为弱边缘点。小于低阈值的点则被抑制掉。
所有像素点标记完滞后,针对每个弱边缘点,检查其周围8个连通领域像素,只要有强边缘点存在,则保留该边缘点,否则抛弃。
实例
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('laugh.jpg', 0)
canny = cv2.Canny(img, 100, 200)
titles = ['Original','canny']
imgs = [img, canny]
for i in range(2):
plt.subplot(1, 2, i + 1), plt.imshow(imgs[i], cmap='gray')
plt.title(titles[i]), plt.xticks([]), plt.yticks([])
plt.show()