- 环境:
- window 10
- visual studio 2019(nmake/cl/link/lib/dumpbin)
- Qt 5.14.0 (designer/uic/moc)
- OpenCV 4.2.0
- Torch C++ 1.5.1
- cmake
-
注意事项:
- 编译/链接的环境(编译/链接的命令行设置):Makefile/CMake/QMake
- include
- lib
- 运行环境:(设置PATH/或者拷贝到当前路径/或者拷贝到window安装目录下的system32)
- bin
- 动态库也有依赖环境:
- window的运行环境(基本上差不多)
- vcrt160.lib运行时(visual studio自带/动态库自带)
- 编译/链接的环境(编译/链接的命令行设置):Makefile/CMake/QMake
lib库名.主版本号.副版本号.批次号.so
库名
140.dll/库名160.dll
注意
-
libtorch,opencv, qt的动态库所在路径设置为PATH环境变量
- 保存编译后的执行文件能调用到动态库;
-
头文件目录
- opencv
C:\opencv_new\install\include
- qt
C:\Qt\Qt-5.14.0\include
- libtorch
C:\libtorch\include
- opencv
-
lib
- 目录
C:\Qt\Qt-5.14.0\lib
C:\opencv_new\install\x64\vc16\lib
C:\libtorch\lib
- lib文件
- 根据调用的模块
- 目录
CMake
Qt, OpenCV的例子
- 使用Qt显示一副OpenCV读取的图像
#include
#include
#include
#include
int main(int argc, char **argv){
// 1. 创建Qt应用
QApplication app(argc, argv);
// 2. 创建对话框
QDialog dlg;
dlg.setWindowTitle("CMake组织工程");
dlg.resize(600, 400);
dlg.move(100, 100);
// 3. 创建标签狂
QLabel lbl("图像显示", &dlg);
lbl.setGeometry(0, 0, dlg.width(), dlg.height());
// 4. 打开图像
cv::Mat img = cv::imread("gpu.bmp");
cv::cvtColor(img, img, cv::COLOR_BGR2RGB);
// 5. 显示图像
QImage qt_img(img.data, img.cols, img.rows, QImage::Format_RGB888);
QPixmap qt_pixmap = QPixmap::fromImage(qt_img);
lbl.setPixmap(qt_pixmap);
lbl.setScaledContents(true);
dlg.show();
return app.exec();
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(main)
# 控制C++编译选项,链接选项
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQIUIRED True)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
# 属于QT专有
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
# 配置Qt的内置的CMake环境(源代码安装才有cmake目录)
set(CMAKE_PREFIX_PATH "C:/Qt/Qt-5.14.0/lib/cmake")
# Qt的库,头文件自动查找
find_package(Qt5Widgets REQUIRED)
find_package(Qt5Gui REQUIRED)
find_package(Qt5Core REQUIRED)
# 头文件
include_directories(
C:/Qt/Qt-5.14.0/include
C:/opencv_new/install/include
)
# 库路径
link_directories(
C:/opencv_new/install/x64/vc16/lib
C:/Qt/Qt-5.14.0/lib
)
# 源文件
aux_source_directory(. SOURCES)
add_executable(main ${SOURCES})
# 编译库
target_link_libraries(
main
opencv_core420d.lib
opencv_imgcodecs420d.lib
opencv_imgproc420d.lib
Qt5Cored.lib
Qt5Widgetsd.lib
Qt5Guid.lib
)
-
注意:
- 源代码
- 头文件
- lib文件
- 输出的文件名
- C++的编译选项/链接选项
- cmake的封装打包
-
Cmake的抽象在于不需要指定具体的编译器
- 动态侦测,从而实现跨平台安装与部署
Torch C++开发环境与第一个程序
头文件
#include
#include
编译环境
- 两种方法,已注释区分
cmake_minimum_required(VERSION 3.16)
# 指定项目名
project(main)
# 执行Torch的cmake配置的位置
set(CMAKE_PREFIX_PATH "D:/libtorch")
# set(Torch_DIR "D:/libtorch")
# 直接加载Torch C++提供cmake配置
find_package(Torch REQUIRED)
# 直接使用预先定义的变量(服务于Torch项目的编译链接)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FALAG} ${TORCH_CXX_FLAGS}")
# 输出的执行文件
add_executable(main main.cpp)
# 指定编译库
target_link_libraries(main "${TORCH_LIBRARIES}")
# 只对指定的编译目标指定编译器的C++语言标准版本
set_property(TARGET main PROPERTY CXX_STANDARD 14)
# set(CMAKE_CXX_STANDARD 14)
Torch C++编程
Torch C++ 文档
-
https://pytorch.org/cppdocs/api/library_root.html
- at命名空间Tensor
- torch命名:数据集/模型/函数/优化器
数据集
MNIST
Dataset
数据集批次处理
DataLoader
Lenet-5 模型
[图片上传失败...(image-66a07a-1593616320465)]
- 作业:
- 完成文档
- 运行我们的程序;
- 理解我们的程序;
- 可选:
- 自己独立完成,并理解;
- 完成文档
训练
-
训练的算法公式
-
算法步骤
- 使用训练样本(data,target),计算模型的预测值pred;
- 计算误差
- 根据 函数计算在每个训练矩阵的导数grad;
- 使用 更新训练矩阵;
- 最终是的误差接近0
验证与测试
模型保存
#include
#include
// 模型的实现(模型类)
class Lenet5 : public torch::nn::Module{
private:
// 卷积特征运算
torch::nn::Conv2d conv1;
torch::nn::Conv2d conv2;
torch::nn::Conv2d conv3;
torch::nn::Linear fc1;
torch::nn::Linear fc2;
public:
Lenet5():
conv1(torch::nn::Conv2dOptions(1, 6, 5).stride(1).padding(2)), // 1 * 28 * 28 -> 6 * 28 * 28 -> 6 * 14 * 14
conv2(torch::nn::Conv2dOptions(6, 16, 5).stride(1).padding(0)), // 6 * 14 * 14 -> 16 * 10 * 10 -> 16 * 5 * 5
conv3(torch::nn::Conv2dOptions(16, 120, 5).stride(1).padding(0)), // 16 * 5 * 5 -> 120 * 1 * 1 (不需要池化)
fc1(120, 84), // 120 -> 84
fc2(84, 10){ // 84 -> 10 (分量最大的小标就是识别的数字)
// 注册需要学习的矩阵(Kernel Matrix)
register_module("conv1", conv1);
register_module("conv2", conv2);
register_module("conv3", conv3);
register_module("fc1", fc1);
register_module("fc2", fc2);
}
// override
torch::Tensor forward(torch::Tensor x){ // {n * 1 * 28 * 28}
// 1. conv
x = conv1->forward(x); // {n * 6 * 28 * 28}
x = torch::max_pool2d(x, 2); // {n * 6 * 14 * 14}
x = torch::relu(x); // 激活函数 // {n * 6 * 14 * 14}
// 2. conv
x = conv2->forward(x); // {n * 16 * 10 * 10}
x = torch::max_pool2d(x, 2); // {n * 16 * 5 * 5}
x = torch::relu(x); // 激活函数 // {n * 16 * 5 * 5}
// 3. conv
x = conv3->forward(x); // {n * 120 * 1 * 1}
x = torch::relu(x); // 激活函数 // {n * 120 * 1 * 1}
// 做数据格式转换
x = x.view({-1, 120}); // {n * 120}
// 4. fc
x = fc1->forward(x);
x = torch::relu(x);
// 5. fc
x = fc2->forward(x);
return torch::log_softmax(x, 1); // CrossEntryLoss = log_softmax + nll
}
};
// 训练好的模型文件:lenet5.pt
模型加载与识别
#include
#include
int main(){
const char * data_filename = ".\\data";
// 加载模型
std::shared_ptr model = std::make_shared();
torch::load(model, "lenet5.pt");
// 使用测试集中数据识别
auto imgs = torch::data::datasets::MNIST(data_filename, torch::data::datasets::MNIST::Mode::kTest);
// 取一张图像
for(int i = 20; i < 30; i++){
torch::data::Example<> example = imgs.get(i);
// std::cout << "识别的数字是:" << example.target.item() << std::endl;
// 获取图像
torch::Tensor a_img = example.data;
// 预测
a_img = a_img.view({-1, 1, 28, 28}); // 我们的模型只接受4为的固定的数据格式(N * C * H * W)(NCHW格式)
torch::Tensor y = model->forward(a_img);
int32_t result = y.argmax(1).item();
std::cout << "识别的结果是:" << result << "->" << example.target.item() << std::endl;
return 0;
}
类图
- 创建工程目录
- 代码文件
- main.cpp
- CMakeLists.txt
附录
-
OpenCV
https://docs.opencv.org/4.2.0/
-
Qt
https://doc.qt.io/qt-5/
-
Torch
https://pytorch.org/cppdocs/api/library_root.html
-
cmake
https://cmake.org/cmake/help/v3.18/
-
Makeflie
- gnu make:
http://www.gnu.org/software/make/manual/html_node/index.html
- microsoft nmake:
https://docs.microsoft.com/zh-cn/cpp/build/reference/nmake-reference?view=vs-2019
- gnu make:
作业:
-
训练出一个模型:模型文件,验证的结果
- 代码
- 模型文件
- 笔记截图,训练的日志文件
-
利用模型实现识别
- 使用测试数据集测试识别;
-
可选:
- 完成一个完整的识别程序;
- 手写数字识别
- 人脸识别 (人脸采集程序)
- 验收:
- 类图
- 源代码
- 安装包(编译后的程序)
- markdown的说明文档(上传到github)
- 完成一个完整的识别程序;