在Android Studio项目中使用tensorflow lite C++API的问题



我目前正在从事一个关于神经网络的项目。为此,我想构建一个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"

以最终建立它。

之后,我前往安卓工作室,建立了一个基本的项目。为了将共享库添加到项目中,我参考了以下示例:

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

我还添加了平坦缓冲区所需的依赖项。

构建/编译过程成功,没有任何链接器错误(好吧,至少在尝试了几个小时之后…)

APK随后成功安装在安卓设备上,但启动后立即崩溃。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)

我在安卓x86模拟器和真正的arm64-v8a安卓智能手机上尝试过这个。

因此,对我来说,这看起来像是在启动时,应用程序试图加载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项目添加外部库的示例时犯了一个错误。这就是为什么我下载了整个项目并在AndroidStudio中打开它,并验证了该示例是否按预期工作。之后,我用libtensorflowLite.so替换了示例libgperf.so,并保留了其他所有内容,尤其是CMakeLists.txt。但我又遇到了完全相同的错误,因此我怀疑这是libtensorflowLite库本身的问题,而不是android项目的问题(尽管这只是我的猜测)。

我正在安卓工作室3.1.1,NDK版本14和API 24级(安卓7.0)上工作。如果有人知道可能出了什么问题,我们将不胜感激。我也对任何其他方法持开放态度,这些方法允许我将tensorflow lite与C++一起用于android应用程序。

非常感谢

Martin

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

问题是我构建的Tensorflow共享库不包含soname。因此,在构建过程中,库被剥离,由于找不到名称,路径被用作"名称"。我注意到,当我使用linux"字符串"工具进一步研究我的native-lib.so(NDK C++库,然后由应用程序加载)时。在这里,我发现确实设置了从"/home/User/tensorflowtest/app/src/main/cpp/../../../distribution/tensorflow/lib/x86/libtensorflow Lite.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库中,在我的情况下,它被命名为"原生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使用扁平缓冲区序列化库。我想如果你使用bazel构建你的项目,这会自动添加。但使用Android Studio时情况并非如此。当然,您也可以添加一个静态库或共享库。然而,对我来说,最简单的方法是每次都让flatbuffers与我的应用程序的其他部分一起编译(它没有那么大)。我将所有的flatbuffers*.cpp源文件复制到我的项目中,并将它们添加到CMakeLists中。

4.复制TensorflowLite和平板缓冲区所需的标头

在3。我刚刚将cpp文件复制到我的项目中。但是,头文件需要位于我们在步骤2.2中在target_include_directories中设置的目录中。

因此,继续将所有的flatbuffers(从flatbuffer存储库)*.h文件复制到此目录。接下来,从TensorflowLite存储库中,您需要tensorflow/contrib/lite目录中的所有头文件。但是,您应该保留文件夹结构

对我来说,它看起来像这样:

  • 分布
    • lib
      • arm64-v8a
        • libtensorflowLite
      • 亚美尼亚-v7a
        • libtensorflowLite
    • 包括
      • 平板缓冲器
      • tensorflow
        • contrib
          • lite
            • 内核
            • nnapi
            • 模式
            • 工具

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

致问候,

Martin

最新更新