libtorch是pytorch的C++版本,在需要多进程、提高推理速度等需求下会比python语言更具有优势。本文根据较新的yolov5的6.0版本,利用其自带的export.py将已经训练好的权重文件通过libtorch进行部署推理,基本的环境软件版本如下:
yolov5和pytorch的安装,可以直接在yolov5的官网或者github上下载并安装。
各种版本的libtorch下载地址:https://blog.csdn.net/weixin_43742643/article/details/115218126
注:Libtorch的版本尽量和你训练使用的虚拟环境中的pytorch版本一致。下载之前先去你的虚拟环境中查看Pytorch版本。此外1.10.1版本上述连接的博文中没有给出,可以直接修改后面的版本号,例如CPU-Release版本的地址为:
https://download.pytorch.org/libtorch/cpu/libtorch-win-shared-with-deps-1.10.1%2Bcpu.zip
(1) 选择对应的版本
(2) 选择Release or Debug
(3) 选择CPU or CUDA(GPU)
[Opencv下载地址:https://opencv.org/releases/](
下载安装后,文件夹如下图
libtorch-yolov5-master, Cmake3.23.2的安装可以详细参考博文【1】。
如果不打算新建一个自己的项目,可以直接跳过当前章节,使用下一节中libtorch-yolov5-master的编译生成的vs的解决方案。
详细的Visual studio 2019配置流程可以参考博文【1】,需要注意的是需要将opencv和libtorch的文件夹路径改为你自己电脑上的路径,此外,由于我这里是release版本,在配置项目属性页时,需要注意版本与平台的对应关系。
由于我这里的opencv的版本是4.5.5,注意其中的.lib文件为:
opencv_world455d.lib(Debug)
opencv_world455.lib(Release)
Libtorch的版本不同,其库文件略有差异,需要注意不要直接复制粘贴,1.10.1版本的附加依赖项为:
c10.lib
torch.lib
asmjit.lib
caffe2_detectron_ops.lib
caffe2_module_test_dynamic.lib
clog.lib
cpuinfo.lib
dnnl.lib
fbgemm.lib
Caffe2_perfkernels_avx.lib
Caffe2_perfkernels_avx2.lib
Caffe2_perfkernels_avx512.lib
fbjni.lib
libprotobuf.lib
libprotobuf-lite.lib
libprotoc.lib
mkldnn.lib
pthreadpool.lib
pytorch_jni.lib
torch_cpu.lib
XNNPACK.lib
最后,编译成功运行Libtorch或者opencv提示缺少dll,可以将对应的dll复制到编译好的exe同目录下或者将以下两个路径加入到系统的PATH路径中
D:\AI_project\opencv\opencv\build\x64\vc15\bin;D:\AI_project\libtorch\lib
基本的编译过程参考博文【2】(其中主要是通过cmake在不同平台下实现编译),得到的build文件夹为:
打开上图中的libtorch-yolov5.sln,以下主要对libtorch-yolov5的源码进行改动,以通过编译和适用自己训练的模型。
detector.h
由于未安装cuda,注释掉这两行,如果安装了cuda和cudnn就无需注释
//#include
//#include
修改了Run函数,增加了一个函数输入,目的是调整推理时,输入网络的图片尺寸大小
std::vector>
Run(const cv::Mat& img, float conf_threshold, float iou_threshold, int image_size);
detector.cpp
Demo函数的结尾部分,增加保存绘图的图片,并修改了cv的对话框的模式:
cv::namedWindow("Result", cv::WINDOW_NORMAL);
cv::imshow("Result", img);
cv::imwrite(".//out.jpg", img);
cv::waitKey(0);
main函数中,命令行解释部分,新增了“label”和“imgsz”,分别是标签文件所在的路径和输入网络的尺寸大小
int main(int argc, const char* argv[]) {
cxxopts::Options parser(argv[0], "A LibTorch inference implementation of the yolov5");
// TODO: add other args
parser.allow_unrecognised_options().add_options()
("label", "*.name path", cxxopts::value()->default_value("..//..//weights//coco.names"))
("weights", "model.torchscript.pt path", cxxopts::value())
("source", "source", cxxopts::value())
("conf-thres", "object confidence threshold", cxxopts::value()->default_value("0.4"))
("iou-thres", "IOU threshold for NMS", cxxopts::value()->default_value("0.5"))
("gpu", "Enable cuda device or cpu", cxxopts::value()->default_value("false"))
("view-img", "display results", cxxopts::value()->default_value("false"))
("imgsz", "image size for network input", cxxopts::value()->default_value("640"))
("h,help", "Print usage");
auto opt = parser.parse(argc, argv);
if (opt.count("help")) {
std::cout << parser.help() << std::endl;
exit(0);
}
// check if gpu flag is set
bool is_gpu = opt["gpu"].as();
// set device type - CPU/GPU
torch::DeviceType device_type;
if (torch::cuda::is_available() && is_gpu) {
device_type = torch::kCUDA;
} else {
device_type = torch::kCPU;
}
// load class names from dataset for visualization
std::vector class_names = LoadNames(opt["label"].as());
if (class_names.empty()) {
return -1;
}
// load network
std::string weights = opt["weights"].as();
auto detector = Detector(weights, device_type);
// load input image
std::string source = opt["source"].as();
cv::Mat img = cv::imread(source);
if (img.empty()) {
std::cerr << "Error loading the image!\n";
return -1;
}
// run once to warm up
std::cout << "Run once on empty image" << std::endl;
int image_size = opt["imgsz"].as();
auto temp_img = cv::Mat::zeros(img.rows, img.cols, CV_32FC3);
detector.Run(temp_img, 1.0f, 1.0f, image_size);
// set up threshold
float conf_thres = opt["conf-thres"].as();
float iou_thres = opt["iou-thres"].as();
// inference
auto result = detector.Run(img, conf_thres, iou_thres, image_size);
// visualize detections
if (opt["view-img"].as()) {
Demo(img, result, class_names);
}
cv::destroyAllWindows();
return 0;
}
修改之后,重新编译所有,在文件夹Release下生成了libtorch-yolov5.exe和相应的动态库文件。
打开python版本的yolov5文件夹下,打开cmd,并且cd到yolov5文件夹下,yolov5s.pt导出torchscript文件(libtorch读取的权重文件格式)的命令为:
python export.py --weights yolov5s.pt --data ./data/coco.yaml --imgsz 640 --device cpu --nms --include torchscript
自己训练的参数权重为pcban.pt,但是输入的网络的图片大小是1280*1280,则导出命令为:
python export.py --weights pcban.pt --data ./data/pcban.yaml --imgsz 1280 --device cpu --nms --include torchscript
将上述导出的 yolov5s.torchscript 和pcban.torchscript复制到weights权重文件下(知道这两个文件的路径就行,后续运行时直接改路径即可)
打开cmd,并且cd到自己电脑libtorch-yolov5-master/build/release下,
运行以下命令(注意修改自己的路径),在release目录上同时生成了对应的out.jpg图片:
libtorch-yolov5.exe --label ..//..//weights//coco.names --source ..//..//images//bus.jpg --imgsz 640 --weights ..//..//weights//yolov5s.torchscript --view-img
在weights权重文件下新建pcban.txt,并修改后缀名为.names,每行输入自己训练的标签名,自己训练的图片名为pcb_1.jpg,运行以下命令
libtorch-yolov5.exe --label ..//..//weights//pcban.names --source ..//..//images//pcb_1.jpg --imgsz 1280 --weights ..//..//weights//pcban.torchscript --view-img
【1】https://blog.csdn.net/weixin_39931579/article/details/119862099?spm=1001.2014.3001.5501
【2】https://zhuanlan.zhihu.com/p/403293735