目录

一、下载PaddleSeg

二、下载模型

三、模型导出

1、动态图模型转静态图

2、静态图转onnx

四、C++部署

1、环境

2、onnx模型查看

3、C++代码

参考:


本文将PaddleSeg的人像分割(PP-HumanSeg)模型导出为onnx,并使用C++部署到windows,实现人像分割,效果如下图所示。

一、下载PaddleSeg

git clone https://github.com/PaddlePaddle/PaddleSeg.git

二、下载模型

进入PP-HumanSeg目录,使用pip安装paddleseg库,并使用PaddleSeg提供的脚本下载预训练模型,该模型将用于后续导出为onnx。

%cd ~/PaddleSeg/contrib/PP-HumanSeg
!pip install paddleseg
!python pretrained_model/download_pretrained_model.py

三、模型导出

上一步下载的模型是paddlepaddle动态图模型,为了将其转换为onnx,先将paddlepaddle动态图模型转换为静态图模型,再利用paddle2onnx工具将其转为onnx模型(附录提供转换好的模型,如使用该模型,请直接跳到四、C++部署)。

1、动态图模型转静态图

这里提供超轻量级模型PP-HumanSeg-Lite转换示例,该模型适用于移动端实时分割场景,例如手机自拍、Web视频会议,模型输入大小(192, 192)。为了防止后续onnx模型使用报错,需要加上--input_shape参数。(命令中的文件路径如有需要请更换成自己的路径)

%cd ~/PaddleSeg/contrib/PP-HumanSeg
!python ../../export.py \
--config configs/fcn_hrnetw18_small_v1_humanseg_192x192_mini_supervisely.yml \
--model_path pretrained_model/fcn_hrnetw18_small_v1_humanseg_192x192/model.pdparams \
--save_dir export_model/fcn_hrnetw18_small_v1_humanseg_192x192 \--with_softmax  --input_shape 1 3 192 192

2、静态图转onnx

得到paddlepaddle动态图模型后,使用paddle2onnx工具将静态图模型转换为onnx模型,首先安装paddle2onnx库。

!pip install paddle2onnx 

使用paddle2onnx工具,将上一步得到的paddlepaddle静态图的模型转换为onnx。

%cd ~/PaddleSeg/contrib/PP-HumanSeg
! paddle2onnx --model_dir ./export_model/fcn_hrnetw18_small_v1_humanseg_192x192/ \--model_filename model.pdmodel \--params_filename model.pdiparams \--save_file onnx_model/model.onnx \--opset_version 12

四、C++部署

1、环境

windows 10x64 cpu

onnxruntime-win-x64-1.10.0

opencv 4.5.5

visual studio 2019

2、onnx模型查看

使用Netron 查看模型结构,如下图所示。

查看模型相关属性,这里需要注意模型的输出类型是int64,如下图。

3、C++代码

目录结构如下图所示,包含HumanSeg.h/HumanSeg.cpp/main.cpp共3个文件。

HumanSeg.h内容如下:

#pragma once
#include <string>
#include <onnxruntime_cxx_api.h>
#include <iostream>
#include <vector>
#include <opencv2/opencv.hpp>
#include <time.h>class HumanSeg
{
protected:Ort::Env env_;Ort::SessionOptions session_options_;Ort::Session session_{nullptr};Ort::RunOptions run_options_{nullptr};std::vector<Ort::Value> input_tensors_;std::vector<const char*> input_node_names_;std::vector<int64_t> input_node_dims_;size_t input_tensor_size_{ 1 };std::vector<const char*> out_node_names_;size_t out_tensor_size_{ 1 };int image_h;int image_w;cv::Mat normalize(cv::Mat& image);cv::Mat preprocess(cv::Mat image);public:HumanSeg() =delete;HumanSeg(std::wstring model_path, int num_threads, std::vector<int64_t> input_node_dims);cv::Mat predict_image(cv::Mat& src);void predict_image(const std::string& src_path, const std::string& dst_path);void predict_camera();};

