图像处理之边缘检测:Sobel、Scharr、Laplacian、Canny

目录

  • 一. Sobel
  • 二. Scharr
  • 三. Laplacian
  • 四. Canny
  • 总结

一. Sobel

1. 简介
Sobel算子利用一个横轴方向的算子和纵轴方向的算子,分别求得图像的亮度差分,再计算平方和或者绝对值之和,得到近似梯度(即最后的结果)。
横轴方向的算子为:
图像处理之边缘检测:Sobel、Scharr、Laplacian、Canny_第1张图片

纵轴方向的算子为:图像处理之边缘检测:Sobel、Scharr、Laplacian、Canny_第2张图片
(注意:这里正负号在左边还是右边并无影响,因为最后计算的时候会取左右或者上下之差的绝对值)

2. 举例说明
假设现在有输入,如下所示:
图像处理之边缘检测:Sobel、Scharr、Laplacian、Canny_第3张图片

以a13点为例,

  • 横轴方向计算:
    图像处理之边缘检测:Sobel、Scharr、Laplacian、Canny_第4张图片

可得:Gx = (a9 - a7) + 2 * (a14 - a12) +(a19 - a17)

  • 纵轴方向计算:图像处理之边缘检测:Sobel、Scharr、Laplacian、Canny_第5张图片
    可得:Gy = (a19 - a9) +2 * (a18 - a8) +(a17 - a7)

  • 计算平方和,求得a13点的值:
    在这里插入图片描述

一般会将其简化为:在这里插入图片描述
所以:G = |Gx| + |Gy| = |(a9 - a7) + 2 * (a14 - a12) +(a19 - a17) | + |(a19 - a9) +2 * (a18 - a8) +(a17 - a7)|

3. 代码
现在有图片ten.jpg,如下图所示:
图像处理之边缘检测:Sobel、Scharr、Laplacian、Canny_第6张图片
代码如下:

import cv2

img = cv2.imread('ten.jpg', 0)
img = cv2.resize(img, (300, 500))
_, img = cv2.threshold(img, 180, 250, 0)

# 只计算纵轴方向结果
sobel_y = cv2.Sobel(img, -1, 0, 1)
cv2.imshow('sobel_y', sobel_y)

# 只计算横轴方向结果
sobel_x = cv2.Sobel(img, -1, 1, 0)
cv2.imshow('sobel_x', sobel_x)

# 同时计算x,y轴方向结果
sobel_xy = cv2.Sobel(img, -1, 1, 1)
cv2.imshow('sobel_xy', sobel_xy)

# 清除窗口
cv2.waitKey()
cv2.destroyAllWindows()

输出结果如下图所示:
图像处理之边缘检测:Sobel、Scharr、Laplacian、Canny_第7张图片
从上述结果中发现,直接在x,y轴上调用cv2.Sobel并不能得到满意的结果。

改进方法:分别计算x轴,y轴的近似梯度,在加权求和。

改进代码如下:

import cv2

img = cv2.imread('ten.jpg', 0)
img = cv2.resize(img, (300, 500))
_, img = cv2.threshold(img, 180, 250, 0)

x = cv2.Sobel(img, cv2.CV_64F, 1, 0)
x = cv2.convertScaleAbs(x)

y = cv2.Sobel(img, cv2.CV_64F, 0, 1)
y = cv2.convertScaleAbs(y)

z = cv2.addWeighted(x, 0.5, y, 0.5, 0)
cv2.imshow('z', z)

cv2.waitKey()
cv2.destroyAllWindows()

输出结果如下所示:
图像处理之边缘检测:Sobel、Scharr、Laplacian、Canny_第8张图片

二. Scharr

1. 简介
Scharr算子跟Sobel类似,只不过它的算子系数如下所示:

  • 横轴方向:
    图像处理之边缘检测:Sobel、Scharr、Laplacian、Canny_第9张图片
  • 纵轴方向:
    图像处理之边缘检测:Sobel、Scharr、Laplacian、Canny_第10张图片
    2. 代码
    Scharr的使用跟Sobel一样,代码如下:
import cv2

img = cv2.imread('ten.jpg', 0)
img = cv2.resize(img, (300, 500))
_, img = cv2.threshold(img, 180, 250, 0)

x = cv2.Scharr(img, cv2.CV_64F, 1, 0)
x = cv2.convertScaleAbs(x)

