簡體   English   中英

在 Android Studio 項目中使用 tensorflow lite C++ API 的問題

[英]Problems with using tensorflow lite C++ API in Android Studio Project

我目前正在研究一個關於神經網絡的項目。 為此,我想構建一個 Android 應用程序,它應該使用 tensorflow [lite] 來解決一些對象檢測/識別問題。

由於我希望代碼盡可能具有可移植性,因此我想用 C++ 編寫大部分代碼,從而在 Java API / 包裝器上使用 tensorflow lite 的 C++ API。 因此,我修改了 tensorflow/contrib/lite/BUILD 並添加了以下內容以便能夠創建共享的 tensorflow 庫。

cc_binary(
name = "libtensorflowLite.so",

linkopts=["-shared", "-Wl"],
linkshared=1,

copts = tflite_copts(),
deps = [
    ":framework",
    "//tensorflow/contrib/lite/kernels:builtin_ops",
],

)

(這是基於這個問題的答案: https : //github.com/tensorflow/tensorflow/issues/17826

然后我用

bazel build //tensorflow/contrib/lite:libtensorflowLite.so --crosstool_top=//external:android/crosstool --cpu=arm64-v8a --host_crosstool_top=@bazel_tools//tools/cpp:toolchain --cxxopt="-std=c++11"

最終構建它。

之后我前往 Android Studio 並建立了一個基本項目。 將共享庫添加到項目中,我參考了這個例子:

https://github.com/googlesamples/android-ndk/tree/840858984e1bb8a7fab37c1b7c571efbe7d6eb75/hello-libs

我還為 flatbuffers 添加了所需的依賴項。

構建/編譯過程成功而沒有任何鏈接器錯誤(好吧,至少在嘗試了幾個小時之后......)。

然后該 APK 成功安裝在 Android 設備上,但啟動后立即崩潰。 Logcat 提供以下輸出:

04-14 20:09:59.084 9623-9623/com.example.hellolibs E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.hellolibs, PID: 9623
    java.lang.UnsatisfiedLinkError: dlopen failed: library "/home/User/tensorflowtest/app/src/main/cpp/../../../../distribution/tensorflow/lib/x86/libtensorflowLite.so" not found
        at java.lang.Runtime.loadLibrary0(Runtime.java:1016)
        at java.lang.System.loadLibrary(System.java:1657)
        at com.example.hellolibs.MainActivity.<clinit>(MainActivity.java:36)
        at java.lang.Class.newInstance(Native Method)
        at android.app.Instrumentation.newActivity(Instrumentation.java:1174)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2669)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6494)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

我在一個 android x86 模擬器和一個真正的 arm64-v8a android 智能手機上試過這個。

所以對我來說,這看起來像是在啟動時應用程序嘗試加載 tensorflowLite 共享庫,但無法找到它。 使用 zip 存檔管理器打開 apk 我可以驗證依賴於平台(arm、x86)的 .so 文件是否按預期打包到 APK 中(通過將以下內容添加到 build.gradle:

sourceSets {
        main {
            // let gradle pack the shared library into apk
            jniLibs.srcDirs = ['../distribution/tensorflow/lib']
        }
})

我不明白的是為什么它會在我將它放在我的 Ubuntu 17.10 PC 上的路徑中查找該庫。 所以,我認為我在嘗試修改我之前提到的關於將外部庫添加到 Android Studio 項目的示例時犯了一個錯誤。 這就是為什么我下載了整個項目並在 Android Studio 中打開它並驗證該示例按預期工作的原因。 之后,我將示例 libgperf.so 替換為 libtensorflowLite.so 並保留其他所有內容,尤其是 CMakeLists.txt,保持不變。 但是我再次得到完全相同的錯誤,因此我懷疑這是 libtensorflowLite 庫本身的問題,而不是 android 項目的問題(盡管這只是我的猜測)。

我正在開發 android studio 3.1.1、NDK 版本 14 和 API 級別 24(Android 7.0)。 如果有人知道什么可能是錯的,任何幫助將不勝感激。 我也願意接受任何其他方法,這些方法允許我將 tensorflow lite 與 C++ 一起用於 android 應用程序。

非常感謝,

馬丁

我只記得幾周前我問過這個問題。 同時,我找到了問題的解決方案,TensorflowLite 現在很好地嵌入到我的 Android 項目中,我在那里使用 C++ API 進行所有編程!

問題是我構建的 Tensorflow 共享庫不包含 soname。 因此,在構建過程中,庫被剝離並且沒有找到名稱,路徑被用作“名稱”。 我注意到,當我使用 linux“strings”工具進一步調查我的 native-lib.so(然后由應用程序加載的 NDK C++ 庫)時。 在這里我發現確實是從“/home/User/tensorflowtest/app/src/main/cpp/../../../../distribution/tensorflow/lib/x86/libtensorflowLite”加載庫的路徑.so”被設置。 在 BUILD 文件的構建選項中添加“-Wl,-soname=libtensorflowLite.so”修復了這個問題! 您可以在下面找到我使用的整個規則。