HumanSeg.cpp内容如下:

#include "HumanSeg.h"HumanSeg::HumanSeg(std::wstring model_path, int num_threads = 1, std::vector<int64_t> input_node_dims = { 1, 3, 192, 192 }) {input_node_dims_ = input_node_dims;for (int64_t i : input_node_dims_) {input_tensor_size_ *= i;out_tensor_size_ *= i;}//std::cout << input_tensor_size_ << std::endl;session_options_.SetIntraOpNumThreads(num_threads);session_options_.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED);try {session_ = Ort::Session(env_, model_path.c_str(), session_options_);}catch (...) {}Ort::AllocatorWithDefaultOptions allocator;//获取输入nameconst char* input_name = session_.GetInputName(0, allocator);input_node_names_ = { input_name };//std::cout << "input name:" << input_name << std::endl;const char* output_name = session_.GetOutputName(0, allocator);out_node_names_ = { output_name };//std::cout << "output name:" << output_name << std::endl;
}cv::Mat HumanSeg::normalize(cv::Mat& image) {std::vector<cv::Mat> channels, normalized_image;cv::split(image, channels);cv::Mat r, g, b;b = channels.at(0);g = channels.at(1);r = channels.at(2);b = (b / 255. - 0.5) / 0.5;g = (g / 255. - 0.5) / 0.5;r = (r / 255. - 0.5) / 0.5;normalized_image.push_back(r);normalized_image.push_back(g);normalized_image.push_back(b);cv::Mat out = cv::Mat(image.rows, image.cols, CV_32F);cv::merge(normalized_image, out);return out;
}/*
* preprocess: resize -> normalize
*/
cv::Mat HumanSeg::preprocess(cv::Mat image) {image_h = image.rows;image_w = image.cols;cv::Mat dst, dst_float, normalized_image;cv::resize(image, dst, cv::Size(int(input_node_dims_[3]), int(input_node_dims_[2])), 0, 0);dst.convertTo(dst_float, CV_32F);normalized_image = normalize(dst_float);return normalized_image;
}/*
* postprocess: preprocessed image -> infer -> postprocess
*/
cv::Mat HumanSeg::predict_image(cv::Mat& src) {cv::Mat preprocessed_image = preprocess(src);cv::Mat blob = cv::dnn::blobFromImage(preprocessed_image, 1, cv::Size(int(input_node_dims_[3]), int(input_node_dims_[2])), cv::Scalar(0, 0, 0), false, true);//std::cout << "load image success." << std::endl;// create input tensorauto memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);input_tensors_.emplace_back(Ort::Value::CreateTensor<float>(memory_info, blob.ptr<float>(), blob.total(), input_node_dims_.data(), input_node_dims_.size()));std::vector<Ort::Value> output_tensors_ = session_.Run(Ort::RunOptions{ nullptr },input_node_names_.data(),input_tensors_.data(),input_node_names_.size(),out_node_names_.data(),out_node_names_.size());int64* floatarr = output_tensors_[0].GetTensorMutableData<int64>();// decoder cv::Mat mask = cv::Mat::zeros(static_cast<int>(input_node_dims_[2]), static_cast<int>(input_node_dims_[3]), CV_8UC1);for (int i{ 0 }; i < static_cast<int>(input_node_dims_[2]); i++) {for (int j{ 0 }; j < static_cast<int>(input_node_dims_[3]); ++j) {mask.at<uchar>(i, j) = static_cast<uchar>(floatarr[i * static_cast<int>(input_node_dims_[3]) + j]);}}cv::resize(mask, mask, cv::Size(image_w, image_h), 0, 0);input_tensors_.clear();return mask;
}void HumanSeg::predict_image(const std::string& src_path, const std::string& dst_path) {cv::Mat image = cv::imread(src_path);cv::Mat mask = predict_image(image);cv::Mat predict_image;cv::bitwise_and(image, image, predict_image, mask = mask);cv::imwrite(dst_path, predict_image);//std::cout << "predict image over" << std::endl;}void HumanSeg::predict_camera() {cv::Mat frame;cv::VideoCapture cap;int deviceID{ 0 };int apiID{ cv::CAP_ANY };cap.open(deviceID, apiID);if (!cap.isOpened()) {std::cout << "Error, cannot open camera!" << std::endl;return;}//--- GRAB AND WRITE LOOPstd::cout << "Start grabbing" << std::endl << "Press any key to terminate" << std::endl;int count{0};clock_t start{clock()}, end;double fps{ 0 };for (;;){// wait for a new frame from camera and store it into 'frame'cap.read(frame);// check if we succeededif (frame.empty()) {std::cout << "ERROR! blank frame grabbed" << std::endl;break;}cv::Mat mask = predict_image(frame);cv::Mat segFrame;cv::bitwise_and(frame, frame, segFrame, mask = mask);// fpsend = clock();++count;fps = count / (float(end - start) / CLOCKS_PER_SEC);if (count >= 100) {count = 0;start = clock();}std::cout << fps << "  " << count << "   " << end - start << std::endl;//设置绘制文本的相关参数std::string text{ std::to_string(fps) };int font_face = cv::FONT_HERSHEY_COMPLEX;double font_scale = 1;int thickness = 2;int baseline;cv::Size text_size = cv::getTextSize(text, font_face, font_scale, thickness, &baseline);//将文本框居中绘制cv::Point origin;origin.x = 20;origin.y = 20;cv::putText(segFrame, text, origin, font_face, font_scale, cv::Scalar(0, 255, 255), thickness, 8, 0);// show live and wait for a key with timeout long enough to show imagesimshow("Live", segFrame);if (cv::waitKey(5) >= 0)break;}return;
}

main.cpp内容如下(model_path路径为上面步骤中导出的onnx路径):

#include <vector>
#include <iostream>
#include <opencv2/opencv.hpp>
#include "HumanSeg.h"
#include <string>int main()
{std::wstring model_path(L"D:\\C_code\\humanseg\\x64\\Debug\\onnx_model\\model.onnx");std::cout << "infer...." << std::endl;HumanSeg human_seg(model_path, 1, { 1, 3, 192, 192 });human_seg.predict_image("C:\\Users\\langdu\\Pictures\\test1.jpeg", "C:\\Users\\langdu\\Pictures\\p1.png");human_seg.predict_camera(); //使用摄像头return 0;
}

五、效果

下方为测试图片及分割效果:

参考:

已经转换好的onnx模型,和上面的代码配套

PP-HuanSeg python部署到树莓派

PaddleSeg

onnx模型导出Aistudio教程

人像分割PP-HumanSeg模型onnx C++ windows部署相关推荐

  1. PaddleHub人像分割模型:AI人像抠图及图像合成

    点击上方"AI搞事情"关注我们 本项目根据DeepLabv3+模型一键抠图示例,主要采用PaddleHub DeepLabv3+模型(deeplabv3p_xception65_h ...

  2. AI深度学习入门与实战19 语义分割:打造简单高效的人像分割模型

    上一讲,我向你介绍了语义分割的原理.在理解上一课时中 U-Net 语义分割网络的基础上,这一讲,让我们来实际构建一个人像分割模型吧. 语义分割的评估 我们先简单回顾一下语义分割的目的:把一张图中的每一 ...

  3. 荟聚NeurIPS顶会模型、智能标注10倍速神器、人像分割SOTA方案、3D医疗影像分割利器,PaddleSeg重磅升级!

    导读 图像分割是计算机视觉三大任务之一,基于深度学习的图像分割技术也发挥日益重要的作用,广泛应用于智慧医疗.工业质检.自动驾驶.遥感.智能办公等行业. 然而在实际业务中,图像分割依旧面临诸多挑战,比如 ...

  4. python人像精细分割_PaddleHub人像分割模型:AI人像抠图及图像合成

    本项目根据DeepLabv3+模型一键抠图示例,主要采用PaddleHub DeepLabv3+模型(deeplabv3p_xception65_humanseg)和python图像处理库opencv ...

  5. 基于百度Paddlepaddle深度学习框架应用于Windows端Pycharm在本地实现视频的人像分割和美颜教程

    根据本教程实现的效果如下: 前言 第一步:Windows下的环境的配置 第二步: 实现视频美颜和人像分割在本地IDE Pycharm上运行 第三步:将两个项目进行整合,实现input--operati ...

  6. 基于图像分割网络HRNet实现人像分割

    基于图像分割网络HRNet实现人像分割 人像分割是图像分割领域非常常见的应用,PaddleSeg推出了在大规模人像数据上训练的人像分割PPSeg模型,满足在服务端.移动端.Web端多种使用场景的需求. ...

  7. PaddleSeg快速开始之 人像分割

    PaddleSeg快速开始: 通过本实例可以学习使用预训练好的图像分割模型进行预测(以常见的人像分割任务为例) 人像分割: 人像分割任务旨在识别图像中的人体轮廓,与背景进行分离,返回分割后的二值图.灰 ...

  8. 视频人像分割算法—C++推理(视频抠图 图片抠图)

    本文章记录对RobustVideoMatting模型进行C++推理的过程. 文章目录 相关参考 一.基于lite.ai.toolkit的RVM推理编译 1.源码编译 二.推理功能改进与完善 1.修改背 ...

  9. 【Matting】MODNet:实时人像抠图模型-NCNN C++量化部署

    相关链接: [Matting]MODNet:实时人像抠图模型-onnx python部署 [Matting]MODNet:实时人像抠图模型-笔记 [Matting]MODNet:实时人像抠图模型-on ...

最新文章

  1. AIX如何查看文件系统分布在哪个物理磁盘上
  2. golang字符型及使用细节
  3. mongodb2.2.1安装
  4. Kubernetes学习之路(四)之Node节点二进制部署
  5. C/C++面试题—合并两个排序的链表【递归和循环两种方式】
  6. ECCV 2020 论文大盘点 - OCR 篇
  7. php获取localstorage的值,localStorage的设置和取值Demo
  8. java之Calendar类
  9. mybait-plus实现动态自定义查询条件
  10. 【小项目】STM32环境监测 | MQ2可燃气体传感器+雨滴传感器+DHT11温湿度传感器+OLED屏幕
  11. 【源码】垂直偶极子天线的矩量法
  12. 阿里云认证有什么用?考哪个比较好?
  13. 技术系统进化法则包括_八大技术系统进化法则主要包括哪些
  14. 一文看懂量子十问(上篇)
  15. 波士顿大学计算机硕士排名,波士顿大学计算机工程硕士排名第28(2020年TFE Times排名)...
  16. .Net微服务架构:API网关
  17. 【云扩RPA】Table
  18. 高通平台开发系列讲解(系统篇)系统关机流程
  19. 《拳皇15》先导预告 多位角色回归、细节下月公开
  20. XSS Challenges stage#1-10闯关详解

热门文章

  1. 微信红包php算法,基于PHP微信红包的算法探讨
  2. 独上高楼之清华艺术博物馆
  3. 可道云+Apache+windows开启WebDAV 过程
  4. Android GB∕T 19056-2021 汽车行驶记录仪-定位性能测试
  5. 杰理之音箱方案喇叭输出底噪大【篇】
  6. 让AI 作画更快一点
  7. Abaqus子程序HETVAL模拟混凝土水化热温度场
  8. OkHttp3错误异常: java.net.ProtocolException: unexpected end of stream 源码分析
  9. 区块链基础知识25讲
  10. 三观 - 三观 - 三观