本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.
python 3.8
pytorch 1.7 + cu110
yolov5 6.1
opencv 4.5.5
在 yolov5
的 C++
部署方案中, opencv
应该是最能被想到的一种,从 3.3 版本后, opencv
就加入了 dnn
这个模块,有了这个模块,很多的机器学习项目就可以通过它来实现部署了,下面我们就来看看具体的实现步骤。
yolov5
由于 opencv
无法直接读取 yolov5
中的 pt
模型文件,因此,需要将原来的 pt
文件转换成 opencv
能直接读取的 onnx
模型文件
使用目前最新的版本 v6.1
, https://github.com/ultralytics/yolov5/releases/tag/v6.1 ,解压后,进入源码目录,下载对应的模型文件,这里使用 yolov5s.pt
为例,地址: https://github.com/ultralytics/yolov5/releases/download/v6.1/yolov5s.pt
接下来使用源码中的转换脚本 export.py
,将 pt
文件转成 onnx
格式
python export.py --weights yolov5s.pt --include onnx
完成后就可以在当前目录下生成了 yolov5s.onnx
文件
来到官网, https://cmake.org/download/ ,下载后,傻瓜式安装,然后,将其也加入到系统环境变量中
接下来,我们配置一下 opencv
的环境,来到官网 https://opencv.org/releases/ ,下载 windows
对应的版本,现在最新的版本是 4.5.5
下载完成后解压
然后新增一个环境变量 OpenCV_DIR
,对应的值就是解压后的文件夹下的 build
目录,如下
修改系统环境变量 Path
,将 opencv
包中的 build\x64\vc\15\bin
添加进去
开始部署
我们创建一个 C++
项目,包含了源码文件 yolov5.cpp
、 CMakeLists.txt
、目标名称文本文件 class.txt
、 yolov5s.onnx
和若干测试图片
CMakeLists.txt
定义了编译的规则
cmake_minimum_required(VERSION 3.20) set(CMAKE_CXX_STANDARD 11) project(yolov5<a href="https://xugaoxiang.com/tag/dnn/" title="查看关于 dnn 的文章" target="_blank">dnn</a>) find_package(OpenCV REQUIRED) add_executable(yolov5<a href="https://xugaoxiang.com/tag/dnn/" title="查看关于 dnn 的文章" target="_blank">dnn</a> yolov5.cpp) include_directories(${OpenCV_INCLUDE_DIRS}) target_link_libraries(yolov5<a href="https://xugaoxiang.com/tag/dnn/" title="查看关于 dnn 的文章" target="_blank">dnn</a> ${OpenCV_LIBS} ${CMAKE_THREAD_LIBS_INIT})
class.txt
是待检测的目标,默认模型是 coco
的80个目标
最后来看源码文件 yolov5.cpp
#include <opencv2/opencv.hpp> #include <fstream> using namespace cv; using namespace std; using namespace cv::dnn; // 常量 const float INPUT_WIDTH = 640.0; const float INPUT_HEIGHT = 640.0; const float SCORE_THRESHOLD = 0.5; const float NMS_THRESHOLD = 0.45; const float CONFIDENCE_THRESHOLD = 0.45; // 显示相关 const float FONT_SCALE = 0.7; const int FONT_FACE = FONT_HERSHEY_SIMPLEX; const int THICKNESS = 1; Scalar BLACK = Scalar(0,0,0); Scalar BLUE = Scalar(255, 178, 50); Scalar YELLOW = Scalar(0, 255, 255); Scalar RED = Scalar(0,0,255); // 画框函数 void draw_label(Mat& input_image, string label, int left, int top) { int baseLine; Size label_size = getTextSize(label, FONT_FACE, FONT_SCALE, THICKNESS, &baseLine); top = max(top, label_size.height); Point tlc = Point(left, top); Point brc = Point(left + label_size.width, top + label_size.height + baseLine); rectangle(input_image, tlc, brc, BLACK, FILLED); putText(input_image, label, Point(left, top + label_size.height), FONT_FACE, FONT_SCALE, YELLOW, THICKNESS); } // 预处理 vector<Mat> pre_process(Mat &input_image, Net &net) { Mat blob; blobFromImage(input_image, blob, 1./255., Size(INPUT_WIDTH, INPUT_HEIGHT), Scalar(), true, false); net.setInput(blob); vector<Mat> outputs; net.forward(outputs, net.getUnconnectedOutLayersNames()); return outputs; } // 后处理 Mat post_process(Mat &input_image, vector<Mat> &outputs, const vector<string> &class_name) { vector<int> class_ids; vector<float> confidences; vector<Rect> boxes; float x_factor = input_image.cols / INPUT_WIDTH; float y_factor = input_image.rows / INPUT_HEIGHT; float *data = (float *)outputs[0].data; const int dimensions = 85; const int rows = 25200; for (int i = 0; i < rows; ++i) { float confidence = data[4]; if (confidence >= CONFIDENCE_THRESHOLD) { float * classes_scores = data + 5; Mat scores(1, class_name.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 > SCORE_THRESHOLD) { confidences.push_back(confidence); class_ids.push_back(class_id.x); float cx = data[0]; float cy = data[1]; float w = data[2]; float h = data[3]; int left = int((cx - 0.5 * w) * x_factor); int top = int((cy - 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 += 85; } vector<int> indices; NMSBoxes(boxes, confidences, SCORE_THRESHOLD, NMS_THRESHOLD, indices); for (int i = 0; i < indices.size(); i++) { int idx = indices[i]; Rect box = boxes[idx]; int left = box.x; int top = box.y; int width = box.width; int height = box.height; rectangle(input_image, Point(left, top), Point(left + width, top + height), BLUE, 3*THICKNESS); string label = format("%.2f", confidences[idx]); label = class_name[class_ids[idx]] + ":" + label; draw_label(input_image, label, left, top); } return input_image; } // 主函数 int main(int argc, char **argv) { vector<string> class_list; ifstream ifs("class.txt"); string line; while (getline(ifs, line)) { class_list.push_back(line); } Mat frame; frame = imread(argv[1]); Net net; net = readNet("yolov5s.onnx"); vector<Mat> detections; detections = pre_process(frame, net); Mat img = post_process(frame.clone(), detections, class_list); vector<double> layersTimes; double freq = getTickFrequency() / 1000; double t = net.getPerfProfile(layersTimes) / freq; string label = format("Inference time : %.2f ms", t); putText(img, label, Point(20, 40), FONT_FACE, FONT_SCALE, RED); imshow("Output", img); waitKey(0); return 0; }
一切准备就绪,马上开始编译,进入到工程目录,打开 powershell
,然后一次执行
mkdir build cmake -B build cmake --build build --config Release
编译成功后,就可以找图片来测试了
.\build\Release\yolov5dnn.exe bus.jpg
可以看到图片的检测时间花费了210毫秒,显然是没有用到 gpu
,如果需要启用 gpu
加速的话,就需要重新编译 opencv
,使能 cuda
,这部分内容可以去参考 windows 编译 opencv,支持 cuda 加速 和 ubuntu 下编译 opencv,支持 cuda 加速
Be First to Comment