目錄
1.安裝 vs2015、cuda9.0、python3.5+
7.vs無法解析的外部符號與powershell編譯出現無法解析的外部符號錯誤
8.準備tensorflow的dll、lib、include
8.1 簡版tensorflow的dll、lib、include
這篇博客寫着,是因爲windows+bazel+tensorflow-r1.12(GPU)編譯生成dll與lib,上一篇,雖然編譯成功了,但是在使用tensorflow\examples\label_image\main.cc怎麼都不能成功生成解決方案,因爲缺很多鏈接庫。所以寫了這篇。
其中,我已經編譯好的tensorflow-v1.12.0 dll,lib,include,如何出現缺鏈接庫的話,自己可以將錯誤的符號加入tf_exported_symbols_msvc.lds文件中,自己編譯生成自己的dll與lib文件:
下載鏈接:
tensorflow-v1.12.0 dll,lib,include
提取碼:
0ugf
0.最終環境
- win10
- vs2015
- cuda9.0
- cudnn7.6
- python3.5.2
- tensorflow-v1.12.0
- bazel0.22.0
- msys2-x86_64-20190524
1.安裝 vs2015、cuda9.0、python3.5+
2.安裝 MSYS2
其中這個最好按照默認路徑安裝,否則編譯時沒過幾秒鐘,可能會出現下面的問題:
/usr/bin/env: 'python': No such file or directory windows
所有安裝步驟都默認一路next下去,安裝完成以後,需要配置:
- 將目錄C:\msys64和C:\msys64\usr\bin 加入系統環境變量path
- 打開cmd.exe,輸入一下命令(有時可能安裝不了,多試幾次好了):
pacman -Syuu patch
3.安裝 Bazel
在github下載Bazel,https://github.com/bazelbuild/bazel/releases,這裏我下載的bazel-0.22.0-windows-x86_64.exe
將下載好的 bazel-0.22.1-windows-x86_64.exe複製到C:\msys64下,更名爲bazel.exe。
配置bazel:新建環境變量:BAZEL_SH,BAZEL_VC ,BAZEL_VS
4.下載tensorflow-v1.12
git與直接下載都可以,我是直接下載的壓縮包。
5.修改文件配置
此處參考https://github.com/guikarist/tensorflow-windows-build-script
1.將tensorflow-1.12.0.zip解壓到tensorflow-1.12.0目錄下,並將目錄下的tensorflow-1.12.0文件夾重命名爲source;
2.下載tensorflow-windows-build-script的壓縮包,解壓,將其中的patchs與build.ps.1複製到tensorflow-1.12.0目錄下;
3.將patchs下的eigen_half.patch複製到tensorflow-1.12.0\source\third_party下;
4.將patchs下的tf_exported_symbols_msvc.lds複製到tensorflow-1.12.0\source\tensorflow下,這裏使用我編譯使用過的tf_exported_symbols_msvc.lds替換;
5.可以使用Notepad++修改build.ps1文件如下(後面可能會出現Copy-Item命令的問題);
修改 line154-182:
- # Apply patches to source.
- if ($buildVersion -eq "v1.11.0") {
- # Eigen Patch for v1.11.0
- git apply --ignore-space-change --ignore-white "..\patches\eigen.1.11.0.patch"
- Copy-Item ..\patches\eigen_half.patch third_party\
- } elseif ($buildVersion -eq "v1.12.0") {
- # Eigen Patch for v1.12.0
- git apply --ignore-space-change --ignore-white "..\patches\eigen.1.12.0.patch"
- Copy-Item ..\patches\eigen_half.patch third_party\
- } elseif ($buildVersion -eq "v1.13.1") {
- [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
- (Invoke-WebRequest https://github.com/tensorflow/tensorflow/commit/ec727016282383aacf9d26386b01f6bdbd65b14b.patch).Content | git apply -v --ignore-space-change --ignore-white
- }
-
- if ($BuildCppAPI) {
- if ($buildVersion -eq "v1.11.0") {
- # C++ Symbol Patch for v1.11.0
- git apply --ignore-space-change --ignore-white "..\patches\cpp_symbol.1.11.0.patch"
- Copy-Item ..\patches\tf_exported_symbols_msvc.lds tensorflow\
- } elseif ($buildVersion -eq "v1.12.0") {
- # C++ Symbol Patch for v1.12.0
- git apply --ignore-space-change --ignore-white "..\patches\cpp_symbol.1.12.0.patch"
- Copy-Item ..\patches\tf_exported_symbols_msvc.lds tensorflow\
- } elseif ($buildVersion -eq "v1.13.1") {
- # C++ Symbol Patch for v1.13.1
- git apply --ignore-space-change --ignore-white "..\patches\cpp_symbol.1.13.1.patch"
- Copy-Item ..\patches\tf_exported_symbols_msvc.lds tensorflow\
- }
- }
-
爲:
- 爲:
- # Apply patches to source.
- if ($buildVersion -eq "v1.11.0") {
- # Eigen Patch for v1.11.0
- git apply --ignore-space-change --ignore-white "..\patches\eigen.1.11.0.patch"
- Copy-Item ..\patches\eigen_half.patch third_party\
- } elseif ($buildVersion -eq "v1.12.0") {
- # Eigen Patch for v1.12.0
- git apply --ignore-space-change --ignore-white "..\patches\eigen.1.12.0.patch"
- #Copy-Item ..\patches\eigen_half.patch third_party\
- } elseif ($buildVersion -eq "v1.13.1") {
- [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
- (Invoke-WebRequest https://github.com/tensorflow/tensorflow/commit/ec727016282383aacf9d26386b01f6bdbd65b14b.patch).Content | git apply -v --ignore-space-change --ignore-white
- }
-
- if ($BuildCppAPI) {
- if ($buildVersion -eq "v1.11.0") {
- # C++ Symbol Patch for v1.11.0
- git apply --ignore-space-change --ignore-white "..\patches\cpp_symbol.1.11.0.patch"
- Copy-Item ..\patches\tf_exported_symbols_msvc.lds tensorflow\
- } elseif ($buildVersion -eq "v1.12.0") {
- # C++ Symbol Patch for v1.12.0
- git apply --ignore-space-change --ignore-white "..\patches\cpp_symbol.1.12.0.patch"
- #Copy-Item ..\patches\tf_exported_symbols_msvc.lds tensorflow\
- } elseif ($buildVersion -eq "v1.13.1") {
- # C++ Symbol Patch for v1.13.1
- git apply --ignore-space-change --ignore-white "..\patches\cpp_symbol.1.13.1.patch"
- Copy-Item ..\patches\tf_exported_symbols_msvc.lds tensorflow\
- }
- }
6.使用powershell進行配置與編譯
1.打開powershell
輸入下面命令,這是bazel編譯的選項,爲不同的語言(python、c、c++)其//後對應的都不一樣:
$parameterString = "--config=opt --config=cuda --define=no_tensorflow_py_deps=true --copt=-nvcc_options=disable-warnings //tensorflow:libtensorflow_cc.so --verbose_failures"
bazel編譯的選項,爲不同的語言(python、c、c++)其//後對應的都不一樣,參考https://github.com/guikarist/tensorflow-windows-build-script:
然後輸入:
- .\build.ps1 `
- -BazelBuildParameters $parameterString `
- -BuildCppAPI -ReserveSource
會出現build.ps1錯誤:無法加載文件 ******.ps1,因爲在此係統中禁止執行腳本,參考https://www.cnblogs.com/zhaozhan/archive/2012/06/01/2529384.html,解決輸入一下命令:
set-executionpolicy remotesigned
下面執行開始需要互動配置瞭如下(如果出錯重新編譯的話,最好刪除編譯目錄下,建的venv文件夾):
- (venv) PS D:\Users\tensorflow-1.12.0> .\build.ps1 `
- >> -BazelBuildParameters $parameterString `
- >> -BuildCppAPI -ReserveSource
-
- Select a Tensorflow version:
- [1] 1 - v1.13.1 [2] 2 - v1.12.0 [3] 3 - v1.11.0 [S] Select another version [?] 幫助 (默認值爲“1”): 2
- ...
- Starting local Bazel server and connecting to it...
- .
- INFO: Starting clean.
- WARNING: --batch mode is deprecated. Please instead explicitly shut down your Bazel server using the command "bazel shutdown".
- You have bazel 0.15.0 installed.
- Do you wish to build TensorFlow with Apache Ignite support? [Y/n]: n
- No Apache Ignite support will be enabled for TensorFlow.
-
- Do you wish to build TensorFlow with XLA JIT support? [y/N]: n
- No XLA JIT support will be enabled for TensorFlow.
-
- Do you wish to build TensorFlow with ROCm support? [y/N]: n
- No ROCm support will be enabled for TensorFlow.
-
- Do you wish to build TensorFlow with CUDA support? [y/N]: y
- CUDA support will be enabled for TensorFlow.
-
- Please specify the CUDA SDK version you want to use. [Leave empty to default to CUDA 9.0]:
-
-
- Please specify the location where CUDA 9.0 toolkit is installed. Refer to README.md for more details. [Default is C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v9.0]: D:\Users\CUDA9.0
-
- Please specify the cuDNN version you want to use. [Leave empty to default to cuDNN 7]:
-
-
- Please specify the location where cuDNN 7 library is installed. Refer to README.md for more details. [Default is D:/Users/CUDA9.0]:
-
-
- Please specify a list of comma-separated Cuda compute capabilities you want to build with.
- You can find the compute capability of your device at: https://developer.nvidia.com/cuda-gpus.
- Please note that each additional compute capability significantly increases your build time and binary size. [Default is: 3.5,7.0]: 5.0
-
-
- Please specify optimization flags to use during compilation when bazel option "--config=opt" is specified [Default is /arch:AVX]:
-
-
- Would you like to override eigen strong inline for some C++ compilation to reduce the compilation time? [Y/n]: y
- Eigen strong inline overridden.
-
- Starting local Bazel server and connecting to it...
- .....................
- WARNING: The following configs were expanded more than once: [cuda]. For repeatable flags, repeats are counted twice and may lead to unexpected behavior.
編譯好以後,將
7.vs無法解析的外部符號與powershell編譯出現無法解析的外部符號錯誤
出現下面問題都按這個步驟做,只不過第4步中,vs是加上,powershell可能會加上或者去掉tf_exported_symbols_msvc.lds文件EXPORTS中的鏈接:
1.如果是vs無法解析的外部符號,這樣解決,錯誤與解決方案如下(爲了能清):
- 錯誤:
無法解析的外部符號 "public: virtual __cdecl tensorflow::internal::LogMessage::~LogMessage(void)" (??1LogMessage@internal@tensorflow@@UEAA@XZ),該符號在函數 "public: void __cdecl tensorflow::internal::LogMessage::`vbase destructor'(void)" (??_DLogMessage@internal@tensorflow@@QEAAXXZ) 中被引用huawei_video_tensorflow
- 解決方法:函數調用後與(該符號該符號在函數)前括號內的內容添加
??1LogMessage@internal@tensorflow@@UEAA@XZ
複製到tensorflow-1.12.0\source\tensorflow\tf_exported_symbols_msvc.lds文件的EXPORTS最後面。
2.如果powershell編譯出現無法解析的外部符號錯誤
- 錯誤:
無法解析的外部符號 "public: virtual __cdecl tensorflow::internal::LogMessage::~LogMessage(void)" (??1LogMessage@internal@tensorflow@@UEAA@XZ),該符號在函數 "public: void __cdecl tensorflow::internal::LogMessage::`vbase destructor'(void)" (??_DLogMessage@internal@tensorflow@@QEAAXXZ) 中被引用
- 解決方法:(該符號該符號在函數)前括號內的內容,從tensorflow-1.12.0\source\tensorflow\tf_exported_symbols_msvc.lds文件中所有包含的部分全部刪除:
??1LogMessage@internal@tensorflow@@UEAA@XZ
最後不斷解決重複解決遇到的頭文件與無法解析外部符號問題,終於編譯工程並能夠正常運行程序了。
通過這個步驟,在另外一臺機器上也測試成功了:
8.準備tensorflow的dll、lib、include
新建tensorflow文件夾,然後在目錄下再新建兩個目錄,dll與lib
dll:將上面圖片中路徑的libtensorflow_cc.so改名爲tensorflow_cc.dll,放到tensorflow\dll目錄下(vs調試時會調用的文件,路徑要加入到環境變量中,或者也可以將文件加到當前工程運行的release目錄下)
lib:將上面圖片路徑下的liblibtensorflow_cc.so.ifso改名爲tensorflow_cc.lib,放到tensorflow\lib目錄下 (路徑要加入到vs包含庫目錄中,或將文件放到當前工程運行的release目錄下,並添加庫目錄)
include:將bazel_source目錄下,不管是快捷方式(可能會複製不全,可以去上圖中目錄下,找到對應文件,反正一定要複製全,不要有空目錄,不然就會缺失頭文件,報錯),還是文件內的文件,都需要一起復制到tensorflow下。
下面是我自己編譯好的tensorflow-v1.12.0 dll,lib,include,如何出現缺鏈接庫的話,自己可以將錯誤的符號加入tf_exported_symbols_msvc.lds文件中,自己編譯生成自己的dll與lib文件:
提取碼:
0ugf
可以使用Dependency Walker工具查看該DLL:
8.1 簡版tensorflow的dll、lib、include
此處感謝cjAlgthm!!!
1.首先給出文件結構圖:
libtensorflow │ ├─bin │ tensorflow_cc.dll │ ├─include │ ... │ └─lib tensorflow_cc.lib
2.下載鏈接:
鏈接:libtensorflow
提取碼:rgzf
3.使用
--解壓後,將libtensorflow/include加入包含目錄;
--將lib路徑包含到庫目錄,也可以放到對應工程的生成目錄中,比如x64/Release/,同樣也需要包含到庫目錄下;(這裏我是放到對應工程的生成目錄下,使用的相對路徑)
且需要放到附加依賴項中:
--將dll放到對應工程的生成目錄中,比如x64/Release/,或者將路徑添加到系統環境變量中也可以。
4.如何整理此環境,可以參考
https://blog.csdn.net/carbon06/article/details/82588150
5.對於想更改tensorflow_cc名的
請注意:
不要改,很麻煩,lib裏面已經將dll名稱寫固定了; 想嘗試的,備份!!!lib修改後,有可能損壞。
6.demo測試結果
7.對tensorflow的庫做了如下修改
修改了下面三個文件:
將這幾個文件中的min與max修改成下面這樣:
- string_view.h
- line491-493
-
- private:
- static constexpr size_type kMaxSize =
- std::numeric_limits<difference_type>::max();
-
- 改爲
- private:
- static constexpr size_type kMaxSize =
- (std::numeric_limits<difference_type>::max)();
9.調用模型測試程序
下面是我自己的批處理測試單張圖像代碼:
- #define COMPILER_MSVC
- #define NOMINMAX
- #define PLATFORM_WINDOWS // 指定使用tensorflow/core/platform/windows/cpu_info.h
-
- #include<iostream>
- #include<opencv2/opencv.hpp>
- #include"tensorflow/core/public/session.h"
- #include "tensorflow/core/platform/env.h"
- #include <time.h>
-
- using namespace tensorflow;
- using namespace cv;
- using std::cout;
- using std::endl;
-
- int main() {
- const std::string model_path = "E:/c++work/tf_testdemo/tf_testdemo/latest_model_MobileUNet_0603.pb";// tensorflow模型文件,注意不能含有中文
- const std::string image_path = "E:/c++work/tf_testdemo/tf_testdemo/001_00045.png"; // 待inference的圖片grace_hopper.jpg
- // 設置輸入圖像
- cv::Mat img = cv::imread(image_path);
- cv::cvtColor(img, img, cv::COLOR_BGR2RGB);
- //resize(img, img, cv::Size(512, 512),0,0, INTER_NEAREST);
- int height = img.rows;
- int width = img.cols;
- int depth = img.channels();
-
- // 圖像預處理
- img = (img - 0) / 255.0;
- img.convertTo(img, CV_32F);
-
- // 取圖像數據,賦給tensorflow支持的Tensor變量中
- const float* source_data = (float*)img.data;
- tensorflow::Tensor input_tensor(DT_FLOAT, TensorShape({ 1, height, width, depth })); //這裏只輸入一張圖片,參考tensorflow的數據格式NHWC
- auto input_tensor_mapped = input_tensor.tensor<float, 4>(); // input_tensor_mapped相當於input_tensor的數據接口,“4”表示數據是4維的。後面取出最終結果時也能看到這種用法
-
- // 把數據複製到input_tensor_mapped中,實際上就是遍歷opencv的Mat數據
- for (int i = 0; i < height; i++) {
- const float* source_row = source_data + (i * width * depth);
- for (int j = 0; j < width; j++) {
- const float* source_pixel = source_row + (j * depth);
- for (int c = 0; c < depth; c++) {
- const float* source_value = source_pixel + c;
- input_tensor_mapped(0, i, j, c) = *source_value;
- //printf("%d");
- }
- }
- }
- // 初始化tensorflow session
- Session* session;
- Status status = NewSession(SessionOptions(), &session);
- if (!status.ok()) {
- std::cerr << status.ToString() << endl;
- return -1;
- }
- else {
- cout << "Session created successfully" << endl;
- }
-
-
- // 讀取二進制的模型文件到graph中
- tensorflow::GraphDef graph_def;
- status = ReadBinaryProto(Env::Default(), model_path, &graph_def);
- if (!status.ok()) {
- std::cerr << status.ToString() << endl;
- return -1;
- }
- else {
- cout << "Load graph protobuf successfully" << endl;
- }
-
-
- // 將graph加載到session
- status = session->Create(graph_def);
- if (!status.ok()) {
- std::cerr << status.ToString() << endl;
- return -1;
- }
- else {
- cout << "Add graph to session successfully" << endl;
- }
- // 輸入inputs,“ x_input”是我在模型中定義的輸入數據名稱
- std::vector<std::pair<std::string, tensorflow::Tensor>> inputs = {
- { "input_x:0", input_tensor },
- };
-
- // 輸出outputs
- //tensorflow::Tensor outputs(DT_FLOAT, TensorShape({ 1, height, width, depth }));
- std::vector<tensorflow::Tensor> outputs;
-
- //批處理識別
- for (int k = 0; k < 200; k++) {
-
- double start = clock();
- // 運行會話,計算輸出"x_predict",即我在模型中定義的輸出數據名稱,最終結果保存在outputs中
- status = session->Run(inputs, { "outimage:0" }, {}, &outputs);
- if (!status.ok()) {
- std::cerr << status.ToString() << endl;
- return -1;
- }
- else {
- cout << "Run session successfully" << endl;
- }
-
- // 下面進行輸出結果的可視化
- // 下面進行輸出結果的可視化
- tensorflow::Tensor output = std::move(outputs.at(0)); // 模型只輸出一個結果,這裏首先把結果移出來(也爲了更好的展示)
- auto out_shape = output.shape(); // 這裏的輸出結果爲1x4x16
- auto out_val = output.tensor<float, 4>(); // 與開頭的用法對應,4代表結果的維度
-
- cv::Mat out_image(height, width, CV_32FC(depth));
- float* data = (float*)out_image.data;
- for (int y = 0; y < height; ++y)
- {
- float* dataRow = data + (y * width * depth);
- for (int x = 0; x < width; ++x)
- {
- float* dataPixel = dataRow + (x * depth);
- for (int c = 0; c < depth; ++c)
- {
- float* dataValue = dataPixel + c;
- *dataValue = out_val(0, y, x, c);
- }
- }
-
- }
-
-
- cv::imshow("outimage", out_image);
- cv::waitKey(1);
- double finish = clock();
- double duration = (double)(finish - start) / CLOCKS_PER_SEC;
- cout <<"spend time:" << duration << endl;
- }
- /*cv::cvtColor(out_image, out_image, cv::COLOR_RGB2BGR);
- cv::cvtColor(out_image_final, out_image_final, cv::COLOR_RGB2BGR);
- cv::imwrite("E:/c++work/tf_testdemo/tf_testdemo/001_00045_out.png", out_image*255);
- cv::imwrite("E:/c++work/tf_testdemo/tf_testdemo/001_00045_out_final.png", out_image_final * 255);*/
- system("PAUSE");
- }
環境配置:
包含目錄:
E:\Users\tensorflow\bazel-source;E:\Users\tensorflow\bazel-source\external\org_tensorflow;E:\Users\tensorflow\bazel-source\external\protobuf_archive\src;E:\Users\tensorflow\bazel-source\external\com_google_absl;E:\Users\tensorflow\bazel-source\external\eigen_archive;E:\Users\tensorflow\bazel-source\bazel-out\x64_windows-opt\genfiles;E:\Users\tensorflow;E:\Opencv-vs2015\opencv342\include;E:\Opencv-vs2015\opencv342\include\opencv;E:\Opencv-vs2015\opencv342\include\opencv2;
E:\Users\tensorflow\bazel-source;E:\Users\tensorflow\bazel-source\external\org_tensorflow;E:\Users\tensorflow\bazel-source\external\protobuf_archive\src;E:\Users\tensorflow\bazel-source\external\com_google_absl;E:\Users\tensorflow\bazel-source\external\eigen_archive;E:\Users\tensorflow\bazel-source\bazel-out\x64_windows-opt\genfiles;E:\Users\tensorflow;E:\Opencv-vs2015\opencv342\include;E:\Opencv-vs2015\opencv342\include\opencv;E:\Opencv-vs2015\opencv342\include\opencv2;
庫目錄:
E:\Opencv-vs2015\opencv342\lib;E:\c++work\tf_testdemo\x64\Debug;
鏈接器輸入附加依賴項:
opencv_world342d.lib;opencv_world342.lib;tensorflow_cc.lib;
10.嘗試編譯debug版本
因爲項目需要,我自己也嘗試了編譯tensorflow的debug版本:
- $parameterString = "-c fastbuild --config=cuda --define=no_tensorflow_py_deps=true --copt=-nvcc_options=disable-warnings //tensorflow:libtensorflow_cc.so --verbose_failures"
- .\build.ps1 `
- -BazelBuildParameters $parameterString `
- -BuildCppAPI -ReserveSource -ReserveVenv
與
- $parameterString = "-c dbg--config=cuda --define=no_tensorflow_py_deps=true --copt=-nvcc_options=disable-warnings //tensorflow:libtensorflow_cc.so --verbose_failures"
- .\build.ps1 `
- -BazelBuildParameters $parameterString `
- -BuildCppAPI -ReserveSource -ReserveVenv
但是卡在了下面的錯誤
tensorflow::io::GetTempFilename
這個錯誤相關的github issue有這兩個issue1,issue2和我自己開了一個issue,但是都建議編譯release version。所以這裏我暫停了繼續編譯debug版本。
11.遇到的問題
1.c++運行相同的pb模型,使用gpu,測試單張圖像速度比python慢?
python測試單張圖像20ms,c++測試單張圖像26ms;
2.編譯時遇到max的問題:
- string_view.h
- line491-493
-
- private:
- static constexpr size_type kMaxSize =
- std::numeric_limits<difference_type>::max();
-
- 改爲
- private:
- static constexpr size_type kMaxSize =
- (std::numeric_limits<difference_type>::max)();
其他出錯的頭文件部分一一修改。
12.參考
[1]編譯windows tensorflowv1.11-1.13 dll release version
[3]Tensorflow C++的Tensor和OpenCV的Mat相互轉換
這裏非常感謝TerryBryant,給我解答問題,應用部分主要參考他的;也非常感謝guikarist分享的編譯方法。