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

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

被折叠的 条评论
为什么被折叠?



