opencv4.1無法加載python-cnn模型,編譯第三方庫libtensorflow_cc.so巨坑

兩個月前同事在python下訓練的cnn模型(加了batchnorm層、dropout層,模型是.pb結尾),但發現opencv不支持加載(可能是這樣)。於是我找啊找,發現可以自己編譯第三方庫libtensorflow_cc.so,就可以加載python下的任何模型,不管是什麼layer不管是.pb還是.meta等模型。

那時候我按照 https://www.jianshu.com/p/d46596558640 、 https://www.jianshu.com/p/0bf9f1d85f4c 、https://www.cnblogs.com/hrlnw/p/7007648.html 等各位大神的都試過:

按照他們的教程,我只到達這一步!即耗時巨久,終於成功編譯出libtensorflow_cc.so。但是

這些文件夾都編譯出來了。但是在我將libtensorflow_cc.so加入環境變量時報錯:到此,我查了N多方法,還是沒搞定!!!

 

今天我又不得不開始搞這個,我沒有全部重新編譯,只是接着兩個月前編譯好的libtensorlow_cc.so,按照 https://blog.csdn.net/dragonchow123/article/details/80682787  這個大神的下半段操作“5.整理頭文件和so庫”。這次不會上次的信息了“魔數錯誤”!但是運行例子時發現即使我配置好了include和lib,仍舊始終顯示找不到libtensorflow_cc.so!!!我再次試了網上所有提到的方法:比如將路徑加入ld.so.conf 然後ldconfig更新;加入bashrc或profile然後source更新,加入LD_LIBRARY_PATH或LIBRARY_PATH、或者直接將其拷貝到/lib或/usr/lib下面等等等等。結果運行例子時還是顯示找不到libtensorflow_cc.so!!!

我直接運行ldconfig -v顯示下面的信息:

可以看到明明這個文件夾裏有這個庫,可是報錯卻像睜眼說瞎話一樣說沒有這個庫!!!氣死我了!gcc -ltensorflow_cc --verbose肯定也是一樣找不到這個庫!那運行例子肯定也一樣找不到這個庫!!!

此時又試了N多網上將庫添加進環境變量之類的方法,仍無效!!!

後來無聊將這幾個庫都拷貝進來:然後竟然不報錯了哈哈!!!!!!!!!

但是gcc -ltensorflow_cc --verbose還是沒有這個庫。於是我又試着將這5個庫都拷貝到/usr/lib下面,然後ldconfig一下!然後竟然可以了!!!!

看,這個例子終於正確輸出第三方庫tensorflow的信息了!!!!此時加班到九點的我淚流滿面!!!

 

今天我又來試了另一個例子:

於是我開始按照它報的這些錯,添加-std=c++11,並將報錯的fatal error: absl/strings/string_view.h: 沒有那個文件或目錄   、CXX11/Tensor:1:42: fatal error: unsupported/Eigen/CXX11/Tensor: 沒有那個文件或目錄   加入include中

它還沒完沒了了。我查了一下,網上有人說:(看下圖最後一句)

對c/c_api.h的支持最好,難怪我昨天那個例子可以成功?!!!

google/protobuf/extension_set.h:692:62: error: ‘move’ is not a member of ‘std’

bazel-genfiles/tensorflow/core/lib/core/error_codes.pb.h:76:58: error: ‘<::’ cannot begin a template-argument list [-fpermissive]

