Machine Vision Series 5 : modèle pytorch de déploiement C++ onnxruntime

 Répertoire des articles de la série

Chapitre 1 : Création de la DLL de la bibliothèque de liens dynamiques de Visual Studio 2019

Chapitre deux : Débogage des DLL de la bibliothèque de liens dynamiques VS

Chapitre 3 :  Configuration de l'environnement  OpenCV VS2019

Chapitre 4 : Modèle pytorch de déploiement C++ Libtorch

Chapitre 5 : Modèle pytorch de déploiement C++ onnxruntime


Table des matières

 Répertoire des articles de la série

avant-propos

1. Comment déployer pytorch en C++ ?

Deux, configuration onnxruntime

1. Téléchargez onnxruntime

2. Configuration VS2019 sur nxruntime

2.1 Configurer le répertoire VC++

 2.2 Configurer l'éditeur de liens

 2.3 Configuration des variables d'environnement onnxruntime

3. Convertir le modèle pytorch en onnx

Quatrièmement, l'utilisation de onnxruntime en C++

Cinq, l'utilisation de onnxruntime en python

les références


avant-propos

Environnement : studio visuel 2019 ; OpenCV4.5.5 ; pytorch1.8 ; onnxruntime1.8.1 ;

1. Comment déployer pytorch en C++ ?

Le modèle pytorch est déployé en C++.Le chapitre précédent utilise la version correspondante de Libtorch de pytorch pour le déploiement. En fait, il peut être plus pratique de passer au déploiement onnx. La grande différence dans la précision de la segmentation sémantique avant est due au problème de prétraitement des données. Généralement, les images doivent être standardisées et converties en RVB avant d'être entrées dans le réseau. L'avantage du déploiement onnx est que le format intermédiaire Microsoft est compatible avec diverses plates-formes et est plus pratique. Si la plate-forme de déploiement dispose d'un GPU, il peut être préférable d'utiliser tensorRT.

Deux, configuration onnxruntime

Remarque : Faites attention à la version lorsque le modèle pt est transféré vers onnx, et la version onnxruntime correspond

1. Téléchargez onnxruntime

Site officiel : Galerie NuGet | Accueil

Le lien onnxruntime-win-x64-1.8.1 est le suivant :

https://objects.githubusercontent.com/github-production-release-asset-2e65be/156939672/7bc42680-deed-11eb-89d9-81963670045b?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A% 2F20220809%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20220809T093837Z&X-Amz-Expires=300&X-Amz-Signature=4c9cc7ec4ba93d83ccb3307533b47e5c40303c43c003b 2a791d4d321570dc5b8&X-Amz-SignedHeaders=host&actor_id=0&key_id=0&repo_id=156939672&response-content-disposition=attachment%3B% 20nomfichier%3Donnxruntime-win-x64-1.8.1.zip&response-content-type=application%2Foctet-streamhttps://objects.githubusercontent.com/github-production-release-asset-2e65be/156939672/7bc42680-deed-11eb-89d9-81963670045b?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A% 2F20220809%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20220809T093837Z&X-Amz-Expires=300&X-Amz-Signature=4c9cc7ec4ba93d83ccb3307533b47e5c40303c43c003b 2a791d4d321570dc5b8&X-Amz-SignedHeaders=host&actor_id=0&key_id=0&repo_id=156939672&response-content-disposition=attachment%3B% 20nomfichier%3Donnxruntime-win-x64-1.8.1.zip&response-content-type=application%2Foctet-stream

2. Configuration VS2019 sur nxruntime

2.1 Configurer le répertoire VC++

Configurez d'abord le répertoire d'inclusion et le répertoire de la bibliothèque, correspondant à la même méthode que opencv.

 2.2 Configurer l'éditeur de liens

Ajoutez toutes les bibliothèques en tant que dépendances, entrez le répertoire lib dans cmd, utilisez la commande dir /b *.lib>1.txt pour générer un répertoire et copiez-le pour l'utiliser.

 2.3 Configuration des variables d'environnement onnxruntime

Copiez toutes les DLL dans le répertoire Release ou Debug.

3. Convertir le modèle pytorch en onnx

