Pytorch的C++调用(libtorch+opencv),肺分割模型实例

Pytorch C++接口调用方法

0 引言:

​ Python具有丰富的人工智能开发资源库,且基本上都是开源易获得的,极大的方便了人工智能开发者进行 研究工作。例如实现一整套深度学习算法,用C++编写需要写上千行,在python中只需要几十行。且在数据清洗、特征选取、模型优化和超参数设置等工作进行时,如果使用C++来实现会浪费大量的时间在编写和调试代码上。而Python作为一门解释型语言,编程时更加灵活,结合可视化工具可以方便人工智能开发者在开发的任何阶段灵活调整参数并实时运行,使开发者能专注于自身领域的内容。

​ 但也是由于Python作为一门解释型语言,在运行时往往会有较慢的运行速度,如果要将Python语言编写的程序封装成电脑可以直接运行的可执行文件要将Python解释器和庞大的库一起打包,往往会具有较大的体积,这在实际的工程运用上是难以容忍的。因此,要让人工智能项目落地,将其转换成编译型语言代码,是十分有必要的。以下为Pytorch的C++工程化方法。**

1 Python部分

在工程化工作开始前,要准备好自己的训练网络和已经训练好的模型。训练时通常将模型保存为.pth格式,我们需要将其转换成C++可以加载的.pt格式

1.1 训练网络加载

加载.pth模型训练时使用的网络,这里使用了git-hub上高星的segmentation_models_pytorch库。可以按照自己的训练网络进行更改。

import torch
import segmentation_models_pytorch as smp

model = smp.Unet(
        decoder_channels=(512, 256, 128, 64, 32),
        in_channels=3,
        activation='sigmoid',
    )

1.2 模型加载

将训练好的模型加载进网络,模型的模式设置为eval模式。有cuda的设备可以使用cudu读取模型。

checkpoint = torch.load(r"D:/data/unet/pytorch/beta1/best_model_1.pth",map_location=torch.device('cpu'))
#checkpoint = torch.load(r"D:/data/unet/pytorch/beta1/best_model_1.pth",map_location=torch.device('cuda')) 
model.load_state_dict(checkpoint.state_dict())
model.eval()

Notes:模型有两种模式,model.train()和model.eval(),前者用于模型训练会启用Batch Normalization和Dropout,后者用于预测即用于验证、测试和预测,会关闭Batch Normalization和Dropout,以保证预测结果的一致性。

1.3 模型追踪

使用**torch.jit.trace()**追踪模型对张量的操作。需要给定一个与输入网络形状一样的示例张量,随机生成一个就行。

example = torch.rand(1,3,512,512)
traced_script_module = torch.jit.trace(model,example)

Notes: 个人理解。张量在模型里的变化是很复杂的,很难用函数和逻辑关系表达和转换模型,但是追踪张量的变化是比较简单的。网络就犹如火车轨道网络,经过训练得到模型,可以指导火车在轨道网络里具体的哪条轨道里的运行。如果轨道网络比较简单,取个极限就只有一条火车道,那么火车只需要沿着这一条轨道就能到达目的地,相当于张量只需要经过一个函数关系运算就能得到结果。但现在的神经网络内部结构十分复杂,这样这个火车轨道就被铺的丛横交错,同样要达到目的地,要经过成百上千次变轨,如果还用指定线路的方式去引导火车运行,就要指定若干复杂的路线。所以这时候指定火车运行路线的方式就不如直接追踪火车是怎么运行的,可以让火车以模型的方式在火车轨道网络里运行,追踪火车的运行状态,可以记录的指标有很多,速度、加速度、什么时候换轨、什么时候并轨等等,并将火车运行的变化状态记录下来,就可以指导其他火车在轨道网络里运行。

1.4 模型保存

将转换的script_module保存成.pt格式

traced_script_module.save("torch_script_eval.pt")

2 C++部分

2.0 必要库准备

这里使用了opencv4.3LibTorch 1.81 CPU debug版本opencv需要有vc14运行实况。

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

2.1 VS工程建立

2.1.1 建立一个空项目