absl/base/internal/throw_delegate.h:39:1: error: expected unqualified-id before ‘[’ token

absl/strings/string_view.h:146:9: error: expected nested-name-specifier before ‘traits_type’

現在它報的這些沒完沒了的問題。。。哎這個東西坑太多了。

解決辦法:eigen3/unsupported/Eigen/CXX11/Tensor:1:42: fatal error: unsupported/Eigen/CXX11/Tensor: 沒有那個文件或目錄----------原來是我的系統的python版本里面沒有自帶的tensorflow!於是我按照 https://www.jianshu.com/p/448b3b93184b 這個人的安裝對應版本的tensorflow進python裏。然後將這個python下的tensorflow裏的unsupported文件夾放到我之前弄好的放tensorflow_cc的頭文件的地方:

我之前的錯誤之處在於我是將第三方庫tensorflow裏的unsupported文件夾放在這裏,不對!應該放python的tensorflow的unsupported文件夾纔對!!上面編譯時之前的錯誤消失了。

現在只要解決這個錯誤就好了 “tensorflow/core/lib/core/stringpiece.h:29:38: fatal error: absl/strings/string_view.h: 沒有那個文件或目錄” 我原來的解決辦法是將第三方庫tensorflow裏的隨便找的一個absl文件夾放在上圖的include裏,這是不對的!有人說要 tensorflow/contrib/makefile/downloads/absl/absl 將這個文件夾放進include纔行。但我試了一下,發現報的錯誤更多。煩死了。

剛剛又按照    這個大神的方法在tmp下編譯proto和eigen文件夾。

編譯proto時出現了這個小錯誤沒有eigen3/gebp_neon.patch,但我沒管。

繼續編譯eigen。

然後將我原來的Eigen和unsupported文件夾刪掉,重新替換成剛剛tmp/下proto和eigen中的Eigen和unsupported文件夾。然後將tmp下proto與eigen的include和lib加入工程目錄,以及環境變量。

因爲我之前說過我編譯出的libtensorflow_cc.so的同時竟然沒有libtensorflow_framework.so,只有libtensorflow_framework.so.1、libtensorflow_framework.so.1.13.1。所以我軟鏈接創造了一個libtensorflow_framework.so,並添加到工程目錄。然後竟然編譯成功了,如上圖所示。

但是運行時報了錯:

2019-06-25 20:09:37.474938: E tensorflow/core/common_runtime/session.cc:81] Not found: No session factory registered for the given session options: {target: "" config: } Registered factories are {}.
Not found: No session factory registered for the given session options: {target: "" config: } Registered factories are {}.

不管怎樣,能編譯成功我已經很知足了。

我查了一下,發現別人說這是因爲 libtensorflow-core這個庫沒有鏈接進工程。我嘗試了gitbub上有人說的  -Wl,--allow-multiple-definition -Wl,--whole-archive 發現無用。又試了 https://blog.csdn.net/fly_time2012/article/details/80841377 等大神說的 ./tensorflow/contrib/makefile/build_all_linux.sh 終於看到快成功,然而這個沒有安裝成功:

出現這些報錯:

./unsupported/Eigen/CXX11/Tensor:1:42: error: #include nested too deeply
tensorflow/core/lib/core/threadpool.cc:88:49: error: expected template-name before ‘<’ token
 struct ThreadPool::Impl : Eigen::ThreadPoolTempl<EigenEnvironment> {
                                                 ^
tensorflow/core/lib/core/threadpool.cc:88:49: error: expected ‘{’ before ‘<’ token
tensorflow/core/lib/core/threadpool.cc:88:49: error: expected unqualified-id before ‘<’ token
tensorflow/core/lib/core/threadpool.cc:217:1: error: expected ‘}’ at end of input
 }  // namespace tensorflow
 ^
tensorflow/core/lib/core/threadpool.cc:217:1: error: expected ‘}’ at end of input

然後gen下面也沒有找到libtensorflow_core這個庫。所以應該就是這裏的問題。這些報錯是因爲什麼我找了好久還沒解決。

但是我剛剛又看到 https://spockwangs.github.io/blog/2018/01/13/train-using-tensorflow-c-plus-plus-api/ 這位大神的解說,也就是要麼使用libtensorflow_cc.so動態共享庫,要麼使用libtensorflow_core.a靜態庫。我既然跑通了libtensorflow_cc.so的共享庫,其實是不用再編譯libtensorflow_core.a的!?所以我共享庫跑第二個例子時報錯說沒註冊的問題是不是該用這位大神說的“如果運行時報錯說有些操作沒有註冊,這需要將相應的操作源代碼文件(在tensorflow/core/kernels/下)放到tf_op_files.txt中重新編譯。” 這樣來解決?我試下。然而還是沒成功,因爲那個報錯的session.cc不在core/kernels下。

今天又試了一個辦法:-Wl,--no-as-needed

竟然成功輸出了!但是有紅色部分報警。但是還是算正確輸出了。這是我的include和lib,下面介紹我工程的此時的配置:

暫時告一段落,因爲正確輸出了。太開心了啊啊啊啊啊啊啊啊狂吼

************************************************************************************************************

解決上次的紅色報警部分。

2019-06-27 14:24:40.050190: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: SSE4.1 SSE4.2 AVX AVX2 FMA
2019-06-27 14:24:40.070870: I tensorflow/core/platform/profile_utils/cpu_utils.cc:94] CPU Frequency: 3407965000 Hz
2019-06-27 14:24:40.071201: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x1ef7780 executing computations on platform Host. Devices:
2019-06-27 14:24:40.071216: I tensorflow/compiler/xla/service/service.cc:175]   StreamExecutor device (0): <undefined>, <undefined>
Session successfully created.

即這個問題。我查了一下這個報警是說我的CPU支持更高級的配置,意思就是我還可以升級,那樣tf運算速度會比現在快三倍!!!太開心!!!

