Tensorflow嵌入式部署、联调、C++API深度学习前向推理

本文介绍如何使用C++调用Python脚本实现混合编程,并详细讲解了在嵌入式环境下利用TensorFlow C++ API进行目标检测模型的部署流程。

前言

_____这篇博客可能有点长,涉及到的内容也比较多,只想了解其中一部分的朋友可以直接跳转,之所以写第一部分,是因为在使用C++对应的tensorflow-API之前,听说可以使用混合编程以实现tensorflow框架模型嵌入式部署,但是经过试验,每次将变量恢复到图中时,都会异常跳出,不管是pb文件还是ckpt文件,都会遇到这个问题,但是我觉得这种混合编程的方式可能适合某些场景(因为python脚本中如果不含tensorflow代码时,是可以实现混合编程输出结果的),所以分享出来,希望能帮助大家。

一、C++与python的混合编程

本次使用Visual Studio2013版本,python3.6.5版本,win7和win10都是64位操作系统。

1、需要调用的python脚本

本次C++调用python脚本主要展示在脚本的主程序包含另一个函数,且该函数在自定义包里,而自定义包又含有python的第三方库,待执行脚本名称为Custom_package.py,代码如下:

import function_library as fl

def test_array(a,b):

    print("进入python主程序,开始执行相应操作!")
    print("接受到的实参a={},b={}".format(a,b))
    print("="*30)
    
    return fl.funx2(a,b)+b

自定义包在待执行脚本同一级文件夹里,名称为function_library.py,代码如下:

import numpy as np

def funx2(a,b):
    print("进入python子函数程序,开始执行!")
    initial_data = np.random.randint(1,9,size=(2,3))
    print("生成的numpy数组是:")
    print(initial_data)
    result_data = initial_data * a + b
    print("initial * a + b 的结果是:")
    print(result_data)
    print("执行numpy操作完成,返回结果!")

    return a*2

2、环境准备

新建文件夹,命名为C++_python,将python环境中的include文件夹和libs文件夹复制到该文件夹下(就是配置pycharm时,选择python.exe文件所在的同一层文件夹里有这两个文件夹),将libs文件夹里的python36.lib文件拷贝一份,并重命名为python36_d.lib;打开Visual Studio:
文件>新建>项目>Visual C++>Win32控制台应用程序>名称(改为:cpythonimport)>位置(选择刚才新建的文件夹C++_python)>确定>完成
在这里插入图片描述
设置环境:
项目>cpythonimport属性>配置管理器(右上角)>活动解决方案配置(修改成Release)>活动解决方案平台(修改成X64,没有就点击新建)>关闭
在这里插入图片描述
项目>cpythonimport属性>配置属性>VC++ 目录>包含目录(修改成相对路径,选择C++_python文件夹下的include文件夹)>应用目录(修改成相对路径,选择C++_python文件夹下的include文件夹)>库目录(修改成相对路径,选择C++_python文件夹下的libs文件夹)
在这里插入图片描述
配置属性>链接器>输入>附加依赖项(将python36_d.lib添加到该项里)
在这里插入图片描述
最后点击 应用> 确定 完成配置,复制混合编程中所示代码,编译,将Custom_package.py和function_library.py文件放到C++_python> cpythonimport>x64>Release文件夹下即可

3、混合编程

调用python脚本的C++代码如下:

// cpythonimport.cpp : 定义控制台应用程序的入口点。
//如果不使用预编译头,在 项目>属性>配置属性>C/C++>预编译头>预编译头>不使用预编译头 即可,这个设置在该用例中无碍。
#include "stdafx.h"
#include <Python.h>

//把char *c转 wchar_t * ,因Py_SetPythonHome()函数的形参为wchar_t 类型,也可以直接使用L进行转换
//wchar_t *GetWCHAR(const char *c)
//{
   
   
//	const size_t cSize = strlen(c) + 1;
//	wchar_t* WCHAR = new wchar_t[cSize];
//	mbstowcs(WCHAR, c, cSize);
//	return WCHAR;
//}

