基于 YOLO 的多任务识别算法
摘要:
目标检测是计算机视觉领域的一个基础任务和研究热点。YOLO(You Only Look Once)将目标检测概括为一个回归问题,实现端到端的训练和检测,由于其良好的速度-精度平衡,近几年一直处于目标检测领域的领先地位,被成功地研究、改进和应用到众多不同领域。该文针对YoloV5算法在智能驾驶中的路况识别做出了改进。首先,系统的梳理了YOLO家族及重要改进,包含YOLOv1-v4,YOLOv5,Scaled-YOLOv4,YOLOR和最新的YOLOX。然后,我们针对与车道识别,我们采用了Laplacian算子和Kalman滤波以及滑动窗口的方法来进行识别。最后我们对于YoloV5的transform架构和注意力机制做出了一定的修改,使得YoloV5的识别精度更高。
关键词:YOLO;目标检测;智能驾驶;多任务识别;车道识别;注意力机制;transform架构
1.Yolo系列算法介绍
YOLO(You Only Look Once)系列算法在目标检测领域取得了显著进展。
YOLOv1:
- 图像划分:将输入图像划分为S×S的网格,每个网格负责检测其中心落在该网格内的目标。
- 特征提取:使用卷积神经网络(CNN)提取图像特征。
- 边界框预测:每个网格预测B个边界框,每个框包含位置(x, y, w, h)和置信度。
- 类别预测:每个网格预测C个类别的条件概率。
- 损失函数:使用平方误差损失函数,综合考虑定位误差、置信度误差和分类误差。
YOLOv2:
- 改进特征提取:采用Darknet-19作为主干网络,提升特征提取能力。
- 批量归一化:在每个卷积层后添加批量归一化,稳定训练过程。
- 引入锚框:使用K-means聚类确定锚框尺寸,提高边界框预测的准确性。
- 多尺度训练:在训练过程中随机调整输入图像尺寸,增强模型的多尺度适应性。
- 细粒度特征:通过Passthrough层将高分辨率特征与深层特征融合,改善小目标检测性能。
YOLOv3:
- 主干网络升级:采用Darknet-53,结合残差结构,增强特征提取深度。
- 多尺度预测:在三个不同尺度的特征图上进行检测,提升对不同大小目标的检测能力。
- 分类器调整:使用独立的逻辑分类器代替Softmax,实现多标签分类。
- 边界框预测:在每个尺度上使用3个锚框,共9个锚框进行预测。
YOLOv4:
- 主干网络优化:引入CSPDarknet53,利用Cross Stage Partial网络结构,减少计算量并保持准确性。
- 数据增强:采用Mosaic数据增强和自对抗训练(SAT),丰富训练数据的多样性。
- 特征融合:使用SPP(Spatial Pyramid Pooling)和PANet(Path Aggregation Network)进行多尺度特征融合。
- 损失函数改进:采用CIoU(Complete IoU)损失,提升边界框回归的准确性。
YOLOv5:
- 轻量化设计:提供不同规模的模型(如YOLOv5s、YOLOv5m、YOLOv5l、YOLOv5x),满足多样化需求。
- 自适应锚框计算:自动计算最佳锚框尺寸,简化手动设置。
- 自适应图片缩放:在训练和推理过程中调整输入图像尺寸,保持目标比例。
- 数据增强:结合Mosaic和其他数据增强技术,提升模型泛化能力。
YOLOv6:
- 高效主干网络:采用EfficientRep Backbone,提升推理速度和精度。
- 无锚框设计:移除锚框机制,简化模型并提高检测效率。
- 标签分配策略:使用SimOTA策略,优化正负样本分配。
- 解耦头部:分离分类和回归任务的头部设计,提高检测性能。
YOLOv7:
- 网络结构改进:引入E-ELAN(Extended Efficient Layer Aggregation Network)模块,增强特征融合。
- 训练策略:采用带辅助头的训练方法,提升精度而不增加推理时间。
- 标签分配:使用SimOTA策略,优化样本分配。
YOLOv8:
- 架构升级:采用新的主干网络和特征融合模块,提升检测性能。
- 无锚框设计 :继续优化无锚框机制,简化模型结构。
- 损失函数 :引入DFL(Distribution Focal Loss),提高边界框回归精度。
- 标签分配:采用TAL(Task-Aligned Assigner)策略,优化正负样本匹配。
2.原理介绍
2.1.摄像头矫正
为什么要进行摄像头矫正?
一些针孔相机会给图像带来明显的失真。两种主要的变形是径向变形和切向变形。径向变形会导致直线出现弯曲。距图像中心越远,径向畸变越大。例如,下面显示一个图像,其中棋盘的两个边缘用红线标记。但是,您会看到棋盘的边框不是直线,并且与红线不匹配。所有预期的直线都凸出。
径向变形可以表示成如下:
x
d
i
s
t
o
r
t
e
d
=
x
(
1
+
k
1
r
2
+
k
2
r
4
+
k
3
r
6
)
x_{distorted}=x(1+k_1r^2+k_2r^4+k_3r^6)
xdistorted=x(1+k1r2+k2r4+k3r6)
y d i s t o r t e d = y ( 1 + k 1 r 2 + k 2 r 4 + k 3 r 6 ) y_{distorted}=y(1+k_1r^2+k_2r^4+k_3r^6) ydistorted=y(1+k1r2+k2r4+k3r6)
同样,由于摄像镜头未完全平行于成像平面对齐,因此会发生切向畸变。因此,图像中的某些区域看起来可能比预期的要近。切向畸变的量可以表示为:
x
d
i
s
t
o
r
t
e
d
=
x
+
[
2
p
1
x
y
+
p
2
(
r
2
+
2
x
2
)
]
x_{distorted}=x+[2p_1xy+p_2(r^2+2x^2)]
xdistorted=x+[2p1xy+p2(r2+2x2)]
y d i s t o r t e d = y + [ p 1 ( r 2 + 2 y 2 ) + 2 p 2 x y ] y_{distorted}=y+[p_1(r^2+2y^2)+2p_2xy] ydistorted=y+[p1(r2+2y2)+2p2xy]
简而言之,我们需要找到五个参数,称为失真系数,公式如下:
D
i
s
t
o
r
t
i
o
n
c
o
e
f
f
i
c
i
e
n
t
s
=
(
k
1
k
2
p
1
p
2
k
3
)
Distortioncoefficients=(k_1\quad k_2\quad p_1\quad p_2\quad k_3)
Distortioncoefficients=(k1k2p1p2k3)
除此之外,我们还需要其他一些信息,例如相机的内在和外在参数。内部参数特定于摄像机。它们包括诸如焦距(fx,fy)(f_x,f_y)(fx,fy)和光学中心(cx,cy)(c_x,c_y)(cx,cy)之类的信息。焦距和光学中心可用于创建相机矩阵,该相机矩阵可用于消除由于特定相机镜头而引起的畸变。相机矩阵对于特定相机而言是唯一的,因此一旦计算出,就可以在同一相机拍摄的其他图像上重复使用。它表示为3x3矩阵:
c
a
m
e
r
a
m
a
t
r
i
x
=
[
f
x
0
c
x
0
f
y
c
y
0
0
1
]
cameramatrix=\begin{bmatrix}f_x&0&c_x\\0&f_y&c_y\\0&0&1\end{bmatrix}
cameramatrix=
fx000fy0cxcy1
我们通过MATLABapp中的Camera Calibration中可以进行摄像头的矫正可视化部分:
我们通过MATLAB可以得到Translation vectors和Rotation vectors参数:
F
o
c
a
l
l
e
n
g
t
h
(
p
i
x
e
l
s
)
:
[
1166.1631
+
/
−
3.4537
,
1162.5937
+
/
−
3.4245
]
Focal length (pixels): [ 1166.1631 +/- 3.4537 , 1162.5937 +/- 3.4245 ]
Focallength(pixels):[1166.1631+/−3.4537,1162.5937+/−3.4245]
P r i n c i p a l p o i n t ( p i x e l s ) : [ 672.4929 + / − 3.5805 , 401.3075 + / − 3.1659 ] Principal point (pixels):[ 672.4929 +/- 3.5805 , 401.3075 +/- 3.1659 ] Principalpoint(pixels):[672.4929+/−3.5805,401.3075+/−3.1659]
R a d i a l d i s t o r t i o n : [ − 0.2113 + / − 0.0075 , 0.0044 + / − 0.0217 ] Radial distortion: [ -0.2113 +/- 0.0075 , 0.0044 +/- 0.0217 ] Radialdistortion:[−0.2113+/−0.0075,0.0044+/−0.0217]
在通过MATLAB的工具包,我们可以得到矫正过后的图像,摄像角度以及每张图像的平均重投影误差:
到这一步为止,我们的预处理步骤已经结束了,我们最终可以得到我们经过矫正过后的摄像头视角:
2.2.图像滤波以及曲率检测
2.2.1 图像滤波
2.2.1.1 卡尔曼滤波
卡尔曼滤波(Kalman filter)是一种高效率的递归滤波器(自回归滤波器),它能够从一系列的不完全及包含噪声的测量中,估计动态系统的状态。卡尔曼滤波会根据各测量量在不同时间下的值,考虑各时间下的联合分布,再产生对未知变数的估计,因此会比只以单一测量量为基础的估计方式要准。
卡尔曼滤波的算法是二步骤的程序。在估计步骤中,卡尔曼滤波会产生有关目前状态的估计,其中也包括不确定性。只要观察到下一个量测(其中一定含有某种程度的误差,包括随机噪声)。会通过加权平均来更新估计值,而确定性越高的量测加权比重也越高。算法是迭代的,可以在实时控制系统中执行,只需要目前的输入量测、以往的计算值以及其不确定性矩阵,不需要其他以往的资讯。
卡尔曼滤波建立在线性代数和隐马尔可夫模型(hidden Markov model)上。其基本动态系统可以用一个马尔可夫链表示,该马尔可夫链建立在一个被高斯噪声(即正态分布的噪声)干扰的线性算子上的。系统的状态可以用一个元素为实数的向量表示。随着离散时间的每一个增加,这个线性算子就会作用在当前状态上,产生一个新的状态,并也会带入一些噪声,同时系统的一些已知的控制器的控制信息也会被加入。同时,另一个受噪声干扰的线性算子产生出这些隐含状态的可见输出。
为了从一系列有噪声的观察数据中用卡尔曼滤波器估计出被观察过程的内部状态,必须把这个过程在卡尔曼滤波的框架下建立模型。
也就是说对于每一步k,定义矩阵 :
F
k
,
H
k
,
R
k
,
Q
k
F_{k},H_{k},R_{k},Q_{k}
Fk,Hk,Rk,Qk
有时也需要定义:
B
k
B_{k}
Bk
卡尔曼滤波模型假设k + 1时刻的真实状态是从k时刻的状态演化而来,符合状态方程:
x
k
=
F
k
x
k
−
1
+
B
k
u
k
+
w
k
x_k=F_kx_{k-1}+B_ku_k+w_k
xk=Fkxk−1+Bkuk+wk
其中:
⋅
F
k
是作用在
x
k
−
1
上的状态变换模型(矩阵或者是向量)
·F_{k}是作用在x_{k-1}上的状态变换模型(矩阵或者是向量)
⋅Fk是作用在xk−1上的状态变换模型(矩阵或者是向量)
⋅ B k 是作用在控制器向量 u k 上的输入 − 控制模型 ·B_{k}是作用在控制器向量u_{k}上的输入-控制模型 ⋅Bk是作用在控制器向量uk上的输入−控制模型
⋅ ω k 是过程噪声,并假定其符合均值为 0 ,协方差矩阵为 Q k 的多元正态分布 ·\omega_{k}是过程噪声,并假定其符合均值为0,协方差矩阵为Q_{k}的多元正态分布 ⋅ωk是过程噪声,并假定其符合均值为0,协方差矩阵为Qk的多元正态分布
w k ∼ N ( 0 , Q k ) w_k\sim N(0,Q_k) wk∼N(0,Qk)
其中:
c
o
v
ω
(
k
)
=
Q
(
k
)
表示协方差矩阵
cov{\omega(k)}=Q(k)表示协方差矩阵
covω(k)=Q(k)表示协方差矩阵
对于任意时刻k,对其真实状态
x
k
的一个测量
z
k
满足下式,即为观测方程:
x_{k}的一个测量z_{k}满足下式,即为观测方程:
xk的一个测量zk满足下式,即为观测方程:
z k = H k x k + v k z_k=H_kx_k+v_k zk=Hkxk+vk
其中:
H
k
是观测矩阵
H_{k}是观测矩阵
Hk是观测矩阵
它把真实状态空间映射成观测空间,
v
k
是观测噪声
v_{k}是观测噪声
vk是观测噪声
其均值为零,协方差矩阵服从正态分布:
v
k
∼
N
(
0
,
R
k
)
v_k\sim N(0,R_k)
vk∼N(0,Rk)
其中 c o v { v ( k ) } = R ( k ) 表示协方差矩阵。 \text{其中}cov\{v(k)\}=R(k)\text{表示协方差矩阵。} 其中cov{v(k)}=R(k)表示协方差矩阵。
初始状态以及每一时刻的噪声 { x 0 , w 1 , … , w k , v 1 , … , v k } 都认为是互相独立的。 \text{初始状态以及每一时刻的噪声}\{x_0,w_1,\ldots,w_k,v_1,\ldots,v_k\}\text{都认为是互相独立的。} 初始状态以及每一时刻的噪声{x0,w1,…,wk,v1,…,vk}都认为是互相独立的。
2.2.1.2 算法逻辑
卡尔曼滤波是一种递归的估计,即只要获知上一时刻状态的估计值以及当前状态的观测值就可以计算出当前状态的估计值,因此不需要记录观测或者估计的历史信息。卡尔曼滤波器与大多数滤波器不同之处,在于它是一种纯粹的时域滤波器,它不需要像低通滤波器等频域滤波器那样,需要在频域设计再转换到时域实现。
卡尔曼滤波主要分为预测和更新两部分,需要构建了系统的状态方程和观测方程,且已知系统的初始状态。在预测阶段,滤波器使用上一状态的估计,做出对当前状态的估计。在更新阶段,滤波器利用对当前状态的观测值优化在预测阶段获得的预测值,以获得一个更精确的新估计值。
卡尔曼滤波器的状态由以下两个变量表示:
∙ X ^ k ∣ k = E ( X k ∣ Y 1 , Y 2 , … , Y k ) 表示在时刻k的状态的估计。 \bullet\hat{X}_{k|k}=E(X_k|Y_1,Y_2,\ldots,Y_k)\text{表示在时刻k的状态的估计。} ∙X^k∣k=E(Xk∣Y1,Y2,…,Yk)表示在时刻k的状态的估计。
∙ X ^ k ∣ k − 1 = E ( X k − 1 ∣ Y 1 , Y 2 , … , Y k − 1 ) 表示已知过去k-1个时刻的状态,对k时刻状态的预测。 \bullet\hat{X}_{k|k-1}=E(X_{k-1}|Y_1,Y_2,\ldots,Y_{k-1})\text{表示已知过去k-1个时刻的状态,对k时刻状态的预测。} ∙X^k∣k−1=E(Xk−1∣Y1,Y2,…,Yk−1)表示已知过去k-1个时刻的状态,对k时刻状态的预测。
∙ P ^ k ∣ k ,后验估计误差协方差矩阵,度量估计值的精确程度。 \bullet\hat{P}_{k|k}\text{,后验估计误差协方差矩阵,度量估计值的精确程度。} ∙P^k∣k,后验估计误差协方差矩阵,度量估计值的精确程度。
则预测和更新(Prediction-Correction)过程如下:
X
^
k
−
1
∣
k
−
1
→
P
r
e
d
i
c
t
i
o
n
X
^
k
∣
k
−
1
→
C
o
r
r
e
c
t
i
o
n
X
^
k
∣
k
\hat{X}_{k-1|k-1}\xrightarrow{Prediction}\hat{X}_{k|k-1}\xrightarrow{Correction}\hat{X}_{k|k}
X^k−1∣k−1PredictionX^k∣k−1CorrectionX^k∣k
更新步骤中,根据当前时刻的观测值和预测值,计算出当前时刻的状态估计值。这个估计值是一个更加准确的估计值,因为它已经考虑了当前时刻的观测值。状态估计值的误差协方差矩阵是通过预测步骤中计算得到的误差协方差矩阵、观测噪声协方差矩阵和卡尔曼增益计算得到的。
{
K
k
=
P
k
∣
k
−
1
H
k
T
(
H
k
P
k
∣
k
−
1
H
k
T
+
R
k
)
−
1
x
^
k
∣
k
=
x
^
k
∣
k
−
1
+
K
k
(
z
k
−
H
k
x
^
k
∣
k
−
1
)
P
k
∣
k
=
(
I
−
K
k
H
k
)
P
k
∣
k
−
1
\begin{cases}&K_{k}=P_{k|k-1}H_k^T(H_kP_{k|k-1}H_k^T+R_k)^{-1}\\&\hat{x}_{k|k}=\hat{x}_{k|k-1}+K_k(z_k-H_k\hat{x}_{k|k-1})\\&P_{k|k}=(I-K_kH_k)P_{k|k-1}\end{cases}
⎩
⎨
⎧Kk=Pk∣k−1HkT(HkPk∣k−1HkT+Rk)−1x^k∣k=x^k∣k−1+Kk(zk−Hkx^k∣k−1)Pk∣k=(I−KkHk)Pk∣k−1
以上五个公式为卡尔曼滤波的核心公式。更新步骤更加简洁便于理解的形式为,首先计算以下三个量:
{
y
^
k
=
z
k
−
H
k
x
^
k
∣
k
−
1
(测量残差)
S
k
=
H
k
P
k
∣
k
H
k
T
+
R
k
(测量残差协方差)
K
k
=
P
k
∣
k
−
1
H
k
T
S
k
−
1
(最优卡尔曼增益)
\begin{cases}&\hat{y}_{k}=z_k-H_k\hat{x}_{k|k-1}\quad\text{(测量残差)}\\&S_{k}=H_kP_{k|k}H_k^T+R_k\quad\text{(测量残差协方差)}\\&K_{k}=P_{k|k-1}H_k^TS_k^{-1}\quad\text{(最优卡尔曼增益)}\end{cases}
⎩
⎨
⎧y^k=zk−Hkx^k∣k−1(测量残差)Sk=HkPk∣kHkT+Rk(测量残差协方差)Kk=Pk∣k−1HkTSk−1(最优卡尔曼增益)
然后用它们来更新滤波器变量:
{
x
^
k
∣
k
=
x
^
k
∣
k
−
1
+
K
k
y
^
k
(
更新的状态估计
)
P
k
∣
k
=
(
I
−
K
k
H
k
)
P
k
∣
k
−
1
(
更新的协方差估计
)
\begin{cases}\hat{x}_{k|k}=\hat{x}_{k|k-1}+K_k\hat{y}_k\quad(\text{更新的状态估计})\\\\P_{k|k}=(I-K_kH_k)P_{k|k-1}\quad(\text{更新的协方差估计})&\end{cases}
⎩
⎨
⎧x^k∣k=x^k∣k−1+Kky^k(更新的状态估计)Pk∣k=(I−KkHk)Pk∣k−1(更新的协方差估计)
卡尔曼滤波过程:
通过上图我们可以很清楚的了解到,卡尔曼滤波其通过上一阶段物体的状态,来推测下一阶段物体的状态,在环境不是很理想的条件下,我们采用卡尔曼滤波可以很好的消除环境对于结果的影响。
2.2.1.3 曲率半径的计算
我们可以画一个圆,它与曲线局部部分上的邻近点非常吻合,曲线和圆紧密相连,因为两条曲线在相交点有相同的切线和曲率:
在车道线中函数为:x = f ( y ),此时的曲率半径为:
R
c
u
r
v
e
=
[
1
+
(
d
y
d
x
)
2
]
3
/
2
[
d
2
y
d
x
2
]
\mathrm{R_{curve}}=\frac{[1+(\frac{\mathrm{dy}}{\mathrm{dx}})^2]^{3/2}}{[\frac{\mathrm{d^2y}}{\mathrm{dx^2}}]}
Rcurve=[dx2d2y][1+(dxdy)2]3/2
已知,二次项:
f
(
y
)
=
A
y
2
+
B
y
+
C
\mathrm{f(y)=Ay^2+By+C}
f(y)=Ay2+By+C
他的一阶导数和二阶导数分别为:
f
′
(
y
)
=
d
x
d
y
=
2
A
y
+
B
f
′
′
(
y
)
=
d
2
x
d
y
2
=
2
A
\mathrm{f^{\prime}(y)=\frac{dx}{dy}=2Ay+B}\\\mathrm{f^{\prime\prime}(y)=\frac{d^2x}{dy^2}=2A}
f′(y)=dydx=2Ay+Bf′′(y)=dy2d2x=2A
代入上面的式子,我们就可以得到我们这一个问题的曲率半径的计算公式:
R
c
u
r
v
e
=
(
1
+
(
2
A
y
+
B
)
2
)
3
/
2
∣
2
A
∣
\mathrm{R_{curve}}=\frac{(1+(2\mathrm{A_y}+\mathrm{B})^2)^{3/2}}{|2\mathrm{A}|}
Rcurve=∣2A∣(1+(2Ay+B)2)3/2
最后我们再结合我们的车道识别,我们就可以得到下面的这一种车道识别的效果:
2.3.车辆测距
代码设计思路
该代码将深度学习模型和传统图像处理方法结合,完成目标检测、距离测量和道路检测等功能。以下是详细设计思路:
1. 核心功能的实现
代码完成了以下功能:
-
深度图生成:利用
MiDaS
模型生成深度图,估算场景中的深度信息。 -
目标检测:通过
YOLOv4-tiny
模型实时检测图像中的目标。 -
实例分割:利用
Mask R-CNN
对目标进行分割。 -
距离测量
:通过几何公式估算目标与摄像头的实际距离:
d = w r ⋅ f w p d=\frac{w_r\cdot f}{w_p} d=wpwr⋅f
其中:-
d : 摄像头到目标的实际距离。 d: 摄像头到目标的实际距离。 d:摄像头到目标的实际距离。
-
w r : 目标实际宽度 w_r:目标实际宽度 wr:目标实际宽度
-
f : 摄像头焦距。 f: 摄像头焦距。 f:摄像头焦距。
-
w p : 图像中目标的像素宽度。 w_{p}: 图像中目标的像素宽度。 wp:图像中目标的像素宽度。
-
这些功能模块独立实现,彼此协作。
2. 模型的初始化
代码初始化了以下模型:
- 深度估计模型(MiDaS):
- 提供深度信息,用于估算图像中物体的深度。
- 目标检测模型(YOLOv4-tiny):
- 通过OpenCV加载配置文件和权重,实现实时目标检测。
- 实例分割模型(Mask R-CNN):
- 对目标区域进行精确分割。
初始化过程中,设置了CUDA加速以提高运行效率。
3. 目标检测模块设计
目标检测模块的流程如下:
- 检测目标:获取目标的类别、置信度和边界框参数。
- 提取属性:根据类别筛选目标(如“人”),并存储边界框宽度、坐标信息。
- 为后续距离测量提供数据:例如,目标像素宽度 wp。
设计思路:通过置信度过滤和类别筛选,提取感兴趣目标的信息。
4. 距离测量模块设计
距离测量是代码的核心部分,分为以下步骤:
-
焦距计算:
f = w p 0 ⋅ d 0 w r f=\frac{w_{p0}\cdot d_0}{w_r} f=wrwp0⋅d0
其中:-
f : 焦距。 f: 焦距。 f:焦距。
-
w p 0 : 参考图像中目标的像素宽度。 w_{p0}: 参考图像中目标的像素宽度。 wp0:参考图像中目标的像素宽度。
-
d 0 : 参考图像中目标与摄像头的实际距离。 d_{0}: 参考图像中目标与摄像头的实际距离。 d0:参考图像中目标与摄像头的实际距离。
-
w r : 目标实际宽度。 w_{r}: 目标实际宽度。 wr:目标实际宽度。
-
-
距离估算:
d = w r ⋅ f w p d=\frac{w_r\cdot f}{w_p} d=wpwr⋅f其中:
- w p : 检测图像中目标的像素宽度。 w_{p}: 检测图像中目标的像素宽度。 wp:检测图像中目标的像素宽度。
焦距的准确估算是后续距离测量的基础。
5. 道路检测模块设计
道路检测模块使用传统图像处理方法,分为以下步骤:
-
边缘检测:
- 使用Canny算子提取图像中的边缘信息。
-
ROI区域选取:
- 定义一个多边形掩膜,仅保留道路感兴趣区域。
-
直线检测与平滑:
-
利用霍夫变换检测道路边界线。
-
对检测结果进行线性拟合,计算平均线:
y = k ⋅ x + b y = k \cdot x + b y=k⋅x+b其中:
- k: 检测线的斜率。
- b: 检测线的截距。
-
-
平滑处理:
- 通过线条参数的平均值计算平滑后的边界:
k a v g = ∑ k i n , b a v g = ∑ b i n k_{\mathrm{avg}}=\frac{\sum k_i}{n},\quad b_{\mathrm{avg}}=\frac{\sum b_i}{n} kavg=n∑ki,bavg=n∑bi
- 通过线条参数的平均值计算平滑后的边界:
6. 参数设置
代码中定义了一些全局参数,便于根据不同场景调整:
-
摄像头参数:
-
摄像头高度h、俯仰角α和水平视场角β:
α = arctan ( d min h ⋅ k ) \alpha=\arctan\left(\frac{d_{\min}}{h\cdot k}\right) α=arctan(h⋅kdmin)
x 1 = 2 ⋅ d max ⋅ tan ( β 2 ) ⋅ x 0 − width / 2 width x_1=2\cdot d_{\max}\cdot\tan\left(\frac\beta2\right)\cdot\frac{x_0-\text{width}/2}{\text{width}} x1=2⋅dmax⋅tan(2β)⋅widthx0−width/2
-
2.4.基于YoloV5的交通识别
2.4.1.YOLOv5算法简介
YOLOv5是一种单阶段目标检测算法,该算法在YOLOv4的基础上添加了一些新的改进思路,使其速度与精度都得到了极大的性能提升。主要的改进思路如下所示:
·输入端:在模型训练阶段,提出了一些改进思路,主要包括Mosaic数据增强、自适应锚框计算、自适应图片缩放;
·基准网络:融合其它检测算法中的一些新思路,主要包括:Focus结构与CSP结构;
·Neck网络:目标检测网络在BackBone与最后的Head输出层之间往往会插入一些层,Yolov5中添加了FPN+PAN结构;
·Head输出层:输出层的锚框机制与YOLOv4相同,主要改进的是训练时的损失函数GIOU_Loss,以及预测框筛选的DIOU_nms。

上图展示了YOLOv5目标检测算法的整体框图。对于一个目标检测算法而言,我们通常可以将其划分为4个通用的模块,具体包括:
输入端、基准网络、
N
e
c
k
网络、
H
e
a
d
输出端,
输入端、基准网络、Neck网络、Head输出端,
输入端、基准网络、Neck网络、Head输出端,
YOLOv5算法具有4个版本,具体包括:YOLOv5s、YOLOv5m、YOLOv5l、YOLOv5x四种,本文重点讲解YOLOv5s,其它的版本都在该版本的基础上对网络进行加深与加宽。
**输入端:**输入端表示输入的图片。该网络的输入图像大小为608*608,该阶段通常包含一个图像预处理阶段,即将输入图像缩放到网络的输入大小,并进行归一化等操作。在网络训练阶段,YOLOv5使用Mosaic数据增强操作提升模型的训练速度和网络的精度;并提出了一种自适应锚框计算与自适应图片缩放方法。
**基准网络:**基准网络通常是一些性能优异的分类器种的网络,该模块用来提取一些通用的特征表示。YOLOv5中不仅使用CSPDarknet53结构,而且使用了Focus结构作为基准网络。
**Neck网络:**Neck网络通常位于基准网络和头网络的中间位置,利用它可以进一步提升特征的多样性及鲁棒性。虽然YOLOv5同样用到了SPP模块、FPN+PAN模块,但是实现的细节有些不同。
**Head输出端:**Head用来完成目标检测结果的输出。针对不同的检测算法,输出端的分支个数不尽相同,通常包含一个分类分支和一个回归分支。YOLOv4利用GIOU_Loss来代替Smooth L1 Loss函数,从而进一步提升算法的检测精度。
2.4.2.算法性能比较
2.4.2.1 Squeeze-and-Excitation (SE) 模块介绍1
在图像分类和目标检测任务中,卷积神经网络(CNN)通过特征提取实现对图像信息的深度建模。然而,传统的卷积操作在融合空间信息的同时,并未显式建模特征通道之间的依赖关系,这可能限制网络对关键信息的捕捉能力。为此,SE模块提出了一种通过动态调整特征通道权重的方式,提升网络表达能力的方案。
1. SE模块的基本思想
SE模块是一种轻量化的网络单元,旨在通过对每个通道的全局特征建模,突出重要的通道特征,同时抑制无关通道特征。它的核心是为每个特征通道动态分配权重,从而增强卷积网络对特征表示的灵活性。
2. 模块结构与工作流程
SE模块主要分为两个步骤:
- Squeeze(特征压缩)
对每个特征通道的空间信息进行全局平均池化,将空间维度压缩成一个标量,提取通道级别的全局特征信息。公式如下:
z c = 1 H × W ∑ i = 1 H ∑ j = 1 W U c ( i , j ) z_c=\frac{1}{H\times W}\sum_{i=1}^H\sum_{j=1}^WU_c(i,j) zc=H×W1i=1∑Hj=1∑WUc(i,j)
其中,H 和 W是特征图的空间维度,zc表示通道 c 的全局特征。
2.Excitation(特征重分配)
将提取的全局特征通过两层全连接网络进行处理,生成每个通道的权重。非线性激活函数(ReLU 和 Sigmoid)用于建模通道之间的依赖关系,计算公式如下:
s
=
σ
(
W
2
δ
(
W
1
z
)
)
s=\sigma(W_2\delta(W_1z))
s=σ(W2δ(W1z))
其中,W1和 W2 是全连接层的权重矩阵,δ 是 ReLU 函数,σ 是 Sigmoid 函数。最终得到的 s 是每个通道的权重。
3.特征加权
将生成的通道权重 sss 作用于原始特征图,实现通道级别的重新加权:
U
~
c
=
s
c
⋅
U
c
\tilde{U}_c=s_c\cdot U_c
U~c=sc⋅Uc
- 得到的加权特征图 $ \tilde{U} $ 保留了重要特征,同时降低了无关特征的影响。
3. SE模块的优点
- 轻量化:仅通过全局池化和两层全连接网络实现,参数量和计算开销极小。
- 适配性强:SE模块可以无缝集成到现有网络(如ResNet、Inception、MobileNet)中。
- 性能提升显著:通过动态调整通道权重,在多种任务和数据集上显著提升了模型的性能。
4.实际效果
下面两张图是我们修改之前和添加SE模块之后,通过同一个数据集训练得出的参数,我们可以看出,添加之后的神经网络在训练中具有更高的平滑性和能够更快的下降的特性。
2.4.2.2 BoTNet Transformer模块介绍2
2.4.2.2.1 BoTNet的介绍
BoTNet(Bottleneck Transformers Network)是一种结合卷积神经网络(CNN)与自注意力机制(Transformer)的混合架构,专注于视觉任务中的全局特征建模。BoTNet以ResNet为基础,将传统ResNet中的部分3×3卷积替换为Multi-Head Self-Attention (MHSA),通常在网络的后期阶段引入Transformer模块,用于捕获全局上下文信息。其核心思想是通过自注意力机制实现全局建模能力,同时保留CNN对局部特征的高效提取能力。BoTNet兼顾计算效率和性能,在图像分类、目标检测和语义分割等任务中表现出色,适合需要全局信息建模的场景,如细粒度识别和大型物体检测。
我们仅仅通过改变在ResNet中,用Multi-Head Self-Attention (MHSA)来替换3 × 3 convolution,并且不进行其他任何更改:
2.4.2.2.1 BoTNet的模块结构与工作流程
BoTNet Transform 主要可以分为以下 6 个关键步骤:
1.输入特征处理:从空间特征转换为序列形式
BoTNet 的输入是来自卷积网络提取的特征图,形状为 B×C×H×W,其中 B是批量大小,C 是通道数,H和 W 是特征图的高和宽。
将输入特征图 x 展平为序列形式,以便应用自注意力机制:
X
=
R
e
s
h
a
p
e
(
x
)
w
h
e
r
e
X
∈
R
B
×
(
H
⋅
W
)
×
C
X=\mathrm{Reshape}(x)\quad\mathrm{where}X\in\mathbb{R}^{B\times(H\cdot W)\times C}
X=Reshape(x)whereX∈RB×(H⋅W)×C
这里 (H⋅W) 是序列长度,CCC 是每个特征向量的维度。
2.生成 Query, Key, Value
通过线性变换将输入特征 X 投影为 Query (Q),Key (K) 和 Value (V),维度为 d:
Q
=
X
W
Q
,
K
=
X
W
K
,
V
=
X
W
V
Q=XW_Q,\quad K=XW_K,\quad V=XW_V
Q=XWQ,K=XWK,V=XWV
其中:
∙
W
Q
,
W
K
,
W
V
∈
R
C
×
d
是可学习的投影矩阵。
\bullet W_Q,W_K,W_V\in\mathbb{R}^{C\times d}\text{ 是可学习的投影矩阵。}
∙WQ,WK,WV∈RC×d 是可学习的投影矩阵。
∙ Q , K , V ∈ R B × ( H ⋅ W ) × d 。 \bullet\quad Q,K,V\in\mathbb{R}^{B\times(H\cdot W)\times d}。 ∙Q,K,V∈RB×(H⋅W)×d。
3.计算注意力权重
通过 Query 和 Key 的点积计算相似性(表示特征位置之间的关系),然后通过 Softmax 归一化得到注意力权重:
A
t
t
e
n
t
i
o
n
(
Q
,
K
)
=
S
o
f
t
m
a
x
(
Q
K
⊤
d
k
)
\mathrm{Attention}(Q,K)=\mathrm{Softmax}\left(\frac{QK^\top}{\sqrt{d_k}}\right)
Attention(Q,K)=Softmax(dkQK⊤)
其中:
∙
Q
K
⊤
∈
R
(
H
⋅
W
)
×
(
H
⋅
W
)
是每对位置间的相似性矩阵。
\bullet\quad QK^\top\in\mathbb{R}^{(H\cdot W)\times(H\cdot W)}\text{ 是每对位置间的相似性矩阵。}
∙QK⊤∈R(H⋅W)×(H⋅W) 是每对位置间的相似性矩阵。
∙ d k 是缩放因子,避免点积值过大。 \bullet\sqrt{d_k}是缩放因子,避免点积值过大。 ∙dk是缩放因子,避免点积值过大。
∙ S o f t m a x 操作确保注意力权重在每个位置上归一化。 \bullet\ Softmax 操作确保注意力权重在每个位置上归一化。 ∙ Softmax操作确保注意力权重在每个位置上归一化。
4.加权求和生成输出
利用注意力权重对 V进行加权求和,生成输出特征:
Z
=
A
t
t
e
n
t
i
o
n
(
Q
,
K
)
V
Z=\mathrm{Attention}(Q,K)V
Z=Attention(Q,K)V
KaTeX parse error: Can't use function '$' in math mode at position 4: 其中$̲Z\in\mathbb{R}^…
多头自注意力 (Multi-Head Self-Attention, MHSA)
为了增强模型的表示能力,将特征分为多个子空间,独立计算多个注意力头,然后将结果拼接起来:
M
H
S
A
(
Q
,
K
,
V
)
=
C
o
n
c
a
t
(
h
e
a
d
1
,
h
e
a
d
2
,
…
,
h
e
a
d
h
)
W
O
\mathrm{MHSA}(Q,K,V)=\mathrm{Concat}(\mathrm{head}_1,\mathrm{head}_2,\ldots,\mathrm{head}_h)W_O
MHSA(Q,K,V)=Concat(head1,head2,…,headh)WO
其中:
∙
每个 head
i
=
A
t
t
e
n
t
i
o
n
(
Q
i
,
K
i
,
V
i
)
。
\bullet\text{ 每个 head}_i=\mathrm{Attention}(Q_i,K_i,V_i)。
∙ 每个 headi=Attention(Qi,Ki,Vi)。
∙ W O ∈ R h ⋅ d k × C 是输出的线性映射。 \bullet\ W_O\in \mathbb{R} ^{h\cdot d_k\times C}是输出的线性映射。 ∙ WO∈Rh⋅dk×C是输出的线性映射。
6.残差连接与归一化
为了稳定训练,使用残差连接(ResNet 的核心思想)和 Layer Normalization:
O
u
t
p
u
t
=
L
a
y
e
r
N
o
r
m
(
X
+
M
H
S
A
(
Q
,
K
,
V
)
)
\mathrm{Output}=\mathrm{LayerNorm}(X+\mathrm{MHSA}(Q,K,V))
Output=LayerNorm(X+MHSA(Q,K,V))
其中 X 是输入特征,MHSA(Q,K,V) 是通过多头注意力计算的全局特征。
7.前馈网络
在注意力机制之后,加入一个两层前馈全连接网络,用于进一步特征变换:
F
F
N
(
Z
)
=
R
e
L
U
(
Z
W
1
+
b
1
)
W
2
+
b
2
\mathrm{FFN}(Z)=\mathrm{ReLU}(ZW_1+b_1)W_2+b_2
FFN(Z)=ReLU(ZW1+b1)W2+b2
其中:
∙
W
1
,
W
2
是可学习的权重矩阵。
\bullet\ W_1,W_2是可学习的权重矩阵。
∙ W1,W2是可学习的权重矩阵。
∙ b 1 , b 2 是偏置项。 \bullet\quad b_1,b_2\text{ 是偏置项。} ∙b1,b2 是偏置项。
最终的输出为:
F
i
n
a
l
O
u
t
p
u
t
=
L
a
y
e
r
N
o
r
m
(
O
u
t
p
u
t
+
F
F
N
(
O
u
t
p
u
t
)
)
Final Output=LayerNorm(Output+FFN(Output))
FinalOutput=LayerNorm(Output+FFN(Output))
2.4.2.2.1 BoTNet 模块的优点
1. 强大的全局特征建模能力
- Transformer 的全局上下文建模: BoTNet 的 Multi-Head Self-Attention (MHSA) 模块能够捕获全局范围内的特征依赖关系,而卷积神经网络 (CNN) 的感受野受到局部卷积核的限制。通过引入 MHSA,BoTNet 在大范围特征建模上比传统 CNN 更高效。
2. 保留 CNN 的局部特征提取优势
- BoTNet 并未完全抛弃 CNN,而是在网络的后期阶段引入 MHSA,用以增强全局信息建模能力,同时在前期阶段保留了 CNN 的局部特征提取能力。这种设计能够更好地处理局部与全局特征。
3. 模型结构高效
- 部分替换策略: BoTNet 并非从头使用全 Transformer(如 Vision Transformer, ViT),而是仅用 Transformer 替代 ResNet 中后期的 3×3 卷积。这种方式在保证性能提升的同时降低了计算复杂度。
- 更轻量的参数量: 相较于完全基于 Transformer 的视觉模型,BoTNet 参数量更少,训练和推理更快,适合资源有限的场景。
4.更好的泛化能力
- 由于引入了 Transformer 的全局建模能力,BoTNet 在小样本学习、细粒度分类等需要全局信息的场景中泛化能力更强。
实际效果比较:
实际效果
经过训练之后,我们在实践中对于YoloV5进行测试,可以我们可以得到YoloV5神经网络的真实训练效果:
参考文献:
- [1] J. Hu, L. Shen and G. Sun, “Squeeze-and-Excitation Networks,” 2018 IEEE/CVF Conference on Computer Vision and Pattern Recognition, Salt Lake City, UT, USA, 2018, pp. 7132-7141, doi: 10.1109/CVPR.2018.00745. keywords: {Computer architecture;Computational modeling;Convolution;Task analysis;Convolutional codes;Adaptation models;Stacking},
YoloV5伪代码:
# Step 1: 输入预处理
def preprocess(image, input_size):
# Resize image to input size
resized_image = resize(image, input_size)
# Normalize pixel values to [0, 1]
normalized_image = normalize(resized_image)
return normalized_image
# Step 2: Backbone 提取特征
def backbone(image):
# 使用 CSPDarknet 提取多尺度特征
feature_maps = CSPDarknet(image) # 返回多级特征图
return feature_maps
# Step 3: Neck 提取多尺度上下文信息
def neck(feature_maps):
# 使用 PANet 聚合特征
aggregated_features = PANet(feature_maps)
return aggregated_features
# Step 4: Head 预测框和类别
def head(aggregated_features):
# Generate bounding boxes, confidence scores, and class probabilities
predictions = []
for feature_map in aggregated_features:
predictions.append(detect(feature_map)) # 每个特征层的预测
return predictions
# Step 5: 非极大值抑制(NMS)
def nms(predictions, confidence_threshold, iou_threshold):
filtered_predictions = []
for prediction in predictions:
# 只保留高置信度预测
filtered_boxes = filter_by_confidence(prediction, confidence_threshold)
# 去掉重叠的框(使用 IoU 筛选)
final_boxes = non_max_suppression(filtered_boxes, iou_threshold)
filtered_predictions.append(final_boxes)
return filtered_predictions
# Step 6: YOLOv5 主流程
def yolov5(image, input_size, confidence_threshold, iou_threshold):
# 输入预处理
input_image = preprocess(image, input_size)
# 特征提取(Backbone)
feature_maps = backbone(input_image)
# 特征聚合(Neck)
aggregated_features = neck(feature_maps)
# 检测预测(Head)
predictions = head(aggregated_features)
# 后处理(NMS)
final_predictions = nms(predictions, confidence_threshold, iou_threshold)
return final_predictions
# Example Usage
image = load_image("example.jpg")
input_size = (640, 640) # YOLOv5 默认输入尺寸
confidence_threshold = 0.25 # 置信度阈值
iou_threshold = 0.45 # IoU 阈值
# 获取目标检测结果
detections = yolov5(image, input_size, confidence_threshold, iou_threshold)
filtered_predictions
Step 6: YOLOv5 主流程
def yolov5(image, input_size, confidence_threshold, iou_threshold):
# 输入预处理
input_image = preprocess(image, input_size)
# 特征提取(Backbone)
feature_maps = backbone(input_image)
# 特征聚合(Neck)
aggregated_features = neck(feature_maps)
# 检测预测(Head)
predictions = head(aggregated_features)
# 后处理(NMS)
final_predictions = nms(predictions, confidence_threshold, iou_threshold)
return final_predictions
Example Usage
image = load_image(“example.jpg”)
input_size = (640, 640) # YOLOv5 默认输入尺寸
confidence_threshold = 0.25 # 置信度阈值
iou_threshold = 0.45 # IoU 阈值
获取目标检测结果
detections = yolov5(image, input_size, confidence_threshold, iou_threshold)