win10+vs2017+opencv4.5.3+yolov5-5.0
- 1,安装OpenCV4.5.3配置环境
- 2,进行模型加载
- 3,如何导出自己的onnx模型
- (1)下载YOLOv5的5.0版本的代码
- (2)在谷歌实验室更改两部分代码
- (1-1)在model文件下的common.py更改focus模块为下(直接参考上面链接即可,下面的模块选择第一个就行,另一个注释掉即可)
- (1-2),最后发现只要修改下yolo.py中的detect模块就可以达到修改网络输出格式的效果。先看下/models/yolo.py里面的detect模块
- (2)导出onnx,在输入如下
- (3)打包导出工具
- 4,C++加载onnx模型识别图片
- Yolo.h
- Yolo.cpp
- Main.cpp
- 5,测试本人的阀板训练模型
注意此处一定要使用的OpenCV版本是4.5版本以上,否则在后面读取onnx模型会失败
这里使用opencv的dnn模块读取onnx模型进行目标检测,目前已经部署成功了
也就是说明不需要安装libtorch安装包就能直接读取onnx模型,不需要读取pt模型
https://blog.csdn.net/dongjuexk/article/details/124243178
opencv dnn模块实现Yolov5_6.1
1,安装OpenCV4.5.3配置环境
下载OpenCV版本如下
https://sourceforge.net/projects/opencvlibrary/files/4.5.3/opencv-4.5.3-vc14_vc15.exe/download
下载直接解压到当前文件即可
属性表的配置如下:
这里是debug 64位的
2,进行模型加载
https://blog.csdn.net/nihate/article/details/112731327#comments_14884604
用opencv的dnn模块做yolov5目标检测(重要必看,运行成功)
下方的github文件是使用OpenCV部署YOLOX,支持YOLOX-S、YOLOX-M、YOLOX-L、YOLOX-X、YOLOX-Darknet53五种结构
而本人之前是使用yolov5_s结构的所以应该是可以的
https://github.com/hpc203/yolox-opencv-dnn
下载上方的github文件
只需要main.cpp coco.names images即可
使用OpenCV部署YOLOX,支持YOLOX-S、YOLOX-M、YOLOX-L、YOLOX-X、YOLOX-Darknet53五种结构,包含C++和Python两种版本的程序
onnx文件在百度云盘,下载链接:https://pan.baidu.com/s/11UAVSPWbDKY_LmmoHlUQXw 提取码:147w
下载完成后,把文件放在代码文件所在目录里,就可以运行程序了。如果出现读取onnx文件失败, 那很有可能是你的opencv版本低了,需要升级到4.5以上的
在本地新建空项目,直接把main.cpp文件导入,配置好OpenCV环境,然后读取其给的yolox_s.onnx模型
目前已经成功读取onnx读取测试成功了
关键就是如何获得onnx的模型文件,即如何将pt模型转换成onnx模型,用于此处的导入
3,如何导出自己的onnx模型
https://blog.csdn.net/qq_34124780/article/details/114666312
c++下使用opencv部署yolov5模型(一)(必看)
https://blog.csdn.net/qq_34124780/article/details/115363855
2021.04.15更新 c++下使用opencv部署yolov5模型 (二)
https://blog.csdn.net/qq_34124780/article/details/116464727
2021.09.02更新说明 c++下使用opencv部署yolov5模型 (三)
上方三个博客是必看的,最后本人成功将其导出onnx并进行部署了
参考上方文件后,将
发现使用yolov5的export直接导出时,出现问题,无法导出onnx(本地环境问题直接使用了谷歌实验室导出成功)
https://github.com/ultralytics/yolov5
在本地导出yolov5的onnx模型失败
尝试在谷歌实验室导出看看效果
https://blog.csdn.net/qq_45057749/article/details/115016683
yolov5模型转换(一) pt文件转onnx
注意这里的是在YOLOV5的releases的5.0的源代码版本下导出的
并且在谷歌实验室及云服务器导出onnx成功了,本地环境导出onnx失败,可能是被污染了环境
所以这次在本地搭建界面,然后去云服务器pyinstaller为exe再转到本地运行看看效果
注意直接导出的onnx是1个输入,3个输出,在前面的C++程序代码中无法调用,需要更改导出的代码
(1)下载YOLOv5的5.0版本的代码
https://github.com/ultralytics/yolov5/releases
此版本代码也是本地进行模型训练的代码,注意不是主页的代码,而是历史5.0的代码
https://github.com/ultralytics/yolov5/issues/251
注意要先安装相关环境如下:
git clone https://github.com/ultralytics/yolov5cd yolov5
pip install -r requirements.txt
pip install -U coremltools onnx scikit-learn==0.19.2 # export requirements
python models/export.py --weights weights/yolov5s.pt --img 640 --batch 1
# export at 640x640 with batch size 1
(2)在谷歌实验室更改两部分代码
参考https://blog.csdn.net/qq_34124780/article/details/115363855
2021.04.15更新 c++下使用opencv部署yolov5模型 (二)
要更改两部分代码
1.一个是focus切片代码
2.一个是将三个输出变成一个输出代码(注意改完后,要再次进行训练的时候需要改回来,可以将一个YOLOV5-5.0代码作为专门用于将pt文件转onnx的工具,这样之间就不会相互干扰了)
(1-1)在model文件下的common.py更改focus模块为下(直接参考上面链接即可,下面的模块选择第一个就行,另一个注释掉即可)
class Focus(nn.Module):
# Focus wh information into c-space
def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups
super(Focus, self).__init__()
#self.contract=Conv(c1 * 4, c2, k, s, p, g, act)
self.conv = Conv(c1 * 4, c2, k, s, p, g, act)
def forward(self, x): # x(b,c,w,h) -> y(b,4c,w/2,h/2)
#return self.conv(torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]], 1))
N, C, H, W = x.size() # assert (H / s == 0) and (W / s == 0), 'Indivisible gain'
s = 2
x = x.view(N, C, H // s, s, W // s, s) # x(1,64,40,2,40,2)
x = x.permute(0, 3, 5, 1, 2, 4).contiguous() # x(1,2,2,64,40,40)
y=x.view(N, C * s * s, H // s, W // s) # x(1,256,40,40)
return self.conv(y)
################################################################
############## 另外一种比较简单的方法 ##############
################################################################
class Focus(nn.Module):
# Focus wh information into c-space
def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups
super().__init__()
self.conv = Conv(c1 * 4, c2, k, s, p, g, act)
def forward(self, x): # x(b,c,w,h) -> y(b,4c,w/2,h/2)
contract = Contract(gain=2)
#return self.conv(torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]], 1))
return self.conv(contract(x))
(1-2),最后发现只要修改下yolo.py中的detect模块就可以达到修改网络输出格式的效果。先看下/models/yolo.py里面的detect模块
(2)导出onnx,在输入如下
!python models/export.py --weights weights/yolov5s.pt --img 640 --batch 1
导出后生成的yolov5s.onnx会在yolov5s.pt权重文件所在文件夹下生成的
然后下载,最后就是和C++联合使用了
(3)打包导出工具
专门用来导出的文件已经更改完成了,这样就不用一值更改其它文件了
4,C++加载onnx模型识别图片
这里直接使用的是OpenCV4.5.3的代码
参考https://blog.csdn.net/qq_34124780/article/details/116464727
2021.09.02更新说明 c++下使用opencv部署yolov5模型 (三)
上面的博客一定要读
本人已经在本地搭建好了环境如下
本人将此运行成功的环境及模型放到百度云上,
一共三个代码文件如下
Yolo.h
//yolo.h
#pragma once
#include<iostream>
#include<math.h>
#include<opencv2/opencv.hpp>
//结果结构体
struct Output {
int id;//结果类别id
float confidence;//结果置信度
cv::Rect box;//矩形框
};
class Yolo {
//参数为私有参数,当然也可以是设置成公开或者保护。
private:
//计算归一化函数
float Sigmoid(float x) {
return static_cast<float>(1.f / (1.f + exp(-x)));
}
//anchors
const float netAnchors[3][6] = { { 10.0, 13.0, 16.0, 30.0, 33.0, 23.0 },{ 30.0, 61.0, 62.0, 45.0, 59.0, 119.0 },{ 116.0, 90.0, 156.0, 198.0, 373.0, 326.0 } };
//stride
const float netStride[3] = { 8.0, 16.0, 32.0 };
const int netWidth = 640; //网络模型输入大小
const int netHeight = 640;
float nmsThreshold = 0.45;
float boxThreshold = 0.35;
float classThreshold = 0.35;
//类名
std::vector<std::string> className = { "person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat", "traffic light",
"fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow",
"elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee",
"skis", "snowboard", "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard",
"tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple",
"sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair", "couch",
"potted plant", "bed", "dining table", "toilet", "tv", "laptop", "mouse", "remote", "keyboard", "cell phone",
"microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors", "teddy bear",
"hair drier", "toothbrush" };
public:
//在yolo.h中的 Yolo类中添加成员函数readModel:
bool readModel(cv::dnn::Net &net, std::string &netPath, bool isCuda);
bool Detect(cv::Mat &SrcImg, cv::dnn::Net &net, std::vector<Output> &output);
void drawPred(cv::Mat &img, std::vector<Output> result, std::vector<cv::Scalar> color);
Yolo() {
}
~Yolo() {}
};
Yolo.cpp
//yolo.cpp中实现readModel函数
//在yolo.cpp中使用命名空间
#include "yolo.h"
using namespace std;
using namespace cv;
using namespace dnn;
bool Yolo::readModel(Net &net, string &netPath, bool isCuda = false) {
try {
net = readNetFromONNX(netPath);
}
catch (const std::exception&) {
return false;
}
//cuda
if (isCuda) {
net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);
net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA);
}
//cpu
else {
net.setPreferableBackend(cv::dnn::DNN_BACKEND_DEFAULT);
net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);
}
return true;
}
bool Yolo::Detect(Mat &SrcImg, Net &net, vector<Output> &output) {
Mat blob;
blobFromImage(SrcImg, blob, 1 / 255.0, cv::Size(netWidth, netHeight), Scalar(0, 0, 0), true, false);
net.setInput(blob);
vector<Mat> netOutputImg;
net.forward(netOutputImg, net.getUnconnectedOutLayersNames());
//接上面
vector<int> classIds;//结果id数组
vector<float> confidences;//结果每个id对应置信度数组
vector<Rect> boxes;//每个id矩形框
float ratio_h = (float)SrcImg.rows / netHeight;
float ratio_w = (float)SrcImg.cols / netWidth;
int net_width = className.size() + 5; //输出的网络宽度是类别数+5
float* pdata = (float*)netOutputImg[0].data;
for (int stride = 0; stride < 3; stride++) { //stride
int grid_x = (int)(netWidth / netStride[stride]);
int grid_y = (int)(netHeight / netStride[stride]);
for (int anchor = 0; anchor < 3; anchor++) { //anchors
const float anchor_w = netAnchors[stride][anchor * 2];
const float anchor_h = netAnchors[stride][anchor * 2 + 1];
for (int i = 0; i < grid_y; i++) {
for (int j = 0; j < grid_y; j++) {
float box_score = Sigmoid(pdata[4]);//获取每一行的box框中含有某个物体的概率
if (box_score > boxThreshold) {
//为了使用minMaxLoc(),将85长度数组变成Mat对象
cv::Mat scores(1, className.size(), CV_32FC1, pdata + 5);
Point classIdPoint;
double max_class_socre;
minMaxLoc(scores, 0, &max_class_socre, 0, &classIdPoint);
max_class_socre = Sigmoid((float)max_class_socre);
if (max_class_socre > classThreshold) {
//rect [x,y,w,h]
float x = (Sigmoid(pdata[0]) * 2.f - 0.5f + j) * netStride[stride]; //x
float y = (Sigmoid(pdata[1]) * 2.f - 0.5f + i) * netStride[stride]; //y
float w = powf(Sigmoid(pdata[2]) * 2.f, 2.f) * anchor_w; //w
float h = powf(Sigmoid(pdata[3]) * 2.f, 2.f) * anchor_h; //h
int left = (x - 0.5*w)*ratio_w;
int top = (y - 0.5*h)*ratio_h;
classIds.push_back(classIdPoint.x);
confidences.push_back(max_class_socre);
boxes.push_back(Rect(left, top, int(w*ratio_w), int(h*ratio_h)));
}
}
pdata += net_width;//指针移到下一行
}
}
}
}
//接上面
//执行非最大抑制以消除具有较低置信度的冗余重叠框(NMS)
vector<int> nms_result;
NMSBoxes(boxes, confidences, classThreshold, nmsThreshold, nms_result);
for (int i = 0; i < nms_result.size(); i++) {
int idx = nms_result[i];
Output result;
result.id = classIds[idx];
result.confidence = confidences[idx];
result.box = boxes[idx];
output.push_back(result);
}
if (output.size())
return true;
else
return false;
}
//这里的color是颜色数组,对没一个id随机分配一种颜色
void Yolo::drawPred(Mat &img, vector<Output> result, vector<Scalar> color) {
for (int i = 0; i < result.size(); i++) {
int left, top;
left = result[i].box.x;
top = result[i].box.y;
int color_num = i;
rectangle(img, result[i].box, color[result[i].id], 2, 8);
string label = className[result[i].id] + ":" + to_string(result[i].confidence);
int baseLine;
Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
top = max(top, labelSize.height);
//rectangle(frame, Point(left, top - int(1.5 * labelSize.height)), Point(left + int(1.5 * labelSize.width), top + baseLine), Scalar(0, 255, 0), FILLED);
putText(img, label, Point(left, top), FONT_HERSHEY_SIMPLEX, 1, color[result[i].id], 2);
}
//namedWindow("窗口名", 0);//创建窗口
//imshow("res", img);
//imwrite("./result.jpg", img);
//waitKey();
//destroyAllWindows();
}
Main.cpp
#include "yolo.h"
#include <iostream>
#include<opencv2//opencv.hpp>
#include<math.h>
using namespace std;
using namespace cv;
using namespace dnn;
int main()
{
cout << "Hello World" << endl;
string img_path = "./bus.jpg";
string model_path = "./yolov5s.onnx";
Yolo test;
Net net;
if (test.readModel(net, model_path, true)) {
cout << "read net ok!" << endl;
}
else {
return -1;
}
//生成随机颜色
vector<Scalar> color;
srand(time(0));
for (int i = 0; i < 80; i++) {
int b = rand() % 256;
int g = rand() % 256;
int r = rand() % 256;
color.push_back(Scalar(b, g, r));
}
vector<Output> result;
Mat img = imread(img_path);
if (test.Detect(img, net, result)) {
test.drawPred(img, result, color);
namedWindow("res", 0);//创建窗口Flags=0,是WINDOW_NORMAL
imshow("res", img);
}
else {
cout << "Detect Failed!" << endl;
}
//system("pause");
waitKey(0);
destroyAllWindows();
return 0;
}
最后运行结果如下
5,测试本人的阀板训练模型
将之前的阀板训练模型 云服务器bs10 epoch50 workers4
的导出onnx看效果如何
注意,更换onnx的时候要在 头文件中更改类名
如下std::vector<std::string> className = { "gridingDefect" };
检测结果如下: