文章介紹
本文是篇基于yolov5模型的一個(gè)工程,主要是利用c++將yolov5模型進(jìn)行調(diào)用并測試,從而實(shí)現(xiàn)目標(biāo)檢測任務(wù) 任務(wù)過程中主要重點(diǎn)有兩個(gè),第一 版本問題,第二配置問題
一,所需軟件及版本
? ? ? 訓(xùn)練部分 pytorch==1.13.0? opencv==3.4.1? ?其他的直接pip即可
? ? ? c++部署?
? ? ? ?vs2019或者vs2022? ??
? ? ? ? libtorch-1.13.0
? ? ? ? opencv==3.4.1? ? 鏈接:https://pan.baidu.com/s/1XPWUNfS7PTFiDkHTG8yvcQ?
提取碼:d9g4
? ? ? ? 有的可能需要cmake反正我沒用? ? 鏈接:https://pan.baidu.com/s/1-eLo7ecgQg94Mjtw-pQcXw?
提取碼:rg0x
二,安裝vs
官網(wǎng)地址:
Visual Studio 較舊的下載 - 2019、2017、2015 和以前的版本
?上訴鏈接可能為2017推薦安裝 Visual Studio Installer? 2019或者2022
環(huán)境配置以及任務(wù)準(zhǔn)備可以借鑒我上一篇文章
libtorch-yolov5部署pytorch版本_該醒醒了~的博客-CSDN博客
好的屁話不多說,正文開始
首先在vs中創(chuàng)建新文件
在源文件中新建一個(gè)cpp文件,在頭文件新建一個(gè).h 頭文件
下載yolov5 libtorch
文件鏈接:https://pan.baidu.com/s/1oIP1btJd10gQddxAHijg7w?
提取碼:lntf
- 粘貼 src/YoloV5.cpp 中的代碼到上面的 YoloV5.cpp 文件中
- 粘貼 nclude/YoloV5.h 中的代碼到上面的 YoloV5.h 文件中
- 更改 YoloV5.cpp 中頭文件引入方式為 "YoloV5.h
-
"
改為
?在源文件里新建一個(gè)main.cpp 文件 此文件是用來調(diào)用yolov5的
將代碼復(fù)制到main.cpp中
這是讀取攝像頭實(shí)時(shí)監(jiān)測的
#include "YoloV5.h"
int main()
{
// 第二個(gè)參數(shù)為是否啟用 cuda 詳細(xì)用法可以參考 YoloV5.h 文件
YoloV5 yolo("C:/Users/hwx/Documents/Github/YoloV5-LibTorch/test/yolov5s.cuda.pt", true);
// 讀取分類標(biāo)簽(我們用的官方的所以這里是 coco 中的分類)
// 其實(shí)這些代碼無所謂哪 只是后面預(yù)測出來的框沒有標(biāo)簽罷了
std::ifstream f("C:/Users/hwx/Documents/Github/YoloV5-LibTorch/test/coco.txt");
std::string name = "";
int i = 0;
std::map<int, std::string> labels;
while (std::getline(f, name))
{
labels.insert(std::pair<int, std::string>(i, name));
i++;
}
// 用 OpenCV 打開攝像頭讀取文件(你隨便咋樣獲取圖片都OK哪)
cv::VideoCapture cap = cv::VideoCapture(0);
// 設(shè)置寬高 無所謂多寬多高后面都會通過一個(gè)算法轉(zhuǎn)換為固定寬高的
// 固定寬高值應(yīng)該是你通過YoloV5訓(xùn)練得到的模型所需要的
// 傳入方式是構(gòu)造 YoloV5 對象時(shí)傳入 width 默認(rèn)值為 640,height 默認(rèn)值為 640
cap.set(cv::CAP_PROP_FRAME_WIDTH, 1000);
cap.set(cv::CAP_PROP_FRAME_HEIGHT, 800);
cv::Mat frame;
while (cap.isOpened())
{
// 讀取一幀
cap.read(frame);
if (frame.empty())
{
std::cout << "Read frame failed!" << std::endl;
break;
}
// 預(yù)測
// 簡單吧,兩行代碼預(yù)測結(jié)果就出來了,封裝的還可以吧 嘚瑟
std::vector<torch::Tensor> r = yolo.prediction(frame);
// 畫框根據(jù)你自己的項(xiàng)目調(diào)用相應(yīng)的方法,也可以不畫框自己處理
frame = yolo.drawRectangle(frame, r[0], labels);
// show 圖片
cv::imshow("", frame);
if (cv::waitKey(1) == 27) break;
}
return 0;
}
這個(gè)是讀取文件夾內(nèi)所有的圖片
#if 0
#include "YOLOv5.h"
#include"Ex.h"
#include<opencv2\opencv.hpp>
#include<io.h>
#include<iostream>
int main()
{
YoloV5 yolo("D:\\Besktop\\best.torchscript.pt", true);
// 讀取分類標(biāo)簽
std::ifstream f("D:\\Besktop\\voc.txt");
std::string name = "";
int i = 0;
std::map<int, std::string> labels;
while (std::getline(f, name))
{
labels.insert(std::pair<int, std::string>(i, name));
std::cout << labels << std::endl;
i++;
}
//cv::Mat frame = cv::imread("D:\\Besktop\\000\\劃傷_2023032218553818.bmp");
string path = "D:\\Besktop\\000\\";
String dest = "D:\\Besktop\\1\\";
String savedfilename;
int len = path.length();
vector<cv::String> filenames;
cv::glob(path, filenames);
for (int i = 0; i < filenames.size(); i++)
{
Mat frame;
frame = imread(filenames[i], i);
//frame = 255 - frame; //對每一張圖片取反
savedfilename = dest + filenames[i].substr(len);
cout << savedfilename << endl;
// 預(yù)測
std::vector<torch::Tensor> r = yolo.prediction(frame);
std::cout << r << std::endl;
// 畫框
frame = yolo.drawRectangle(frame, r[0], labels);
//bool is = yolo.existencePrediction(r);
//std::cout << is << std::endl;
// show 圖片
//cv::imshow("", frame);
//imwrite(fileName, frame);
imwrite(savedfilename, frame);
cv::waitKey(0);
//if (cv::waitKey(1) == 27);
}
return 0;
}
#endif // 0
讀取一張圖片
#if 1
#include "YoloV5.h"
int main()
{
YoloV5 yolo("../dataset/best.torchscript.pt", true);
// 讀取分類標(biāo)簽
std::ifstream f("../dataset/voc.txt");
std::string name = "";
int i = 0;
std::map<int, std::string> labels;
while (std::getline(f, name))
{
labels.insert(std::pair<int, std::string>(i, name));
i++;
}
// 用 OpenCV 打開攝像頭讀取文件
//cv::VideoCapture cap = cv::VideoCapture(0);
//cap.set(cv::CAP_PROP_FRAME_WIDTH, 1000);
//cap.set(cv::CAP_PROP_FRAME_HEIGHT, 800);
//cv::Mat frame;
//while (cap.isOpened())
//{
// // 讀取一幀
// cap.read(frame);
// if (frame.empty())
// {
// std::cout << "Read frame failed!" << std::endl;
// break;
// }
cv::Mat frame = cv::imread("D:\\Besktop\\000\\斷柵_2.bmp");
// 預(yù)測
std::vector<torch::Tensor> r = yolo.prediction(frame);
std::cout << r << std::endl;
// 畫框處理
frame = yolo.drawRectangle(frame, r[0], labels);
// show 圖片
cv::imshow("", frame);
cv::waitKey(0);
//if (cv::waitKey(1) == 27) break;
return 0;
}
#endif // 1
在 VC++目錄/包含目錄 中添加頭文件
?
在 VC++目錄/庫目錄 中添加 .lib 庫 有就添加沒有就不添加
在 輸入/附加依賴項(xiàng) 中添加 lib 庫名稱 有就添加,沒有就不添加
路徑在你libtorch和opencv文件中的lib文件夾內(nèi)
torch.lib
torch_cuda.lib
torch_cuda_cu.lib
torch_cuda_cpp.lib
torch_cpu.lib
c10_cuda.lib
caffe2_nvrtc.lib
c10.lib
kineto.lib
dnnl.lib
fbgemm.lib
asmjit.lib
XNNPACK.lib
cpuinfo.lib
clog.lib
libprotoc.lib
pthreadpool.lib
libprotobuf.lib
libprotobuf-lite.lib
opencv_world341.lib
注意? ?將此libtorch和opencv文件夾下的.dll?文件復(fù)制到 你項(xiàng)目文件下的環(huán)境內(nèi)
libtorch? ?.dll文件打開就可以看到
opencv 的藏得比較深? .......opencv3.4.1\opencv\build\x64\vc15\lib
我的項(xiàng)目文件添加路勁為.......active2\x64\Release
?/INCLUDE:"?ignore_this_library_placeholder@@YAHXZ"?
然后修改main函數(shù)中的模型路徑和下方的標(biāo)簽路徑最后運(yùn)行就好了
最后我們再來核對一下
程序中將會有這些文件其中 只需要看main.cpp tesst.cpp YOLOV5.h Yolov5.cpp
這些文件
main.cpp在上面有這里就不粘貼了
tesst.cpp
#if 0
#include "YOLOv5.h"
#include"Ex.h"
#include<opencv2\opencv.hpp>
#include<io.h>
#include<iostream>
int main()
{
YoloV5 yolo("D:\\Besktop\\best.torchscript.pt", true);
// 讀取分類標(biāo)簽
std::ifstream f("D:\\Besktop\\voc.txt");
std::string name = "";
int i = 0;
std::map<int, std::string> labels;
while (std::getline(f, name))
{
labels.insert(std::pair<int, std::string>(i, name));
std::cout << labels << std::endl;
i++;
}
//cv::Mat frame = cv::imread("D:\\Besktop\\000\\劃傷_2023032218553818.bmp");
string path = "D:\\Besktop\\000\\";
String dest = "D:\\Besktop\\1\\";
String savedfilename;
int len = path.length();
vector<cv::String> filenames;
cv::glob(path, filenames);
for (int i = 0; i < filenames.size(); i++)
{
Mat frame;
frame = imread(filenames[i], i);
//frame = 255 - frame; //對每一張圖片取反
savedfilename = dest + filenames[i].substr(len);
cout << savedfilename << endl;
// 預(yù)測
std::vector<torch::Tensor> r = yolo.prediction(frame);
std::cout << r << std::endl;
// 畫框
frame = yolo.drawRectangle(frame, r[0], labels);
//bool is = yolo.existencePrediction(r);
//std::cout << is << std::endl;
// show 圖片
//cv::imshow("", frame);
//imwrite(fileName, frame);
imwrite(savedfilename, frame);
cv::waitKey(0);
//if (cv::waitKey(1) == 27);
}
return 0;
}
#endif // 0
yolov5.h
#pragma once
#include <torch/torch.h>
#include <torch/script.h>
#include <opencv2/opencv.hpp>
#include <iostream>
#include <ctime>
/**
* ImageResizeData 圖片處理過后保存圖片的數(shù)據(jù)結(jié)構(gòu)
*/
class ImageResizeData
{
public:
// 添加處理過后的圖片
void setImg(cv::Mat img);
// 獲取處理過后的圖片
cv::Mat getImg();
// 當(dāng)原始圖片寬高比大于處理過后圖片寬高比時(shí)此函數(shù)返回 true
bool isW();
// 當(dāng)原始圖片高寬比大于處理過后圖片高寬比時(shí)此函數(shù)返回 true
bool isH();
// 添加處理之后圖片的寬
void setWidth(int width);
// 獲取處理之后圖片的寬
int getWidth();
// 添加處理之后圖片的高
void setHeight(int height);
// 獲取處理之后圖片的高
int getHeight();
// 添加原始圖片的寬
void setW(int w);
// 獲取原始圖片的寬
int getW();
// 添加原始圖片的高
void setH(int h);
// 獲取原始圖片的高
int getH();
// 添加從原始圖片到處理過后圖片所添加黑邊大小
void setBorder(int border);
// 獲取從原始圖片到處理過后圖片所添加黑邊大小
int getBorder();
private:
// 處理過后圖片高
int height;
// 處理過后圖片寬
int width;
// 原始圖片寬
int w;
// 原始圖片高
int h;
// 從原始圖片到處理圖片所添加的黑邊大小
int border;
// 處理過后的圖片
cv::Mat img;
};
/**
* YoloV5 的實(shí)現(xiàn)類
*/
class YoloV5
{
public:
/**
* 構(gòu)造函數(shù)
* @param ptFile yoloV5 pt文件路徑
* @param isCuda 是否使用 cuda 默認(rèn)不起用
* @param height yoloV5 訓(xùn)練時(shí)圖片的高
* @param width yoloV5 訓(xùn)練時(shí)圖片的寬
* @param confThres 非極大值抑制中的 scoreThresh
* @param iouThres 非極大值抑制中的 iouThresh
*/
YoloV5(std::string ptFile, bool isCuda = false, bool isHalf = false, int height = 640, int width = 640, float confThres = 0.25, float iouThres = 0.45);
/**
* 預(yù)測函數(shù)
* @param data 語言預(yù)測的數(shù)據(jù)格式 (batch, rgb, height, width)
*/
std::vector<torch::Tensor> prediction(torch::Tensor data);
/**
* 預(yù)測函數(shù)
* @param filePath 需要預(yù)測的圖片路徑
*/
std::vector<torch::Tensor> prediction(std::string filePath);
/**
* 預(yù)測函數(shù)
* @param img 需要預(yù)測的圖片
*/
std::vector<torch::Tensor> prediction(cv::Mat img);
/**
* 預(yù)測函數(shù)
* @param imgs 需要預(yù)測的圖片集合
*/
std::vector<torch::Tensor> prediction(std::vector <cv::Mat> imgs);
/**
* 改變圖片大小的函數(shù)
* @param img 原始圖片
* @param height 要處理成的圖片的高
* @param width 要處理成的圖片的寬
* @return 封裝好的處理過后圖片數(shù)據(jù)結(jié)構(gòu)
*/
static ImageResizeData resize(cv::Mat img, int height, int width);
/**
* 改變圖片大小的函數(shù)
* @param img 原始圖片
* @return 封裝好的處理過后圖片數(shù)據(jù)結(jié)構(gòu)
*/
ImageResizeData resize(cv::Mat img);
/**
* 改變圖片大小的函數(shù)
* @param imgs 原始圖片集合
* @param height 要處理成的圖片的高
* @param width 要處理成的圖片的寬
* @return 封裝好的處理過后圖片數(shù)據(jù)結(jié)構(gòu)
*/
static std::vector<ImageResizeData> resize(std::vector <cv::Mat> imgs, int height, int width);
/**
* 改變圖片大小的函數(shù)
* @param imgs 原始圖片集合
* @return 封裝好的處理過后圖片數(shù)據(jù)結(jié)構(gòu)
*/
std::vector<ImageResizeData> resize(std::vector <cv::Mat> imgs);
/**
* 根據(jù)輸出結(jié)果在給定圖片中畫出框
* @param imgs 原始圖片集合
* @param rectangles 通過預(yù)測函數(shù)處理好的結(jié)果
* @param labels 類別標(biāo)簽
* @param thickness 線寬
* @return 畫好框的圖片
*/
std::vector<cv::Mat> drawRectangle(std::vector<cv::Mat> imgs, std::vector<torch::Tensor> rectangles, std::map<int, std::string> labels, int thickness = 2);
/**
* 根據(jù)輸出結(jié)果在給定圖片中畫出框
* @param imgs 原始圖片集合
* @param rectangles 通過預(yù)測函數(shù)處理好的結(jié)果
* @param thickness 線寬
* @return 畫好框的圖片
*/
std::vector<cv::Mat> drawRectangle(std::vector<cv::Mat> imgs, std::vector<torch::Tensor> rectangles, int thickness = 2);
/**
* 根據(jù)輸出結(jié)果在給定圖片中畫出框
* @param imgs 原始圖片集合
* @param rectangles 通過預(yù)測函數(shù)處理好的結(jié)果
* @param colors 每種類型對應(yīng)顏色
* @param labels 類別標(biāo)簽
* @return 畫好框的圖片
*/
std::vector<cv::Mat> drawRectangle(std::vector<cv::Mat> imgs, std::vector<torch::Tensor> rectangles, std::map<int, cv::Scalar> colors, std::map<int, std::string> labels, int thickness = 2);
/**
* 根據(jù)輸出結(jié)果在給定圖片中畫出框
* @param img 原始圖片
* @param rectangle 通過預(yù)測函數(shù)處理好的結(jié)果
* @param thickness 線寬
* @return 畫好框的圖片
*/
cv::Mat drawRectangle(cv::Mat img, torch::Tensor rectangle, int thickness = 2);
/**
* 根據(jù)輸出結(jié)果在給定圖片中畫出框
* @param img 原始圖片
* @param rectangle 通過預(yù)測函數(shù)處理好的結(jié)果
* @param labels 類別標(biāo)簽
* @param thickness 線寬
* @return 畫好框的圖片
*/
cv::Mat drawRectangle(cv::Mat img, torch::Tensor rectangle, std::map<int, std::string> labels, int thickness = 2);
/**
* 根據(jù)輸出結(jié)果在給定圖片中畫出框
* @param img 原始圖片
* @param rectangle 通過預(yù)測函數(shù)處理好的結(jié)果
* @param colos 每種類型對應(yīng)顏色
* @param labels 類別標(biāo)簽
* @param thickness 線寬
* @return 畫好框的圖片
*/
cv::Mat drawRectangle(cv::Mat img, torch::Tensor rectangle, std::map<int, cv::Scalar> colors, std::map<int, std::string> labels, int thickness = 2);
/**
* 用于判斷給定數(shù)據(jù)是否存在預(yù)測
* @param clazz 通過預(yù)測函數(shù)處理好的結(jié)果
* @return 如果圖片中存在給定某一種分類返回 true
*/
bool existencePrediction(torch::Tensor clazz);
/**
* 用于判斷給定數(shù)據(jù)是否存在預(yù)測
* @param classs 通過預(yù)測函數(shù)處理好的結(jié)果
* @return 如果圖片集合中存在給定某一種分類返回 true
*/
bool existencePrediction(std::vector<torch::Tensor> classs);
private:
// 是否啟用 cuda
bool isCuda;
// 是否使用半精度
bool isHalf;
// 非極大值抑制中的第一步數(shù)據(jù)清理
float confThres;
// 非極大值抑制中 iou
float iouThres;
// 模型所需要的圖片的高
float height;
// 模型所需要的圖片的寬
float width;
// 畫框顏色 map
std::map<int, cv::Scalar> mainColors;
// 模型
torch::jit::script::Module model;
// 隨機(jī)獲取一種顏色
cv::Scalar getRandScalar();
// 圖片通道轉(zhuǎn)換為 rgb
cv::Mat img2RGB(cv::Mat img);
// 圖片變?yōu)?Tensor
torch::Tensor img2Tensor(cv::Mat img);
// (center_x center_y w h) to (left, top, right, bottom)
torch::Tensor xywh2xyxy(torch::Tensor x);
// 非極大值抑制算法
torch::Tensor nms(torch::Tensor bboxes, torch::Tensor scores, float thresh);
// 預(yù)測出來的框根據(jù)原始圖片還原算法
std::vector<torch::Tensor> sizeOriginal(std::vector<torch::Tensor> result, std::vector<ImageResizeData> imgRDs);
// 非極大值抑制算法整體
std::vector<torch::Tensor> non_max_suppression(torch::Tensor preds, float confThres = 0.25, float iouThres = 0.45);
};
?yolov5.cpp
#include "YoloV5.h"
YoloV5::YoloV5(std::string ptFile, bool isCuda, bool isHalf, int height, int width, float confThres, float iouThres)
{
model = torch::jit::load(ptFile);
if (isCuda)
{
model.to(torch::kCUDA);
}
if (isHalf)
{
model.to(torch::kHalf);
}
this->height = height;
this->width = width;
this->isCuda = isCuda;
this->iouThres = iouThres;
this->confThres = confThres;
this->isHalf = isHalf;
model.eval();
unsigned seed = time(0);
std::srand(seed);
}
std::vector<torch::Tensor> YoloV5::non_max_suppression(torch::Tensor prediction, float confThres, float iouThres)
{
torch::Tensor xc = prediction.select(2, 4) > confThres;
int maxWh = 4096;
int maxNms = 30000;
std::vector<torch::Tensor> output;
for (int i = 0; i < prediction.size(0); i++)
{
output.push_back(torch::zeros({ 0, 6 }));
}
for (int i = 0; i < prediction.size(0); i++)
{
torch::Tensor x = prediction[i];
x = x.index_select(0, torch::nonzero(xc[i]).select(1, 0));
if (x.size(0) == 0) continue;
x.slice(1, 5, x.size(1)).mul_(x.slice(1, 4, 5));
torch::Tensor box = xywh2xyxy(x.slice(1, 0, 4));
std::tuple<torch::Tensor, torch::Tensor> max_tuple = torch::max(x.slice(1, 5, x.size(1)), 1, true);
x = torch::cat({ box, std::get<0>(max_tuple), std::get<1>(max_tuple) }, 1);
x = x.index_select(0, torch::nonzero(std::get<0>(max_tuple) > confThres).select(1, 0));
int n = x.size(0);
if (n == 0)
{
continue;
}
else if (n > maxNms)
{
x = x.index_select(0, x.select(1, 4).argsort(0, true).slice(0, 0, maxNms));
}
torch::Tensor c = x.slice(1, 5, 6) * maxWh;
torch::Tensor boxes = x.slice(1, 0, 4) + c, scores = x.select(1, 4);
torch::Tensor ix = nms(boxes, scores, iouThres).to(x.device());
output[i] = x.index_select(0, ix).cpu();
}
return output;
}
cv::Scalar YoloV5::getRandScalar()
{
return cv::Scalar(std::rand() % 256, std::rand() % 256, std::rand() % 256);
}
cv::Mat YoloV5::img2RGB(cv::Mat img)
{
int imgC = img.channels();
if (imgC == 1)
{
cv::cvtColor(img, img, cv::COLOR_GRAY2RGB);
}
else
{
cv::cvtColor(img, img, cv::COLOR_BGR2RGB);
}
return img;
}
torch::Tensor YoloV5::img2Tensor(cv::Mat img)
{
torch::Tensor data = torch::from_blob(img.data, { (int)height, (int)width, 3 }, torch::kByte);
data = data.permute({ 2, 0, 1 });
data = data.toType(torch::kFloat);
data = data.div(255);
data = data.unsqueeze(0);
return data;
}
torch::Tensor YoloV5::xywh2xyxy(torch::Tensor x)
{
torch::Tensor y = x.clone();
y.select(1, 0) = x.select(1, 0) - x.select(1, 2) / 2;
y.select(1, 1) = x.select(1, 1) - x.select(1, 3) / 2;
y.select(1, 2) = x.select(1, 0) + x.select(1, 2) / 2;
y.select(1, 3) = x.select(1, 1) + x.select(1, 3) / 2;
return y;
}
torch::Tensor YoloV5::nms(torch::Tensor bboxes, torch::Tensor scores, float thresh)
{
auto x1 = bboxes.select(1, 0);
auto y1 = bboxes.select(1, 1);
auto x2 = bboxes.select(1, 2);
auto y2 = bboxes.select(1, 3);
auto areas = (x2 - x1) * (y2 - y1);
auto tuple_sorted = scores.sort(0, true);
auto order = std::get<1>(tuple_sorted);
std::vector<int> keep;
while (order.numel() > 0)
{
if (order.numel() == 1)
{
auto i = order.item();
keep.push_back(i.toInt());
break;
}
else
{
auto i = order[0].item();
keep.push_back(i.toInt());
}
auto order_mask = order.narrow(0, 1, order.size(-1) - 1);
auto xx1 = x1.index({ order_mask }).clamp(x1[keep.back()].item().toFloat(), 1e10);
auto yy1 = y1.index({ order_mask }).clamp(y1[keep.back()].item().toFloat(), 1e10);
auto xx2 = x2.index({ order_mask }).clamp(0, x2[keep.back()].item().toFloat());
auto yy2 = y2.index({ order_mask }).clamp(0, y2[keep.back()].item().toFloat());
auto inter = (xx2 - xx1).clamp(0, 1e10) * (yy2 - yy1).clamp(0, 1e10);
auto iou = inter / (areas[keep.back()] + areas.index({ order.narrow(0,1,order.size(-1) - 1) }) - inter);
auto idx = (iou <= thresh).nonzero().squeeze();
if (idx.numel() == 0)
{
break;
}
order = order.index({ idx + 1 });
}
return torch::tensor(keep);
}
std::vector<torch::Tensor> YoloV5::sizeOriginal(std::vector<torch::Tensor> result, std::vector<ImageResizeData> imgRDs)
{
std::vector<torch::Tensor> resultOrg;
for (int i = 0; i < result.size(); i++)
{
torch::Tensor data = result[i];
ImageResizeData imgRD = imgRDs[i];
for (int j = 0; j < data.size(0); j++)
{
torch::Tensor tensor = data.select(0, j);
// (left, top, right, bottom)
if (imgRD.isW())
{
tensor[1] -= imgRD.getBorder();
tensor[3] -= imgRD.getBorder();
tensor[0] *= (float)imgRD.getW() / (float)imgRD.getWidth();
tensor[2] *= (float)imgRD.getW() / (float)imgRD.getWidth();
tensor[1] *= (float)imgRD.getH() / (float)(imgRD.getHeight() - 2 * imgRD.getBorder());
tensor[3] *= (float)imgRD.getH() / (float)(imgRD.getHeight() - 2 * imgRD.getBorder());
}
else
{
tensor[0] -= imgRD.getBorder();
tensor[2] -= imgRD.getBorder();
tensor[1] *= (float)imgRD.getH() / (float)imgRD.getHeight();
tensor[3] *= (float)imgRD.getH() / (float)imgRD.getHeight();
tensor[0] *= (float)imgRD.getW() / (float)(imgRD.getWidth() - 2 * imgRD.getBorder());
tensor[2] *= (float)imgRD.getW() / (float)(imgRD.getWidth() - 2 * imgRD.getBorder());
}
// 加了黑邊之后預(yù)測結(jié)果可能在黑邊上,就會造成結(jié)果為負(fù)數(shù)
for (int k = 0; k < 4; k++)
{
if (tensor[k].item().toFloat() < 0)
{
tensor[k] = 0;
}
}
}
resultOrg.push_back(data);
}
return resultOrg;
}
std::vector<torch::Tensor> YoloV5::prediction(torch::Tensor data)
{
if (!data.is_cuda() && this->isCuda)
{
data = data.cuda();
}
if (data.is_cuda() && !this->isCuda)
{
data = data.cpu();
}
if (this->isHalf)
{
data = data.to(torch::kHalf);
}
torch::Tensor pred = model.forward({ data }).toTuple()->elements()[0].toTensor();
return non_max_suppression(pred, confThres, iouThres);
}
std::vector<torch::Tensor> YoloV5::prediction(std::string filePath)
{
cv::Mat img = cv::imread(filePath);
return prediction(img);
}
std::vector<torch::Tensor> YoloV5::prediction(cv::Mat img)
{
ImageResizeData imgRD = resize(img);
cv::Mat reImg = img2RGB(imgRD.getImg());
torch::Tensor data = img2Tensor(reImg);
std::vector<torch::Tensor> result = prediction(data);
std::vector<ImageResizeData> imgRDs;
imgRDs.push_back(imgRD);
return sizeOriginal(result, imgRDs);
}
std::vector<torch::Tensor> YoloV5::prediction(std::vector<cv::Mat> imgs)
{
std::vector<ImageResizeData> imageRDs;
std::vector<torch::Tensor> datas;
for (int i = 0; i < imgs.size(); i++)
{
ImageResizeData imgRD = resize(imgs[i]);
imageRDs.push_back(imgRD);
cv::Mat img = img2RGB(imgRD.getImg());
datas.push_back(img2Tensor(img));
}
torch::Tensor data = torch::cat(datas, 0);
std::vector<torch::Tensor> result = prediction(data);
return sizeOriginal(result, imageRDs);
}
ImageResizeData YoloV5::resize(cv::Mat img, int height, int width)
{
ImageResizeData imgResizeData;
int w = img.cols, h = img.rows;
imgResizeData.setH(h);
imgResizeData.setW(w);
imgResizeData.setHeight(height);
imgResizeData.setWidth(width);
bool isW = (float)w / (float)h > (float)width / (float)height;
cv::resize(img, img, cv::Size(
isW ? width : (int)((float)height / (float)h * w),
isW ? (int)((float)width / (float)w * h) : height));
w = img.cols, h = img.rows;
if (isW)
{
imgResizeData.setBorder((height - h) / 2);
cv::copyMakeBorder(img, img, (height - h) / 2, height - h - (height - h) / 2, 0, 0, cv::BORDER_CONSTANT);
}
else
{
imgResizeData.setBorder((width - w) / 2);
cv::copyMakeBorder(img, img, 0, 0, (width - w) / 2, width - w - (width - w) / 2, cv::BORDER_CONSTANT);
}
imgResizeData.setImg(img);
return imgResizeData;
}
ImageResizeData YoloV5::resize(cv::Mat img)
{
return YoloV5::resize(img, height, width);
}
std::vector<ImageResizeData> YoloV5::resize(std::vector<cv::Mat> imgs, int height, int width)
{
std::vector<ImageResizeData> imgRDs;
for (int i = 0; i < imgs.size(); i++)
{
imgRDs.push_back(YoloV5::resize(imgs[i], height, width));
}
return imgRDs;
}
std::vector<ImageResizeData> YoloV5::resize(std::vector<cv::Mat> imgs)
{
return YoloV5::resize(imgs, height, width);
}
std::vector<cv::Mat> YoloV5::drawRectangle(std::vector<cv::Mat> imgs, std::vector<torch::Tensor> rectangles, std::map<int, std::string> labels, int thickness)
{
std::map<int, cv::Scalar> colors;
return drawRectangle(imgs, rectangles, colors, labels, thickness);
}
std::vector<cv::Mat> YoloV5::drawRectangle(std::vector<cv::Mat> imgs, std::vector<torch::Tensor> rectangles, int thickness)
{
std::map<int, cv::Scalar> colors;
std::map<int, std::string> labels;
return drawRectangle(imgs, rectangles, colors, labels, thickness);
}
std::vector<cv::Mat> YoloV5::drawRectangle(std::vector<cv::Mat> imgs, std::vector<torch::Tensor> rectangles, std::map<int, cv::Scalar> colors, std::map<int, std::string> labels, int thickness)
{
std::vector<cv::Mat> results;
for (int i = 0; i < imgs.size(); i++)
{
results.push_back(drawRectangle(imgs[i], rectangles[i], colors, labels, thickness));
}
return results;
}
cv::Mat YoloV5::drawRectangle(cv::Mat img, torch::Tensor rectangle, int thickness)
{
std::map<int, cv::Scalar> colors;
std::map<int, std::string> labels;
return drawRectangle(img, rectangle, colors, labels, thickness);
}
cv::Mat YoloV5::drawRectangle(cv::Mat img, torch::Tensor rectangle, std::map<int, std::string> labels, int thickness)
{
std::map<int, cv::Scalar> colors;
return drawRectangle(img, rectangle, colors, labels, thickness);
}
cv::Mat YoloV5::drawRectangle(cv::Mat img, torch::Tensor rectangle, std::map<int, cv::Scalar> colors, std::map<int, std::string> labels, int thickness)
{
std::map<int, cv::Scalar>::iterator it;
std::map<int, std::string>::iterator labelIt;
for (int i = 0; i < rectangle.size(0); i++)
{
int clazz = rectangle[i][5].item().toInt();
it = colors.find(clazz);
cv::Scalar color = NULL;
if (it == colors.end())
{
it = mainColors.find(clazz);
if (it == mainColors.end())
{
color = getRandScalar();
mainColors.insert(std::pair<int, cv::Scalar>(clazz, color));
}
else
{
color = it->second;
}
}
else
{
color = it->second;
}
cv::rectangle(img, cv::Point(rectangle[i][0].item().toInt(), rectangle[i][1].item().toInt()), cv::Point(rectangle[i][2].item().toInt(), rectangle[i][3].item().toInt()), color, thickness);
labelIt = labels.find(clazz);
std::ostringstream oss;
if (labelIt != labels.end())
{
oss << labelIt->second << " ";
}
oss << rectangle[i][4].item().toFloat();
std::string label = oss.str();
cv::putText(img, label, cv::Point(rectangle[i][0].item().toInt(), rectangle[i][1].item().toInt()), cv::FONT_HERSHEY_PLAIN, 1, color, thickness);
}
return img;
}
bool YoloV5::existencePrediction(torch::Tensor clazz)
{
return clazz.size(0) > 0 ? true : false;
}
bool YoloV5::existencePrediction(std::vector<torch::Tensor> classs)
{
for (int i = 0; i < classs.size(); i++)
{
if (existencePrediction(classs[i]))
{
return true;
}
}
return false;
}
void ImageResizeData::setImg(cv::Mat img)
{
this->img = img;
}
cv::Mat ImageResizeData::getImg()
{
return img;
}
bool ImageResizeData::isW()
{
return (float)w / (float)h > (float)width / (float)height;
}
bool ImageResizeData::isH()
{
return (float)h / (float)w > (float)height / (float)width;
}
void ImageResizeData::setWidth(int width)
{
this->width = width;
}
int ImageResizeData::getWidth()
{
return width;
}
void ImageResizeData::setHeight(int height)
{
this->height = height;
}
int ImageResizeData::getHeight()
{
return height;
}
void ImageResizeData::setW(int w)
{
this->w = w;
}
int ImageResizeData::getW()
{
return w;
}
void ImageResizeData::setH(int h)
{
this->h = h;
}
int ImageResizeData::getH()
{
return h;
}
void ImageResizeData::setBorder(int border)
{
this->border = border;
}
int ImageResizeData::getBorder()
{
return border;
}
?這個(gè)效果是讀取攝像圖進(jìn)行試試檢測博文中有將攝像頭替換為圖片進(jìn)行檢測的案例
文章來源:http://www.zghlxwxcb.cn/news/detail-469191.html
?文章來源地址http://www.zghlxwxcb.cn/news/detail-469191.html
看得懂就看,看不懂的評論區(qū)問我
到了這里,關(guān)于c++讀取yolov5模型進(jìn)行目標(biāo)檢測(讀取攝像頭實(shí)時(shí)監(jiān)測)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!