C++:Windows平台下利用LibTorch调用PyTorch模型

本文介绍了如何在Windows环境下,使用PyTorch将.pth权重文件转换为.pt模型,并利用LibTorch在C++中进行预测。首先,展示了在Python中加载AlexNet模型并进行图像分类的步骤,然后详细说明了C++项目配置,包括包含目录、库目录和附加依赖项设置。最后,提供了C++代码实现模型预测,并展示了C#调用C++库进行预测的过程。
摘要由CSDN通过智能技术生成

参考:C++调用PyTorch模型:LibTorch

环境

Windows10
VS2017
CPU

OpenCV3.0.0

Pytorch1.10.2  torchvision0.11.3
Libtorch1.10.2

Libtorch下载

Pytorch官网

在这里插入图片描述
解压后:注意红框文件夹路径,之后需要添加到项目属性配置中。
在这里插入图片描述

Pytorch将.pth转为.pt文件

所使用的模型为基于AlexNet的分类模型:AlexNet:论文阅读及pytorch网络搭建

python环境下的预测

输出结果:rose

在这里插入图片描述
在这里插入图片描述

新建pt模型生成文件

# tmp.py

import os
import torch
from PIL import Image
from torchvision import transforms
from model import AlexNet

def main():
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    print("using {} device.".format(device))

    # create model
    model = AlexNet(num_classes=5).to(device)

    image = Image.open("rose2.jpg").convert('RGB')
    data_transform = transforms.Compose(
        [transforms.Resize((224, 224)),
         transforms.ToTensor(),
         transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
    img = data_transform(image)
    img = img.unsqueeze(dim=0)
    print(img.shape)

    # load model weights
    weights_path = "AlexNet.pth"
    assert os.path.exists(weights_path), "file: '{}' dose not exist.".format(weights_path)

    testsize = 224

    if torch.cuda.is_available():
        modelState = torch.load(weights_path, map_location='cuda')
        model.load_state_dict(modelState, strict=False)
        model = model.cuda()
        model = model.eval()
        # An example input you would normally provide to your model's forward() method.
        example = torch.rand(1, 3, testsize, testsize)
        example = example.cuda()
        traced_script_module = torch.jit.trace(model, example)

        output = traced_script_module(img.cuda())
        print(output.shape)
        pred = torch.argmax(output, dim=1)
        print(pred)

        traced_script_module.save('model_cuda.pt')
    else:
        modelState = torch.load(weights_path, map_location='cpu')
        model.load_state_dict(modelState, strict=False)
        example = torch.rand(1, 3, testsize, testsize)
        example = example.cpu()
        traced_script_module = torch.jit.trace(model, example)

        output = traced_script_module(img.cpu())
        print(output.shape)
        pred = torch.argmax(output, dim=1)
        print(pred)

        traced_script_module.save('model.pt')

if __name__ == '__main__':
    main()

输出结果:rose

在这里插入图片描述

在这里插入图片描述

C++调用pytorch模型

新建空项目pt_alex

在这里插入图片描述

项目属性配置

修改配置管理器

Release/x64
在这里插入图片描述

属性>VC++目录>包含目录

添加:(libtorch解压位置)
在这里插入图片描述

注意还应有opencv目录:(继承值修改可参考
在这里插入图片描述

属性>VC++目录>库目录

添加:
在这里插入图片描述

属性>链接器>输入>附加依赖项

添加:
在这里插入图片描述

注意:
如果后续出现error:找不到c10.dll,
直接把该目录下的相应dll复制到项目pt_alex/x64/Release文件夹下。

注意还应有opencv目录:(Debug下为lib*d.lib)
在这里插入图片描述

注意CUDA下的情况

链接器>命令行,添加:

/INCLUDE:?warp_size@cuda@at@@YAHXZ

属性>C/C++

常规>SDL检查:选择否
语言>符合模式:选择否

项目下新建test.cpp

c++调用后分类结果不准确的参考:
Ptorch 与libTorch 使用过程中问题记录
注意python和c++中的图像预处理过程需要完全一致。

// test.cpp

#include <torch/script.h> // One-stop header.
#include "torch/torch.h"
#include <opencv2/opencv.hpp>
#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgcodecs.hpp"
#include <vector>
#include <chrono>
#include <string>
#include <vector>
#include <iostream>
#include <memory>

// class_list
/*
	"0": "daisy",
	"1": "dandelion",
	"2": "roses",
	"3": "sunflowers",
	"4": "tulips"
*/

std::string classList[5] = { "daisy", "dandelion", "rose", "sunflower", "tulip" };

std::string image_path = "rose2.jpg";

int main(int argc, const char* argv[]) {

	// Deserialize the ScriptModule from a file using torch::jit::load().
	//std::shared_ptr<torch::jit::script::Module> module = torch::jit::load("../../model_resnet_jit.pt");
	using torch::jit::script::Module;
	Module module = torch::jit::load("model.pt");

	std::cout << "测试图片:" << image_path << std::endl;

	std::cout << "cuda support:" << (torch::cuda::is_available() ? "ture" : "false") << std::endl;
	std::cout << "CUDNN:  " << torch::cuda::cudnn_is_available() << std::endl;
	std::cout << "GPU(s): " << torch::cuda::device_count() << std::endl;

	// module.to(at::kCUDA); //cpu下会在(auto image = cv::imread(image_path, cv::IMREAD_COLOR))行引起c10:error,未经处理的异常
	module.eval();
	module.to(at::kCPU);

	//assert(module != nullptr);
	//std::cout << "ok\n";

	//输入图像
	auto image = cv::imread(image_path, cv::IMREAD_COLOR);
	cv::cvtColor(image, image, cv::COLOR_BGR2RGB);
	cv::Mat image_transfomed = cv::Mat(cv::Size(224, 224), image.type());
	cv::resize(image, image_transfomed, cv::Size(224, 224));

	//cv::cvtColor(image_transfomed, image_transfomed, cv::COLOR_BGR2RGB);

	// 转换为Tensor
	torch::Tensor tensor_image = torch::from_blob(image_transfomed.data,
		{ image_transfomed.rows, image_transfomed.cols,3 }, torch::kByte);
	tensor_image = tensor_image.permute({ 2,0,1 });
	tensor_image = tensor_image.toType(torch::kFloat);
	auto tensor_image_Tmp = torch::autograd::make_variable(tensor_image, false);
	tensor_image = tensor_image.div(255);
	tensor_image = tensor_image.unsqueeze(0);
	// tensor_image = tensor_image.to(at::kCUDA);
	tensor_image = tensor_image.to(at::kCPU);

	// 网络前向计算
	at::Tensor output = module.forward({ tensor_image }).toTensor();
	std::cout << "output:" << output << std::endl;

	auto prediction = output.argmax(1);
	std::cout << "prediction:" << prediction << std::endl;

	int maxk = 5;
	auto top3 = std::get<1>(output.topk(maxk, 1, true, true));

	std::cout << "top3: " << top3 << '\n';

	std::vector<int> res;
	for (auto i = 0; i < maxk; i++) {
		res.push_back(top3[0][i].item().toInt());
	}
	// for (auto i : res) {
	// 	std::cout << i << " ";
	// }
	// std::cout << "\n";

	int pre = torch::Tensor(prediction).item<int>();
	std::string result = classList[pre];
	std::cout << "This is:" << result << std::endl;

	cvWaitKey();

	return 0;
	// system("pause");
}

出现以下报错不影响项目生成:
在这里插入图片描述

输出结果:rose

在这里插入图片描述

C# Demo

新建C++空项目,封装DLL

  • 传入图像路径
  • 传出类别序号
  • // class_list
    /*
    “0”: “daisy”,
    “1”: “dandelion”,
    “2”: “roses”,
    “3”: “sunflowers”,
    “4”: “tulips”
    */

源码

// test.cpp

#include <torch/script.h> // One-stop header.
#include "torch/torch.h"
#include <opencv2/opencv.hpp>
#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgcodecs.hpp"
#include <vector>
#include <chrono>
#include <string>
#include <vector>
#include <iostream>
#include <memory>

#include "test.h"

std::string classList[5] = { "daisy", "dandelion", "rose", "sunflower", "tulip" };

int TestAlex(char* img)
{
	// Deserialize the ScriptModule from a file using torch::jit::load().
	//std::shared_ptr<torch::jit::script::Module> module = torch::jit::load("../../model_resnet_jit.pt");
	using torch::jit::script::Module;
	Module module = torch::jit::load("D:/model.pt");

	// ...... 略

	int pre = torch::Tensor(prediction).item<int>();
	std::string result = classList[pre];
	//std::cout << "This is:" << result << std::endl;

	return pre;
	// system("pause");
}
//test.h

#pragma once

#include <windows.h>
#include <opencv2/opencv.hpp>

extern "C" __declspec(dllexport) int TestAlex(char* img);

项目属性

  • 输出目录改为C#项目路径
    在这里插入图片描述

点击生成解决方案,生成DLL

新建C#窗体应用

DLLFun.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;

namespace AlexDemo
{
    class DllFun
    {
        public string img;

        [DllImport("AlexDLL.dll", CallingConvention = CallingConvention.Cdecl)]
        public extern static int TestAlex(string img); // 注意 C++ char* 对应 C# string
    }
}

窗体Form2.cs核心代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using AlexDemo;

namespace Demo
{
    public partial class Form2 : Form
    {
        public Form2()
        {
            InitializeComponent();
            this.Load += new EventHandler(Form2_Load); //窗体启动后自动执行事件
        }

        private void Form2_Load(object sender, EventArgs e)
        {
            string[] classList = { "daisy", "dandelion", "rose", "sunflower", "tulip" };
            string fname = "path.txt";
            //StreamReader sr = new StreamReader(fname, Encoding.Default);
            StreamReader sr = new StreamReader(fname, Encoding.GetEncoding("gb2312"));
            string line = sr.ReadLine();
            //读取txt文件
            if (line != null)
            {
                this.pictureBox1.Image = Image.FromFile(line);
                if (line.Contains("\\"))
                {
                    line = line.Replace("\\", "/");
                }
            }
            int result;
            result = DllFun.TestAlex(line);
            //string r = result.ToString();
            label2.Text = classList[result];

            //StringBuilder img;
            //img = new StringBuilder(1024);
            //img.Append(line);
            //int r = DllFun.TestAlex(img);
            //label2.Text = "123";
        }

        private void label1_Click(object sender, EventArgs e)
        {

        }

    }
}

结果

在这里插入图片描述

libtorch是一个为C++语言设计的机器学习库,用于使用PyTorch训练好的模型进行预测。实例分割是一种计算机视觉任务,旨在从图像中找出并准确地标记出每个特定物体的位置和形状。 要使用libtorch进行实例分割模型的预测,首先需要加载训练好的模型。可以使用torch::jit::load函数加载PyTorch模型的序列化文件,将其加载到libtorch中。 加载模型之后,需要预处理输入图像。实例分割模型通常需要输入是归一化的图像,并且可能需要特定的图像尺寸。可以使用OpenCV等图像处理库将输入图像转换为模型所需的格式和大小。 接下来,将预处理后的图像输入加载的模型。可以使用torch::jit::IValue对象来表示输入图像,并使用torch::jit::Module::forward函数对图像进行前向传播,得到模型的输出。 模型的输出通常是一个包含预测信息的张量。可以将输出张量转换为可视化的实例分割结果,以便对模型的预测进行可视化展示。可以使用图像处理库或标注库来标记实例分割结果并绘制在原始图像上。 最后,根据需求进行后处理,如删除重复的预测、应用阈值等。根据具体的使用场景,可能还需要根据实际需求对模型的输出结果进行进一步的处理和解释。 总的来说,使用libtorch进行实例分割模型的预测包括加载模型、预处理输入图像、进行前向传播、后处理和结果可视化等步骤。通过这些步骤,可以从图像中准确地识别和标记出每个特定物体的位置和形状。
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值