由於缺乏解釋,設置一切都很痛苦(似乎 TensorflowLite 主要通過 Android 上的 Java API 使用?),我想就如何在 Android Studio 中使用 TensorflowLite 的 C++ API 提供一個簡短的指導(來自 Android NDK 項目)。

1.為您的架構構建庫

要使用 C++ API,首先需要構建 TensorflowLite 庫。 為此,將以下規則添加到 tensorflow/contrib/lite 中的 BUILD 文件中:

cc_binary(

name = "libtensorflowLite.so",
linkopts=[
    "-shared", 
    "-Wl,-soname=libtensorflowLite.so",
],
linkshared = 1,
copts = tflite_copts(),
deps = [
    ":framework",
    "//tensorflow/contrib/lite/kernels:builtin_ops",
],

)

注意:有了這個,就可以構建一個共享庫了! 靜態的也可能有效。

現在您可以使用構建庫

bazel build //tensorflow/contrib/lite:libtensorflowLite.so --crosstool_top=//external:android/crosstool --cpu=arm64-v8a --host_crosstool_top=@bazel_tools//tools/cpp:toolchain --cxxopt="-std=c++11"

如果要支持多種架構,則必須多次構建庫並相應地更改 --cpu 標志。

注意:這至少適用於 arm64-v8a 和 armeabi-v7a(尚未使用 MIPS 對其進行測試,因此這可能也適用)。 但是,在 x86 設備上,我收到了本主題中已經提到的“atomic_store_8”錯誤: https : //github.com/tensorflow/tensorflow/issues/16589

2.添加要包含在您的 Android Studio 項目中的庫和所需的頭文件

構建庫后,您現在需要確保它也鏈接到您的應用程序(更具體地說:鏈接到您的 Android NDK 庫,在我的例子中被命名為“native-lib”)。 我將簡要介紹如何執行此操作,但是如果您需要更詳細的解釋,您可以參考我在初始問題中提供的 github 鏈接: https : //github.com/googlesamples/android-ndk/tree/ 840858984e1bb8a7fab37c1b7c571efbe7d6eb75/hello-libs

2.1. 在您的 Android Studio 項目中,打開 CMakeLists.txt

2.2. 添加以下內容:

    # This will create a new "variable" holding the path to a directory
    # where we will put our library and header files.
    # Change this to your needs
    set(distribution_DIR ${CMAKE_SOURCE_DIR}/distribution)

    # This states that there exists a shared library called libtensorflowLite
    # which will be imported (means it is not built with the rest of the project!)
    add_library(libtensorflowLite SHARED IMPORTED)

    # This indicates where the libtensorflowLite.so for each architecture is found relative to our distribution directory
    set_target_properties(libtensorflowLite PROPERTIES IMPORTED_LOCATION
        ${distribution_DIR}/lib/${ANDROID_ABI}/libtensorflowLite.so)

    # This indicates where the header files are found relative to our distribution dir
    target_include_directories(native-lib PRIVATE
                       ${distribution_DIR}/include)

    # Finally, we make sure our libtensorflowLite.so is linked to our native-lib and loaded during runtime 
    target_link_libraries( # Specifies the target library.
                   native-lib
                   libtensorflowLite
                   # Links the target library to the log library
                   # included in the NDK.
                   ${log-lib} )

2.3. 打開您的模塊的 build.gradle:App(不是項目之一!)

2.4. 確保我們的庫將打包到您的 APK 中

在 Android 部分添加以下內容:

    sourceSets {
        main {
            // let gradle pack the shared library into apk
            jni.srcDirs = []
            jniLibs.srcDirs = ['distribution/lib']
        }
    }

您可能需要根據需要編輯路徑:此處的文件將被打包到 lib 目錄中的 .apk 中。

3. 包括 flatbuffers

TensorflowLite 使用 flatbuffers 序列化庫。 我想如果您使用 bazel 構建項目,這將自動添加。 但使用 Android Studio 時並非如此。 當然,您也可以添加靜態或共享庫。 然而,對我來說,每次讓 flatbuffers 與我的應用程序的其余部分一起編譯是最簡單的(它不是那么大)。 我將所有 flatbuffers *.cpp源文件復制到我的項目並將它們添加到 CMakeLists。

4. 復制 TensorflowLite 和 flatbuffers 所需的頭文件

在 3. 我只是將 cpp 文件復制到我的項目中。 但是,頭文件需要位於我們在步驟 2.2 中在 target_include_directories 中設置的目錄中。

因此,繼續將所有 flatbuffers(來自 flatbuffers 存儲庫)*.h 文件復制到此目錄。 接下來,從 TensorflowLite 存儲庫,您需要 tensorflow/contrib/lite 目錄中的所有頭文件。 但是,您應該保留文件夾結構

對我來說,它看起來像這樣:

  • 分布
      • arm64-v8a
        • libtensorflowLite
      • armeabi-v7a
        • libtensorflowLite
    • 包括
      • 平面緩沖區
      • 張量流
        • 貢獻
          • 精簡版
            • 內核
            • 納皮
            • 模式
            • 工具

所以,如果我沒有忘記任何事情,現在一切都應該正確設置! 希望這有幫助,它對你有用,就像對我一樣;)

最好的問候,

馬丁

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM