一、前言
前两篇博客已经介绍了如何在Linux环境下安装配置Opencv,以及编译Tensorflow的C++接口,由于在项目中常常同时用到这两个库,即使用Opencv读取和简单处理图片,再输入至Tensorflow创建的神经网络中进行进一步处理。
故本文主要介绍如何编写程序读取tensorflow训练并固化的pb模型,调用Opencv读取图片,并转换为tensorflow所需要的格式,输入至模型中得到识别结果。
二、代码编写
头文件包含
#include "tensorflow/core/public/session.h"
#include "tensorflow/cc/ops/standard_ops.h"
#include "tensorflow/core/protobuf/meta_graph.pb.h"
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace tensorflow;
图像格式转换
Tensorflow中模型的输入数据结构为Tensor,故需要将Opencv的Mat结构转化为Tensor,转换函数如下
void CVMat_to_Tensor(cv::Mat img, Tensor* output_tensor, int input_rows, int input_cols)
{
img.convertTo(img, CV_32FC1);
float *p = output_tensor->flat<float>().data();
cv::Mat tempMat(input_rows, input_cols, CV_32FC1, p);
img.convertTo(tempMat, CV_32FC1);
}
通过一个指针指向Tensor的data区域,再将图像复制到该指针指向的位置即可。
模型读取与调用
int main()
{
//新建会话
Session* session;
Status status = NewSession(SessionOptions(), &session);
if (!status.ok()) {
std::cout << status.ToString() << std::endl;
}
else {
std::cout << "Session created successfully" << std::endl;
}
//新建图,并读取模型文件
GraphDef graph_def;
status = ReadBinaryProto(Env::Default(), "chi.pb", &graph_def);
if (!status.ok()) {
std::cout << status.ToString() << std::endl;
}
else {
std::cout << "Load graph protobuf successfully" << std::endl;
}
//新建会话,并将图模型导入会话中
status = session->Create(graph_def);
if (!status.ok()) {
std::cout << status.ToString() << std::endl;
}
else {
std::cout << "Add graph to session successfully" << std::endl;
}
//读取图片
cv::Mat img = cv::imread("char_33.jpg", 0);
if (!img.empty()) {
std::cout << "open the image successfully" << std::endl;
}
//对图片大小进行缩放
cv::Mat resize_image;
int length = img.cols * 32 / float(img.rows);
cv::resize(img, resize_image, cv::Size(length, 32));
cv::transpose(resize_image, resize_image);
//将Mat格式图片转为Tensor
Tensor input_tensor(DT_FLOAT, TensorShape({ 1,length,32,1 }));
CVMat_to_Tensor(resize_image, &input_tensor, length, 32);
cout << input_tensor.DebugString() << endl;
//新建seq_len,后续模型调用时用到
Tensor seq_len(DT_INT32, TensorShape({ 1 }));
int *s = seq_len.flat<int>().data();
std::vector<int> mydata;
mydata.push_back (length / 4 - 1);
copy_n(mydata.begin(), 1, s); //复制mydata到dst
cout << seq_len.DebugString() << endl;
cout << endl << "<-------------Running the model with test_image--------------->" << endl;
//模型调用
vector<tensorflow::Tensor> outputs;
string output_node = "output";
clock_t start, ends;
start = clock();
//Tensor input_tensor(DT_FLOAT, TensorShape({ 1,length,32,1 }));
CVMat_to_Tensor(resize_image, &input_tensor, length, 32);
status_run = session->Run({ { "input", input_tensor },{ "seq_len", seq_len } }, { output_node }, {}, &outputs);
ends = clock();
std::cout << ends - start <<"ms"<< std::endl;
if (!status_run.ok()) {
cout << "ERROR: RUN failed..." << std::endl;
cout << status_run.ToString() << "\n";
return -1;
}
std::cout << outputs[0].DebugString() << std::endl;
//输出识别结果
for (int i = 0; i < outputs[0].shape().dim_size(1); ++i)
{
std::cout << outputs[0].flat<tensorflow::int64>().data()[i] << " ";
}
std::string chi_list = "产地省市区北京天津上海重庆黑龙江吉林辽宁河山东苏安徽浙福建广南云贵州四川湖西陕甘肃青台湾新疆藏夏内蒙古香港澳门通沈阳成都食品名称类型加工配料表营养分净含量规格生厂商经销址联系电话贮存条件储方式许可证编号标准代项目每克日期有效至批保质合年月赏味限次班组检验员甲乙丙码份执行";
std::string result;
for (int i = 0; i < outputs[0].shape().dim_size(1); ++i)
{
result += chi_list.substr(int(outputs[0].flat<tensorflow::int64>().data()[i] * 3), 3);
}
std::cout << result << std::endl;
}
三、CMakeLists.txt文件编写
cmake_minimum_required (VERSION 2.8.8)
project (tfcpptest)
find_package(OpenCV REQUIRED)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -std=c++11 -W")
link_directories(./lib)
include_directories(
include/tf/
include/tf/tensorflow
include/tf/bazel-genfiles
include/tf/downloads/nsync/public
include/tf/gen/protobuf/include
/usr/local/include/eigen3
)
add_executable(tfcpptest tfcpptest.cpp)
target_link_libraries(tfcpptest ${OpenCV_LIBS} tensorflow_cc tensorflow_framework)
注意将tensorflow的动态库和头文件放在相应目录下
四、编译运行
cmake .
make
./tfcpptest
得到输出结果如下: