tensorflow的c++接口编译以及调用图像分类模型代码

一、编译TensorFlow的c++接口

在调用TensorFlow的c++接口之前,首先要安装bazel、protobuf、Eigen等软件,然后下载TensorFlow源码进行编译,整体过程还是比较麻烦。

1、配置C++版tensorflow使用时的第三方依赖

(1)protobuf下载及安装

Protobuf这玩意儿是重中之重,它的版本与tensorflow的版本密切相关,它的版本错了就无法work。

下载地址:https://github.com/google/protobuf/releases ,我下载的是3.5.0版本,如果你是下载新版的tensorflow,请确保protobuf版本也是最新的,安装步骤:
 将下载的压缩包解压出来,获得一个protobuf-3.5.0的文件夹 
     cd prtobuf-3.5.0 
     ./configure 
     sudo make -j8 
     make check -j8 
     sudo make install 
     sudo ldconfig 
     在protobuf-3.5.0文件中执行: 
     ./configure --prefix=/home/xxx/Anaconda/protobuf-3.5.0
 以上步骤可以完成Protubuf的源码的编译和安装 
 如果遇到什么问题,建议去看Protobuf的官方的编译安装指南:https://github.com/google/protobuf/blob/master/src/README.md

(2)Eigen下载与安装

Eigen是一个C++端的矩阵运算库,这个库只要下载压缩包,解压到某个自己知道的路径下即可
	 先下载eigen的压缩包 
	 wget http://bitbucket.org/eigen/eigen/get/3.3.4.tar.bz2 
	 下载之后解压,重新命名为eigen3,放到某个路径下,安装就好 
	 mkdir build 
	 cd build 
	 cmake .. 
	 sudo make install

2、编译安装Tensorflow

(1)下载安装编译工具bazel

    先下载Bazel的安装包 https://github.com/bazelbuild/bazel/releases,我下载的是bazel-0.24.1-installer-linux-x86_64.sh 
    然后执行安装 ./bazel-0.24.1-installer-linux-x86_64.sh --user
    安装完成后,需要添加环境变量:
    sudo gedit ~/.bashrc
    在文本最后添加语句: export PATH=/home/xxx/bin:$PATH
    source ~/.bashrc
    注意:bazel版本不能过高,否则会报错

(2)Tensorflow下载与编译

     # 先下载tensorflow源码 ,我下载的是tensorflow1.14
	 git clone https://github.com/tensorflow/tensorflow.git 
	 # 进入tensorflow文件夹 
	 cd tensorflow
	 # 执行configure 
	 ./configure 
	 这一步需要你指定python路径,需要有各种y/N的选择 
	 建议如下: python路径用anaconda的路径:/home/xxx/anaconda3/bin/python 
	 pyhon路径选择默认路径就可以了
	 cuda要选择y,然后会自动搜索cudnn版本 
	 nccl选择默认的1.3, 
	 后面的不是选择N就是默认 
       利用bazel进行编译
       bazel build --config=opt //tensorflow:libtensorflow_cc.so // 无显卡,cpu版本
 	 bazel build --config=opt --config=cuda //tensorflow:libtensorflow_cc.so // 有显卡
 
	 ....漫长的等待编译,大约20分钟 
	 # 最后显示类似如下的信息,说明编译成功了:
	 .... Target //tensorflow:libtensorflow_cc.so up-to-date: 
	 bazel-bin/tensorflow/libtensorflow_cc.so 
	 INFO: Elapsed time: 1192.883s, Critical Path: 174.02s 
	 INFO: 654 processes: 654 local. 
	 INFO: Build completed successfully, 656 total actions
     然后回到tensorflow目录下执行:
	./tensorflow/contrib/makefile/download_dependencies.sh
	#完成后会有一个downloads文件夹在makefile文件夹中
	
	需要先安装相应依赖:
	sudo apt-get install autoconf automake libtool
	然后在tensorflow/contrib/makefile下执行
	bash ./build_all_linux.sh文件,成功后会出现一个gen文件夹。
        编译完成后,整理库文件和头文件
		库文件:
			mkdir -p ../tf_test/lib
			cp bazel-bin/tensorflow/libtensorflow_cc.so ../tf_test/lib/
			cp bazel-bin/tensorflow/libtensorflow_framework.so ../tf_test/lib/  # 之前编译r0.12和r1.3版本的库,只需要
			libtensorflow_cc.so,1.4版本的似乎分成了两个so文件,即还需要libtensorflow_framework.so
			cp /tmp/proto/lib/libprotobuf.a ../tf_test/lib/
		头文件:
			mkdir -p ../tf_test/include/tensorflow
			cp -r bazel-genfiles/* ../tf_test/include/
			cp -r tensorflow/cc ../tf_test/include/tensorflow
			cp -r tensorflow/core ../tf_test/include/tensorflow
			cp -r third_party ../tf_test/include
			cp -r /tmp/proto/include/* ../tf_test/include
			cp -r /tmp/eigen/include/eigen3/* ../tf_test/include
			cp -r tensorflow/contrib/makefile/downloads/nsync/public ../tf_test/include/external/nsync/public

		不需要的.cc文件可以删掉:
			cd ../tf_test/
			find . -name "*.cc" -type f -delete

至此,TensorFlow编译工作已经全部结束,接下来就是如何使用TensorFlow的c++调用Python环境下训练的模型。

二、tensorflow的c++接口调用图像分类模型

通过上面的的过程,已经编译好了c++版本的TensorFlow,接下来,将利用TensorFlow的c++接口来调用训练好的图像分类模型。图像分类网络选用的是inception_v3,其pb模型可以直接从网上下载,地址为:百度网盘-链接不存在,提取码:21uc

下面的c++代码是在eclipse中进行编写运行,在运行运行之前,需要将tensorflow、opencv等动态链接库(.so)包含进去。以下是main函数,亲测可用。

/*
 * main.cpp
 *
 *  Created on: 2020年3月31日
 *      Author: xxx
 */

