C++ 調用tensorflow

1.安裝protobuf 3.6

# 安裝依賴軟件
sudo apt-get install autoconf automake libtool curl make g++ unzip
# 克隆版本爲3.6.0的protobuf源碼
git clone -b v3.6.0 https://github.com/protocolbuffers/protobuf.git
# 下載 protobuf 的子模塊
cd protobuf
git submodule update --init --recursive


./autogen.sh
./configure
make
# make check 如果失敗會導致使用某些功能時報錯
make check
sudo make install
sudo ldconfig
# 輸出protobuf版本信息則表示安裝成功
protoc --version

2.安裝eigen3

Tensorflow依賴Eigen矩陣運算庫,在編譯和使用視需要安裝對應的版本.可以在 tensorflow/tensorflow/workspace.bzl 中找到對應版本的下載地址

  tf_http_archive(
      name = "eigen_archive",
      urls = [
          "https://mirror.bazel.build/bitbucket.org/eigen/eigen/get/2355b229ea4c.tar.gz",
           "https://bitbucket.org/eigen/eigen/get/2355b229ea4c.tar.gz",
       ],
# 解壓下載的文件夾到 /usr/local/include
sudo tar -xzvf eigen-eigen-5a0156e40feb.tar.gz -C /usr/local/include
# 重命名爲 eigen3
sudo mv /usr/local/include/eigen-eigen-5a0156e40feb /usr/local/include/eigen3

3.依賴C++庫 Abseil 庫

下載abseil-cpp,並放在子目錄中

在cmake中添加 abseil-cpp

add_subdirectory(abseil-cpp)

target_link_libraries(xx ...  absl::base absl::synchronization absl::strings)

4.編譯tensorflow

5.C++調用邏輯

tensorflow加載文件構建網絡,所需加載的pb文件由checkpoints 和 pb 凍結生成.

創建頭文件

#ifndef TENSORFLOW_C_RECOVER_TOOL_H
#define TENSORFLOW_C_RECOVER_TOOL_H

#include <strings.h>

#include <stdio.h>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

#include <tensorflow/core/framework/tensor_shape.h>
#include <tensorflow/core/platform/init_main.h>
#include <tensorflow/core/public/session.h>
#include "opencv2/opencv.hpp"

using namespace tensorflow;
using tensorflow::Tensor;
using tensorflow::Status;
using tensorflow::string;

using tensorflow::int32;
using namespace std;
using namespace cv;

class RecoverTool {

public:
    RecoverTool();

    int modelLoader(const string &model_path);

    string predict(cv::Mat &cvImg);

    void resize(Mat &inImg, Mat &outImg);

private:
    Session *session;

    string dict_vector = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-'_&.!?,\\\"";

    bool session_statu = false;

    void padding_img(Mat &src, Mat &out);

    string tensor_to_str(vector<Tensor> &outputs);
};


#endif //TENSORFLOW_C_RECOVER_TOOL_H

生成Session

RecoverTool::RecoverTool() {
    Status status = NewSession(SessionOptions(), &this->session);
    if (!status.ok()) {
        cout << "Session create failed.\n";
        cout << status.ToString() << "\n";
        this->session_statu = false;
    } else {
        this->session_statu = true;
        cout << "Session successfully created.\n";
    }
}

加載pb模型

int RecoverTool::modelLoader(const string &model_path) {
    tensorflow::GraphDef graphdef;
    Status status_load = ReadBinaryProto(Env::Default(), model_path, &graphdef);
    if (!status_load.ok()) {
        cout << "ERROR: Loading model failed..." << model_path << endl;
        cout << status_load.ToString() << "\n";
        return -1;
    }

    // Add the graph to the session
    Status status_create = this->session->Create(graphdef);
    if (!status_create.ok()) {
        cout << "ERROR: Creating graph in session failed..." << status_create.ToString() << endl;
        return -1;
    }
    return 0;
}

預測函數,需要先生成輸入 tensor,tensor的維度( b,h,w,c)和 dtype 需要與模型一致.

string RecoverTool::predict(cv::Mat &cvImg) {
    std::vector<Tensor> outputs;

    int inputHeight = cvImg.size().height;
    int inputWidth = cvImg.size().width;

    // Run tensorflow
    cv::TickMeter tm;
    tm.start();

    // 生成輸入 tensor
    tensorflow::Tensor imgTensorWithSharedData(
            tensorflow::DT_UINT8, {16, inputHeight, inputWidth, cvImg.channels()});

    // 將圖片的數據加載到 tensor 中
    auto x_map = imgTensorWithSharedData.tensor<uint8_t, 4>();
    for (int b = 0; b < 16; b++) {
        for (int h = 0; h < inputHeight; h++) {
            const uchar *inData = cvImg.ptr<uchar>(h);
            for (int w = 0; w < inputWidth; w++) {
                x_map(b, h, w, 0) = inData[w];
            }
        }
    }

    tensorflow::Status run_status = this->session->Run({{"input:0", imgTensorWithSharedData}},
                                                       {"crnn/dense_out:0"}, {}, &outputs);
    if (!run_status.ok()) {
        std::cerr << "TF_Session->Run Error: " << run_status << std::endl;
    }
    tm.stop();

    cout << "spent:" << tm.getTimeMilli() << endl;

    return tensor_to_str(outputs);
}

結果處理

string RecoverTool::tensor_to_str(vector<Tensor> &outputs) {

    Tensor t = outputs[0];                   // Fetch the first tensor
    int ndim = t.shape().dims();

    // Get the dimension of the tensor
    tensorflow::TTypes<int>::Flat scores = outputs[0].flat<int>(); // Tensor Shape: [batch_size, target_class_num]

    string result = "";
    for (int i = 0; i < scores.size(); i++) {
        int index = scores.data()[i];
        if (index != -1 && index < this->dict_vector.size()) {
            result += this->dict_vector.at(index);
        }
    }

    return result;
}

cmake 文件

cmake_minimum_required(VERSION 3.6)
project(tensorflow_c__)

set(CMAKE_CXX_STANDARD 14)

find_package(Threads REQUIRED)
find_package(OpenCV REQUIRED)

# protobuf
find_package(Protobuf REQUIRED)

include_directories(${Protobuf_INCLUDE_DIRS})

#opencv
include_directories(/usr/local/include)

#tensorflow
include_directories(~/tensorflow)
include_directories(/usr/local/include/eigen3)
include_directories(~/anaconda3/lib/python3.6/dist-packages/tensorflow/include)
include_directories(~/tensorflow/bazel-genfiles/)

#abseil-cpp
add_subdirectory(abseil-cpp)

add_executable(tensorflow_c__ main.cpp recover_tool.cpp recover_tool.h)

target_link_libraries(tensorflow_c__
        ~/tensorflow/bazel-bin/tensorflow/libtensorflow_cc.so
        ~/tensorflow/bazel-bin/tensorflow/libtensorflow_framework.so
        ${OpenCV_LIBS} ${Protobuf_LIBRARIES} absl::base absl::synchronization absl::strings)

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章