int main()
{
   
   
	/*Py_SetPythonHome()设置python脚本运行的依赖环境,防止python脚本调用第三方包时出错;如果不设置,在不调用第三方包时,不会报错;
	一旦调用第三方包,则会出现异常,该路径为python的安装路径(有python.exe的那个文件夹的路径,且需使用绝对路径);
	即相应包都下载在该路径下。*/
	Py_SetPythonHome(L"D:\\ArtificialIntelligence\\Anaconda");
	Py_Initialize();//初始化相应python的依赖环境

	//设定参数值
	int a = 2;
	int b = 3;
	PyObject *pModule;//定义PyObject对象
	PyObject *pFunction;
	PyObject *pArgs;
	PyObject *pRetValue;
	//导入python脚本(自定义的python待执行脚本的文件名)
	pModule = PyImport_ImportModule("Custom_package");

	if (!pModule) {
   
   
		printf("import python failed!!\n");
		return -1;
	}
	//获取对应Custom_package.py中的def test_array(a,b)主程序(需要执行的函数)
	pFunction = PyObject_GetAttrString(pModule, "test_array");

	if (!pFunction) {
   
   
		printf("get python function failed!!!\n");
		return -1;
	}
	//申明python中的tuple对象
	pArgs = PyTuple_New(2);
	//将a,b放到元祖中,索引为0的值为a,索引为1的值为b
	PyTuple_SetItem(pArgs, 0, Py_BuildValue("i", a));
	PyTuple_SetItem(pArgs, 1, Py_BuildValue("i", b));
	//调用获取的函数并传参
	pRetValue = PyObject_CallObject(pFunction, pArgs);
	//python3中只有long,PyLong_AsLong(python2中是PyInt_AsLong),注意返回值的类型,接受的函数类型和返回的数据类型需要一致
	printf("==============================\n");
	printf("python脚本调用完毕,输出返回结果!\n");
	printf("%d + %d = %ld\n", a, b, PyLong_AsLong(pRetValue));
	/*CPython垃圾收集器使用“引用计数”,即它维护对象的引用列表。如果对象的引用计数降至零,则表示垃圾收集器释放该对象的空间是安全的。
	因此,当定义PyObjects类时,必须显式调用Py_INCREF和Py_DECREF,分别增加和减少对象的引用计数。*/
	Py_DECREF(pModule);//减少对象的引用计数
	Py_DECREF(pFunction);
	Py_DECREF(pArgs);
	Py_DECREF(pRetValue);

	if (!pModule) {
   
   
		printf("import python failed!!\n");
		return -1;
	}
	//如果是多次导入PyImport_ImportModule模块
	//只有最后一步才调用Py_Finalize(),此时python全局变量一直保存着
	Py_Finalize();
	system("pause");
	return 0;
}

4、结果展示

运行结果如下(如果运行显示无法访问.....exe,关闭360,或者添加信任即可):
在这里插入图片描述

二、嵌入式部署(目标检测)

1、模型准备

①、模型网络(目标检测)

模型为YOLOv3改得,但是输入和输出名称没有变,所以仍然可以用yolov3的模型。

②、将ckpt文件转成pb文件

将ckpt文件转换成pb文件需要提前知道网络的input_name和output_name,和自己骨干网络有关。 Tensor("input/input_data:0", shape=(?, 256, 256, 3), dtype=float32) Tensor("pred_sbbox/concat_2:0", shape=(?, ?, ?, 3, 8), dtype=float32) Tensor("pred_mbbox/concat_2:0", shape=(?, ?, ?, 3, 8), dtype=float32) Tensor("pred_lbbox/concat_2:0", shape=(?, ?, ?, 3, 8), dtype=float32) 具体代码如下:

import tensorflow as tf
from core.yolov3 import YOLOV3
import os

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
os.environ["CUDA_VISIBLE_DEVICES"] = '-1'

ckpt_file = "./checkpoint/Abrasions_test_loss=9.5520train_loss=3.4781.ckpt-1900"#自己的ckpt文件
pb_file = "./voc.pb"#要保存的pb文件名称和路径
output_node_names = ["input/input_data", "pred_sbbox/concat_2", "pred_mbbox/concat_2", "pred_lbbox/concat_2"]#模型的输入和输出的名称

with tf.name_scope('input'):
    input_data = tf.placeholder(dtype=tf.float32, shape=[None, 256, 256, 3], name='input_data')#定义输入
model = YOLOV3(input_data, trainable=False)#通过实例化代码构建模型图,训练时也是需要实例化改骨干网络
print(input_data)#打印输入的tensor信息
print(model.pred_sbbox, model.pred_mbbox, model.pred_lbbox)#打印输出的tensor信息

sess = tf.Session(config=tf.ConfigProto(allow_soft_placement=True))#构建会话
saver = tf.train.Saver()
saver.restore(sess, ckpt_file)#构建恢复模型

converted_graph_def = tf.graph_util.convert_variables_to_constants(sess,
                                                                   input_graph_def=sess.graph.as_graph_def(),
                                                                   output_node_names=output_node_names)#会话和output_node_names传入到转换函数里面去(函数的意思是将变量转换成常量,pb文件中的值是常量,已经不再是变量)

with tf.gfile.GFile(pb_file, "wb") as f:#将得到的converted_graph_def(得到的图)写入到指定路径
    f.write(converted_graph_def.SerializeToString())#将图写入磁盘,并序列化成字符串
    print('成功输出模型')

2、CPU环境

本次使用的各平台版本为:Visual Studio2015、python3.6.5,tensorflow为1.8.0(编译的指令集为sse,基于cmake编译,对应VS为2015)win7和win10都是64位操作系统,具体编译操作先略过。

①、环境配置

1、 准备好tensorflow的c++环境文件,看cpu指令集是sse还是avx2的,不知道就用sse指令集
2、打开vs2015新建工程:
在这里插入图片描述
3、将tensorflow的C++环境包解压,将解压后将该文件夹下的include和lib文件夹复制到Tread_detection文件夹的同一层:
在这里插入图片描述
4、将opencv的lib文件复制到新建的lib文件夹下,lib文件夹下的lib压缩包有如下:
在这里插入图片描述
5、将opencv的头文件拷贝到include文件夹下:
在这里插入图片描述
6、将对应的opencv的.dll和tensorflow的.dll文件放到程序执行的环境中,这里是如下路径:如果没有x

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值