Remarque : modèle d'entrée BCHW, mode d'inférence model.eval(), version de sortie opset_version=11

import torch

x = torch.randn(1, 3, 512, 512, device="cpu")
model = torch.load('best_model.pth', map_location=torch.device('cpu'))
model.eval()
input_names = ["input"]
output_names = ["output"]

torch.onnx.export(model, x, "GlandUnet.onnx", verbose=True, input_names=input_names, output_names=output_names, opset_version=11)

Quatrièmement, l'utilisation de onnxruntime en C++

Remarque : 1. Assurez-vous de faire attention au prétraitement avant que l'image ne soit entrée dans le modèle, qu'elle soit standardisée, qu'elle soit convertie en RVB

                  2. La sortie d'inférence de onnxruntime en C++ est la première adresse du tableau, qui peut être extraite par un pointeur pour générer Mat

/****************************************
@brief     :  分割onnxruntime
@input	   :  图像
@output    :  掩膜
*****************************************/
void SegmentAIONNX(Mat& imgSrc, int width, int height)
{
	模型信息/
	Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "OnnxModel");
	Ort::SessionOptions session_options;
	session_options.SetIntraOpNumThreads(1);
#ifdef _WIN32
	const wchar_t* model_path = L"GlandUnet.onnx";
#else
	const char* model_path = "RedUnet.onnx";
#endif
	Ort::Session session(env, model_path, session_options);
	Ort::AllocatorWithDefaultOptions allocator;
	size_t num_input_nodes = session.GetInputCount();	//batchsize
	size_t num_output_nodes = session.GetOutputCount();
	const char* input_name = session.GetInputName(0, allocator);
	const char* output_name = session.GetOutputName(0, allocator);
	auto input_dims = session.GetInputTypeInfo(0).GetTensorTypeAndShapeInfo().GetShape();	//输入输出维度
	auto output_dims = session.GetOutputTypeInfo(0).GetTensorTypeAndShapeInfo().GetShape();
	std::vector<const char*> input_names{ input_name };
	std::vector<const char*> output_names = { output_name };
	输入处理//
	Mat imgBGR = imgSrc;	//输入图片预处理
	Mat imgBGRresize;
	resize(imgBGR, imgBGRresize, Size(input_dims[3], input_dims[2]), InterpolationFlags::INTER_CUBIC);
	Mat imgRGBresize = imgBGRresize;
	//cvtColor(imgBGRresize, imgRGBresize, COLOR_BGR2RGB);	//smp未转RGB
	Mat resize_img;
	imgRGBresize.convertTo(resize_img, CV_32F, 1.0 / 255);  //divided by 255转float
	cv::Mat channels[3]; //分离通道进行HWC->CHW
	cv::split(resize_img, channels);
	std::vector<float> inputTensorValues;
	float mean[] = { 0.485f, 0.456f, 0.406f };	//
	float std_val[] = { 0.229f, 0.224f, 0.225f };
	for (int i = 0; i < resize_img.channels(); i++)	//标准化ImageNet
	{
		channels[i] -= mean[i];  // mean均值
		channels[i] /= std_val[i];   // std方差
	}
	for (int i = 0; i < resize_img.channels(); i++)  //HWC->CHW
	{
		std::vector<float> data = std::vector<float>(channels[i].reshape(1, resize_img.cols * resize_img.rows));
		inputTensorValues.insert(inputTensorValues.end(), data.begin(), data.end());
	}
	Ort::MemoryInfo memoryInfo = Ort::MemoryInfo::CreateCpu(OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault);
	vector<Ort::Value> inputTensors;
	inputTensors.push_back(Ort::Value::CreateTensor<float>(memoryInfo, inputTensorValues.data(), inputTensorValues.size(), input_dims.data(), input_dims.size()));
	//clock_t startTime, endTime;	//计算推理时间
	//startTime = clock();
	auto outputTensor = session.Run(Ort::RunOptions{ nullptr }, input_names.data(), inputTensors.data(), 1, output_names.data(), 1);   // 开始推理
	//endTime = clock();
	打印模型信息/
	//printf("Using Onnxruntime C++ API\n");
	//printf("Number of inputs = %zu\n", num_input_nodes);
	//printf("Number of output = %zu\n", num_output_nodes);
	//std::cout << "input_name:" << input_name << std::endl;
	//std::cout << "output_name: " << output_name << std::endl;
	//std::cout << "input_dims:" << input_dims[0] << input_dims[1] << input_dims[2] << input_dims[3] << std::endl;
	//std::cout << "output_dims:" << output_dims[0] << output_dims[1] << output_dims[2] << output_dims[3] << std::endl;
	//std::cout << "The run time is:" << (double)(endTime - startTime) / CLOCKS_PER_SEC << "s" << std::endl;
	
	//输出处理//
	float* mask_ptr = outputTensor[0].GetTensorMutableData<float>(); //outtensor首地址
	vector< unsigned char >results(512 * 512);
	for (int i = 0; i < 512 * 512; i++)
	{
		if (mask_ptr[i] >= 0.5)
		{
			results[i] = 0;
		}
		else
		{
			results[i] = 255;
		}
	}
	unsigned char* ptr = &results[0];
	Mat mask = Mat(output_dims[2], output_dims[3], CV_8U, ptr);
	resize(mask, imgSrc, Size(imgBGR.cols, imgBGR.rows));
	//原图展示分割结果//
	//cvtColor(imgSrc, imgSrc, COLOR_GRAY2BGR);
	//Mat imgAdd;
	//addWeighted(imgBGR, 1, imgSrc, 0.3, 0, imgAdd);
}