#include <fstream>
#include <utility>
#include <iostream>
#include <vector>
#include <opencv2/opencv.hpp>
#include "tensorflow/core/public/session.h"
#include "tensorflow/core/graph/default_device.h"
#include "tensorflow/cc/client/client_session.h"

// 定义一个函数将Opencv的Mat数据转化为tensorflow的tensor,在python里面只要对cv2.imread()读进来的矩阵进行np.reshape之后,数据类型就成了一个tensor,即tensor与矩阵一样,然后就连可以输入到网络的入口了
// 而C++版本,网络的输入也需要是tensor数据类型,因此需要将输入图片转换成一个tensor,若使用Opencv读取图片,格式是一个Mat,需要考虑怎样将一个Mat转换为tensor
void CVMat_to_Tensor(cv::Mat img,tensorflow::Tensor * output_tensor,int input_rows, int input_cols)
{
	imshow("input image",img);
	// 对输入图像进行resize处理
	resize(img,img,cv::Size(input_cols,input_rows));
	imshow("resizes image",img);

	// 归一化
	img.convertTo(img,CV_32FC1);
	img = 1 - img/255;

	//创建一个指向tensor的内容指针
	float * p = output_tensor->flat<float>().data();

	// 创建一个Mat,与tensor的指针进行绑定,改变这个Mat的值,就相当于改变tensor的值
	cv::Mat tempMat(input_rows,input_cols,CV_32FC1,p);
	img.convertTo(tempMat,CV_32FC1);

	cv::waitKey(1000);
	cv::destroyAllWindows();
}

int main(int argc, char ** argv)
{
	/* --------------------配置关键信息------------------------------------*/
	std::string model_path = "./model/inception_v3_2016_08_28_frozen.pb"; // pb模型地址
	std::string image_path = "./model/cat.jpg"; // 测试图片
	int input_height = 299; // 输入网络的图片高度
	int input_width = 299; // 输入网络的图片宽度
	std::string input_tensor_name = "input"; // 网络的输入节点的名称
	std::string output_tensor_name = "InceptionV3/Predictions/Reshape_1"; // 网络的输出节点的名称

	/* --------------------创建session------------------------------------*/
	tensorflow::Session * session;
	tensorflow::Status status = tensorflow::NewSession(tensorflow::SessionOptions(), &session); // 创建新会话Session

	/* --------------------从pb文件中读取模型------------------------------------*/
	tensorflow::GraphDef graphdef; //为当前的模型定义一张图
	tensorflow::Status status_load = tensorflow::ReadBinaryProto(tensorflow::Env::Default(),model_path,&graphdef); // 从pb文件中读取图模型
	if (!status_load.ok()) // 判断读取模型是否正确,错误的话则打印出错误的信息
	{
		std::cout << "ERROR: Loading model failed..." << model_path << std::endl;
		std::cout << status_load.ToString() << "\n";
		return -1;
	}
	tensorflow::Status status_create = session->Create(graphdef); // 将模型导入会话Session中
	if (!status_create.ok()) // 判断将模型导入会话中是否成功,错误的话打印出错误信息
	{
		std::cout << "ERROR: Creating graph in session failed..." << status_create.ToString() << std::endl;
		return -1;
	}
	std::cout << "<------Sucessfully created session and load graph------>" << std::endl;

	/* --------------------载入测试图片------------------------------------*/
	cv::Mat img = cv::imread(image_path,0); // 读取图片,读取灰度图
	if (img.empty())
	{
		std::cout << "can't open the image!!!!!" << std::endl;
		return -1;
	}
	// 创建一个tensor作为输入网络的接口
	tensorflow::Tensor resized_tensor(tensorflow::DT_FLOAT,tensorflow::TensorShape({1,input_height,input_width,3}));
	// 将opencv读取的Mat格式的图片存入tensor
	CVMat_to_Tensor(img,&resized_tensor,input_height,input_width);
	std::cout << resized_tensor.DebugString() << std::endl;

	/* --------------------用网络进行测试------------------------------------*/
	std::cout << std::endl << "<------------------Runing the model with test_image------------------->" << std::endl;
	// 前向运行,输出结果一定是一个tensor的vector
	std::vector<tensorflow::Tensor> outputs;
	std::string output_node = output_tensor_name; // 输出节点名
	tensorflow::Status status_run = session->Run({{input_tensor_name,resized_tensor}},{output_node},{},&outputs);
	if (!status_run.ok())
	{
		std::cout << "ERROR: Run failed..." << std::endl;
		std::cout << status_run.ToString() << std::endl;
		return -1;
	}

	// 把输出值提取出来
	std::cout << "Output tensor size: " << outputs.size() << std::endl;
	for (std::size_t i = 0; i < outputs.size();i++)
	{
		std::cout << outputs[i].DebugString() << std::endl;
	}
	tensorflow::Tensor t = outputs[0];
	auto tmap = t.tensor<float,2>();
	int output_dim = t.shape().dim_size(1);

	int output_class_id = -1;
	double output_prob = 0.0;
	for (int j = 0; j < output_dim; j++)
	{
		std::cout << "Class " << j << " prob: " << tmap(0,j) << "," << std::endl;
		if (tmap(0,j) >= output_prob)
		{
			output_class_id = j;
			output_prob = tmap(0,j);
		}
	}
	// 输出结果
	std::cout << "Final class id : " << output_class_id << std::endl;
	std::cout << "Final class prob : " << output_prob << std::endl;
}


