目录
0 写在前面
从本节开始,计算机视觉教程进入第三章节——图像特征提取。在本章,你会见到一张简简单单的图片中蕴含着这么多你没注意到的细节特征,而这些特征将会在今后更高级的应用中发挥着极其重要的作用。本文讲解基础特征之一——图像边缘。
本文采用面向对象设计,定义了一个边缘检测类
EdgeDetect
,使图像边缘检测算法的应用更简洁,例如
import cv2
import numpy as np
import matplotlib.pyplot as plt
Detector = EdgeDetect('1.jpg')
Prewitt = Detector.prewitt()
plt.imshow(Prewitt ,'gray')
plt.show()
这个类的构造函数为
classEdgeDetect:def__init__(self, img)->None:
self.src = cv2.imread(img)
self.gray = cv2.cvtColor(self.src, cv2.COLOR_BGR2GRAY)
读取的是图像的基本信息。
1 一阶微分算子
图像边缘是数字图像的高频成分,对应图像梯度的极值。在二维离散数字图像上,某个方向上图像强度函数微分使用有限差分法来近似,即:
{
∂
f
∂
x
[
i
,
j
]
≈
f
[
i
+
1
,
j
]
−
f
[
i
,
j
]
∂
f
∂
y
[
i
,
j
]
≈
f
[
i
,
j
+
1
]
−
f
[
i
,
j
]
\begin{cases} \frac{\partial f}{\partial x}\left[ i,j \right] \approx f\left[ i+1,j \right] -f\left[ i,j \right]\\ \frac{\partial f}{\partial y}\left[ i,j \right] \approx f\left[ i,j+1 \right] -f\left[ i,j \right]\\\end{cases}
{∂x∂f[i,j]≈f[i+1,j]−f[i,j]∂y∂f[i,j]≈f[i,j+1]−f[i,j]
因此图像边缘检测即是对图像的差分运算。
1.1 Prewitt算子
Prewitt算子本质上就是
x
x
x或
y
y
y方向上相邻像素的差分。
那我们常说的图像梯度是什么意思呢?
其实就是用
x
x
x与
y
y
y方向上相邻像素的差分为方向的向量
∇
f
=
[
∂
f
∂
x
∂
f
∂
y
]
\nabla f=\left[ \begin{matrix} \frac{\partial f}{\partial x}& \frac{\partial f}{\partial y}\\\end{matrix} \right]
∇f=[∂x∂f∂y∂f]
在编程实现上,就是构造上图的两个方向的滤波算子,然后将
x
x
x、
y
y
y两个方向的边缘合成就是整张图各方向的边缘检测结果
defprewitt(self):# Prewitt 算子
kernelX = np.array([[1,1,1],[0,0,0],[-1,-1,-1]], dtype=int)
kernelY = np.array([[-1,0,1],[-1,0,1],[-1,0,1]], dtype=int)# 对图像滤波
x = cv2.filter2D(self.gray, cv2.CV_16S, kernelX)
y = cv2.filter2D(self.gray, cv2.CV_16S, kernelY)# 转 uint8 ,图像融合
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)return cv2.addWeighted(absX,0.5, absY,0.5,0)
1.2 Sobel算子
对高斯核函数
x
x
x、
y
y
y方向求导,并将其模板化即得**Sobel算子**。Sobel算子相比于Prewitt算子有更强的抗噪能力,因为其结合了高斯滤波的效果。
在编程实现上,就是构造上图的两个方向的滤波算子,然后将
x
x
x、
y
y
y两个方向的边缘合成就是整张图各方向的边缘检测结果
defsobel(self):# Sobel 算子
kernelX = np.array([[1,2,1],[0,0,0],[-1,-2,-1]],dtype=int)
kernelY = np.array([[-1,-2,-1],[0,0,0],[1,2,1]],dtype=int)# 对图像滤波
x = cv2.filter2D(self.gray, cv2.CV_16S, kernelX)
y = cv2.filter2D(self.gray, cv2.CV_16S, kernelY)# 转 uint8 ,图像融合
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)return cv2.addWeighted(absX,0.5, absY,0.5,0)
2 二阶微分算子
2.1 Laplace算子
将Laplace算子
∇
2
f
=
∂
2
f
∂
x
2
+
∂
2
f
∂
y
2
\nabla ^2f=\frac{\partial ^2f}{\partial x^2}+\frac{\partial ^2f}{\partial y^2}
∇2f=∂x2∂2f+∂y2∂2f
写成差分方程形式为
∇
2
f
=
f
(
x
+
1
,
y
)
+
f
(
x
−
1
,
y
)
+
f
(
x
,
y
+
1
)
+
f
(
x
,
y
−
1
)
−
4
f
(
x
,
y
)
\nabla ^2f=f\left( x+1,y \right) +f\left( x-1,y \right) +f\left( x,y+1 \right) +f\left( x,y-1 \right) -4f\left( x,y \right)
∇2f=f(x+1,y)+f(x−1,y)+f(x,y+1)+f(x,y−1)−4f(x,y)
将差分方程进一步写成卷积核形式如图(a),可将其扩展为图(b)使之具有各向同性。微分算子属于高通滤波,在锐化边缘的同时也增强了噪点,因此Laplace算子抗噪能力弱,且不能检测边缘方向。
在编程实现上,就是构造上图的滤波算子
# Laplace 算子deflaplace(self):
kernel = np.array([[0,-1,0],[-1,4,-1],[0,-1,0]], dtype=int)
img = cv2.filter2D(self.gray, cv2.CV_16S, kernel)return cv2.convertScaleAbs(img)
2.2 LoG算子
为克服Laplace算子抗噪能力弱这一问题,引入高斯-拉普拉斯算子(LoG, Laplace of Gaussian),即先低通滤除噪声,再高通强化边缘,LoG算子本质上是带通滤波器。
在编程实现上,就是构造上图的滤波算子
# LoG算子defLoG(self):
kernel = np.array([[0,0,1,0,0],[0,1,2,1,0],[1,2,-16,2,1],[0,1,2,1,0],[0,0,1,0,0]], dtype=int)
img = cv2.filter2D(self.gray, cv2.CV_16S, kernel)return cv2.convertScaleAbs(img)
3 Canny边缘检测
Canny边缘检测算法可以分为以下步骤。
- 使用Sobel算子滤除原图像噪声,并得到梯度图;
- 应用**非极大值抑制(Non-Maximum Suppression, NMS)**以消除边缘检测、目标检测带来的杂散响应,即对待测边缘或目标,应尽可能有唯一的准确响应
- 应用双阈值(Double-Threshold)检测来确定真实的和潜在的边缘。
使用如下双阈值检测算法解决因噪声引起的杂散边缘响应。
i f G p ⩾ H i g h T h r e s h o l d : G p = S t r o n g E d g e e l s e i f G p ⩾ L o w T h r e s h o l d : G p = W e a k E d g e e l s e G p = S u p p r e s s e d E d g e if\,\,G_p\geqslant HighThreshold: G_p=Strong\,\,Edge\\else\,\,if\,\,G_p\geqslant LowThreshold: G_p=Weak\,\,Edge\\else\,\, G_p=Suppressed\,\,Edge ifGp⩾HighThreshold:Gp=StrongEdgeelseifGp⩾LowThreshold:Gp=WeakEdgeelseGp=SuppressedEdge
阈值的选择取决于给定输入图像的内容。下面对弱边缘进一步审查,即
i f G p = W e a k E d g e a n d G p i s c o n n e c t e d w i t h S t r o n g E d g e : G p = S t r o n g E d g e e l s e : G p = S u p p r e s s e d E d g e if\,\,G_p=Weak\,\,Edge\,\,and\,\,G_p\,\,is\,\,connected\,\,with\,\,Strong\,\,Edge:\\\,\, G_p=Strong\,\,Edge\\else:\\\,\, G_p=Suppressed\,\,Edge ifGp=WeakEdgeandGpisconnectedwithStrongEdge:Gp=StrongEdgeelse:Gp=SuppressedEdge
通常,由真实边缘引起的弱边缘像素将连接到强边缘像素,而噪声响应未连接。为了跟踪边缘连接,通过查看弱边缘像素的8个邻域像素是否存在强边缘像素,来决定是否滤除该弱边缘点。
下面是Canny边缘检测算法的效果。
本文的完整工程代码请通过下方名片联系我获取
🔥 更多精彩专栏:
- 《ROS从入门到精通》
- 《机器人原理与技术》
- 《机器学习强基计划》
- 《计算机视觉教程》
- …
👇源码获取 · 技术交流 · 抱团学习 · 咨询分享 请联系👇
版权归原作者 Mr.Winter` 所有, 如有侵权,请联系我们删除。