Cinq, l'utilisation de onnxruntime en python

# -*- coding:utf-8 -*-
import cv2
import numpy as np
import onnxruntime as ort
import imgviz
import time

class_names = ['_background_', 'conjunctiva_area']
### 定义一些数据前后处理的工具
def preprocess(input_data):
    # convert the input data into the float32 input
    img_data = input_data.astype('float32')
    # normalize
    mean_vec = np.array([0.485, 0.456, 0.406])
    stddev_vec = np.array([0.229, 0.224, 0.225])
    norm_img_data = np.zeros(img_data.shape).astype('float32')
    for i in range(img_data.shape[0]):
        norm_img_data[i, :, :] = (img_data[i, :, :] / 255 - mean_vec[i]) / stddev_vec[i]
    # add batch channel
    norm_img_data = norm_img_data.reshape(1, 3, 512, 512).astype('float32')
    return norm_img_data

def softmax(x):
    x = x.reshape(-1)
    e_x = np.exp(x - np.max(x))
    return e_x / e_x.sum(axis=0)

def postprocess(result):
    return softmax(np.array(result)).tolist()


session = ort.InferenceSession('GlandUnet.onnx')
img0 = cv2.imread('test.bmp')
h0, w0 = img0.shape[0:2]
img = cv2.cvtColor(img0, cv2.COLOR_BGR2RGB)
img = cv2.resize(img, [512, 512])
image_data = np.array(img).transpose(2, 0, 1) # HWC->CHW
input_data = preprocess(image_data)
time_start = time.time()  # 记录开始时间
raw_result = session.run([], {'input': input_data})
time_end = time.time()  # 记录结束时间
time_sum = time_end - time_start  # 计算的时间差为程序的执行时间,单位为秒/s
print(time_sum)
# label_result = np.argmax(raw_result, dim=1)  # 缺argmax
# out = np.squeeze(raw_result)
# result_img = np.array(out, dtype=np.uint8)
# result_img = cv2.resize(result_img, (w0, h0))

out1 = raw_result[0][0]

cv2.imshow('2', out1[1])
cv2.waitKey(0)

les références

https://onnxruntime.ai/

https://github.com/leimao/ONNX-Runtime-Inference/blob/main/src/inference.cpp

Déploiement C++ du modèle de segmentation sémantique du réseau de neurones (VS2019 + ONNXRuntime + OpenCV)_Shijunfeng00's Blog-CSDN Blog

Image opencv format HWC au format CHW - Programmeur recherché

Je suppose que tu aimes

Origine blog.csdn.net/weixin_42748604/article/details/127078298
conseillé
Classement