作者:Sam (甄峰) sam_code@hotmail.com
0.目标检测算法:
目标检测算法两个分支,单阶段和两阶段目标检测
Yolo(You Only Look
Once)是目标检测单阶段模型。它是一个精度尚可,速度很快的模型。即精度速度性价比很高的模型。
1. Yolo的思路:
如何可以只Look一次呢? 就是将目标检测当作一个单一的回归任务。
1.1:核心思路:
A. 将图像划分为SxS个网格。
B. 物体的真实框中心落在哪个网格,就由该网格对应的Anchor负责检测该物体。
1.2:模型输出:
输出W x H x C:
W 和H,代表划分的网格数(S x S). 例如13x13.
C通道上,则存储 x, y, h, w, project, classification(one-hot).
1.3:模型特点:
骨干网络使用DarkNet. YoloV3采用了DarkNet53
采用全卷积网络结构: Conv+Batch Norm.
Kmeans聚类产生9个Anchor.
多尺度训练。跨尺度特征融合。
分类使用sigmoid激活,支持目标多分类。
2. 模型详解:
2.1:Yolo模型结构:
骨干网络采用Darknet53, 分5个阶段,每个阶段使用倍数为2的下采样。
最后分别输出32倍,16倍,8倍下采样。共3个特征图。
2.2:模型的输入和输出:
输入:shape为[N, 3, 416, 416].
N 为batch size.
3: RGB三通道
416, 416:输入图像的H,W.
共输出三个特征图,分别对应32,16,8倍下采样。
416/8=52, 416/16=26
416/32=13.
所以对应的特征图的w和h 分别是13x13, 26x26, 52x52.
每个特征图分配3个锚框。
2.3:锚框(Anchor)和它存在的理由:
锚框,Anchor:就是用KMeans聚类,在训练集上聚类出的一些框,给定HW.
这些框,表示了物体最有可能出现的长宽。是一个先验框。
Yolo把最大的三个Anchor分配和视野最大的一个特征图,把最小的三个Anchor分配和视野最小的一个特征图。
所以,Yolo的每个输出特征图,都会分配3个Anchor.
这样预测框仅需要在锚框的基础上微调,就可以更快的找到和表达真实框。
2.4:从锚框(Anchor)到预测框:
在特征图的每个网格中(SxS), 都放入分配给它的锚框--通常是3个。
例如:13x13的网格中,每个网格都分配给3个较大的Anchor.
所以,预测框的数量为:
网格数 x 分配给它的锚框数 --->13x13 x3
每个预测框的数据包括:x, y, h, w , p + class_num
5 + class_num
3. Yolo的预测流程:
获得了(13x13 + 26x26 + 52x52) *3个 class, score, x, y, w,
h后。再做NMS, 把重复的预测框去掉。
即先按class分类, 再按score排序,
之后从score高的到score低的计算IoU,如果高于某个阈值,则表明它们意见相同,表示的是同一个真实框,则把score低的去掉。
最后保留下来的就是预测的真实框。
4. YoloV3-Ting:
对于速度要求较高的项目,可以选择YoloV3_tiny.
它在Yolo基础上去掉了一些特征图,只保留了两个独立的预测分支。
N, 255, 13, 13
N, 255, 26, 26
其中255是class_num=80时计算的。实际情况根据class_num计算。
5. 使用OpenCV DNN推理:
可以使用OpenCV DNN 来推理Yolo模型。
5.1:打开模型:
因为骨干网络是darknet
auto net = cv::dnn::readNetFromDarknet(models_cfg,
models_weights);
这是Yolo的特殊点:
std::vector vecOutNames =
net.getUnconnectedOutLayersNames();
5.2:准备数据:
matInputBlob = cv::dnn::blobFromImage(matFrame, 1 / 255.F,
cv::Size(416, 416), cv::Scalar(), true, false);
可以看到,归一化的mean={0, 0, 0}
scale=1/255.
输入Mat resize成 416x416.
5.3:指定数据:
net.setInput(matInputBlob);
5.4:推理:
net.forward(vecOutMat, vecOutNames);
Yolo输出就在vecOutMat中。
后面就要根据Yolo的输出特征图格式,来解析输出数据,得到推理出来的预测框了。
5.5:输出特征图的数据分析:
vecOutMat.size(),
有多少个输出分支,它的size就多大。如果采用Yolo,则有3个分支。如果采用Yolo_tiny, 则有两个分支。
vecOutMat[i].rows, vecOutMat[i].cols:
分别表示这个分支上预测框的个数,以及每个预测框的具体信息。
vecOutMat[i].rows--此分支上预测框个数。 按之前所说: 网格数 x 分配给它的锚框数
--->13x13 x3
则表明这个分支上预测框个数为: 13x13x3.
vecOutMat[i].cols:每个预测框的内容。其中包括: x, y, w, h, p +
class_id(one-hot)
例如:如果是个4分类的模型,则有5+4共9 位。
NMS:
cv::dnn::NMSBoxes(vecBoxesRect, vecConfidences, 0.5, 0.2,
vecIndices);
把所有符合条件的预测框填入vecBoxesRect, 对应的Score填入vecConfidences.
进行NMS.
NMS结果会填入最后一个参数。参数中是索引值。
6. 使用Tengine_Lite推理: