Yolov5训练自己的数据集+TensorRT加速+Qt部署
本人由于項(xiàng)目要求,需要利用Yolov5網(wǎng)絡(luò)訓(xùn)練自己的目標(biāo)檢測與分類模型,并利用TensorRT加速將其部署到Qt界面上。目前已經(jīng)實(shí)現(xiàn)了整個(gè)流程,寫下這篇博客供需要的各位參考。(本文描述的重點(diǎn)主要是在后續(xù)對(duì)經(jīng)過加速的模型進(jìn)行打包后在Qt中進(jìn)行部署實(shí)現(xiàn),其余過程可以參考文中相應(yīng)鏈接的博客與視頻)
目錄
一、環(huán)境配置
二、在Pycharm中利用Yolov5網(wǎng)絡(luò)進(jìn)行模型訓(xùn)練
2.1 Yolov5下載
2.2 訓(xùn)練自己的數(shù)據(jù)集
三、利用TensorRT進(jìn)行模型的加速
3.1 生成.wts文件
3.2 生成.engine文件
四、對(duì)模型進(jìn)行封裝
五、利用Qt進(jìn)行部署
5.1 Qt的安裝
5.2 Qt與VS2017相關(guān)聯(lián)
5.3 進(jìn)行模型的部署
5.3.1 在VS中創(chuàng)建新的Qt項(xiàng)目
5.3.2 進(jìn)行環(huán)境的配置
5.3.3 添加.cu文件
5.3.4編寫Qt代碼
一、環(huán)境配置
該項(xiàng)目工程配置的環(huán)境是:
Win10
cuda 11.3
cudnn 8.2.0
TensorRT 8.2.1.8
Visual Studio 2017
Opencv 4.1.0
Qt 5.14.2
二、在Pycharm中利用Yolov5網(wǎng)絡(luò)進(jìn)行模型訓(xùn)練
2.1 Yolov5下載
Yolov5的源代碼下載鏈接:https://github.com/ultralytics/yolov5
在下載Yolov5源代碼時(shí),需要留意下載的版本(需要與后續(xù)利用TensorRT加速的版本相同),筆者這里當(dāng)時(shí)也不太了解,因此就是直接下載了master,也就是默認(rèn)。
2.2 訓(xùn)練自己的數(shù)據(jù)集
訓(xùn)練自己的數(shù)據(jù)集,需要先使用labelImg進(jìn)行圖像標(biāo)注,然后進(jìn)行數(shù)據(jù)集的劃分以及相應(yīng)配置文件的更改。具體的步驟可以參考這位大佬寫的博客:Yolov5訓(xùn)練自己的數(shù)據(jù)集(詳細(xì)完整版)_締宇diyu的博客-CSDN博客_yolov5訓(xùn)練自己的數(shù)據(jù)集
也可以觀看這個(gè)視頻了解學(xué)習(xí)Yolov5的網(wǎng)絡(luò)架構(gòu)和訓(xùn)練:帶你一行行讀懂yolov5代碼,yolov5源碼_嗶哩嗶哩_bilibili
調(diào)整好自己相應(yīng)的模型和數(shù)據(jù)集之后,在Pycharm的Terminal中輸入命令:
python train.py --weights weights/yolov5s.pt --cfg models/yolov5s.yaml --data data/myvoc.yaml --batch-size 8 --img 640 --device 0可以根據(jù)自己的需要去修改相應(yīng)的參數(shù),模型訓(xùn)練完成后會(huì)得到.pt文件,后面會(huì)利用該文件生成.wts文件去進(jìn)行模型的加速。
三、利用TensorRT進(jìn)行模型的加速
本文采用的技術(shù)路線是:.pt文件→.wts文件→.engine文件。利用TensorRT進(jìn)行Yolov5模型推理,需要下載tensorrtx,具體的下載鏈接:https://github.com/wang-xinyu/tensorrtx,注意!筆者個(gè)人理解這里的tensorrtx版本最好與之前下載的Yolov5版本相同,因?yàn)椴煌姹局g的網(wǎng)絡(luò)架構(gòu)是存在一些差異的,可能會(huì)導(dǎo)致后續(xù)生成的.engine文件與采用的網(wǎng)絡(luò)架構(gòu)不同而出現(xiàn)一系列報(bào)錯(cuò)。筆者這里同樣是下載的master(默認(rèn))。
3.1 生成.wts文件
下載完成后,將文件中yolov5子文件中的gen_wts.py文件復(fù)制到Y(jié)olov5工程目錄下,運(yùn)行該文件生成.wts文件。
具體方法:打開Pycharm的Terminal,輸入命令:
python gen_wts.py -w yolov5s.pt -o yolov5s.wts.pt文件就是之前Yolov5網(wǎng)絡(luò)訓(xùn)練完成生成的文件,根據(jù)自己的文件名修改即可。命令后半部分就是生成的.wts文件,同樣可以根據(jù)自己的需要更改命名。
3.2 生成.engine文件
詳細(xì)的操作可以參照這位大佬寫的文章來進(jìn)行操作,實(shí)戰(zhàn)教程:win10環(huán)境下用TensorRT推理YOLOv5_脆皮茄條的博客-CSDN博客_tensorrt yolov5
?這位大佬寫的十分詳細(xì),包括從環(huán)境配置到工程編譯的全過程。如果需要針對(duì)自己的數(shù)據(jù)集進(jìn)行更改,需要在VS中打開yololayer.h文件,對(duì)分類種類數(shù)目進(jìn)行更改。筆者僅僅是對(duì)種類數(shù)量進(jìn)行了更改,可以根據(jù)需要修改輸入圖像的尺寸等其他參數(shù)。
修改完成后,利用Win+R運(yùn)行cmd,將當(dāng)前目錄切換到該工程目錄下,同時(shí)將之前生成的.wts文件復(fù)制到該文件夾下。輸入命令:
yolov5_tensorrt.exe -s yolov5s.wts yolov5s.engine s生成.engine文件,利用該.engine文件輸入命令進(jìn)行模型預(yù)測:
yolov5_tensorrt.exe -d yolov5s.engine ./image_dir?進(jìn)行預(yù)測得到的結(jié)果,由于是自己的項(xiàng)目訓(xùn)練集,不方便展示效果圖。
四、對(duì)模型進(jìn)行封裝
在Qt中調(diào)用經(jīng)過TensorRT推理加速后的模型,需要對(duì)模型進(jìn)行封裝,即封裝成動(dòng)態(tài)鏈接庫(.dll)的形式在Qt中進(jìn)行調(diào)用。我這里是將yolov5模型封裝成了一個(gè)類,并且其中包含了兩個(gè)成員函數(shù),一個(gè)是初始化函數(shù)inital(),另外一個(gè)是推理檢測函數(shù)detect()。在VS中創(chuàng)建與調(diào)用動(dòng)態(tài)鏈接庫的方法可以參考以下幾篇博客:
VS2017創(chuàng)建動(dòng)態(tài)鏈接庫與調(diào)用_北斗星辰001的博客-CSDN博客_vs2017創(chuàng)建動(dòng)態(tài)鏈接庫
抽象類作為接口使用的DLL實(shí)現(xiàn)方法_Christo3的博客-CSDN博客
私有類封裝為DLL的方法_Christo3的博客-CSDN博客
這是我自己封裝模型時(shí)的頭文件和源文件,可以根據(jù)自己的情況去任意修改。
頭文件YoloV5.h:
// 任何項(xiàng)目上不應(yīng)定義此符號(hào)。這樣,源文件中包含此文件的任何其他項(xiàng)目都會(huì)將 // YOLOV5_API 函數(shù)視為是從 DLL 導(dǎo)入的,而此 DLL 則將用此宏定義的 // 符號(hào)視為是被導(dǎo)出的。 #ifdef YOLOV5_EXPORTS #define YOLOV5_API __declspec(dllexport) #else #define YOLOV5_API __declspec(dllimport) #endif#pragma once#define USE_FP16 // set USE_INT8 or USE_FP16 or USE_FP32 #define DEVICE 0 // GPU id #define NMS_THRESH 0.4 #define CONF_THRESH 0.5 #define BATCH_SIZE 1#define NET s // s m l x #define NETSTRUCT(str) createEngine_##str #define CREATENET(net) NETSTRUCT(net) #define STR1(x) #x #define STR2(x) STR1(x)#include <iostream> #include <chrono> #include "cuda_runtime_api.h" #include "logging.h" #include "common.hpp" #include "utils.h" #include "calibrator.h" #include "cuda_utils.h"using namespace std; using namespace cv;#define USE_FP16 // set USE_INT8 or USE_FP16 or USE_FP32 #define DEVICE 0 // GPU id #define NMS_THRESH 0.4 #define CONF_THRESH 0.5 #define BATCH_SIZE 1#define NET s // s m l x #define NETSTRUCT(str) createEngine_##str #define CREATENET(net) NETSTRUCT(net) #define STR1(x) #x #define STR2(x) STR1(x)YOLOV5_API class YoloV5 { public:YOLOV5_API YoloV5();YOLOV5_API ~YoloV5();YOLOV5_API bool inital(const string& enginePath);YOLOV5_API void detect(const Mat& inputImg, vector<Rect>& vRect);private:char* trtModelStream;size_t size;Logger gLogger;int INPUT_H;int INPUT_W;int CLASS_NUM;int OUTPUT_SIZE;const char* INPUT_BLOB_NAME;const char* OUTPUT_BLOB_NAME;void* buffers[2];float* data;float* prob;IExecutionContext* context;cudaStream_t stream; private:YOLOV5_API void doInference(IExecutionContext& context, cudaStream_t& stream, void **buffers, float* input, float* output, int batchSize);cv::Rect get_rect(cv::Mat& img, float bbox[4]);float iou(float lbox[4], float rbox[4]);//bool cmp(const Yolo::Detection& a, const Yolo::Detection& b);void nms(std::vector<Yolo::Detection>& res, float *output, float conf_thresh, float nms_thresh = 0.5);};inline bool cmp_1(const Yolo::Detection& a, const Yolo::Detection& b) {return a.conf > b.conf; }extern YOLOV5_API int nYoloV5;YOLOV5_API int fnYoloV5(void);源文件YoloV5.cpp:
// YoloV5.cpp : 定義 DLL 的導(dǎo)出函數(shù)。 //#include "YoloV5.h" YOLOV5_API YoloV5::YoloV5() { }YOLOV5_API YoloV5::~YoloV5() { }YOLOV5_API bool YoloV5::inital(const string& enginePath) {INPUT_H = Yolo::INPUT_H;INPUT_W = Yolo::INPUT_W;CLASS_NUM = Yolo::CLASS_NUM;OUTPUT_SIZE = Yolo::MAX_OUTPUT_BBOX_COUNT * sizeof(Yolo::Detection) / sizeof(float) + 1;INPUT_BLOB_NAME = "data";OUTPUT_BLOB_NAME = "prob";data = new float[BATCH_SIZE * 3 * INPUT_H * INPUT_W];prob = new float[BATCH_SIZE * OUTPUT_SIZE];std::ifstream file(enginePath, std::ios::binary);if (file.good()) {file.seekg(0, file.end);size = file.tellg();file.seekg(0, file.beg);trtModelStream = new char[size];assert(trtModelStream);file.read(trtModelStream, size);file.close();}IRuntime* runtime = createInferRuntime(gLogger);assert(runtime != nullptr);ICudaEngine* engine = runtime->deserializeCudaEngine(trtModelStream, size);assert(engine != nullptr);context = engine->createExecutionContext();assert(context != nullptr);delete[] trtModelStream;assert(engine->getNbBindings() == 2);// In order to bind the buffers, we need to know the names of the input and output tensors.// Note that indices are guaranteed to be less than IEngine::getNbBindings()const int inputIndex = engine->getBindingIndex(INPUT_BLOB_NAME);const int outputIndex = engine->getBindingIndex(OUTPUT_BLOB_NAME);assert(inputIndex == 0);assert(outputIndex == 1);// Create GPU buffers on deviceCUDA_CHECK(cudaMalloc(&buffers[inputIndex], BATCH_SIZE * 3 * INPUT_H * INPUT_W * sizeof(float)));CUDA_CHECK(cudaMalloc(&buffers[outputIndex], BATCH_SIZE * OUTPUT_SIZE * sizeof(float)));// Create streamCUDA_CHECK(cudaStreamCreate(&stream));return true; }YOLOV5_API void YoloV5::detect(const Mat& inputImg, vector<Rect>& vRect) {Mat imgCopy;inputImg.copyTo(imgCopy);cv::Mat pr_img = preprocess_img(imgCopy, INPUT_W, INPUT_H); // letterbox BGR to RGBint i = 0;int b = 0;for (int row = 0; row < INPUT_H; ++row) {uchar* uc_pixel = pr_img.data + row * pr_img.step;for (int col = 0; col < INPUT_W; ++col) {data[b * 3 * INPUT_H * INPUT_W + i] = (float)uc_pixel[2] / 255.0;data[b * 3 * INPUT_H * INPUT_W + i + INPUT_H * INPUT_W] = (float)uc_pixel[1] / 255.0;data[b * 3 * INPUT_H * INPUT_W + i + 2 * INPUT_H * INPUT_W] = (float)uc_pixel[0] / 255.0;uc_pixel += 3;++i;}}// Run inferenceauto start = std::chrono::system_clock::now();YoloV5::doInference(*context, stream, buffers, data, prob, BATCH_SIZE);auto end = std::chrono::system_clock::now();std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms" << std::endl;int fcount = 1;std::vector<std::vector<Yolo::Detection>> batch_res(fcount);for (int b = 0; b < fcount; b++) {auto& res = batch_res[b];nms(res, &prob[b * OUTPUT_SIZE], CONF_THRESH, NMS_THRESH);}for (int b = 0; b < fcount; b++) {auto& res = batch_res[b];//std::cout << res.size() << std::endl;Mat img;inputImg.copyTo(img);if (inputImg.channels() == 1){cv::cvtColor(img, img, cv::COLOR_GRAY2BGR);}for (size_t j = 0; j < res.size(); j++) {cv::Rect r = get_rect(img, res[j].bbox);if (res[j].class_id == 0){vRect.push_back(r);}cv::rectangle(img, r, cv::Scalar(0, 0, 255), 2);if ((int)res[j].class_id == 0){cv::putText(img, "First", cv::Point(r.x - 50, r.y - 1), cv::FONT_HERSHEY_PLAIN, 1.2, cv::Scalar(0, 0, 255), 2);cv::putText(img, std::to_string((float)res[j].conf), cv::Point(r.x + 50, r.y - 1), cv::FONT_HERSHEY_PLAIN, 1.2, cv::Scalar(0, 0, 255), 2);}if ((int)res[j].class_id == 1){cv::putText(img, "Second", cv::Point(r.x - 50, r.y - 1), cv::FONT_HERSHEY_PLAIN, 1.2, cv::Scalar(0, 0, 255), 2);cv::putText(img, std::to_string((float)res[j].conf), cv::Point(r.x + 50, r.y - 1), cv::FONT_HERSHEY_PLAIN, 1.2, cv::Scalar(0, 0, 255), 2);}if ((int)res[j].class_id == 2){cv::putText(img, "Third", cv::Point(r.x - 50, r.y - 1), cv::FONT_HERSHEY_PLAIN, 1.2, cv::Scalar(0, 0, 255), 2);cv::putText(img, std::to_string((float)res[j].conf), cv::Point(r.x + 50, r.y - 1), cv::FONT_HERSHEY_PLAIN, 1.2, cv::Scalar(0, 0, 255), 2);}if ((int)res[j].class_id == 3){cv::putText(img, "Fourth", cv::Point(r.x - 50, r.y - 1), cv::FONT_HERSHEY_PLAIN, 1.2, cv::Scalar(0, 0, 255), 2);cv::putText(img, std::to_string((float)res[j].conf), cv::Point(r.x + 50, r.y - 1), cv::FONT_HERSHEY_PLAIN, 1.2, cv::Scalar(0, 0, 255), 2);}}//img.copyTo(outputImg);cv::imwrite("E:\\res.jpg", img);}}YOLOV5_API void YoloV5::doInference(IExecutionContext& context, cudaStream_t& stream, void **buffers, float* input, float* output, int batchSize) {// DMA input batch data to device, infer on the batch asynchronously, and DMA output back to hostCUDA_CHECK(cudaMemcpyAsync(buffers[0], input, batchSize * 3 * INPUT_H * INPUT_W * sizeof(float), cudaMemcpyHostToDevice, stream));context.enqueue(batchSize, buffers, stream, nullptr);CUDA_CHECK(cudaMemcpyAsync(output, buffers[1], batchSize * OUTPUT_SIZE * sizeof(float), cudaMemcpyDeviceToHost, stream));cudaStreamSynchronize(stream); }cv::Rect YoloV5::get_rect(cv::Mat& img, float bbox[4]) {int l, r, t, b;float r_w = Yolo::INPUT_W / (img.cols * 1.0);float r_h = Yolo::INPUT_H / (img.rows * 1.0);if (r_h > r_w) {l = bbox[0] - bbox[2] / 2.f;r = bbox[0] + bbox[2] / 2.f;t = bbox[1] - bbox[3] / 2.f - (Yolo::INPUT_H - r_w * img.rows) / 2;b = bbox[1] + bbox[3] / 2.f - (Yolo::INPUT_H - r_w * img.rows) / 2;l = l / r_w;r = r / r_w;t = t / r_w;b = b / r_w;}else {l = bbox[0] - bbox[2] / 2.f - (Yolo::INPUT_W - r_h * img.cols) / 2;r = bbox[0] + bbox[2] / 2.f - (Yolo::INPUT_W - r_h * img.cols) / 2;t = bbox[1] - bbox[3] / 2.f;b = bbox[1] + bbox[3] / 2.f;l = l / r_h;r = r / r_h;t = t / r_h;b = b / r_h;}return cv::Rect(l, t, r - l, b - t); }float YoloV5::iou(float lbox[4], float rbox[4]) {float interBox[] = {(std::max)(lbox[0] - lbox[2] / 2.f , rbox[0] - rbox[2] / 2.f), //left(std::min)(lbox[0] + lbox[2] / 2.f , rbox[0] + rbox[2] / 2.f), //right(std::max)(lbox[1] - lbox[3] / 2.f , rbox[1] - rbox[3] / 2.f), //top(std::min)(lbox[1] + lbox[3] / 2.f , rbox[1] + rbox[3] / 2.f), //bottom};if (interBox[2] > interBox[3] || interBox[0] > interBox[1])return 0.0f;float interBoxS = (interBox[1] - interBox[0])*(interBox[3] - interBox[2]);return interBoxS / (lbox[2] * lbox[3] + rbox[2] * rbox[3] - interBoxS); }void YoloV5::nms(std::vector<Yolo::Detection>& res, float *output, float conf_thresh, float nms_thresh) {int det_size = sizeof(Yolo::Detection) / sizeof(float);std::map<float, std::vector<Yolo::Detection>> m;for (int i = 0; i < output[0] && i < Yolo::MAX_OUTPUT_BBOX_COUNT; i++) {if (output[1 + det_size * i + 4] <= conf_thresh) continue;Yolo::Detection det;memcpy(&det, &output[1 + det_size * i], det_size * sizeof(float));if (m.count(det.class_id) == 0) m.emplace(det.class_id, std::vector<Yolo::Detection>());m[det.class_id].push_back(det);}for (auto it = m.begin(); it != m.end(); it++) {//std::cout << it->second[0].class_id << " --- " << std::endl;auto& dets = it->second;std::sort(dets.begin(), dets.end(), cmp);for (size_t m = 0; m < dets.size(); ++m) {auto& item = dets[m];res.push_back(item);for (size_t n = m + 1; n < dets.size(); ++n) {if (iou(item.bbox, dets[n].bbox) > nms_thresh) {dets.erase(dets.begin() + n);--n;}}}} }資源管理器:
該項(xiàng)目同樣需要引入yolov5文件夾中的頭文件和.cu文件。配置好相應(yīng)的環(huán)境(與3.2節(jié)中在VS中生成.engine文件的環(huán)境配置相同),編寫好封裝的函數(shù)后(YoloV5.h和YoloV5.cpp),在Release環(huán)境下開始進(jìn)行編譯運(yùn)行,會(huì)生成yolov5.dll的動(dòng)態(tài)鏈接庫文件以及對(duì)應(yīng)的lib文件。
完成之后,可以先在VS中進(jìn)行檢測封裝好的模型是否能夠運(yùn)行。創(chuàng)建一個(gè)test項(xiàng)目,創(chuàng)建一個(gè)新的源文件Test.cpp,并且將yololayer.cu添加進(jìn)項(xiàng)目中,同樣需要跟之前一樣配置相應(yīng)的環(huán)境才能夠正常運(yùn)行。
源文件Test.cpp:
// Test.cpp : 定義控制臺(tái)應(yīng)用程序的入口點(diǎn)。 // #pragma warning(disable:4996)#include <iostream> #include "..//Yolov5/YoloV5.h" #include "..//Yolov5/dirent.h"int main() {YoloV5 yolov5_1;string enginePath = "E:\\PyTorch\\TensorRT\\C_VS\\yolov5_tensorrt\\x64\\Release\\yolov5s.engine";yolov5_1.inital(enginePath);vector<cv::String> vImgPath;string filter = "E:\\PyTorch\\淘汰的網(wǎng)絡(luò)\\make_dataset_Faster_RCNN\\photo\\12.jpg";glob(filter, vImgPath);for (auto i = 0; i < vImgPath.size(); i++){//cout << "vImgPath.size() : " << vImgPath.size() << endl;Mat img = imread(vImgPath[i]);if (img.channels() == 1){cvtColor(img, img, cv::COLOR_GRAY2BGR);}vector<Rect> res1;yolov5_1.detect(img, ref(res1));}return 0; }運(yùn)行Test.cpp文件,可以看到生成的檢測圖像。正確生成檢測圖像之后,就可以開始利用Qt進(jìn)行模型的部署了。
五、利用Qt進(jìn)行部署
5.1 Qt的安裝
筆者個(gè)人建議可以在VS中進(jìn)行Qt代碼的編寫以及界面的設(shè)計(jì),個(gè)人認(rèn)為相較于在Qt Creator中進(jìn)行代碼編寫在VS中更有利于代碼的調(diào)試。首先需要進(jìn)行Qt的安裝,安裝教程可以參考這位老師寫的博客:Qt 入門 | 愛編程的大丙 (subingwen.cn)里面的第二章詳細(xì)描述了Qt的安裝。
5.2 Qt與VS2017相關(guān)聯(lián)
由于在VS中進(jìn)行程序調(diào)試更方便一些,因此需要將安裝好的Qt與VS進(jìn)行關(guān)聯(lián),從而可以在VS中進(jìn)行Qt代碼的編寫。Qt與VS相關(guān)聯(lián)的方法可以參考這篇博客:Qt5.11.1安裝與VS2017配置_GJXAIOU的博客-CSDN博客_qt vs2017
5.3 進(jìn)行模型的部署
5.3.1 在VS中創(chuàng)建新的Qt項(xiàng)目
在VS中進(jìn)行Qt的編寫,從而進(jìn)行模型的部署。先創(chuàng)建一個(gè)新項(xiàng)目,選擇Qt Widgets Application,點(diǎn)擊確定→Next→勾選對(duì)應(yīng)的Qt版本以及開發(fā)系統(tǒng)和平臺(tái),Next→根據(jù)自己的情況命名,Finish→項(xiàng)目→重新生成解決方案。
當(dāng)看到生成成功時(shí),即代表Qt和VS的關(guān)聯(lián)成功。
5.3.2 進(jìn)行環(huán)境的配置
右鍵項(xiàng)目,選擇屬性,進(jìn)入項(xiàng)目頁點(diǎn)擊VC++目錄,選擇包含目錄,將TensorRT、CUDA以及Qt的include文件夾路徑添加到包含目錄中。添加完成后再選擇庫目錄,同樣將TensorRT、CUDA以及Qt的lib文件夾路徑添加進(jìn)庫目錄中。
添加完成后點(diǎn)擊應(yīng)用,在選擇鏈接器→輸入→附加依賴項(xiàng),添加上相應(yīng)的.lib文件,這是筆者添加的文件:
$(Qt_LIBS_) YoloV5.lib opencv_world410.lib nvparsers.lib nvonnxparser.lib nvinfer_plugin.lib nvinfer.lib cublas.lib cublasLt.lib cuda.lib cudadevrt.lib cudart.lib cudart_static.lib cudnn.lib cudnn64_8.lib cudnn_adv_infer.lib cudnn_adv_infer64_8.lib cudnn_adv_train.lib cudnn_adv_train64_8.lib cudnn_cnn_infer.lib cudnn_cnn_infer64_8.lib cudnn_cnn_train.lib cudnn_cnn_train64_8.lib cudnn_ops_infer.lib cudnn_ops_infer64_8.lib cudnn_ops_train.lib cudnn_ops_train64_8.lib cufft.lib cufftw.lib curand.lib cusolver.lib cusolverMg.lib cusparse.lib nppc.lib nppial.lib nppicc.lib nppidei.lib nppif.lib nppig.lib nppim.lib nppist.lib nppisu.lib nppitc.lib npps.lib nvblas.lib nvjpeg.lib nvml.lib nvrtc.lib OpenCL.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib添加完成后,同樣點(diǎn)擊應(yīng)用,完成環(huán)境配置。
環(huán)境配置好之后,需要將之前封裝好的.dll文件與.lib文件復(fù)制到當(dāng)前項(xiàng)目文件夾中。
5.3.3 添加.cu文件
在源文件中添加上之前的yololayer.cu文件,
添加完成后, 右鍵項(xiàng)目,選擇生成依賴項(xiàng),點(diǎn)擊生成自定義,勾選CUDA。
?完成后,右鍵yololayer.cu文件,選擇屬性,在項(xiàng)類型中選擇CUDA C/C++,點(diǎn)擊應(yīng)用,完成.cu文件的生成配置。
5.3.4編寫Qt代碼
完成相關(guān)的環(huán)境配置之后,就可以開始進(jìn)行Qt代碼的編寫。 首先可以先進(jìn)入.ui文件中,進(jìn)行相應(yīng)控件添加,設(shè)計(jì)自己的UI界面。
設(shè)計(jì)完ui界面之后,進(jìn)行相應(yīng)代碼的編寫,分別給每個(gè)空間添加上相應(yīng)的功能。
Test2.h頭文件:
#pragma once #ifndef TEST2_H #define TEST2_H #include <QtWidgets/QMainWindow> #include "ui_Test2.h" #if _MSC_VER >= 1600 #pragma execution_character_set("utf-8") #endif #include "opencv2/opencv.hpp" #include "opencv.hpp"using namespace std;QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACEclass Test2 : public QMainWindow {Q_OBJECTpublic:Test2(QWidget *parent = nullptr);~Test2();// 聲明全局變量// 存放原圖像static cv::Mat image;private:Ui::Test2Class *ui; private slots:// 載入圖像void on_load_picture_clicked();// 模型預(yù)測void on_model_predict_clicked(); }; #endifTest2.cpp源文件:
#include "Test2.h" #include "qfile.h" #include "E://PyTorch//TensorRT/C_VS/yolov5_dll/YoloV5/YoloV5/YoloV5.h" // 封裝模型的頭文件路徑 #include "E://PyTorch//TensorRT/C_VS/yolov5_dll/YoloV5/YoloV5/dirent.h" // dirent.h的路徑// 定義全局變量 // 存放原圖像 Mat Test2::image;// 構(gòu)造函數(shù) Test2::Test2(QWidget *parent): QMainWindow(parent) {ui->setupUi(this); } // 析構(gòu)函數(shù) Test2::~Test2() {} // 加載圖像 void Test2::on_load_picture_clicked() {// 利用Qt的方式去讀入圖像QFile file("E:/1.jpg");//判斷當(dāng)前路徑是否存在 一定要添加上if else 判斷QFile文件是否讀取成功if (!file.open(QFile::ReadOnly)){// 路徑不存在 則提示讀取失敗ui->textEdit->append(QString("圖像載入失敗..."));}else{QByteArray ba = file.readAll();// 再使用imdecode函數(shù)將比特流解碼成Mat類image = imdecode(std::vector<char>(ba.begin(), ba.end()), 1);ui->textEdit->append(QString("圖像載入成功..."));}cv::Mat temp;QImage qImg;// 將圖像的三通道格式從BGR改成RGBcv::cvtColor(image, temp, COLOR_BGR2RGB);// 進(jìn)行圖像的縮放cv::resize(temp, temp, Size(612, 512));// 將Mat類轉(zhuǎn)換成QImageQImage Qimg = QImage((const unsigned char*)(temp.data), temp.cols, temp.rows, temp.step, QImage::Format_BGR888);// 在Label窗口進(jìn)行顯示// 先清空之前的圖像,再顯示ui->image_window->clear();ui->image_window->setPixmap(QPixmap::fromImage(Qimg));ui->image_window->resize(Qimg.size());ui->image_window->show(); } // 模型檢測 void Test2::on_model_predict_clicked() {// 輸出提示信息ui->textEdit->append(QString("正在進(jìn)行模型預(yù)測,請(qǐng)耐心等待..."));// 建立YoloV5模型YoloV5 yolov5_1;// 讀入.engine模型string enginePath = "E:\\PyTorch\\TensorRT\\C_VS\\yolov5_tensorrt\\x64\\Release\\yolov5s.engine";// 模型初始化yolov5_1.inital(enginePath);cv::Mat img = image;// 判斷圖像是否是單通道圖像if (img.channels() == 1){// 將單通道圖像轉(zhuǎn)變?yōu)槿ǖ缊D像cvtColor(img, img, cv::COLOR_GRAY2BGR);}// 存放預(yù)測框坐標(biāo)vector<Rect> res1;// 進(jìn)行預(yù)測yolov5_1.detect(img, ref(res1));// 利用Qt的方式去讀取圖像QFile file("E:/res.jpg");// 判斷預(yù)測結(jié)果是否存在 一定要添加上if else 判斷QFile文件是否讀取成功if (!file.open(QFile::ReadOnly)){// 路徑不存在 輸出模型預(yù)測失敗ui->textEdit->append(QString("模型預(yù)測失敗..."));}else{// 輸出成功信息ui->textEdit->append(QString("模型預(yù)測成功..."));// 路徑存在 讀入圖像QByteArray ba = file.readAll();// 再使用imdecode函數(shù)將比特流解碼成mat類cv::Mat image = imdecode(std::vector<char>(ba.begin(), ba.end()), 1);cv::Mat temp;// 將圖像的三通道格式從bgr改成rgb//cv::cvtcolor(image, temp, color_bgr2rgb);// 進(jìn)行圖像的縮放cv::resize(image, temp, Size(612, 512));// 將mat類轉(zhuǎn)換成qimageQImage Qimg = QImage((const unsigned char*)(temp.data), temp.cols, temp.rows, temp.step, QImage::Format_BGR888);// 在label窗口進(jìn)行顯示// 先清空之前的圖像,在進(jìn)行顯示ui->image_window->clear();ui->image_window->setPixmap(QPixmap::fromImage(Qimg));ui->image_window->resize(Qimg.size());ui->image_window->show();} }注意!筆者的代碼中采用了QFile去進(jìn)行文件的讀取,后面一定要對(duì)QFile文件是否讀取成功進(jìn)行判斷,即添加上if else的條件判斷語句,否則程序在調(diào)試時(shí)會(huì)出現(xiàn)下圖的報(bào)錯(cuò)情況,并且運(yùn)行不調(diào)試時(shí)ui界面會(huì)出現(xiàn)閃退的情況。
編寫完成后,就可以在Release環(huán)境下運(yùn)行代碼了。點(diǎn)擊加載圖像和模型預(yù)測就可以在顯示框中顯示出圖像內(nèi)容。(這里由于筆者的個(gè)人原因,不方便對(duì)模型預(yù)測后的圖像進(jìn)行展示。)
筆者是對(duì)自己項(xiàng)目經(jīng)歷過程中所用到的技術(shù)進(jìn)行了一個(gè)粗略的歸納,其中引用了許多位大佬的博客,僅供各位參考,希望能對(duì)有需要的人提供一些幫助。文中可能存在筆者有所疏漏或沒有說明到的地方,望海涵。歡迎各位在評(píng)論區(qū)提出問題和建議。
總結(jié)
以上是生活随笔為你收集整理的Yolov5训练自己的数据集+TensorRT加速+Qt部署的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ABC190 D - Staircase
- 下一篇: Redis之时间轮机制(五)