一、背景
最近需要在android上用tensorflow做一些东西,网上找了很多教程,关于windows环境下的教程不多,而且运行起来报错非常多,所以就写篇文章记录一下自己的实践经历。
二、实验效果
先给出最终运行好的实验效果图,首先是安装好了官方demo的4个app,分别是物体分类,目标检测,语言识别以及风格转换:
下面打开风格转化app,随便测试一下,可以简单看一下效果:
三、实验过程
1. 准备工作
这一步主要是确认自己的开发环境和准备相关材料。首先先给出一些网上比较好的教程供参考:
[1]在Windows7上用Android Studio编译Tensorflow_Android_Demo
[2]window 下 Android studio 上运行tensorflow
开发环境如下:
win10
Android studio
下面就是准备工作,我们需要准备的材料包括
(1)android studio开发环境
首先下载andriod studio并安装,配置好开发环境。网上教程很多,可以参考:Android Studio的下载、安装与配置
(2)tensorflow的官方demo
在github上找到tensorflow的官方源码,地址:https://github.com/tensorflow/tensorflow
进入后直接打包download就可以:
解压好之后,找到路径'./tensorflow-master/tensorflow/examples/android'下的文件,这些文件就是我们需要用到的文件,可以直接将其拷贝出来,放入到新的文件夹:
(3)预下载的辅助文件
为了节省时间,还需预下载一部分文件,网上其实也很好找,我把这部分文件放到了百度云上,下面直接给出地址:
百度云链接:https://pan.baidu.com/s/1rQLimZrNwPRJCijBFBX_hQ
提取码:psx2
下载好之后,在工程目录下直接放入gradleBuild文件夹及其文件,并新建libs文件夹,放入libtensorflow_demo.so文件。
2. 修改build.gradle
这一步是关键,修改的内容比较多,大部分可以根据报错来直接修改,下面来详细说一下修改的部分:
(1)修改jcenter()
将build.gradle中的所有jcenter() 修改为:
jcenter { url "http://jcenter.bintray.com" }
目的是下载相关的依赖包。
(2)修改gradle:3.0.1的版本
由于android studio的版本比较新,因此原demo基于老版本会报错:Could not find com.android.tools.build:gradle:3.0.1.。所以我们直接将gradle的版本修改至最新就可以了。
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
classpath 'org.apache.httpcomponents:httpclient:4.5.5'
}
(3)修改nativeBuildSystem的类型
直接修改为:
def nativeBuildSystem = 'none'
(4)修改buildToolsVersion版本
将buildToolsVersion的版本修改为最新版本即可:
buildToolsVersion '26.0.2'
(5)将compile用implementation替代并全部添加到defaultconfig中
运行原demo中的代码会提示:Configuration 'compile' is obsolete and has been replaced with 'implementation' adn 'api'。就是说compile在新版本中已经用implementation替代了,并需要写到defaultconfig中,因此注释掉原demo中含有compile名字的变量,并添加:
defaultConfig {
compileSdkVersion 28
minSdkVersion 21
targetSdkVersion 28
} //添加在android{}内
3. 开启VT技术
上述修改的地方修改完之后,直接在android studio中运行还是会报错:emulator: ERROR: x86 emulation currently requires hardware acceleration! 解决办法可以参考链接:https://www.cnblogs.com/csulennon/p/4178404.html
一方面开启硬件加速,另一方面在BIOS中打开Virtualization Technology即可。
4. 运行
完成以上的所有设置,make project,然后运行,即可在虚拟手机中找到做好的4个app了。
四、附build.gradle中所有代码
// This file provides basic support for building the TensorFlow demo
// in Android Studio with Gradle.
//
// Note that Bazel is still used by default to compile the native libs,
// and should be installed at the location noted below. This build file
// automates the process of calling out to it and copying the compiled
// libraries back into the appropriate directory.
//
// Alternatively, experimental support for Makefile builds is provided by
// setting nativeBuildSystem below to 'makefile'. This will allow building the demo
// on Windows machines, but note that full equivalence with the Bazel
// build is not yet guaranteed. See comments below for caveats and tips
// for speeding up the build, such as enabling ccache.
// NOTE: Running a make build will cause subsequent Bazel builds to *fail*
// unless the contrib/makefile/downloads/ and gen/ dirs are deleted afterwards.
// The cmake build only creates libtensorflow_demo.so. In this situation,
// libtensorflow_inference.so will be acquired via the tensorflow.aar dependency.
// It is necessary to customize Gradle's build directory, as otherwise
// it will conflict with the BUILD file used by Bazel on case-insensitive OSs.
project.buildDir = 'gradleBuild'
getProject().setBuildDir('gradleBuild')
buildscript {
repositories {
jcenter { url "http://jcenter.bintray.com" }
maven {
url 'https://maven.google.com'
}
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
classpath 'org.apache.httpcomponents:httpclient:4.5.5'
}
}
allprojects {
repositories {
jcenter { url "http://jcenter.bintray.com" }
google()
}
}
// set to 'bazel', 'cmake', 'makefile', 'none'
def nativeBuildSystem = 'none'
// Controls output directory in APK and CPU type for Bazel builds.
// NOTE: Does not affect the Makefile build target API (yet), which currently
// assumes armeabi-v7a. If building with make, changing this will require
// editing the Makefile as well.
// The CMake build has only been tested with armeabi-v7a; others may not work.
def cpuType = 'armeabi-v7a'
// Output directory in the local directory for packaging into the APK.
def nativeOutDir = 'libs/' + cpuType
// Default to building with Bazel and override with make if requested.
def nativeBuildRule = 'buildNativeBazel'
def demoLibPath = '../../../bazel-bin/tensorflow/examples/android/libtensorflow_demo.so'
def inferenceLibPath = '../../../bazel-bin/tensorflow/contrib/android/libtensorflow_inference.so'
// Override for Makefile builds.
if (nativeBuildSystem == 'makefile') {
nativeBuildRule = 'buildNativeMake'
demoLibPath = '../../../tensorflow/contrib/makefile/gen/lib/android_' + cpuType + '/libtensorflow_demo.so'
inferenceLibPath = '../../../tensorflow/contrib/makefile/gen/lib/android_' + cpuType + '/libtensorflow_inference.so'
}
// If building with Bazel, this is the location of the bazel binary.
// NOTE: Bazel does not yet support building for Android on Windows,
// so in this case the Makefile build must be used as described above.
def bazelLocation = '/usr/local/bin/bazel'
// import DownloadModels task
project.ext.ASSET_DIR = projectDir.toString() + '/assets'
project.ext.TMP_DIR = project.buildDir.toString() + '/downloads'
apply plugin: 'com.android.application'
android {
//compileSdkVersion 28
buildToolsVersion '28.0.3'
if (nativeBuildSystem == 'cmake') {
defaultConfig {
applicationId = 'org.tensorflow.demo'
//minSdkVersion 25
//targetSdkVersion 28
ndk {
abiFilters "${cpuType}"
}
externalNativeBuild {
cmake {
arguments '-DANDROID_TOOLCHAIN=gcc', '-DANDROID_STL=gnustl_static'
}
}
}
externalNativeBuild {
cmake {
path './jni/CMakeLists.txt'
}
}
}
lintOptions {
abortOnError false
}
sourceSets {
main {
if (nativeBuildSystem == 'bazel' || nativeBuildSystem == 'makefile') {
// TensorFlow Java API sources.
java {
srcDir '../../java/src/main/java'
exclude '**/examples/**'
}
// Android TensorFlow wrappers, etc.
java {
srcDir '../../contrib/android/java'
}
}
// Android demo app sources.
java {
srcDir 'src'
}
manifest.srcFile 'AndroidManifest.xml'
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = [project.ext.ASSET_DIR]
jniLibs.srcDirs = ['libs']
}
debug.setRoot('build-types/debug')
release.setRoot('build-types/release')
}
defaultConfig {
compileSdkVersion 28
minSdkVersion 21
targetSdkVersion 28
}
}
task buildNativeBazel(type: Exec) {
workingDir '../../..'
commandLine bazelLocation, 'build', '-c', 'opt', \
'tensorflow/examples/android:tensorflow_native_libs', \
'--crosstool_top=//external:android/crosstool', \
'--cpu=' + cpuType, \
'--host_crosstool_top=@bazel_tools//tools/cpp:toolchain'
}
task buildNativeMake(type: Exec) {
environment "NDK_ROOT", android.ndkDirectory
// Tip: install ccache and uncomment the following to speed up
// builds significantly.
// environment "CC_PREFIX", 'ccache'
workingDir '../../..'
commandLine 'tensorflow/contrib/makefile/build_all_android.sh', \
'-s', \
'tensorflow/contrib/makefile/sub_makefiles/android/Makefile.in', \
'-t', \
'libtensorflow_inference.so libtensorflow_demo.so all' \
, '-a', cpuType \
//, '-T' // Uncomment to skip protobuf and speed up subsequent builds.
}
task copyNativeLibs(type: Copy) {
from demoLibPath
from inferenceLibPath
into nativeOutDir
duplicatesStrategy = 'include'
dependsOn nativeBuildRule
fileMode 0644
}
tasks.whenTaskAdded { task ->
if (nativeBuildSystem == 'bazel' || nativeBuildSystem == 'makefile') {
if (task.name == 'assembleDebug') {
task.dependsOn 'copyNativeLibs'
}
if (task.name == 'assembleRelease') {
task.dependsOn 'copyNativeLibs'
}
}
}
// Download default models; if you wish to use your own models then
// place them in the "assets" directory and comment out this line.
apply from: "download-models.gradle"
dependencies {
if (nativeBuildSystem == 'cmake' || nativeBuildSystem == 'none') {
implementation 'org.tensorflow:tensorflow-android:+'
}
}