TensorFlow 2 可以使用 C++ API 进行编译,以便在 C 或 C++调用。以下是一些基本步骤: 1. 首先,您需要安装 TensorFlow 2 的 C++ API。可以使用以下命令: ```shell pip install tensorflow-cpp ``` 2. 然后,您需要编写一个 C 或 C++ 程序,以便在其中调用 TensorFlow 2。例如,以下是一个简单的 C++ 程序,它使用 TensorFlow 2 来执行一个简单的矩阵乘法: ```c++ #include <iostream> #include <tensorflow/c/c_api.h> int main() { // 创建 TensorFlow 会话 TF_SessionOptions* session_options = TF_NewSessionOptions(); TF_Status* status = TF_NewStatus(); TF_Session* session = TF_NewSession(session_options, status); TF_DeleteSessionOptions(session_options); // 加载模型 TF_Graph* graph = TF_NewGraph(); TF_Buffer* graph_def = TF_NewBuffer(); TF_SessionRunOptions* run_options = TF_NewSessionRunOptions(); TF_SessionPRunSetup* prun_setup = TF_NewSessionPRunSetup(); TF_Buffer* error_msg = TF_NewBuffer(); TF_Status* status_load = TF_NewStatus(); TF_SessionOptions* sess_opts = TF_NewSessionOptions(); TF_GraphImportGraphDef(graph, graph_def, NULL, status_load); // 创建输入张量 const std::int64_t dims[] = {2, 2}; const std::size_t ndims = 2; float input_values[] = {1, 2, 3, 4}; TF_Tensor* input_tensor = TF_NewTensor(TF_FLOAT, dims, ndims, input_values, sizeof(input_values), NULL, NULL); // 创建输出张量 TF_Output output_op = {TF_GraphOperationByName(graph, "MatMul"), 0}; TF_Tensor* output_tensor = NULL; // 运行会话 TF_SessionRun(session, run_options, &output_op, &input_tensor, 1, &output_tensor, 1, NULL, 0, prun_setup, status); TF_DeleteSessionRunOptions(run_options); TF_DeleteSessionPRunSetup(prun_setup); // 打印结果 float* output_values = static_cast<float*>(TF_TensorData(output_tensor)); std::cout << output_values[0] << ", " << output_values[1] << std::endl; // 清理资源 TF_DeleteGraph(graph); TF_DeleteBuffer(graph_def); TF_DeleteTensor(input_tensor); TF_DeleteTensor(output_tensor); TF_DeleteStatus(status); TF_DeleteStatus(status_load); TF_DeleteSession(session, status); TF_DeleteSessionOptions(sess_opts); return 0; } ``` 3. 编译上述程序。在 Linux 上,可以使用以下命令: ```shell g++ -std=c++11 -I/usr/local/include -L/usr/local/lib -ltensorflow -o my_program my_program.cpp ``` 注意,在这个命令中,我们使用了 TensorFlow 库的标准名称“libtensorflow.so”来链接库。 4. 运行编译后的程序。 ```shell ./my_program ``` 这将输出矩阵乘积的结果。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AI追随者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值