configuration 设置为debug x64模式

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

2.1.2 包含目录以及库配置

包含目录配置

添加libtorch和opencv对应的include目录

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

库目录配置

添加libtorch和opencv对应的lib目录

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

附加依赖项配置

连接所需的lib目录
在这里插入图片描述

在这里插入图片描述

可直接复制粘贴(cuda版本需自己另外配置)

opencv_world430d.lib
c10d.lib
c10.lib
torch.lib
torch_cpu.lib
fbgemm.lib
caffe2_detectron_ops.lib
caffe2_module_test_dynamic.lib
cpuinfo.lib

添加所需dll

(1)opencv类

./opencv/build/x64/vc14/bin 目录下的 opencv_world430d.dll

(2)libtorch类

./libtorch/lib目录下的 c10.dll torch_cpu.dll libiomp5md.dll fbgemm.dll asmjit.dll

将上述dll复制到工程对应的configuration目录

在这里插入图片描述

2.1.3 添加头文件

在这里插入图片描述

DemoPytorch.h代码

#pragma once

#include <iostream>

2.2 简单实现代码

2.2.1 引用的头文件
#include "DemoPytorch.h"
#include <iostream>
#include <memory>
#include <algorithm>
#include <stdio.h>
#include <opencv2/core.hpp>
#include <opencv2/opencv.hpp>
#include <torch/torch.h>
#include <torch/script.h>
2.2.2 模型加载

按照pytorch官方提供的模型加载方法

需要用CUDA版本修改注释部分

torch::jit::script::Module module;
	try {
		module = torch::jit::load("./torch_script_eval.pt");
		module.to(torch::kCPU);  // set model to cpu mode
		/*module.to(torch::kCUDA);*/  // set model to cuda mode
		module.eval();
		std::cout << "MODEL LOADED";
	}
	catch (const c10::Error& e) {
		std::cerr << "error loading the model\n";
	}
2.2.3 读取图片并进行格式转换

如果需要展示原图需要拷贝一份图片,因为进行数据操作的时候是直接在内存地址上进行的会更改原有的内存数据。

**Notes:**libtorch库提供了和pytorch使用方式基本一致的张量运算,适用广播机制,可以用于预加载训练数据。

    // load img
	cv::Mat img_original = cv::imread("./00011584_002.png",0);
	cv::Mat img = cv::Mat(img_original);
	// normalize
	cv::resize(img, img, cv::Size(512, 512));
	img.convertTo(img, CV_32FC1);
	// img to tensor
	torch::Tensor mean = torch::tensor({ 0.485,0.456,0.406 });
	torch::Tensor std = torch::tensor({ 0.229, 0.224, 0.225 });
	auto input_tensor = torch::from_blob(img.data, { 512,512,1 });
	input_tensor = input_tensor / 255.0f;
	input_tensor = input_tensor - mean;
	input_tensor = input_tensor / std;
	input_tensor = input_tensor.permute({ 2,0,1 });
	input_tensor = input_tensor.to(torch::kCPU);
	/*input_tensor = input_tensor.to(torch::kCUDA);*/
	input_tensor = input_tensor.unsqueeze(0);
	std::vector<torch::jit::IValue> input;
	input.push_back(input_tensor);
2.2.4 进行模型预测并显示结果

调用模型的forward方法进行预测,并用detach方法将结果取出,如果张量在CUDA上计算需要进行to(torch::kCPU)将结果转换回CPU类型。

    // pred begin
	auto pred = module.forward(input).toTensor();
	// pred tensor to mat
	pred = pred.squeeze().detach();
	pred = pred * 255;
	pred = pred.to(torch::kU8);
	pred = pred.to(torch::kCPU);
	cv::Mat output_mat(cv::Size{ 512,512 }, CV_8UC1, pred.data_ptr());
	// show result
	cv::imshow("original img", img_original);
	cv::imshow("mask", output_mat);
	cv::waitKey(0);
	cv::destroyWindow("original img");
	cv::destroyWindow("mask");

结果展示:

在这里插入图片描述

2.2.5 整体代码

DemoPytorch.cpp代码

