最近想用 Qt 写一个界面在 C++ 下调用 Pytorch 模型,整个环境配置下来遇到了不少问题,因此记录一下。
1. 下载安装相关文件
由于我的开发环境是 Win10,因此需要下载的东西挺多的,IDE 用的 Visual Studio 2019,在 这里 下载社区版,安装的时候可以选择需要的编译器,我装的是 MSVC v142,vs2019 里要装一个 cmake,单独的 cmake 可下可不下。
VS 中的 cmake.exe 在
D:\Microsoft Visual Studio\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin
路径下。
使用 cmake 安装包安装的会多一个 cmake-gui.exe 可视化程序可以用。
然后下载 Qt5,在 清华源 里可以直接下载,在页面右边的获取下载链接的应用软件里。
这里选择 MSVC 2017,虽然 VS 是 2019,但不影响。如果要用 MinGW 重新编译 OpenCV 的就要装 MinGW。这里 可以看他们的区别。
安装好之后把 Qt 的 bin 文件夹加入环境变量,我的路径是
E:\Qt\5.14.2\msvc2017_64\bin
。
接着下载 OpenCV,Windows 平台的安装包解压后有
build
和
sources
两个文件夹。
build
文件夹下的就是已经用 VS 编译好的,可以直接用。
sources
就是源文件,可以自己按需要编译。
我没有重新编译,直接用原生提供的,网上有说在 Qt 中用 OpenCV 要自己重新编译,但我目前使用发现没什么问题。
这里给出两个如何自行编译的链接。如果需要就重新编译一下。
- VS 编译 OpenCV
- MinGW 编译 OpenCV(需要在安装 Qt 时安装 MinGW)
Pytorch 有专门的 C++ 库,叫 Libtorch,在 官网 上就可以下载到。这里我选择的是 cpu 版本的,cuda 版本的还需要额外的配置,先把简单的搞定。
Python 和 C++ 的 Pytorch 版本最好一样,免得读取模型出问题,我这里统一用了 1.6 版本。
下载完成,解压后,可以把 libtorch/lib 文件夹也添加到环境变量里。
这是我添加的几个环境变量。
如果出现找不到 dll 的错误,大概率是环境变量没配置好,还有一个更粗暴的方法就是把缺的 dll 文件放到 exe 文件夹下。
2. 转换 Pytorch 模型
这一步需要将 Pytorch 模型转换成 Torch Script,这东西具体是个啥可以看 官方文档。
转换模型可以有两种方法,第一种是 tracing,使用一个示例输入来获取模型结构。
第二种是在模型中添加显式 annotations,以告知 Torch Script 编译器可以根据 Torch Script 语言施加的约束直接解析和编译模型代码。
2.1 通过 Tracing 转换
import torch
import torchvision
# 模型实例
model = torchvision.models.resnet18()
# 设置一个示例输入
example = torch.rand(1, 3, 224, 224)
# 使用 torch.jit.trace 生成 torch.jit.ScriptModule
traced_script_module = torch.jit.trace(model, example)
# 保存转换后的模型
traced_script_module.save("traced_resnet_model.pt")
2.2 通过 Annotation 转换
如果模型有特定的控制流,例如控制流依赖于输入:
import torch
class MyModule(torch.nn.Module):
def __init__(self, N, M):
super(MyModule, self).__init__()
self.weight = torch.nn.Parameter(torch.rand(N, M))
def forward(self, input):
if input.sum() > 0:
output = self.weight.mv(input)
else:
output = self.weight + input
return output
# 然后通过 `torch.jit.script` 将模块转换为 ScriptModule。
my_module = MyModule(10,20)
sm = torch.jit.script(my_module)
# 保存模型
my_module.save("my_module_model.pt")
3. 新建项目
新建项目时我用的不是 vs2019,而是 Qt Creator,因为 Libtorch 给的是 cmake 的构建方式,使用 Qt 新建 cmake 项目时会建立所有需要的东西。
新建一个 widget 应用。
这里选择 cmake。
项目建立完成后可以选择直接用 Qt Creator 写,也可以用 vs2019 打开 cmake 文件继续修改项目。我是改用了 vs2019,毕竟宇宙第一 ide 。
4. 配置 cmake
使用 Qt 创建项目后 cmake 文件里已经添加了部分必要的命令,这里只需要进行简单的修改。
下面贴出我的 cmake 文件,其中有些路径需要替换成自己的
cmake_minimum_required(VERSION 3.5)
project(SegTool LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(Qt5_DIR E:/Qt/5.14.2/msvc2017_64/lib/cmake/Qt5)
set(Qt5Widgets_DIR E:/Qt/5.14.2/msvc2017_64/lib/cmake/Qt5Widgets)
set(Qt5Gui_DIR E:/Qt/5.14.2/msvc2017_64/lib/cmake/Qt5Gui)
set(Qt5Core_DIR E:/Qt/5.14.2/msvc2017_64/lib/cmake/Qt5Core)
find_package(Qt5 COMPONENTS Widgets Core Gui REQUIRED)
set(OpenCV_DIR "D:/Lib/opencv/build/x64/vc15/lib")
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
set(Torch_DIR "D:/Lib/libtorch-debug-cpu-1.6/share/cmake/Torch")
find_package(Torch REQUIRED)
include_directories(${TORCH_INCLUDE_DIRS})
qt5_wrap_ui(UI_HEADERS mainwindow.ui) # 通过 ui 文件生成对应的头文件
add_executable(${PROJECT_NAME}
main.cpp
mainwindow.cpp
mainwindow.h
${UI_HEADERS}
)
target_link_libraries(${PROJECT_NAME} Qt5::Widgets)
target_link_libraries(${PROJECT_NAME} ${TORCH_LIBRARIES} ${OpenCV_LIBS})
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 11)
这里我设置了 Qt 路径,因为如果用 Python 的可能 Anaconda 里也有,但是用 Anaconda 里的会有问题。
在 vs2019 中打开 CMakeSettings.json 可以看到其他配置信息,把
显示高级变量
勾选上可以看到所有 cmake 变量,可以看看设置都对不对。
CMakeLists.txt
里定义的变量会覆盖 json 文件里的。
配置完成后 Ctrl + S 就可以看到 cmake 完成了。
5. 测试
接下来用一段 C++ 代码来测试是否成功,修改
mainwindow.cpp
文件(根据你创建时的类命名的)。
代码里的模型和图片路径替换成你的就行了。
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include <iostream>
#include <opencv2/opencv.hpp>
#undef slots
#include <torch/torch.h>
#include <torch/script.h>
#define slots Q_SLOTS
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
torch::jit::script::Module module;
module = torch::jit::load("model.pt"); // 模型路径
std::cout << "Succeed in loading model" << std::endl;
cv::Mat image = cv::imread("C:/Users/lan/Desktop/2.jpg", 1); // 图片路径
cv::namedWindow("My Image");
cv::imshow("My Image", image);
}
MainWindow::~MainWindow()
{
delete ui;
}
代码中有一段
#undef
,不加会发生语法错误,不知道为什么,反正加上就对了。
然后就可以编译、生成了,编译的时候会出现很多警告,应该是
#undef
导致的,可以忽略。
这是全部完成后我的文件夹里的文件
这是运行结果
成功!
所有的准备操作到这里就结束了,后面就可以开始写 qt 界面了。