(目标检测)基于opencv dnn模块的yolov5部署

  • 这边文章将介绍基于dnn模块的yolov5 onnx模型的部署 包括读取模型和数据处理和后处理
  • 先给出整个项目的源码
  • yolov5版本为4.0
  • opencv 为 4.5.2
  • 使用的模型是自己训练的 类别数为5的模型
  • 不同的版本此源码可能会报错 由于opencv版本报错解决办法
  • 部署需要一些yolov5的基本知识支持 网上比比皆是

文章目录

opencv dnn模块的使用

onnx模型导出

  • 具体可以参考yolov5的官方github的Tutorials
    (目标检测)基于opencv dnn模块的yolov5部署
  • 运行py文件 yolov5-4.0/models/export.py即可导出onnx
python models/export.py --weights ./weights/yolov5s.pt --img 640 --batch 1

onnx模型分析

  • 可以使用“Netron”这个软件去查看onnx模型的具体网络

  • (目标检测)基于opencv dnn模块的yolov5部署
    (目标检测)基于opencv dnn模块的yolov5部署(目标检测)基于opencv dnn模块的yolov5部署

  • 上图可以看出网络输出有三层 由先到后为

    • 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

(目标检测)基于opencv dnn模块的yolov5部署

输出层数据处理

  • 代码中每一行都有详细的注释
	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;
		
	}

数据处理图解

(目标检测)基于opencv dnn模块的yolov5部署

上一篇:processing:float转换int


下一篇:C#中的类型转换