赞
踩
目录
Cmake编译C++ opencv4.5.5(此过程大约20-40分钟)
引言:
有人说要骑着蜗牛闯世界
源码地址以及环境搭建:
yoloV5源码地址:GitHub地址
github下载不下来的可以去我的百度网盘:链接:https://pan.baidu.com/s/1YrWVWLn6NxT1xtOITU7wgQ
提取码:2233准备python环境:python3.8 CUDA:11.5,cudnn需要对应cuda版本 (这里是我自己的环境)
分享一个cuda的安装教程:(转自 Greif_Hairline博主)cuda安装教程+cudnn安装教程_hw@c14h10的博客-CSDN博客_cuda安装
命令行下载需要安装的内容 :pip install -r requirements.txt
这里的torch是cpu的如果需要安装GPU版本 可以去torch的官网下载
我的torch版本为 1.11.0+cu5 与其对应的 torchvision版本 0.12.0+cu115 (注:cu代表gpu,win10x64为电脑版本以及多少核,改gpu只支持英伟达显卡,我的显卡是2080SUPER+8G独显)
后续缺少安装包需要根据报错自行安装 注意版本需要对应
数据集标注:
采用labelimage进行标注,标注格式为VOC的数据格式
VOC2007
|--------------Annotations
|--------------images
我们的数据集进行标注
标注完后将XML文件转为TXT格式:
参考链接:https://www.csdn.net/tags/MtjaUgxsNjg4NTctYmxvZwO0O0OO0O0O.html
这里我将代码贴出来:
1.将数据集切分 split.py 通常按照训练集9,测试集1的比例
import random import argparse parser = argparse.ArgumentParser() parser.add_argument('--xml_path', default=r'E:\yolov5-master\VOCData\Annotations', type=str, help='input xml label path') parser.add_argument('--txt_path', default=r'E:\yolov5-master\VOCData\labels', type=str, help='output txt label path') opt = parser.parse_args() trainval_percent = 1.0 train_percent = 0.9 xmlfilepath = opt.xml_path txtsavepath = opt.txt_path total_xml = os.listdir(xmlfilepath) if not os.path.exists(txtsavepath): os.makedirs(txtsavepath) num = len(total_xml) list_index = range(num) tv = int(num * trainval_percent) tr = int(tv * train_percent) trainval = random.sample(list_index, tv) train = random.sample(trainval, tr) file_trainval = open(txtsavepath + '/trainval.txt', 'w') file_test = open(txtsavepath + '/test.txt', 'w') file_train = open(txtsavepath + '/train.txt', 'w') file_val = open(txtsavepath + '/val.txt', 'w') for i in list_index: name = total_xml[i][:-4] + '\n' if i in trainval: file_trainval.write(name) if i in train: file_train.write(name) else: file_val.write(name) else: file_test.write(name) file_trainval.close() file_train.close() file_val.close() file_test.close()2.将xml转为txt格式 txt2yolo_label.py
# txt2yolo_label.py # -*- coding: utf-8 -*- import xml.etree.ElementTree as ET from tqdm import tqdm import os from os import getcwd sets = ['train', 'val', 'test'] classes = ['element'] def convert(size, box): dw = 1. / (size[0]) dh = 1. / (size[1]) x = (box[0] + box[1]) / 2.0 - 1 y = (box[2] + box[3]) / 2.0 - 1 w = box[1] - box[0] h = box[3] - box[2] x = x * dw w = w * dw y = y * dh h = h * dh return x, y, w, h def convert_annotation(image_id): # try: in_file = open(r'E:\yolov5-master\VOCData\Annotations/%s.xml' % (image_id), encoding='utf-8') out_file = open(r'E:\yolov5-master\VOCData\labels/%s.txt' % (image_id), 'w', encoding='utf-8') tree = ET.parse(in_file) root = tree.getroot() size = root.find('size') w = int(size.find('width').text) h = int(size.find('height').text) for obj in root.iter('object'): # difficult = obj.find('difficult').text cls = obj.find('name').text # if cls not in classes or int(difficult) == 1: if cls not in classes: continue cls_id = classes.index(cls) xmlbox = obj.find('bndbox') b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text)) b1, b2, b3, b4 = b # 标注越界修正 if b2 > w: b2 = w if b4 > h: b4 = h b = (b1, b2, b3, b4) bb = convert((w, h), b) out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n') wd = getcwd() for image_set in sets: if not os.path.exists(r'E:\yolov5-master\VOCData\labels/'): os.makedirs(r'E:\yolov5-master\VOCData\labels/') image_ids = open(r'E:\yolov5-master\VOCData\labels/%s.txt' % (image_set)).read().strip().split() list_file = open(r'E:\yolov5-master\VOCData\labels/%s.txt' % (image_set), 'w') for image_id in tqdm(image_ids): list_file.write('E:\yolov5-master\VOCData\images/%s.jpg\n' % (image_id)) convert_annotation(image_id) list_file.close()制作自己的配置文件ymal:
在data下制作自己的yaml配置文件,仿照COCO数据集的配置文件 有多少类别nc就为几 coco为80类
train: E:\yolov5-master\VOCData\labels\train.txt val: E:\yolov5-master\VOCData\labels\val.txt test: E:\yolov5-master\VOCData\labels\test.txt # Classes nc: 1 # number of classes names: ['element'] #注,这里的顺序和txt2yolo_label.py文件中的classes保持一致修改参数:
- train.py下 parse_opt函数中需要修改的信息
--weights
--cfg
--data
--epochs
--batch-siza (显存不足建议往小调整)
--imgsz (640*640) 显存不足调整为460
2.yolov5 detect.py预测部分:(这里根据自己数据集的训练结果来更改)
--weights 使用自己的训练路径
--source 使用收集好的测试集
--data 使用训练时的配置文件
--imgsz 640*640 显存不够460*460
--conf-thres 0.7
--iou-thres 0.5
训练完后更改完以上配置 来进行一波预测:
python detect.py --weights 自己训练完的权重 --source 图片路径 --data 自己配置的ymal文件路径
查看效果:
可以看到效果还是不错的 这个零件拿在手上得需要放大镜才能看到
这里我用的是大华的相机和镜头 分辨率为3072*2048
模型转换pt转为onnx (这里我就替换成官网的权重)
python E:\yolov5-master\export.py --weights ./models/yolov5s.pt --img 640 --batch 1 --opset 12
(官网提示opset设置为12,原来默认值是13,在opencv下会报错,原因未知)
Cmake编译C++ opencv4.5.5(此过程大约20-40分钟)
Opencv官网下载
/Opencv网址
opencv_contrib官网下载https://github.com/opencv/opencv_contrib/releases/tag
cmake官网下载
(推荐下载windows_x86_x64.zip,根据自身系统以及配置自行下载)
通过cmake/bin目录下的 cmake-gui.exe进行编译
Where is the source code:opencv的路径
Where to build the binaries:新建一个bulid文档
Condigure:安装(此过程需要一直重复并解决红色字段的报错,直至没有红色字段出现)
Generate: 开始配置(配置好的东西都在build/install中)
Open Project:打开此项目
红色字段报错的大致信息:
缺少重要的exe,dill,cmake文件未下载 (需要去install/CMakeDownloadLog.txt中查看报错以及提示需要下载的链接,将链接输入GitHub Proxy 代理加速进行下载,最后根据txt中的提示改名并放入指定文件夹内)
VS打开 切换relase x64
’ 右击CMake Targets下的INSTALL点击重新生成
C++调用opencv的依赖
将刚刚编译好的opencv写入环境变量
Opencv/bulid/install/x64/vc16/bin
Opencv/bulid/bin
配置完成后重启电脑(如果不重启会发生文件丢失情况)
打开VS,新建空项目,
VS调至 relase x64
右击解决方案: 》 配置管理器 》配置relase 平台x64
添加opencv包含目录和库目录:
右击项目->属性->VC++目录
包含目录:opencv/build/install/include
opencv/build/install/include /opencv
opencv/build/install/include /opencv2
这里我的include中只有opencv2,所以我只添加一个
库目录:opencv/build/install/x64/vc16/lib
添加依赖项:
链接器》输入》附加依赖心》进入附加依赖项,添加lib文件路径
Lib也在生成的install文件的子文件x64/vc16/lib/*.lib
Relase 版的话:opencv/build/install/x64/vc16/lib/*.lib
Debug 版的话: opencv/build/install/x64/vc16/lib/*d.lib
yoloV5 C++部署
新建立yolo.h
附上代码段:
#pragma once #include<iostream> #include<opencv2/opencv.hpp> #define YOLO_P6 false //是否使用P6模型 struct Output { int id; //结果类别id float confidence; //结果置信度 cv::Rect box; //矩形框 }; class Yolo { public: Yolo() { } ~Yolo() {} 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); private: #if(defined YOLO_P6 && YOLO_P6==true) const float netAnchors[4][6] = { { 19,27, 44,40, 38,94 },{ 96,68, 86,152, 180,137 },{ 140,301, 303,264, 238,542 },{ 436,615, 739,380, 925,792 } }; const int netWidth = 1280; //ONNX图片输入宽度 const int netHeight = 1280; //ONNX图片输入高度 const int strideSize = 4; //stride size #else const float netAnchors[3][6] = { { 10,13, 16,30, 33,23 },{ 30,61, 62,45, 59,119 },{ 116,90, 156,198, 373,326 } }; const int netWidth = 640; //ONNX图片输入宽度 const int netHeight = 640; //ONNX图片输入高度 const int strideSize = 3; //stride size #endif // YOLO_P6 const float netStride[4] = { 8, 16.0,32,64 }; float boxThreshold = 0.25; float classThreshold = 0.25; float nmsThreshold = 0.45; float nmsScoreThreshold = boxThreshold * classThreshold; 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" }; //COCO数据集类别 /*std::vector<std::string> className = { "element"};*/ // };新建yolo.cpp文件
附上代码段:
#include"yolo.h" using namespace std; using namespace cv; using namespace cv::dnn; bool Yolo::readModel(Net& net, string& netPath, bool isCuda = false) { try { net = readNet(netPath); } catch (const std::exception&) { return false; } //cuda if (isCuda) { net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA); net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA_FP16); } //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; int col = SrcImg.cols; int row = SrcImg.rows; int maxLen = MAX(col, row); Mat netInputImg = SrcImg.clone(); if (maxLen > 1.2 * col || maxLen > 1.2 * row) { Mat resizeImg = Mat::zeros(maxLen, maxLen, CV_8UC3); SrcImg.copyTo(resizeImg(Rect(0, 0, col, row))); netInputImg = resizeImg; } blobFromImage(netInputImg, blob, 1 / 255.0, cv::Size(netWidth, netHeight), cv::Scalar(0, 0, 0), true, false); //如果在其他设置没有问题的情况下但是结果偏差很大,可以尝试下用下面两句语句 //blobFromImage(netInputImg, blob, 1 / 255.0, cv::Size(netWidth, netHeight), cv::Scalar(104, 117, 123), true, false); //blobFromImage(netInputImg, blob, 1 / 255.0, cv::Size(netWidth, netHeight), cv::Scalar(114, 114,114), true, false); net.setInput(blob); std::vector<cv::Mat> netOutputImg; //vector<string> outputLayerName{"345","403", "461","output" }; //net.forward(netOutputImg, outputLayerName[3]); //获取output的输出 net.forward(netOutputImg, net.getUnconnectedOutLayersNames()); std::vector<int> classIds;//结果id数组 std::vector<float> confidences;//结果每个id对应置信度数组 std::vector<cv::Rect> boxes;//每个id矩形框 float ratio_h = (float)netInputImg.rows / netHeight; float ratio_w = (float)netInputImg.cols / netWidth; int net_width = className.size() + 5; //输出的网络宽度是类别数+5 float* pdata = (float*)netOutputImg[0].data; for (int stride = 0; stride < strideSize; 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_x; j++) { float box_score = pdata[4]; ;//获取每一行的box框中含有某个物体的概率 if (box_score >= boxThreshold) { 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 = (float)max_class_socre; if (max_class_socre >= classThreshold) { //rect [x,y,w,h] float x = pdata[0]; //x float y = pdata[1]; //y float w = pdata[2]; //w float h = pdata[3]; //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 * box_score); 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, nmsScoreThreshold, 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; } 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); } imshow("1", img); //imwrite("out.bmp", img); waitKey(); //destroyAllWindows(); }源文件中 main.cpp调用
附上代码段:
//#include "stdafx.h" #include "yolo.h" #include <iostream> #include<opencv2//opencv.hpp> #include<math.h> using namespace std; using namespace cv; using namespace dnn; int main() { //string img_path = "C:\\Users\\m\\source\\repos\\yolov5\\wifi_01.jpg"; string img_path = "C:\\Users\\m\\source\\repos\\yolov5\\bus.jpg"; //string model_path = "C:\\Users\\m\\source\\repos\\yolov5\\best.onnx"; string model_path = "C:\\Users\\m\\source\\repos\\yolov5\\yolov5s.onnx"; //int num_devices = cv::cuda::getCudaEnabledDeviceCount(); //if (num_devices <= 0) { //cerr << "There is no cuda." << endl; //return -1; //} //else { //cout << num_devices << endl; //} Yolo test; Net net; if (test.readModel(net, model_path, false)) { 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); } else { cout << "Detect Failed!" << endl; } system("pause"); return 0; }加载完后重新生成
去指定的文件中查找
双击yoloV5.exe
黑窗口提示read net ok 程序正常运行!!!
(这里用的是yoloV5S 轻量级的效果还是可以的,如果训练自己的数据集,特征不明显的话还是推荐比V5L或者更大参数量的V5X当然计算时间也会延长,最后根据自己模型的实际情况对置信度和非极大值抑制做出调整)
==============================2023.5.26更新=============================
增加视频流检测代码
main.cpp修改内容
- VideoCapture cap(0); //视频检测将0改成视频地址
- while (true) {
- Mat fram;
- cap >> fram;
- vector<Output> result;
- if (test.Detect(fram, net, result))
- test.drawPred(fram, result, color);
- imshow("detect output", fram);
- if (waitKey(2)==27) break; //esc退出
- }
- cap.release();
- destroyAllWindows();
将drawPred()中的inshow和waitkey注释掉。
瑞思拜
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。