【问题标题】:Problems with using tensorflow lite C++ API in Android Studio Project在 Android Studio 项目中使用 tensorflow lite C++ API 的问题
【发布时间】:2018-09-24 20:51:10
【问题描述】:

我目前正在从事一个有关神经网络的项目。 为此,我想构建一个 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 中打开它并验证该示例是否按预期工作。之后,我用 libtensorflowLite.so 替换了示例 libgperf.so 并保留了其他所有内容,尤其是 CMakeLists.txt,保持不变。 但是我再次遇到完全相同的错误,因此我怀疑这是 libtensorflowLite 库本身的问题,而不是 android 项目的问题(尽管这只是我的猜测)。

我正在开发 android studio 3.1.1、NDK 版本 14 和 API 级别 24 (Android 7.0)。 如果有人知道可能出了什么问题,我们将不胜感激任何帮助。 我也愿意接受任何其他允许我将 tensorflow lite 与 C++ 一起用于 android 应用程序的方法。

非常感谢,

马丁

【问题讨论】:

    标签: android c++ tensorflow android-ndk android-studio-3.0


    【解决方案1】:

    我只记得几周前我问过这个问题。 同时,我找到了问题的解决方案,TensorflowLite 现在很好地嵌入到我的 Android 项目中,我使用 C++ API 进行所有编程!

    问题是我构建的 Tensorflow 共享库不包含 soname。因此,在构建过程中,库被剥离并且由于没有找到名称,所以路径被用作“名称”。我注意到,虽然我使用 linux“字符串”工具进一步调查了我的 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 使用?),我想就如何使用 TensorflowLite 的 C++ API 提供一个简短的指导Android Studio(来自 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:应用程序(不是项目之一!)

    2.4。确保将我们的库打包到您的 APK 中

    在 Android 部分添加这个:

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

    您可能需要根据需要编辑路径:此处的文件将打包到 lib 目录中的 .apk 中。

    3。包括平面缓冲区

    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
      • 包括
        • 平面缓冲区
        • 张量流
          • 贡献
            • 精简版
              • 内核
              • napi
              • 架构
              • 工具

    所以,如果我没有忘记任何事情,那么现在一切都应该正确设置! 希望这对您有所帮助,并且对您有用;)

    最好的问候,

    马丁

    【讨论】:

    • 当我运行这个命令“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" " 我得到这个错误 "private/var/tmp/_bazel_aqeeliqbal/87ac5c09a3d175ad1ae7ebdfbb941b38/external/flatbuffers/BUILD.bazel:94:1: C++规则'@flatbuffers//:flatc'的编译失败(退出1)外部/flatbuffers/src/idl_gen_js.cpp:473:22:错误:命名空间'std'中没有名为'to_string'的成员返回“NS”+ std: :to_string( ~~~~~^
    • 对于像我这样不知道的新手,必须先运行./configure,然后才能使用 bazel 构建 so。
    • 嘿@martinharz 你有没有在使用libtensorflowlite.so 编译时遇到错误undefined reference to 'tflite::InterpreterBuilder::operator()?因为那是唯一未定义的引用(它会找到其他所有内容)。
    • 对于其他人来说,这个错误是因为我没有使用 NDK18
    • 嘿。当你这样做时。您如何确保 C++ api 使用为 Android 优化的 GPU 内核?还是一切都在 CPU 上运行?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-12-05
    • 2015-02-04
    • 1970-01-01
    • 2019-10-04
    • 2022-01-15
    • 1970-01-01
    • 2019-08-01
    相关资源
    最近更新 更多