#include "DemoPytorch.h"
#include <iostream>
#include <memory>
#include <algorithm>
#include <stdio.h>
#include <opencv2/core.hpp>
#include <opencv2/opencv.hpp>
#include <torch/torch.h>
#include <torch/script.h>

int main() {
	// load model
	torch::jit::script::Module module;
	try {
		module = torch::jit::load("./torch_script_eval.pt");
		module.to(torch::kCPU);  // set model to cpu mode
		/*module.to(torch::kCUDA);*/  // set model to cuda mode
		module.eval();
		std::cout << "MODEL LOADED";
	}
	catch (const c10::Error& e) {
		std::cerr << "error loading the model\n";
	}

	// load img
	cv::Mat img_original = cv::imread("./00011584_002.png",0);
	cv::Mat img = cv::Mat(img_original);
	// normalize
	cv::resize(img, img, cv::Size(512, 512));
	img.convertTo(img, CV_32FC1);
	// img to tensor
	torch::Tensor mean = torch::tensor({ 0.485,0.456,0.406 });
	torch::Tensor std = torch::tensor({ 0.229, 0.224, 0.225 });
	auto input_tensor = torch::from_blob(img.data, { 512,512,1 });
	input_tensor = input_tensor / 255.0f;
	input_tensor = input_tensor - mean;
	input_tensor = input_tensor / std;
	input_tensor = input_tensor.permute({ 2,0,1 });
	input_tensor = input_tensor.to(torch::kCPU);
	/*input_tensor = input_tensor.to(torch::kCUDA);*/
	input_tensor = input_tensor.unsqueeze(0);
	std::vector<torch::jit::IValue> input;
	input.push_back(input_tensor);
	// pred begin
	auto pred = module.forward(input).toTensor();
	// pred tensor to mat
	pred = pred.squeeze().detach();
	pred = pred * 255;
	pred = pred.to(torch::kU8);
	pred = pred.to(torch::kCPU);
	cv::Mat output_mat(cv::Size{ 512,512 }, CV_8UC1, pred.data_ptr());
	// show result
	cv::imshow("original img", img_original);
	cv::imshow("mask", output_mat);
	cv::waitKey(0);
	cv::destroyWindow("original img");
	cv::destroyWindow("mask");

	return 0;
}
  • 1
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 人工智能在医学影像领域的应用非常广泛,其中分割是一个常见的应用。通过使用深度学习算法,可以对医学影像中的部进行自动分割,从而帮助医生更准确地诊断和治疗部疾病。例如,可以使用卷积神经网络对 CT 或 MRI 影像中的部进行分割,从而得到部的三维模型,进一步辅助医生进行诊断和手术规划。 ### 回答2: 人工智能在医学影像领域具有广泛应用,其中分割是一个常见的应用案例。分割是指将医学影像中的部区域从其他组织或结构中准确且自动地分割出来。 传统的分割方法主要依赖于医生的手动操作,需要耗费大量的时间和精力,并且存在主观因素影响分割结果的准确性。而人工智能技术的应用可以很好地解决这些问题。 通过深度学习算法,人工智能可以自动识别医学影像中的部,并区分部与其他器官或组织的界限。首先,人工智能模型会经过大量训练,从而能够学习到部的特征和形状。然后,当给定一张未经分割的医学影像时,人工智能模型会运用其学习到的知识,自动将部区域分割出来。 利用人工智能进行分割有很多优点。首先,这种分割方法减少了医生手动操作的复杂性,提高了工作效率。其次,人工智能模型在识别部区域时能够更精确,减少了主观因素的干扰,提高了分割结果的准确性。最重要的是,通过此技术,部病变可以更早地被发现和诊断,为患者的治疗和康复提供了更好的机会。 当然,人工智能的医学影像分割仍然面临一些挑战,如数据样本的数量和质量、算法的性能等。但随着人工智能技术的不断发展和改进,相信在不久的将来,人工智能将在医学影像领域发挥更大的作用,为医生提供更准确、高效的分割方法,从而提升诊断和治疗的水平,造福更多的患者。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值