#include "tensorflow/cc/client/client_session.h"
#include "tensorflow/cc/ops/standard_ops.h"
#include "tensorflow/core/framework/tensor.h"

int main() {
  using namespace tensorflow;
  using namespace tensorflow::ops;
  Scope root = Scope::NewRootScope();
  // Matrix A = [3 2; -1 0]
  auto A = Const(root, { {3.f, 2.f}, {-1.f, 0.f}});
  // Vector b = [3 5]
  auto b = Const(root, { {3.f, 5.f}});
  // v = Ab^T
  auto v = MatMul(root.WithOpName("v"), A, b, MatMul::TransposeB(true));
  std::vector<Tensor> outputs;
  ClientSession session(root);
  // Run and fetch v
  TF_CHECK_OK(session.Run({v}, &outputs));
  // Expect outputs[0] == [19; -3]
  LOG(INFO) << outputs[0].matrix<float>();
  return 0;
}

我運行了下這個例子,可以正確輸出 19 -3

2019-06-27 14:58:54.463627: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: SSE4.1 SSE4.2 AVX AVX2 FMA
2019-06-27 14:58:54.480724: I tensorflow/core/platform/profile_utils/cpu_utils.cc:94] CPU Frequency: 3407935000 Hz
2019-06-27 14:58:54.481503: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x2b1d6d0 executing computations on platform Host. Devices:
2019-06-27 14:58:54.481545: I tensorflow/compiler/xla/service/service.cc:175]   StreamExecutor device (0): <undefined>, <undefined>
2019-06-27 14:58:54.484842: I tensorflow/core/common_runtime/optimization_registry.cc:35] Running all optimization passes in grouping 0. If you see this a lot, you might be extending the graph too many times (which means you modify the graph many times before execution). Try reducing graph modifications or using SavedModel to avoid any graph modification
2019-06-27 14:58:54.487872: I tensorflow/core/common_runtime/optimization_registry.cc:35] Running all optimization passes in grouping 1. If you see this a lot, you might be extending the graph too many times (which means you modify the graph many times before execution). Try reducing graph modifications or using SavedModel to avoid any graph modification
2019-06-27 14:58:54.491739: I tensorflow/core/common_runtime/optimization_registry.cc:35] Running all optimization passes in grouping 2. If you see this a lot, you might be extending the graph too many times (which means you modify the graph many times before execution). Try reducing graph modifications or using SavedModel to avoid any graph modification
2019-06-27 14:58:54.493086: W tensorflow/compiler/jit/mark_for_compilation_pass.cc:1337] (One-time warning): Not using XLA:CPU for cluster because envvar TF_XLA_FLAGS=--tf_xla_cpu_global_jit was not set.  If you want XLA:CPU, either set that envvar, or use experimental_jit_scope to enable XLA:CPU.  To confirm that XLA is active, pass --vmodule=xla_compilation_cache=1 (as a proper command-line flag, not via TF_XLA_FLAGS) or set the envvar XLA_FLAGS=--xla_hlo_profile.
2019-06-27 14:58:54.493679: I tensorflow/core/common_runtime/optimization_registry.cc:35] Running all optimization passes in grouping 3. If you see this a lot, you might be extending the graph too many times (which means you modify the graph many times before execution). Try reducing graph modifications or using SavedModel to avoid any graph modification
2019-06-27 14:58:54.499085: I ../src/main.cc:27] 19
-3

雖然也會報警,但報警的部分先忽略。

太開心了,可以用起來了。

*************************************************************************************************

測試網上的例程 加載模型測試:https://blog.csdn.net/u012968002/article/details/80440799

但是結果卻報了錯:

../src/tf.cpp:55:37: error: ‘using StringPiece = class absl::string_view {aka class absl::string_view}’ has no member named ‘ToString’
   output->scalar<string>()() = data.ToString();
                                     ^
