- 这边文章将介绍基于dnn模块的yolov5 onnx模型的部署 包括读取模型和数据处理和后处理
- 先给出整个项目的源码
- yolov5版本为4.0
- opencv 为 4.5.2
- 使用的模型是自己训练的 类别数为5的模型
- 不同的版本此源码可能会报错 由于opencv版本报错解决办法
- 部署需要一些yolov5的基本知识支持 网上比比皆是
文章目录
opencv dnn模块的使用
- 这里并不详细介绍 这里给出一篇详细的dnn模块的使用方法
关于深度学习实时检测的三种方法(三)——Opencv DNN加载ONNX模型
onnx模型导出
- 具体可以参考yolov5的官方github的Tutorials
- 运行py文件 yolov5-4.0/models/export.py即可导出onnx
python models/export.py --weights ./weights/yolov5s.pt --img 640 --batch 1
onnx模型分析
-
可以使用“Netron”这个软件去查看onnx模型的具体网络
-
-
上图可以看出网络输出有三层 由先到后为
- 1×3×80×80×10
- 1×3×40×40×10
- 1×3×20×20×10
-
10 == 类别数(5) + bbox 的 xywh + confidence
-
80 40 20分别为不同数量的grid cell 用于检测大物体 中物体 小物体
-
3 代表每个grid cell有三个anchor boxs
输出层数据处理
- 代码中每一行都有详细的注释
int n = 0, q = 0, i = 0, j = 0, nout = this->classes.size() + 5, row_ind = 0;
// 遍历三种大小的grid cell
for (n = 0; n < 3; n++)
{
int num_grid_x = (int)(this->inpWidth / this->stride[n]);
int num_grid_y = (int)(this->inpHeight / this->stride[n]);
// 遍历每个grid cell 的 3种大小 anchor box
for (q = 0; q < 3; q++)
{
const float anchor_w = this->anchors[n][q * 2];
const float anchor_h = this->anchors[n][q * 2 + 1];
// 每种anchor box 遍历每个grid cell
for (i = 0; i < num_grid_y; i++)
{
for (j = 0; j < num_grid_x; j++)
{
// 访问第q种anchor box对应的数据
float *ptrData = (float*)outs[n].data + q * num_grid_x * num_grid_y * nout;
//float* pdata = (float*)outs[0].data + q * num_grid_x * num_grid_y * nout;
// 访问第 ij 个 grid cell 的10个数据
int offset = i * num_grid_x * nout + j * nout;
ptrData += offset;
// 访问10个数据的 第5个数据 即 confidence置信度
// cout << "pdata[4]:" << pdata[4] << endl ;
auto pBox = ptrData + 4;
// 归一化置信度
float box_score = sigmoid_xx(*pBox);
if (box_score > this->objThreshold)
{
// cout << "outs[n].cols: " << outs[n].cols << endl;
// cout << "outs[n].rows: " << outs[n].rows << endl;
// cout << "outs[n].row: " << outs[n].row(1) << endl;
// cout << " outs[n].row(row_ind): " << outs[n].row(row_ind) << endl;
// 取出10个数据的后5个 即类别信息 用以判断哪个类别
Mat score_test ;
for(uint8_t iter = 5 ; iter < nout ; iter ++){
score_test.push_back(*(ptrData + iter));
}
// 把矩阵数据转化为队列
score_test = score_test.reshape(0,1);
// cout << score_test.data << endl;
//Mat scores = outs[0].row(row_ind).colRange(5, outs[0].cols);
Point classIdPoint;
double max_class_socre;
// Get the value and location of the maximum score.
// 获取类别数据中得分最高的数据 并返回索引
minMaxLoc(score_test, 0, &max_class_socre, 0, &classIdPoint);
// 归一化得分最高的数据
max_class_socre = sigmoid_x((float)max_class_socre);
if (max_class_socre > this->confThreshold)
{
//pxy的取值范围是[-0.5,1.5],pwh的取值范围是(0,4*anchors[i]]
//用了跨网格匹配规则,要跨网格预测
// 将每个grid cell中 bbox 的相对坐标 转换为正个图片的坐标
float cx = (sigmoid_x(*ptrData) * 2.f - 0.5f + j) * this->stride[n]; ///cx
float cy = (sigmoid_x(*(ptrData+1)) * 2.f - 0.5f + i) * this->stride[n]; ///cy
float w = powf(sigmoid_x(*(ptrData+2)) * 2.f, 2.f) * anchor_w; ///w
float h = powf(sigmoid_x(*(ptrData + 3)) * 2.f, 2.f) * anchor_h; ///h
int left = (cx - 0.5*w)*ratiow;
int top = (cy - 0.5*h)*ratioh;
//cout << "(left,top)" << Point2d(left,top) << endl;
classIds.push_back(classIdPoint.x);
confidences.push_back(max_class_socre);
boxes.push_back(Rect(left, top, (int)(w*ratiow), (int)(h*ratioh)));
}
}
row_ind++;
}
}
}
}
后处理(非极大值抑制&画框)
// Perform non maximum suppression to eliminate redundant overlapping boxes with
// lower confidences
vector<int> indices;
NMSBoxes(boxes, confidences, this->confThreshold, this->nmsThreshold, indices);
for (size_t i = 0; i < indices.size(); ++i)
{
int idx = indices[i];
Rect box = boxes[idx];
this->drawPred(classIds[idx], confidences[idx], box.x, box.y,
box.x + box.width, box.y + box.height, frame);
cout << "bbox x" << box.x << endl;
cout << "bbox y" << box.y << endl;
cout << "bbox width" << box.width << endl;
cout << "bbox height" << box.height << endl;
cout << "--------------" << endl;
}