说到深度学习一般都是在python环境上实施的。几大深度学习框架中Keras代码简洁性,十分利于上手。而可惜的是Keras本身没有C++接口,而一般的生产环境都需要C++环境支持。所以能够在C++上部属深度学习模型至关重要。
所需环境:
1.visual studio 2015(必须2015以上,2010不可以!)
2.tensorflow C++接口(lib文件和include)
3.opencv
4.jsoncpp(配置方法参考:https://blog.csdn.net/z8110/article/details/81152910)
步骤1:tensorflow的visual studio上的配置
tensorflow C++接口的编译和配置的坑有好多,我尝试了多次也没有成功,有兴趣的同学可以自己尝试编译。
我是最终直接下载了别人配置好的项目,他的包里已经包含了tensorflow C++环境所需要的lib和include文件。
(链接:https://pan.baidu.com/s/1fwP7-D1-Usz1Ec5cY-RaYg 密码:0hlb)
步骤2: Keras训练模型转成tensorflow用的pb模型:直接上代码,最终生成了1个pb模型文件
from keras.models import load_model
import tensorflow as tf
from keras import backend as K
from tensorflow.python.framework import graph_io
from models.triplet_model import TripletModel
def freeze_session(session, keep_var_names=None, output_names=None, clear_devices=True):
'''
冻结模型图
'''
from tensorflow.python.framework.graph_util import convert_variables_to_constants
graph = session.graph
with graph.as_default():
freeze_var_names = list(set(v.op.name for v in tf.global_variables()).difference(keep_var_names or []))
output_names = output_names or []
output_names += [v.op.name for v in tf.global_variables()]
input_graph_def = graph.as_graph_def()
if clear_devices:
for node in input_graph_def.node:
node.device = ""
frozen_graph = convert_variables_to_constants(session, input_graph_def,
output_names, freeze_var_names)
return frozen_graph
"""----------------------------------配置路径-----------------------------------"""
h5_model_path='./experiments/triplet_mnist/checkpoints/triplet_loss_model.h5'#Keras训练模型
output_path='./experiments/triplet_mnist/checkpoints/'#转换后pb模型的地址
pb_model_name='triplet_loss_model.pb'#转换后pb模型的文件名
"""----------------------------------导入keras模型------------------------------"""
K.set_learning_phase(0)
net_model = load_model(h5_model_path,custom_objects={'triplet_loss': TripletModel.triplet_loss})
#print('input is :', net_model.input.name)
#print ('output is:', net_model.output.name)
"""----------------------------------保存为.pb格式------------------------------"""
sess = K.get_session()
frozen_graph = freeze_session(K.get_session(), output_names=[net_model.output.op.name])
graph_io.write_graph(frozen_graph, output_path, pb_model_name, as_text=False)
步骤三:python环境下用tensorflow验证导出的pb文件是否正常:
# coding: utf-8
import tensorflow as tf
import numpy as np
import cv2
#创建图
with tf.Graph().as_default():
output_graph_def=tf.GraphDef()
#导入pb模型
pb_file_path='./experiments/triplet_mnist/checkpoints/triplet_loss_model.pb'
with open(pb_file_path,"rb") as f:
output_graph_def.ParseFromString (f.read())
tensors=tf.import_graph_def(output_graph_def,name="")
print ("tensors:",tensors)
#把测试图片传入模型中通过session计算结果(我的这个模型需要3张图片,每张图片生成1个128位的向量)
jpg_path="image_data/221M50A03410000Q-RS_2700_1550.jpg"
with tf.Session() as sess:
init=tf.global_variables_initializer()
sess.run(init)
op=sess.graph.get_operations()
#for i, m in enumerate(op): #查看模型层次结构
# print('op{}:'.format(i),m.values())
input_anc=sess.graph.get_tensor_by_name("anc_input:0")#名称要和模型里名称一致
input_pos=sess.graph.get_tensor_by_name("pos_input:0")#名称要和模型里名称一致
input_neg=sess.graph.get_tensor_by_name("neg_input:0")#名称要和模型里名称一致
outputVec=sess.graph.get_tensor_by_name('model_1_1/dense_4/Relu:0')#名称要和模型里名称一致
img=cv2.imread(jpg_path,0)
img=img.astype(np.float32)
feed_dict={input_anc:np.reshape(img,(1,140,140,1)),
input_pos:np.reshape(img,(1,140,140,1)),
input_neg:np.reshape(img,(1,140,140,1))
}
img_out_vec=sess.run(outputVec,feed_dict)
print(img_out_vec)
最终运行发现模型可以正常运行。(正常生成128位向量,且和h5模型生成的结果一致)
步骤四:在步骤一里下载的项目中添加源代码,注意输入输出的tensor名称要与pb模型里的相一致。
#include "stdafx.h"
#include <fstream>
#include <utility>
#include <Eigen/Core>
#include <Eigen/Dense>
#include <iostream>
#include "tensorflow/cc/ops/const_op.h"
#include "tensorflow/cc/ops/image_ops.h"
#include "tensorflow/cc/ops/standard_ops.h"
#include "tensorflow/core/framework/graph.pb.h"
#include "tensorflow/core/framework/tensor.h"
#include "tensorflow/core/graph/default_device.h"
#include "tensorflow/core/graph/graph_def_builder.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/core/stringpiece.h"
#include "tensorflow/core/lib/core/threadpool.h"
#include "tensorflow/core/lib/io/path.h"
#include "tensorflow/core/lib/strings/stringprintf.h"
#include "tensorflow/core/public/session.h"
#include "tensorflow/core/util/command_line_flags.h"
#include "tensorflow/core/platform/env.h"
#include "tensorflow/core/platform/init_main.h"
#include "tensorflow/core/platform/logging.h"
#include "tensorflow/core/platform/types.h"
#include "json/json.h"
#include "opencv2/opencv.hpp"
using namespace tensorflow::ops;
using namespace tensorflow;
using namespace std;
using namespace cv;
using tensorflow::Flag;
using tensorflow::Tensor;
using tensorflow::Status;
using tensorflow::string;
using tensorflow::int32 ;
void CVMat_to_Tensor(Mat img,Tensor* output_tensor,int input_rows,int input_cols)
{
//imshow("input image",img);
//图像进行resize处理
resize(img,img,cv::Size(input_cols,input_rows));
//imshow("resized image",img);
//归一化
//img.convertTo(img,CV_32FC1);
//img=1-img/255;
//创建一个指向tensor的内容的指针
float *p = output_tensor->flat<float>().data();
cv::Mat tempMat(input_rows, input_cols, CV_32FC1, p);
img.convertTo(tempMat,CV_32FC1);
// waitKey(0);
}
vector<tensorflow::Tensor> image_to_vec(Mat img,string model_path)
{
string input_tensor_name1 = "anc_input";
string input_tensor_name2 = "pos_input";
string input_tensor_name3 = "neg_input";
//string output_tensor_name = "concatenate_6/concat";
string output_tensor_name = "model_1_1/dense_4/Relu";
/*--------------------------------创建session------------------------------*/
Session* session;
SessionOptions opts;
//opts.config.mutable_gpu_options
Status status = NewSession(SessionOptions(), &session);//创建新会话Session
/*--------------------------------从pb文件中读取模型--------------------------------*/
GraphDef graphdef; //Graph Definition for current model
Status status_load = ReadBinaryProto(Env::Default(), model_path, &graphdef); //从pb文件中读取图模型;
if (!status_load.ok()) {
cout << "ERROR: Loading model failed..." << model_path << std::endl;
cout << status_load.ToString() << "\n";
exit(-1);
}
Status status_create = session->Create(graphdef); //将模型导入会话Session中;
if (!status_create.ok()) {
cout << "ERROR: Creating graph in session failed..." << status_create.ToString() << std::endl;
exit(-1);
}
cout << "<----Successfully created session and load graph.------->" << endl;
/*---------------------载入测试图片--------------------------------*/
clock_t startTime, endTime;
cout << endl << "<----------------loading test_image------------------->" << endl;
//Mat img = imread(image_path, 0);
if (!img.data)
{
cout << "loading test_image failed" << endl;
exit(-1);
}
//imshow("picture", img);
if (img.empty())
{
cout << "can't open the image!!!!!!!" << endl;
exit(-1);
}
int input_height = 140;
int input_width = 140;
Tensor input_tensor1(DT_FLOAT, TensorShape({ 1,input_height,input_width,1 }));
Tensor input_tensor2(DT_FLOAT, TensorShape({ 1,input_height,input_width,1 }));
Tensor input_tensor3(DT_FLOAT, TensorShape({ 1,input_height,input_width,1 }));
//将Opencv的Mat格式的图片存入tensor
CVMat_to_Tensor(img, &input_tensor1, input_height, input_width);
//Mat Z = Mat::zeros(input_height, input_width, CV_8UC1);
CVMat_to_Tensor(img, &input_tensor2, input_height, input_width);
CVMat_to_Tensor(img, &input_tensor3, input_height, input_width);
//cout << input_tensor1.DebugString() << endl;
//cout << input_tensor2.DebugString() << endl;
//cout << input_tensor3.DebugString() << endl;
/*-----------------------------------用网络进行测试-----------------------------------------*/
cout << endl << "<-------------Running the model with test_image--------------->" << endl;
//前向运行,输出结果一定是一个tensor的vector
vector<tensorflow::Tensor> outputs;
string output_node = output_tensor_name;
//cout << "Session Running......" << endl;
Status status_run = session->Run({ { input_tensor_name1, input_tensor1 },
{ input_tensor_name2, input_tensor2 },
{ input_tensor_name3, input_tensor3 } },
{ output_node },
{},
&outputs);
//cout << "Session complet......" << endl;
if (!status_run.ok()) {
cout << "ERROR: RUN failed..." << std::endl;
cout << status_run.ToString() << "\n";
exit(-1);
}
session->Close();
return outputs;
}
int main(int argc, char** argv )
{
/*--------------------------------1.配置关键信息(图片路径和模型路径)---------------*/
//string image_path="D:/testTensorflow/test/Debug/25100-000078-RS_550_1800.jpg";
//string image_path = *argv[1];
string model_path = "D:/testTensorflow/test/Debug/triplet_loss_model.pb";
//cout<<argv[3]<<endl;
/*---------------------------------2.从json文件中读取样本图片特征向量---------------*/
Json::Reader reader;
Json::Value root; //从文件中读取
ifstream is;
is.open("Anchorbase.json", ios::binary);
if (reader.parse(is, root))
cout << "<--------------load json secsess!--------------->" << endl;
else
cout << "load json failed!" << endl;
/*--------------------------------3.获得图片特征向量(outputs[0])-------------------*/
vector<tensorflow::Tensor> outputs;
//Mat img = imread(image_path, 0);
Mat img = imread(argv[1], 0);
outputs=image_to_vec(img,model_path); //把输出值给提取出来
cout << "Output tensor size:" << outputs.size() << std::endl;
//for (std::size_t i = 0; i < outputs.size(); i++) {
// cout << outputs[i].DebugString()<<std::endl;
//}
Tensor t = outputs[0]; // Fetch the first tensor
auto tmap = t.tensor<float, 2>(); // Tensor Shape: [batch_size, target_class_num]
/*--------------------------------4.比较测试图片和样本图片特征向量的距离----------*/
auto labels=root.getMemberNames();//样本标签名称
auto iter = labels.begin();//开头标签的指针
// 比较和所有特征向量的距离获取距离最小的作为品类判定
double dist = 100;//距离
String pridict_label;//预测结果
for(auto iter=labels.begin();iter!=labels.end();iter++)
{
double sum = 0;
for (int j = 0; j < 128; j++)
{
sum=sum +abs(root[*iter][j].asDouble() - tmap(0, j));
//cout << "Class " << tmap(0, j) << "," << std::endl;
}
if (sum < dist)
{
dist = sum;
pridict_label = *iter;
}
}
cout << "Pridicted result:" << pridict_label<<endl;
system("pause");
return 0;
}
步骤五:编译生成可执行的exe文件,如果包含目录,库目录等设置正确的话则可以正确生成exe文件。但是exe文件编译完毕执行时会提示丢失tensorflow.dll,不要怕只要把库目录下的dll文件拷贝到exe文件同一目录下即可。
运行效果图:
参考:https://blog.csdn.net/qq_25109263/article/details/81285952