содержание
1.2 Сериальный экспорт модели pth в pt
1.4 Создание консольного проекта win32 C++
Во-вторых, полный код рассуждений
1. Установка среды
1.1 Введение в базовую среду
Экспериментальной средой в этой статье является Windows10, среда IDE выбирает VS 2019, и, наконец, разработана программа C++ win32, которая может выполнять пакетную обработку портретных фотографий без вмешательства пользователя. В этой статье основное внимание уделяется развертыванию модели pth, обученной Pytorch, которая может реализовывать вызовы C++ без платформы python, и завершать реальное развертывание на производственном уровне. Как обучить модель матирования портрета через Pytorch, можно обратиться к другим моимБлог Учебник 1,Блог Учебник 2.
1.2 Сериальный экспорт модели pth в pt
После обучения модели вы можете получить модель pth, а затем использовать следующий код Python для ее сериализации и экспорта (то есть преобразования динамического графа в статический граф):
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@文件 :pth2pt.py
@说明 :模型序列化导出,pth文件转pt文件
@时间 :2020/05/08 14:18:55
@作者 :钱彬
@版本 :1.0
'''
import torch.backends.cudnn as cudnn
import torch
from torch import nn
from model.shm import SHM
from utils import *
import time
import cv2
# 测试图像
img_id='16'
imgPath = './results/'+img_id+'.png'
# 模型参数
#device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
device = torch.device("cpu")
if __name__ == '__main__':
# 预训练模型
checkpoint = "./results/shm_temp.pth"
# 加载模型
checkpoint = torch.load(checkpoint)
model = SHM()
model = model.to(device)
model.load_state_dict(checkpoint['model'])
model.eval()
# 加载图像
img_org = cv2.imread(imgPath, cv2.IMREAD_COLOR)
width = img_org.shape[1]
height = img_org.shape[0]
# 图像预处理
img = cv2.resize(img_org, (320,320), interpolation = cv2.INTER_CUBIC)
img = (img.astype(np.float32) - (114., 121., 134.,)) / 255.0
h, w, c = img.shape
img = torch.from_numpy(img.transpose((2, 0, 1))).view(c, h, w).float()
img= img.view(1, 3, h, w)
# 转移数据至设备
img = img.to(device)
# 模型推理并序列化导出
with torch.no_grad():
alpha = model(img)
alpha = alpha.squeeze(0).float().mul(255).add_(0.5).clamp_(0, 255).permute(1, 2, 0).to('cpu', torch.uint8).numpy()
traced_script_module = torch.jit.trace(model, img)
traced_script_module.save("./results/matting.pt")
1.2 Скачать libtorch
существуетОфициальный сайтЗагрузите libtorch, Поскольку позже нам нужно будет использовать GPU для вывода, загрузите libtorch, соответствующий версии cuda. При загрузке обратите особое внимание на проблему согласованности версий, то есть какая версия cuda используется в конечной платформе вывода, тогда вам также необходимо скачать соответствующую версию cuda В этой статье загружается версия, соответствующая cuda10 .1. В частности, как показано на рисунке ниже:
1.3 Установите OpenCV
Из-за необходимости загрузки изображений и других операций здесь выбрана реализация фреймворка с открытым исходным кодом OpenCV. Для конкретного процесса установки, пожалуйста, обратитесь к моей другой статьеблог. В этой статье используется версия opencv4.2.
1.4 Создание консольного проекта win32 C++
В этой статье VS2019 используется для создания проекта C++ следующим образом:
Переключиться на 64-битную платформу после создания:
Затем настройте проект следующим образом:
(1) Конфигурация свойства — каталог VC++
Здесь я помещаю загруженный ранее libtorch в каталог проекта, и читатель может внести соответствующие изменения в соответствии с указанным выше путем, а затем объединить их с собственными силами дороги.
(2) Конфигурация свойства - каталог библиотеки
(3) Линкер — ввод
Добавьте файл lib следующим образом:
opencv_world420.lib
asmjit.lib
c10.lib
c10_cuda.lib
caffe2_detectron_ops_gpu.lib
caffe2_module_test_dynamic.lib
caffe2_nvrtc.lib
clog.lib
cpuinfo.lib
dnnl.lib
fbgemm.lib
gloo.lib
gloo_cuda.lib
libprotobuf.lib
libprotobuf-lite.lib
libprotoc.lib
mkldnn.lib
torch.lib
torch_cpu.lib
torch_cuda.lib
Здесь я включил все файлы lib в libtorch.В реальном использовании нам не нужно так много.Здесь, во избежание проблем, мы можем сначала включить их все, а затем удалять их постепенно после настройки более поздних программ . Затем скопируйте все файлы dll из libtorch в корневой каталог проекта. Как показано ниже:
И последнее, но не менее важное: в старшей версии VS есть ошибка, нам нужно вручную что-то добавить в наш код, чтобы нормально вызывать GPU. Найдите Linker — Command Line — Other Options и введите: /INCLUDE:?warp_size@cuda@at@@YAHXZ
В частности, как показано на рисунке ниже:
Если вышеуказанные операции не будут реализованы, наш код не сможет позже вызвать GPU.
Во-вторых, полный код рассуждений
После завершения предыдущей настройки вы можете отредактировать код.Полный код выглядит следующим образом, в основном для достижения сквозного матирования:
/*********************************************************************************************************************************
* Copyright(c) 2020-2025
* All rights reserved.
*
* 文件名称:PortraitOpt
* 简要描述:基于C++实现证件人像抠图(GPU和CPU通用)
*
* 创建日期:2020-10-09
* 作者:钱彬
* 版本:V1.0.0
*********************************************************************************************************************************/
//导入深度学习torch库
#undef UNICODE
#include <torch/script.h> // 引入libtorch头文件
#include <torch/torch.h> // cuda相关函数头文件
//导入系统库
#include <iostream>
#include "time.h"
//导入opencv图像处理库
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgproc/types_c.h>
#include <opencv2/videoio/videoio.hpp>
//定义命名空间
using namespace std;
using namespace cv;
int main()
{
//确定当前设备类型(CPU或GPU)
torch::DeviceType device_type = at::kCPU; // 定义设备类型
if (torch::cuda::is_available())
device_type = at::kCUDA;
// 加载抠图模型
torch::jit::script::Module model;
try {
model = torch::jit::load("matting.pt"); //加载模型
}
catch (const c10::Error& e) {
cout << "无法加载模型" << endl;
return 0;
}
model.eval();
model.to(device_type);
//查找文件夹中所有jpg格式图片
cv::String pattern = "imgs/*.jpg";
vector<cv::String> fn;
glob(pattern, fn, false);
vector<Mat> images;
int imgNum = fn.size();
std::cout << "当前需要处理的图像总数: " << imgNum << endl;
//----------------------------------------------循环处理图片-------------------------------------------------
for (int picIndex = 0; picIndex < imgNum; picIndex++)
{
//读取图像
Mat img = imread(fn[picIndex]);
clock_t start, finish;
double duration;
char imgpath[256];
start = clock();
Mat orgimg = img.clone();
// 压缩图像
int org_width = img.cols;
int org_height = img.rows;
resize(img, img, Size(320, 320), 0, 0, INTER_CUBIC);
//数据预处理
std::vector<int64_t> sizes = { 1, img.rows, img.cols,3 };
at::TensorOptions options(at::ScalarType::Byte);
at::Tensor tensor_image = torch::from_blob(img.data, at::IntList(sizes), options);//将opencv的图像数据转为Tensor张量数据
tensor_image = tensor_image.toType(at::kFloat);//转为浮点型张量数据
tensor_image = tensor_image.permute({ 0, 3, 1, 2 });
tensor_image[0][0] = tensor_image[0][0].sub(114).div(255.0);
tensor_image[0][1] = tensor_image[0][1].sub(121).div(255.0);
tensor_image[0][2] = tensor_image[0][2].sub(134).div(255.0);
tensor_image = tensor_image.to(device_type);
// 推理
std::vector<torch::jit::IValue> inputs;
inputs.push_back(tensor_image);
at::Tensor out_tensor = model.forward(inputs).toTensor();
out_tensor = out_tensor.squeeze().detach();
out_tensor = out_tensor.mul(255).add_(0.5).clamp_(0, 255).to(torch::kU8);
out_tensor = out_tensor.to(torch::kCPU);
cv::Mat alphaImg(img.rows, img.cols, CV_8UC1);
std::memcpy((void*)alphaImg.data, out_tensor.data_ptr(), sizeof(torch::kU8) * out_tensor.numel());
// 调整抠图结果大小
resize(alphaImg, alphaImg, Size(org_width, org_height), 0, 0, INTER_CUBIC);
// 与白背景合成
Mat bg(orgimg.size(), orgimg.type(), Scalar(255, 255, 255));//全白图
Mat alpha, comp;
cvtColor(alphaImg, alpha, COLOR_GRAY2BGR);
comp = orgimg.clone();
for (int i = 0; i < alpha.rows; i++)
for (int j = 0; j < alpha.cols; j++)
{
Vec3b alpha_p = alpha.at<Vec3b>(i, j);
Vec3b bg_p = bg.at<Vec3b>(i, j);
Vec3b img_p = orgimg.at<Vec3b>(i, j);
if (alpha_p[0] > 210)
{
alpha_p[0] = 255;
alpha_p[1] = 255;
alpha_p[2] = 255;
}
else
{
alpha_p[0] = 0;
alpha_p[1] = 0;
alpha_p[2] = 0;
}
comp.at<Vec3b>(i, j)[0] = int(img_p[0] * (alpha_p[0] / 255.0) + bg_p[0] * (1.0 - alpha_p[0] / 255.0));
comp.at<Vec3b>(i, j)[1] = int(img_p[1] * (alpha_p[1] / 255.0) + bg_p[1] * (1.0 - alpha_p[1] / 255.0));
comp.at<Vec3b>(i, j)[2] = int(img_p[2] * (alpha_p[2] / 255.0) + bg_p[2] * (1.0 - alpha_p[2] / 255.0));
}
finish = clock();
duration = (double)(finish - start) / CLOCKS_PER_SEC;
printf("抠图推理耗时 %f 毫秒\n", duration * 1000);
//保存抠图结果
sprintf_s(imgpath, "results/%d_matting.jpg", picIndex);
imwrite(imgpath, comp);
//手动释放内存资源
inputs.clear();
img.release();
orgimg.release();
alphaImg.release();
alpha.release();
comp.release();
}
return 0;
}
3. Тест
Результаты теста следующие:
Скорость первых двух изображений будет относительно невысокой, предполагается, что может выполняться некоторое планирование графического процессора, а скорость вывода каждого последующего изображения нормальная. Эффект вырезания следующий:
Исходное изображение:
Результат вырезания:
\