Yolov5训练自己的数据集+TensorRT加速+Qt部署
本人由于項目要求,需要利用Yolov5網絡訓練自己的目標檢測與分類模型,并利用TensorRT加速將其部署到Qt界面上。目前已經實現了整個流程,寫下這篇博客供需要的各位參考。(本文描述的重點主要是在后續對經過加速的模型進行打包后在Qt中進行部署實現,其余過程可以參考文中相應鏈接的博客與視頻)
目錄
一、環境配置
二、在Pycharm中利用Yolov5網絡進行模型訓練
2.1 Yolov5下載
2.2 訓練自己的數據集
三、利用TensorRT進行模型的加速
3.1 生成.wts文件
3.2 生成.engine文件
四、對模型進行封裝
五、利用Qt進行部署
5.1 Qt的安裝
5.2 Qt與VS2017相關聯
5.3 進行模型的部署
5.3.1 在VS中創建新的Qt項目
5.3.2 進行環境的配置
5.3.3 添加.cu文件
5.3.4編寫Qt代碼
一、環境配置
該項目工程配置的環境是:
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網絡進行模型訓練
2.1 Yolov5下載
Yolov5的源代碼下載鏈接:https://github.com/ultralytics/yolov5
在下載Yolov5源代碼時,需要留意下載的版本(需要與后續利用TensorRT加速的版本相同),筆者這里當時也不太了解,因此就是直接下載了master,也就是默認。
2.2 訓練自己的數據集
訓練自己的數據集,需要先使用labelImg進行圖像標注,然后進行數據集的劃分以及相應配置文件的更改。具體的步驟可以參考這位大佬寫的博客:Yolov5訓練自己的數據集(詳細完整版)_締宇diyu的博客-CSDN博客_yolov5訓練自己的數據集
也可以觀看這個視頻了解學習Yolov5的網絡架構和訓練:帶你一行行讀懂yolov5代碼,yolov5源碼_嗶哩嗶哩_bilibili
調整好自己相應的模型和數據集之后,在Pycharm的Terminal中輸入命令:
python train.py --weights weights/yolov5s.pt --cfg models/yolov5s.yaml --data data/myvoc.yaml --batch-size 8 --img 640 --device 0可以根據自己的需要去修改相應的參數,模型訓練完成后會得到.pt文件,后面會利用該文件生成.wts文件去進行模型的加速。
三、利用TensorRT進行模型的加速
本文采用的技術路線是:.pt文件→.wts文件→.engine文件。利用TensorRT進行Yolov5模型推理,需要下載tensorrtx,具體的下載鏈接:https://github.com/wang-xinyu/tensorrtx,注意!筆者個人理解這里的tensorrtx版本最好與之前下載的Yolov5版本相同,因為不同版本之間的網絡架構是存在一些差異的,可能會導致后續生成的.engine文件與采用的網絡架構不同而出現一系列報錯。筆者這里同樣是下載的master(默認)。
3.1 生成.wts文件
下載完成后,將文件中yolov5子文件中的gen_wts.py文件復制到Yolov5工程目錄下,運行該文件生成.wts文件。
具體方法:打開Pycharm的Terminal,輸入命令:
python gen_wts.py -w yolov5s.pt -o yolov5s.wts.pt文件就是之前Yolov5網絡訓練完成生成的文件,根據自己的文件名修改即可。命令后半部分就是生成的.wts文件,同樣可以根據自己的需要更改命名。
3.2 生成.engine文件
詳細的操作可以參照這位大佬寫的文章來進行操作,實戰教程:win10環境下用TensorRT推理YOLOv5_脆皮茄條的博客-CSDN博客_tensorrt yolov5
?這位大佬寫的十分詳細,包括從環境配置到工程編譯的全過程。如果需要針對自己的數據集進行更改,需要在VS中打開yololayer.h文件,對分類種類數目進行更改。筆者僅僅是對種類數量進行了更改,可以根據需要修改輸入圖像的尺寸等其他參數。
修改完成后,利用Win+R運行cmd,將當前目錄切換到該工程目錄下,同時將之前生成的.wts文件復制到該文件夾下。輸入命令:
yolov5_tensorrt.exe -s yolov5s.wts yolov5s.engine s生成.engine文件,利用該.engine文件輸入命令進行模型預測:
yolov5_tensorrt.exe -d yolov5s.engine ./image_dir?進行預測得到的結果,由于是自己的項目訓練集,不方便展示效果圖。
四、對模型進行封裝
在Qt中調用經過TensorRT推理加速后的模型,需要對模型進行封裝,即封裝成動態鏈接庫(.dll)的形式在Qt中進行調用。我這里是將yolov5模型封裝成了一個類,并且其中包含了兩個成員函數,一個是初始化函數inital(),另外一個是推理檢測函數detect()。在VS中創建與調用動態鏈接庫的方法可以參考以下幾篇博客:
VS2017創建動態鏈接庫與調用_北斗星辰001的博客-CSDN博客_vs2017創建動態鏈接庫
抽象類作為接口使用的DLL實現方法_Christo3的博客-CSDN博客
私有類封裝為DLL的方法_Christo3的博客-CSDN博客
這是我自己封裝模型時的頭文件和源文件,可以根據自己的情況去任意修改。
頭文件YoloV5.h:
// 任何項目上不應定義此符號。這樣,源文件中包含此文件的任何其他項目都會將 // YOLOV5_API 函數視為是從 DLL 導入的,而此 DLL 則將用此宏定義的 // 符號視為是被導出的。 #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 的導出函數。 //#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;}}}} }資源管理器:
該項目同樣需要引入yolov5文件夾中的頭文件和.cu文件。配置好相應的環境(與3.2節中在VS中生成.engine文件的環境配置相同),編寫好封裝的函數后(YoloV5.h和YoloV5.cpp),在Release環境下開始進行編譯運行,會生成yolov5.dll的動態鏈接庫文件以及對應的lib文件。
完成之后,可以先在VS中進行檢測封裝好的模型是否能夠運行。創建一個test項目,創建一個新的源文件Test.cpp,并且將yololayer.cu添加進項目中,同樣需要跟之前一樣配置相應的環境才能夠正常運行。
源文件Test.cpp:
// Test.cpp : 定義控制臺應用程序的入口點。 // #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\\淘汰的網絡\\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; }運行Test.cpp文件,可以看到生成的檢測圖像。正確生成檢測圖像之后,就可以開始利用Qt進行模型的部署了。
五、利用Qt進行部署
5.1 Qt的安裝
筆者個人建議可以在VS中進行Qt代碼的編寫以及界面的設計,個人認為相較于在Qt Creator中進行代碼編寫在VS中更有利于代碼的調試。首先需要進行Qt的安裝,安裝教程可以參考這位老師寫的博客:Qt 入門 | 愛編程的大丙 (subingwen.cn)里面的第二章詳細描述了Qt的安裝。
5.2 Qt與VS2017相關聯
由于在VS中進行程序調試更方便一些,因此需要將安裝好的Qt與VS進行關聯,從而可以在VS中進行Qt代碼的編寫。Qt與VS相關聯的方法可以參考這篇博客:Qt5.11.1安裝與VS2017配置_GJXAIOU的博客-CSDN博客_qt vs2017
5.3 進行模型的部署
5.3.1 在VS中創建新的Qt項目
在VS中進行Qt的編寫,從而進行模型的部署。先創建一個新項目,選擇Qt Widgets Application,點擊確定→Next→勾選對應的Qt版本以及開發系統和平臺,Next→根據自己的情況命名,Finish→項目→重新生成解決方案。
當看到生成成功時,即代表Qt和VS的關聯成功。
5.3.2 進行環境的配置
右鍵項目,選擇屬性,進入項目頁點擊VC++目錄,選擇包含目錄,將TensorRT、CUDA以及Qt的include文件夾路徑添加到包含目錄中。添加完成后再選擇庫目錄,同樣將TensorRT、CUDA以及Qt的lib文件夾路徑添加進庫目錄中。
添加完成后點擊應用,在選擇鏈接器→輸入→附加依賴項,添加上相應的.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添加完成后,同樣點擊應用,完成環境配置。
環境配置好之后,需要將之前封裝好的.dll文件與.lib文件復制到當前項目文件夾中。
5.3.3 添加.cu文件
在源文件中添加上之前的yololayer.cu文件,
添加完成后, 右鍵項目,選擇生成依賴項,點擊生成自定義,勾選CUDA。
?完成后,右鍵yololayer.cu文件,選擇屬性,在項類型中選擇CUDA C/C++,點擊應用,完成.cu文件的生成配置。
5.3.4編寫Qt代碼
完成相關的環境配置之后,就可以開始進行Qt代碼的編寫。 首先可以先進入.ui文件中,進行相應控件添加,設計自己的UI界面。
設計完ui界面之后,進行相應代碼的編寫,分別給每個空間添加上相應的功能。
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();// 模型預測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;// 構造函數 Test2::Test2(QWidget *parent): QMainWindow(parent) {ui->setupUi(this); } // 析構函數 Test2::~Test2() {} // 加載圖像 void Test2::on_load_picture_clicked() {// 利用Qt的方式去讀入圖像QFile file("E:/1.jpg");//判斷當前路徑是否存在 一定要添加上if else 判斷QFile文件是否讀取成功if (!file.open(QFile::ReadOnly)){// 路徑不存在 則提示讀取失敗ui->textEdit->append(QString("圖像載入失敗..."));}else{QByteArray ba = file.readAll();// 再使用imdecode函數將比特流解碼成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);// 進行圖像的縮放cv::resize(temp, temp, Size(612, 512));// 將Mat類轉換成QImageQImage Qimg = QImage((const unsigned char*)(temp.data), temp.cols, temp.rows, temp.step, QImage::Format_BGR888);// 在Label窗口進行顯示// 先清空之前的圖像,再顯示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("正在進行模型預測,請耐心等待..."));// 建立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){// 將單通道圖像轉變為三通道圖像cvtColor(img, img, cv::COLOR_GRAY2BGR);}// 存放預測框坐標vector<Rect> res1;// 進行預測yolov5_1.detect(img, ref(res1));// 利用Qt的方式去讀取圖像QFile file("E:/res.jpg");// 判斷預測結果是否存在 一定要添加上if else 判斷QFile文件是否讀取成功if (!file.open(QFile::ReadOnly)){// 路徑不存在 輸出模型預測失敗ui->textEdit->append(QString("模型預測失敗..."));}else{// 輸出成功信息ui->textEdit->append(QString("模型預測成功..."));// 路徑存在 讀入圖像QByteArray ba = file.readAll();// 再使用imdecode函數將比特流解碼成mat類cv::Mat image = imdecode(std::vector<char>(ba.begin(), ba.end()), 1);cv::Mat temp;// 將圖像的三通道格式從bgr改成rgb//cv::cvtcolor(image, temp, color_bgr2rgb);// 進行圖像的縮放cv::resize(image, temp, Size(612, 512));// 將mat類轉換成qimageQImage Qimg = QImage((const unsigned char*)(temp.data), temp.cols, temp.rows, temp.step, QImage::Format_BGR888);// 在label窗口進行顯示// 先清空之前的圖像,在進行顯示ui->image_window->clear();ui->image_window->setPixmap(QPixmap::fromImage(Qimg));ui->image_window->resize(Qimg.size());ui->image_window->show();} }注意!筆者的代碼中采用了QFile去進行文件的讀取,后面一定要對QFile文件是否讀取成功進行判斷,即添加上if else的條件判斷語句,否則程序在調試時會出現下圖的報錯情況,并且運行不調試時ui界面會出現閃退的情況。
編寫完成后,就可以在Release環境下運行代碼了。點擊加載圖像和模型預測就可以在顯示框中顯示出圖像內容。(這里由于筆者的個人原因,不方便對模型預測后的圖像進行展示。)
筆者是對自己項目經歷過程中所用到的技術進行了一個粗略的歸納,其中引用了許多位大佬的博客,僅供各位參考,希望能對有需要的人提供一些幫助。文中可能存在筆者有所疏漏或沒有說明到的地方,望海涵。歡迎各位在評論區提出問題和建議。
總結
以上是生活随笔為你收集整理的Yolov5训练自己的数据集+TensorRT加速+Qt部署的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ABC190 D - Staircase
- 下一篇: java一般自学多久