yolov5 6.1同时支持Opencv dnn、TensorRT、Edge TPU和OpenVINO模块部署,在工程实现上就方便多了。
首先要将pt文件导出为onnx模型文件
运行export.py,--weights参数后加pt模型文件路径,结束后得到.onnx后缀的文件。
python export.py --weights yolov5s.pt --include torchscript onnx
接下来就是opencv dnn的实现了,官方给了参考,不过如果是自己训练的模型,需要注意的是:
下面自己C++封装了一个类:
头文件:
#include <fstream>
#include <sstream>
#include <iostream>
#include <opencv2/dnn.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
using namespace cv;
using namespace dnn;
using namespace std;
class YOLO
{
public:
struct Detection
{
int class_id;
float confidence;
Rect box;
};
public:
YOLO();
~YOLO();
void loadNet(bool is_cuda);
Mat formatYolov5(const Mat &source);
void detect(Mat &image,vector<Detection> &output);
void drawRect(Mat &image,vector<Detection> &output);
private:
Net m_net;
//修改为训练时自己模型Img大小
float inputWidth = 320.0;
float inputHeight = 320.0;
//修改 dimensions = 类别数 + 5
const int dimensions = 9;
//修改 通过Netron可查看,图片大小320,rows为6300,图片大小640,rows为25200
const int rows = 6300;
float scoreThreshold = 0.2;
float nmsThreshold = 0.4;
float confThreshold = 0.4;
public:
//修改为自己的类别数
const vector<string> m_classNames = { "class1","class2","class3","class4" };
const vector<Scalar> colors = { Scalar(255, 255, 0), Scalar(0, 255, 0), Scalar(0, 255, 255), Scalar(255, 0, 0) };
};
cpp文件:
#include "yolov5.h"
YOLO::YOLO()
{
loadNet(false);
}
YOLO::~YOLO()
{
}
void YOLO::loadNet(bool is_cuda)
{
m_net = readNet("/home/yolov5.6.1/best.onnx");
if (is_cuda)
{
cout << "Attempty to use CUDA\n";
m_net.setPreferableBackend(DNN_BACKEND_CUDA);
m_net.setPreferableTarget(DNN_TARGET_CUDA_FP16);
}
else
{
cout << "Running on CPU\n";
m_net.setPreferableBackend(DNN_BACKEND_OPENCV);
m_net.setPreferableTarget(DNN_TARGET_CPU);
}
}
Mat YOLO::formatYolov5(const Mat &source)
{
int col = source.cols;
int row = source.rows;
int _max = MAX(col, row);
Mat result(_max, _max, CV_8UC3, Scalar(255));
source.copyTo(result(Rect(0, 0, col, row)));
return result;
}
void YOLO::detect(Mat &image, vector<Detection> &output)
{
Mat blob;
auto input_image = formatYolov5(image);
blobFromImage(input_image, blob, 1. / 255., Size(inputWidth, inputHeight), Scalar(), true, false);
m_net.setInput(blob);
vector<Mat> outputs;
vector<String> strs {m_net.getUnconnectedOutLayersNames().back()};
m_net.forward(outputs, strs);
float x_factor = float(input_image.cols) / inputWidth;
float y_factor = float(input_image.rows) / inputHeight;
float *data = (float *)outputs[0].data;
vector<int> class_ids;
vector<float> confidences;
vector<Rect> boxes;
for (int i = 0; i < rows; ++i)
{
float confidence = data[4];
if (confidence >= confThreshold)
{
float * classes_scores = data + 5;
Mat scores(1, m_classNames.size(), CV_32FC1, classes_scores);
Point class_id;
double max_class_score;
minMaxLoc(scores, 0, &max_class_score, 0, &class_id);
if (max_class_score > scoreThreshold)
{
confidences.push_back(confidence);
class_ids.push_back(class_id.x);
float x = data[0];
float y = data[1];
float w = data[2];
float h = data[3];
int left = int((x - 0.5 * w) * x_factor);
int top = int((y - 0.5 * h) * y_factor);
int width = int(w * x_factor);
int height = int(h * y_factor);
boxes.push_back(Rect(left, top, width, height));
}
}
data += dimensions;
}
vector<int> nms_result;
NMSBoxes(boxes, confidences, scoreThreshold, nmsThreshold, nms_result);
for (int i = 0; i < nms_result.size(); i++)
{
int idx = nms_result[i];
Detection result;
result.class_id = class_ids[idx];
result.confidence = confidences[idx];
result.box = boxes[idx];
output.push_back(result);
}
}
void YOLO::drawRect(Mat &image,vector<Detection> &output)
{
int detections = output.size();
for (int i = 0; i < detections; ++i)
{
auto detection = output[i];
auto box = detection.box;
auto classId = detection.class_id;
const auto color = colors[classId % colors.size()];
rectangle(image, box, color, 3);
rectangle(image, Point(box.x, box.y - 40), Point(box.x + box.width, box.y), color, FILLED);
putText(image, m_classNames[classId].c_str(), Point(box.x, box.y - 5), FONT_HERSHEY_SIMPLEX, 1.5, Scalar(0, 0, 0), 2);
}
}
使用:
#include "yolov5.h"
#include <iomanip>
int main(int argc, char **argv)
{
Mat frame;
VideoCapture capture("/home/yolov5.6.1/1.mp4");
if (!capture.isOpened())
{
std::cerr << "Error opening video file\n";
return -1;
}
YOLO yolov5;
int frame_count = 0;
float fps = -1;
int total_frames = 0;
auto start = std::chrono::high_resolution_clock::now();
while ( true )
{
capture.read(frame);
if (frame.empty())
{
std::cout << "End of stream\n";
break;
}
++frame_count;
std::vector<YOLO::Detection> output;
yolov5.detect(frame, output);
yolov5.drawRect(frame, output);
if (frame_count >= 30)
{
auto end = std::chrono::high_resolution_clock::now();
fps = frame_count * 1000.0 / std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
start = std::chrono::high_resolution_clock::now();
}
if (fps > 0)
{
std::ostringstream fps_label;
fps_label << std::fixed << setprecision(2);
fps_label << "FPS: " << fps;
std::string fps_label_str = fps_label.str();
cv::putText(frame, fps_label_str.c_str(), cv::Point(20, 50), cv::FONT_HERSHEY_SIMPLEX, 2, cv::Scalar(0, 0, 255), 2);
}
namedWindow("output", WINDOW_NORMAL);
imshow("output", frame);
if (waitKey(1) != -1)
{
std::cout << "finished by user\n";
break;
}
}
std::cout << "Total frames: " << total_frames << "\n";
return 0;
}
运行结果:
同样是CPU,dnn速度反而没有原算法的快,不知道其他人是不是也是这样 ,想要C++运行一些神经网络算法,速度不能落下啊。
参考:
OpenVino实现yolov5.6:
因篇幅问题不能全部显示,请点此查看更多更全内容