y = cv2.Scharr(img, cv2.CV_64F, 0, 1)
y = cv2.convertScaleAbs(y)

z = cv2.addWeighted(x, 0.5, y, 0.5, 0)

cv2.imshow('Scharr', z)
cv2.waitKey()
cv2.destroyAllWindows()

输出结果如下所示:
图像处理之边缘检测:Sobel、Scharr、Laplacian、Canny_第11张图片
注意:调用Scharr算子不能跟Sobel算子一样,同时在横轴和纵轴方向算出结果,必须满足条件 dx >= 0 && dy >= 0 && dx+dy == 1,例如:x = cv2.Scharr(img, cv2.CV_64F, 1, 1) 这样调用就会报错。
在这里插入图片描述

三. Laplacian

1. 简介
拉普拉斯算子,如下所示:
图像处理之边缘检测:Sobel、Scharr、Laplacian、Canny_第12张图片可以看出,拉普拉斯的计算其实就是取中心点跟它相邻的四个点作差,包括横轴和纵轴方向,它在左右方向上算了两次,在上下方向上也算了两次,因此它是二阶的。

2. 代码

import cv2

img = cv2.imread('ten.jpg', 0)
img = cv2.resize(img, (300, 500))
_, img = cv2.threshold(img, 180, 250, 0)

z = cv2.Laplacian(img, cv2.CV_64F)

cv2.imshow('Laplacian', z)
cv2.waitKey()
cv2.destroyAllWindows()

输出结果如下所示:
图像处理之边缘检测:Sobel、Scharr、Laplacian、Canny_第13张图片
可以看到,拉普拉斯算子的调用也是比较简单的,只要一句话就能运行出结果。

四. Canny

1. 简介
Canny算子相比Sobel而言,要稍微复杂一点,它的计算步骤可以总结为以下4点:

1.1 去噪
一般用高斯滤波。高斯滤波的介绍可以我前面写的博文:https://blog.csdn.net/weixin_43508499/article/details/107904998

1.2 计算梯度和方向

  • 梯度大小计算:利用第一节介绍的Sobel算子计算
  • 梯度方向,包括四个方向:水平,垂直,45°,135°,计算方法,利用Sobel算出Gx和Gy之后,角度 θ = argtan-1(Gy/Gx),这个角度用于下一步的非极大值抑制判断

1.3 非极大值抑制

  • 一般通过Sobel计算出来的边缘不止一个像素,边缘可能粗大又明亮。
  • 利用非极大值抑制,可以保留局部最大的梯度点,起到细化边缘的作用。
  • 举例说明,如果计算出来某一点的梯度方向是45°,则它就跟它的右上角和左下角的点进行梯度比较,如果它的梯度最大,就认为它是边缘,保留它。

1.4 滞后阈值
二次判断,设置最小阈值minV和最大阈值maxV:

  • 如果计算出来的梯度小于minV,则将其抛弃,

  • 如果计算出来的梯度大于maxV,则将其保留

  • 如果计算出来的梯度在 minV~maxV之间,就分两种情况考虑:
    ———如果该点与边缘相连,则保留;
    ———如果该点与边缘不相连,则抛弃。

  • 对于阈值,如果设置的越小,保留的细节更丰富。

2. 代码

import cv2

img = cv2.imread('ten.jpg', 0)
img = cv2.resize(img, (300, 500))
_, img = cv2.threshold(img, 180, 250, 0)
canny = cv2.Canny(img, 100, 200)
cv2.imshow('canny', canny)
cv2.waitKey()
cv2.destroyAllWindows()

输出结果如下图所示:
图像处理之边缘检测:Sobel、Scharr、Laplacian、Canny_第14张图片

可以看到,相比于Sobel计算的边缘,用Canny计算出来的结果边缘更细,没有那么粗大明亮。

总结

  • 调用cv2.Sobel的时候,一般不直接计算x, y轴直接结算出结果,而是分别计算出x轴和y轴的结果,再分配权重相加,权重一般取0.5。原因是,如果直接调用Sobel同时计算x,y的结果,一般达不到预期结果。
  • Canny的原理相对复杂一点,但是调用的时候比较简单,直接一句话就能使用了。

你可能感兴趣的:(CV,opencv,计算机视觉,边缘检测,Sobel,Canny)