../src/tf.cpp: In function ‘tensorflow::Status ReadTensorFromImageFile(const string&, int, int, float, float, std::vector<tensorflow::Tensor>*)’:
../src/tf.cpp:86:42: error: ‘using StringPiece = class absl::string_view {aka class absl::string_view}’ has no member named ‘ends_with’
   if (tensorflow::StringPiece(file_name).ends_with(".png")) {
                                          ^
../src/tf.cpp:89:49: error: ‘using StringPiece = class absl::string_view {aka class absl::string_view}’ has no member named ‘ends_with’
   } else if (tensorflow::StringPiece(file_name).ends_with(".gif")) {
                                                 ^
../src/tf.cpp:94:49: error: ‘using StringPiece = class absl::string_view {aka class absl::string_view}’ has no member named ‘ends_with’
   } else if (tensorflow::StringPiece(file_name).ends_with(".bmp")) {
                                                 ^
../src/tf.cpp: In function ‘int main(int, char**)’:
../src/tf.cpp:189:7: warning: unused variable ‘ndim2’ [-Wunused-variable]
   int ndim2 = t.shape().dims();             // Get the dimension of the tensor
       ^
make: *** [src/tf.o] Error 1
src/subdir.mk:32: recipe for target 'src/tf.o' failed

找了很久原因沒解決。因爲我去看string_view的源碼,真的沒有報錯的這幾個成員函數。所以可能是我用錯了版本還是這幾個函數已經被廢棄。

今天終於解決了。原來上面這個例程是按照 https://github.com/tensorflow/tensorflow/blob/master/tensorflow/examples/label_image/main.cc  仿照這個來寫的,而外國人進行了更新,國內很多人以前的博客都是老版本,所以我使用會出問題。

所以我按照外國人的寫法,把以前錯誤的註釋掉:

/*
 * test tensorflow_cc c++ successfully
 * load mnist.pb model successfully
 * 2019.6.28
 * wang dan
 * conference:https://github.com/tensorflow/tensorflow/blob/master/tensorflow/examples/label_image
 * */
#include <fstream>
#include <utility>
#include <vector>
#include <Eigen/Core>
#include <Eigen/Dense>

#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/platform/env.h"
#include "tensorflow/core/platform/init_main.h"
#include "tensorflow/core/platform/logging.h"
#include "tensorflow/core/platform/types.h"
#include "tensorflow/core/public/session.h"
#include "tensorflow/core/util/command_line_flags.h"

using namespace std;
using namespace tensorflow;
using namespace tensorflow::ops;
using tensorflow::Flag;
using tensorflow::Tensor;
using tensorflow::Status;
using tensorflow::string;
using tensorflow::int32;

static Status ReadEntireFile(tensorflow::Env* env, const string& filename,
                             Tensor* output) {
  tensorflow::uint64 file_size = 0;
  TF_RETURN_IF_ERROR(env->GetFileSize(filename, &file_size));

  string contents;
  contents.resize(file_size);

  std::unique_ptr<tensorflow::RandomAccessFile> file;
  TF_RETURN_IF_ERROR(env->NewRandomAccessFile(filename, &file));

  tensorflow::StringPiece data;
  TF_RETURN_IF_ERROR(file->Read(0, file_size, &data, &(contents)[0]));
  if (data.size() != file_size) {
    return tensorflow::errors::DataLoss("Truncated read of '", filename,
                                        "' expected ", file_size, " got ",
                                        data.size());
  }
//  output->scalar<string>()() = data.ToString();
  output->scalar<string>()() = string(data);
  return Status::OK();
}

Status ReadTensorFromImageFile(const string& file_name, const int input_height,
                               const int input_width, const float input_mean,
                               const float input_std,
                               std::vector<Tensor>* out_tensors) {
  auto root = tensorflow::Scope::NewRootScope();
  using namespace ::tensorflow::ops;

  string input_name = "file_reader";
  string output_name = "normalized";

  // read file_name into a tensor named input
  Tensor input(tensorflow::DT_STRING, tensorflow::TensorShape());
  TF_RETURN_IF_ERROR(
      ReadEntireFile(tensorflow::Env::Default(), file_name, &input));

  // use a placeholder to read input data
  auto file_reader =
      Placeholder(root.WithOpName("input"), tensorflow::DataType::DT_STRING);

  std::vector<std::pair<string, tensorflow::Tensor>> inputs = {
      {"input", input},
  };

  // Now try to figure out what kind of file it is and decode it.
  const int wanted_channels = 1;
//  tensorflow::Output image_reader;
//  if (tensorflow::StringPiece(file_name).ends_with(".png")) {
//    image_reader = DecodePng(root.WithOpName("png_reader"), file_reader,
//                             DecodePng::Channels(wanted_channels));
//  } else if (tensorflow::StringPiece(file_name).ends_with(".gif")) {
//    // gif decoder returns 4-D tensor, remove the first dim
//    image_reader =
//        Squeeze(root.WithOpName("squeeze_first_dim"),
//                DecodeGif(root.WithOpName("gif_reader"), file_reader));
//  } else if (tensorflow::StringPiece(file_name).ends_with(".bmp")) {
//    image_reader = DecodeBmp(root.WithOpName("bmp_reader"), file_reader);
//  } else {
//    // Assume if it's neither a PNG nor a GIF then it must be a JPEG.
//    image_reader = DecodeJpeg(root.WithOpName("jpeg_reader"), file_reader,
//                              DecodeJpeg::Channels(wanted_channels));
//  }
  tensorflow::Output image_reader;
	if (tensorflow::str_util::EndsWith(file_name, ".png")) {
	  image_reader = DecodePng(root.WithOpName("png_reader"), file_reader,
							   DecodePng::Channels(wanted_channels));
	} else if (tensorflow::str_util::EndsWith(file_name, ".gif")) {
	  // gif decoder returns 4-D tensor, remove the first dim
	  image_reader =
		  Squeeze(root.WithOpName("squeeze_first_dim"),
				  DecodeGif(root.WithOpName("gif_reader"), file_reader));
	} else if (tensorflow::str_util::EndsWith(file_name, ".bmp")) {
	  image_reader = DecodeBmp(root.WithOpName("bmp_reader"), file_reader);
	} else {
	  // Assume if it's neither a PNG nor a GIF then it must be a JPEG.
	  image_reader = DecodeJpeg(root.WithOpName("jpeg_reader"), file_reader,
								DecodeJpeg::Channels(wanted_channels));
	}
  // Now cast the image data to float so we can do normal math on it.
  auto float_caster =
      Cast(root.WithOpName("float_caster"), image_reader, tensorflow::DT_FLOAT);

  auto dims_expander = ExpandDims(root.WithOpName("expand"), float_caster, 0);

  float input_max = 255;
  Div(root.WithOpName("div"),dims_expander,input_max);

  tensorflow::GraphDef graph;
  TF_RETURN_IF_ERROR(root.ToGraphDef(&graph));

  std::unique_ptr<tensorflow::Session> session(
      tensorflow::NewSession(tensorflow::SessionOptions()));
  TF_RETURN_IF_ERROR(session->Create(graph));
//  std::vector<Tensor> out_tensors;
//  TF_RETURN_IF_ERROR(session->Run({}, {output_name + ":0", output_name + ":1"},
//                                    {}, &out_tensors));
  TF_RETURN_IF_ERROR(session->Run({inputs}, {"div"}, {}, out_tensors));
  return Status::OK();
}


int main()
{
  Session* session;
  Status status = NewSession(SessionOptions(), &session);//創建新會話Session

  string model_path="model.pb";
  GraphDef graphdef; //Graph Definition for current model

  Status status_load = ReadBinaryProto(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;
  }
  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;
  }
  cout << "Session successfully created."<< endl;
  string image_path= "/media/root/Ubuntu311/projects/Ecology_projects/tensorflowtest/digit.jpg";
  int input_height =28;
  int input_width=28;
  int input_mean=0;
  int input_std=1;
  std::vector<Tensor> resized_tensors;
  Status read_tensor_status =
      ReadTensorFromImageFile(image_path, input_height, input_width, input_mean,
                              input_std, &resized_tensors);
  if (!read_tensor_status.ok()) {
    LOG(ERROR) << read_tensor_status;
    cout<<"resing error"<<endl;
    return -1;
  }

  const Tensor& resized_tensor = resized_tensors[0];
  std::cout << resized_tensor.DebugString()<<endl;

  vector<tensorflow::Tensor> outputs;
  string output_node = "softmax";
  Status status_run = session->Run({{"inputs", resized_tensor}}, {output_node}, {}, &outputs);

  if (!status_run.ok()) {
      std::cout << "ERROR: RUN failed..."  << std::endl;
      std::cout << status_run.ToString() << "\n";
      return -1;
  }
  //Fetch output value
  std::cout << "Output tensor size:" << outputs.size() << std::endl;
  for (std::size_t i = 0; i < outputs.size(); i++) {
      std::cout << outputs[i].DebugString()<<endl;
  }

  Tensor t = outputs[0];                   // Fetch the first tensor
  int ndim2 = t.shape().dims();             // Get the dimension of the tensor
  auto tmap = t.tensor<float, 2>();        // Tensor Shape: [batch_size, target_class_num]
  int output_dim = t.shape().dim_size(1);  // Get the target_class_num from 1st dimension
  std::vector<double> tout;

  // Argmax: Get Final Prediction Label and Probability
  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;

  return 0;
}

這樣就可以了。

已經正確輸出了測試結果。

今天我將這個庫複製到別的筆記本上,然後新建測試工程,已經測試通過。所以以後再也不用重新編譯了,直接使用這個庫到工程中即可。

本來想把這個編譯好的動態庫直接上傳供大家直接下載使用,而不用你們自己重新編譯,但是文件大小超過了CSDN的限制,所以傳不了。抱歉。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

po一張狗子的生圖哈哈

 

 

 

 

 

 

 

 

 

 

 

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