diff --git a/.gitignore b/.gitignore index 532601bfe9222eae0d6be7378322ab1b9c2eb110..547e94ea11f048c9e007996d4ee716d22a13742e 100644 --- a/.gitignore +++ b/.gitignore @@ -69,3 +69,10 @@ build # clion building directories cmake-build-debug cmake-build-release + + +#ios demo +demo/ios/PaddleMobileDemo/PaddleMobileDemo/googlenet_combine/ +demo/ios/PaddleMobileDemo/PaddleMobileDemo/*.jpg +demo/ios/PaddleMobileDemo/PaddleMobileDemo/PaddleMobile/*.a +*.xcuserstate \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000000000000000000000000000000000..f81f3717a4ead833784b63da35185f2d07409983 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/operators/kernel/mali/ACL_Android"] + path = src/operators/kernel/mali/ACL_Android + url = https://github.com/halsay/ACL_Android.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c3ff01e5d3e20923021904cdbe9008a11cc30ce..a00d179a0d4972080c8fd392160f8ec451692e4d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,41 +6,76 @@ option(USE_OPENMP "openmp support" OFF) option(USE_EXCEPTION "use std exception" ON) option(LOG_PROFILE "log profile" ON) # select the platform to build -option(CPU "cpu" ON) -option(MALI_GPU "mali gpu" ON) +option(CPU "armv7 with neon" ON) +option(MALI_GPU "mali gpu" OFF) option(FPGA "fpga" OFF) +set(DEBUGING ON) + +if (ARM_LINUX) +include("${CMAKE_CURRENT_LIST_DIR}/tools/arm-platform.cmake") +endif () + +file(GLOB_RECURSE PADDLE_MOBILE_CC src/*.cc src/*.cpp src/*.c src/*.mm) +file(GLOB_RECURSE PADDLE_MOBILE_H src/*.h) if (CPU) - add_definitions(-DPADDLE_MOBILE_CPU) + add_definitions(-DPADDLE_MOBILE_CPU) +else() + list(REMOVE_ITEM PADDLE_MOBILE_CC ./src/operators/kernel/arm/*.h) + list(REMOVE_ITEM PADDLE_MOBILE_CC ./src/operators/kernel/arm/*.cc) + list(REMOVE_ITEM PADDLE_MOBILE_CC ./src/operators/kernel/arm/*.cpp) + endif() if (MALI_GPU) add_definitions(-DPADDLE_MOBILE_MALI_GPU) + add_definitions(-DUSE_ACL=1) + add_definitions(-DUSE_OPENCL) + set(ACL_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/src/operators/kernel/mali/ACL_Android) + include_directories(${ACL_ROOT} ${ACL_ROOT}/include) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${ACL_ROOT}/build") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -larm_compute") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -larm_compute_core") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -larm_compute_graph") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${ACL_ROOT}/build/opencl-1.2-stubs") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -lOpenCL") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUSE_ACL=1") +else() + file(GLOB_RECURSE _tmp_list src/operators/kernel/mali/*.cpp src/operators/kernel/mali/*.cc) + foreach(f ${_tmp_list}) + list(REMOVE_ITEM PADDLE_MOBILE_CC ${f}) + endforeach() + + file(GLOB_RECURSE _tmp_list_h src/operators/kernel/mali/*.h) + foreach(f ${_tmp_list_h}) + list(REMOVE_ITEM PADDLE_MOBILE_H ${f}) + endforeach() endif() if(FPGA) add_definitions(-DPADDLE_MOBILE_FPGA) +else() + list(REMOVE_ITEM PADDLE_MOBILE_CC ${CMAKE_CURRENT_SOURCE_DIR}/src/operators/kernel/fpga/*.h) + list(REMOVE_ITEM PADDLE_MOBILE_CC ${CMAKE_CURRENT_SOURCE_DIR}/src/operators/kernel/fpga/*.cc) + list(REMOVE_ITEM PADDLE_MOBILE_CC ${CMAKE_CURRENT_SOURCE_DIR}/src/operators/kernel/fpga/*.cpp) endif() + set(CMAKE_CXX_FLAGS "-std=c++14 -O3 -s ${CMAKE_CXX_FLAGS}") if (DEBUGING) - set(CMAKE_BUILD_TYPE Debug) - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS}") -else() - set(CMAKE_BUILD_TYPE Release) -endif () - -if(DEBUGING) - message(STATUS "debuging") - add_definitions(-DPADDLE_MOBILE_DEBUG) - if(ANDROID) + message(STATUS "debug") + set(CMAKE_BUILD_TYPE Debug) + set(CMAKE_CXX_FLAGS_DEBUG "-g -DNDEBUG") + add_definitions(-DPADDLE_MOBILE_DEBUG) + if (ANDROID_NDK_TOOLCHAIN_INCLUDED) + add_definitions(-DARMV7) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -llog") - endif() - -else() - message(STATUS "releasing") - add_definitions(-fvisibility=hidden -fvisibility-inlines-hidden) -endif() + endif () +else () + set(CMAKE_BUILD_TYPE Release) + set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG") + add_definitions(-fvisibility=hidden -fvisibility-inlines-hidden) +endif () if (USE_EXCEPTION) message(STATUS "use exception") @@ -54,110 +89,47 @@ if (LOG_PROFILE) add_definitions(-DPADDLE_MOBILE_PROFILE) endif() -if(IS_MAC) - add_definitions(-DX86) -elseif(IS_IOS) - add_definitions(-DIOS) -elseif(V7) - add_definitions(-DARMV7) -elseif(V8) - add_definitions(-DARMV8) -else () - add_definitions(-DX86) +if(USE_OPENMP) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp") + add_definitions(-DPADDLE_MOBILE_USE_OPENMP) endif() -set(CMAKE_VERBOSE_MAKEFILE ON) -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY build) -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY build) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY build) -file(GLOB_RECURSE PADDLE_MOBILE_CC src/*.cc src/*.cpp src/*.c) -file(GLOB_RECURSE PADDLE_MOBILE_H src/*.h) -if (NOT ANDROID) -list(REMOVE_ITEM PADDLE_MOBILE_CC ${CMAKE_CURRENT_SOURCE_DIR}/src/jni/*.cpp) -list(REMOVE_ITEM PADDLE_MOBILE_CC ${CMAKE_CURRENT_SOURCE_DIR}/src/jni/*.h) -list(REMOVE_ITEM PADDLE_MOBILE_CC ${CMAKE_CURRENT_SOURCE_DIR}/src/operators/math/math_func_neon.h) + +if (NOT ANDROID_NDK_TOOLCHAIN_INCLUDED) + list(REMOVE_ITEM PADDLE_MOBILE_H ${CMAKE_CURRENT_SOURCE_DIR}/src/jni/paddle_mobile_jni.h) + list(REMOVE_ITEM PADDLE_MOBILE_CC ${CMAKE_CURRENT_SOURCE_DIR}/src/jni/paddle_mobile_jni.cpp) + list(REMOVE_ITEM PADDLE_MOBILE_H ${CMAKE_CURRENT_SOURCE_DIR}/src/operators/math/math_func_neon.h) endif () include_directories(src/) -if(USE_OPENMP) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp") - add_definitions(-DPADDLE_MOBILE_USE_OPENMP) -endif() +set(CMAKE_VERBOSE_MAKEFILE ON) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY build) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY build) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY build) -if (googlenet) - add_definitions(-DCONCAT_OP) - add_definitions(-DCONV_OP) - add_definitions(-DLRN_OP) - add_definitions(-DMUL_OP) - add_definitions(-DELEMENTWISEADD_OP) - add_definitions(-DFUSION_FC_OP) - add_definitions(-DPOOL_OP) - add_definitions(-DRELU_OP) - add_definitions(-DFUSION_CONVADD_OP) - add_definitions(-DFUSION_CONVADD_RELU_OP) -elseif (mobilenet) - add_definitions(-DCONV_OP) - add_definitions(-DELEMENTWISEADD_OP) - add_definitions(-DRELU_OP) - add_definitions(-DSOFTMAX_OP) - add_definitions(-DSOFTMAX_OP) - add_definitions(-DDEPTHWISECONV_OP) - add_definitions(-DBATCHNORM_OP) - add_definitions(-DPOOL_OP) - add_definitions(-DRESHAPE_OP) -elseif (yolo) - add_definitions(-DBATCHNORM_OP) - add_definitions(-DCONV_OP) - add_definitions(-DRELU_OP) - add_definitions(-DELEMENTWISEADD_OP) -elseif (squeezenet) - add_definitions(-DCONCAT_OP) - add_definitions(-DCONV_OP) - add_definitions(-DRELU_OP) - add_definitions(-DELEMENTWISEADD_OP) - add_definitions(-DPOOL_OP) - add_definitions(-DRESHAPE_OP) - add_definitions(-DSOFTMAX_OP) -elseif(resnet) - add_definitions(-DCONV_OP) - add_definitions(-DBATCHNORM_OP) - add_definitions(-DELEMENTWISEADD_OP) - add_definitions(-DSOFTMAX_OP) - add_definitions(-DMUL_OP) - add_definitions(-DPOOL_OP) - add_definitions(-DRELU_OP) -else () - add_definitions(-DBATCHNORM_OP) - add_definitions(-DBOXCODER_OP) - add_definitions(-DCONCAT_OP) - add_definitions(-DCONV_OP) - add_definitions(-DDEPTHWISECONV_OP) - add_definitions(-DELEMENTWISEADD_OP) - add_definitions(-DFUSION_CONVADD_OP) - add_definitions(-DCONVADDRELU_OP) - add_definitions(-DFUSION_FC_OP) - add_definitions(-DLRN_OP) - add_definitions(-DMUL_OP) - add_definitions(-DMULTICLASSNMS_OP) - add_definitions(-DPOOL_OP) - add_definitions(-DPRIORBOX_OP) - add_definitions(-DRELU_OP) - add_definitions(-DRESHAPE_OP) - add_definitions(-DSIGMOID_OP) - add_definitions(-DSOFTMAX_OP) - add_definitions(-DTRANSPOSE_OP) - add_definitions(-DFUSION_CONVADD_RELU_OP) -endif() +# NET default +set(NET "defult" CACHE STRING "select net type") +set_property(CACHE NET PROPERTY STRINGS "defult" "googlenet" "mobilenet" "yolo" "squeezenet") + +include("${CMAKE_CURRENT_LIST_DIR}/tools/op.cmake") if (IS_IOS) add_library(paddle-mobile STATIC ${PADDLE_MOBILE_CC} ${PADDLE_MOBILE_H}) -elseif(ANDROID) - add_library(paddle-mobile SHARED ${PADDLE_MOBILE_CC} ${PADDLE_MOBILE_H}) else() + list(REMOVE_ITEM PADDLE_MOBILE_H ${CMAKE_CURRENT_SOURCE_DIR}/src/ios_io/PaddleMobile.h) + list(REMOVE_ITEM PADDLE_MOBILE_CC ${CMAKE_CURRENT_SOURCE_DIR}/src/ios_io/PaddleMobile.mm) + list(REMOVE_ITEM PADDLE_MOBILE_H ${CMAKE_CURRENT_SOURCE_DIR}/src/ios_io/op_symbols.h) +endif () + +if (ANDROID_NDK_TOOLCHAIN_INCLUDED) + list(REMOVE_DUPLICATES CMAKE_CXX_FLAGS) + add_library(paddle-mobile SHARED ${PADDLE_MOBILE_CC} ${PADDLE_MOBILE_H}) +elseif(IS_IOS) +else () add_library(paddle-mobile SHARED ${PADDLE_MOBILE_CC} ${PADDLE_MOBILE_H}) endif () @@ -165,4 +137,3 @@ if(DEBUGING) add_subdirectory(test) endif() - diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1a25d65e02afb09dabc96e1ec241346cff34f6f2..a33db73e109042276b686e8ab74261273df87390 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -183,6 +183,9 @@ upstream 接下来等待 review,如果有需要修改的地方,参照上述步骤更新 origin 中的对应分支即可。 +![](http://otkwwi4x8.bkt.clouddn.com/2018-06-20-15294877166787.jpg) +之后就可以提交代码了 + ## 删除远程分支 在 PR 被 merge 进主仓库后,我们可以在 PR 的页面删除远程仓库的分支。 @@ -219,7 +222,7 @@ upstream - 原因:如果仅仅修改一个文件但提交了十几个commit,每个commit只做了少量的修改,这会给评审人带来很大困扰。评审人需要逐一查看每个commit才能知道做了哪些修改,且不排除commit之间的修改存在相互覆盖的情况。 - 建议:每次提交时,保持尽量少的commit,可以通过`git commit --amend`补充上次的commit。对已经Push到远程仓库的多个commit,可以参考[squash commits after push](http://stackoverflow.com/questions/5667884/how-to-squash-commits-in-git-after-they-have-been-pushed)。 - 请注意每个commit的名称:应能反映当前commit的内容,不能太随意。 -3. 如果解决了某个Issue的问题,请在该Pull Request的**第一个**评论框中加上:`fix #issue_number`,这样当该PUll Request被合并后,会自动关闭对应的Issue。关键词包括:close, closes, closed, fix, fixes, fixed, resolve, resolves, resolved,请选择合适的词汇。详细可参考[Closing issues via commit messages](https://help.github.com/articles/closing-issues-via-commit-messages)。 +3. 如果解决了某个Issue的问题,请在该Pull Request的**第一个**评论框中加上:`fix #issue_number`,这样当该Pull Request被合并后,会自动关闭对应的Issue。关键词包括:close, closes, closed, fix, fixes, fixed, resolve, resolves, resolved,请选择合适的词汇。详细可参考[Closing issues via commit messages](https://help.github.com/articles/closing-issues-via-commit-messages)。 此外,在回复评审人意见时,请您遵守以下约定: diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..df7df032acefd39c20051e861d353644e3b91024 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,37 @@ +FROM ubuntu:16.04 + +RUN echo '\ +deb main restricted universe multiverse\n\ +deb -updates main restricted universe multiverse\n\ +deb -backports main restricted universe multiverse\n\ +deb -security main restricted universe multiverse\n'\ +> /etc/apt/sources.list +RUN sed -ie 's||http://mirrors.tuna.tsinghua.edu.cn/ubuntu/|' /etc/apt/sources.list +RUN sed -ie 's||xenial|' /etc/apt/sources.list + +RUN apt-get update && apt-get upgrade -y +RUN apt-get install -y --no-install-recommends \ + curl \ + unzip \ + git \ + make \ + cmake-curses-gui \ + python \ + python-pip \ + python-setuptools \ + clang-format-5.0 \ + graphviz \ + g++-arm-linux-gnueabi \ + gcc-arm-linux-gnueabi +RUN apt-get autoremove -y && apt-get clean +RUN ln -s clang-format-5.0 /usr/bin/clang-format +RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --upgrade pip +RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple wheel +RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pre-commit +RUN cd /tmp && curl -O http://mirrors.neusoft.edu.cn/android/repository/android-ndk-r17b-linux-x86_64.zip +RUN curl -O https://mms-res.cdn.bcebos.com/cmake-3.10.3-Linux-x86_64.tar.gz && \ + tar xzf cmake-3.10.3-Linux-x86_64.tar.gz && \ + mv cmake-3.10.3-Linux-x86_64 /opt/cmake-3.10 && \ + mv /usr/bin/cmake /usr/bin/cmake.bak && ln -s /opt/cmake-3.10/bin/cmake /usr/bin/cmake +RUN cd /opt && unzip /tmp/android-ndk-r17b-linux-x86_64.zip +ENV NDK_ROOT /opt/android-ndk-r17b diff --git a/README.md b/README.md index b6ae2beed999d146c64ffc9ee495373d9b77a175..b299879ab73dde1d4e6b749596cfed6bf1888e9e 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,105 @@ -# Paddle-Mobile - +# Paddle-Mobile [![Build Status](https://travis-ci.org/PaddlePaddle/paddle-mobile.svg?branch=develop&longCache=true&style=flat-square)](https://travis-ci.org/PaddlePaddle/paddle-mobile) -[![License](https://img.shields.io/badge/license-Apache%202-brightgreen.svg)](LICENSE) +[![Documentation Status](https://img.shields.io/badge/中文文档-最新-brightgreen.svg)](https://github.com/PaddlePaddle/paddle-mobile/tree/develop/doc) +[![License](https://img.shields.io/badge/license-Apache%202-blue.svg)](LICENSE) + + + + +欢迎来到 Paddle-Mobile GitHub 项目。 + +Paddle-Moible是PaddlePaddle组织下的项目,是一个致力于嵌入式平台的深度学习的框架。Paddle-Moible设计思想和PaddlePaddle的最新版fluid版本保持了高度一致,同时针对嵌入式做了大量优化。设计之初就对嵌入式的性能、体积、能耗、硬件平台覆盖等方面做了考虑。 + +## 简单搜索线上效果 + +如下gif是简单搜索app的线上主体检测应用效果 + +![ezgif-1-050a733dfb](http://otkwwi4x8.bkt.clouddn.com/2018-07-05-ezgif-1-050a733dfb.gif) + +## Demo目录 + +[点我](https://github.com/PaddlePaddle/paddle-mobile/tree/develop/demo) + +## Features + +- **ARM CPU** + + arm cpu是paddle-mobile的主要支持方向,cpu的通用性一直是其优势。嵌入式深度学习,需要大量的cpu汇编实现。我们正在紧锣密鼓的编码,为的是能充分硬件的每一点加速能力。 + arm cpu的优化工作还在进行中,现在使用了常规的cpu优化。在arm a73上paddle-mobile现在单核运行一次mobilenet 1.0是160+ms,显然这不是我们的最终目标,我们正在用大量的汇编改写,后续性能仍会有巨大提升空间。 + +- **Mali GPU** + + Mali GPU是百度和ARM合作开发的,双方团队近期都在致力于将paddle的op能无缝运行在ACL(arm compute library)。目前已经支持squeezenet,googlenet,resnet等几个网络模型,后续会继续加大力度。使全部移动端paddle op能高效运行在mali gpu上。 + +- **苹果设备的GPU Metal实现** + + 基于Metal实现的苹果设备的GPU预测库,也已经在实现中,近期也会有相应可运行版本。 + +- **FPGA** + + FPGA实现正在进行中,是基于Xilinx的ZU5目标开发板。 + +- **灵活性** + + * paddle-mobile cpu版不依赖任何第三库, 可进行快速集成。 + * 使用泛型特化进行平台切换, 可灵活切换 cpu、gpu 和其他协处理器。 + * 可根据特定的常见网络, 进行编译特定的 op, 降低编译时间, 减小包大小。 + * 使用 docker 编译, 提供统一的编译环境。 + * 高可拓展性, 方便拓展其他协处理器, 提供高性能 arm 算子实现, 方便其他协处理器开发者集成开发。 + * 直接兼容 paddle-fluid 模型, 不需要额外的转换操作。 + +- **体积** + + paddle-mobile从设计之初就深入考虑到移动端的包体积的问题,cpu实现中没有外部依赖。在编译过程中,如果该网络不需要的op是完全不会被打入的。同时编译选项优化也为体积压缩提供了帮助。 + 除了二进制体积,我们对代码体积极力避免过大。整个仓库的代码体积也非常小。 + + +## 文档 + +### 设计文档 + +关于paddle-mobile设计文档在下面链接中,如果想了解更多内容。[issue](https://github.com/PaddlePaddle/paddle-mobile/issues)中会有很多早期的设计和讨论过程。 +[设计文档链接](https://github.com/PaddlePaddle/paddle-mobile/blob/develop/doc/design_doc.md) + +### 开发文档 + +开发文档主要是关于编译、运行等问题。做为开发者,它可以和贡献文档共同结合使用。 +[开发文档]()https://github.com/PaddlePaddle/paddle-mobile/blob/develop/doc/development_doc.md +### 贡献文档 +- [贡献文档链接](https://github.com/PaddlePaddle/paddle-mobile/blob/develop/CONTRIBUTING.md) +- 上面文档中涵盖了主要的贡献代码流程,如果在实践中您还遇到了其他问题,可以发[issue](https://github.com/PaddlePaddle/paddle-mobile/issues)。我们看到后会尽快处理。 -This project is used to develop the next version deep learning freamwork for mobile device. -# Development +## 模型获得 +目前Paddle-Mobile仅支持Paddle fluid训练的模型。如果你手中的模型是不同种类的模型,需要进行模型转换才可以运行。 +### 1. 直接使用Paddle Fluid训练 +该方式最为可靠,推荐方式 +### 2. caffe转为Paddle Fluid模型 +[链接](https://github.com/PaddlePaddle/models/tree/develop/fluid/image_classification/caffe2fluid) +### 3. ONNX +ONNX全称为“Open Neural Network Exchange”,即“开放的神经网络切换”。该项目的目的是让不同的神经网络开发框架做到互通互用。 -[Used model in development](https://mms-mis.cdn.bcebos.com/paddle-mobile/models.zip) +除直接使用PaddlePaddle训练fluid版本的模型外,还可以通过onnx转换得到个别Paddle fluid模型。 -## cross-compilation to android +目前,百度也在做onnx支持工作。相关转换项目在这里:[paddle-onnx](https://github.com/PaddlePaddle/paddle-onnx)。 -* NDK is required -* ANDROID_NDK environment variable is required +![](http://otkwwi4x8.bkt.clouddn.com/2018-07-03-15305875853057.jpg) -```bash -sh build.sh android -``` +### 4. 部分测试模型下载 +[下载链接](https://mms-mis.cdn.bcebos.com/paddle-mobile/models.zip) -## build for x86 -paddle-mobile is to run on arm platform. x86 only used to test not arm assembly code. So do not recommend compiling x86. +## 问题解决 -Now only support osx. +欢迎提出或解决我们的问题,有疑问可以发issue. [Github Issues](https://github.com/PaddlePaddle/paddle-mobile/issues). -``` -sh build.sh mac -``` +## Copyright and License +Paddle-Mobile 提供相对宽松的Apache-2.0开源协议 [Apache-2.0 license](LICENSE). -## Old Version of Mobile-Deep-Learning -The old version of MDL was I moved to here [Mobile-Deep-Learning](https://github.com/allonli/mobile-deep-learning) +## 旧版 Mobile-Deep-Learning +原MDL(Mobile-Deep-Learning)工程被迁移到了这里 [Mobile-Deep-Learning](https://github.com/allonli/mobile-deep-learning) diff --git a/demo/android/PaddleMobile_Android/.gitignore b/demo/android/PaddleMobile_Android/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..39fb081a42a86ccf8f9cf99dbccc8bdf7c828bce --- /dev/null +++ b/demo/android/PaddleMobile_Android/.gitignore @@ -0,0 +1,9 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/demo/android/PaddleMobile_Android/app/.gitignore b/demo/android/PaddleMobile_Android/app/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/demo/android/PaddleMobile_Android/app/.gitignore @@ -0,0 +1 @@ + diff --git a/demo/android/PaddleMobile_Android/app/build.gradle b/demo/android/PaddleMobile_Android/app/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..3d61efb83a083909a0038886469e1c7fce218e8c --- /dev/null +++ b/demo/android/PaddleMobile_Android/app/build.gradle @@ -0,0 +1,23 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 21 + defaultConfig { + applicationId "com.baidu.paddle" + minSdkVersion 15 + targetSdkVersion 21 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'com.android.support:appcompat-v7:21.0.3' +} diff --git a/demo/android/PaddleMobile_Android/app/proguard-rules.pro b/demo/android/PaddleMobile_Android/app/proguard-rules.pro new file mode 100644 index 0000000000000000000000000000000000000000..f1b424510da51fd82143bc74a0a801ae5a1e2fcd --- /dev/null +++ b/demo/android/PaddleMobile_Android/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/demo/android/PaddleMobile_Android/app/src/main/AndroidManifest.xml b/demo/android/PaddleMobile_Android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000000000000000000000000000000000..b9bf81dcb9fffb855d067cc7df1633ebcf586432 --- /dev/null +++ b/demo/android/PaddleMobile_Android/app/src/main/AndroidManifest.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + diff --git a/demo/android/PaddleMobile_Android/app/src/main/java/com/baidu/paddle/MainActivity.java b/demo/android/PaddleMobile_Android/app/src/main/java/com/baidu/paddle/MainActivity.java new file mode 100755 index 0000000000000000000000000000000000000000..febe816681d3845a61c5a8b40630e82ac9b4ea95 --- /dev/null +++ b/demo/android/PaddleMobile_Android/app/src/main/java/com/baidu/paddle/MainActivity.java @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2016 Baidu, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.baidu.paddle; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Environment; + +import android.provider.MediaStore; + +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; + +import static android.graphics.Color.blue; +import static android.graphics.Color.green; +import static android.graphics.Color.red; +import static com.baidu.paddle.MainActivity.TYPE.googlenet; + + +public class MainActivity extends Activity { + public static final int TAKE_PHOTO_REQUEST_CODE = 1001; + + private Context mContext = null; + + private int inputSize = 224; + + enum TYPE { + googlenet + } + + private TYPE type = googlenet; + private ImageView imageView; + private TextView tvSpeed; + private Button button; + private Bitmap bmp; + + static { + try { + System.loadLibrary("paddle-mobile"); + + } catch (SecurityException e) { + e.printStackTrace(); + + } catch (UnsatisfiedLinkError e) { + e.printStackTrace(); + + } catch (NullPointerException e) { + e.printStackTrace(); + + } + + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mContext = this; + setContentView(R.layout.main_activity); + init(); + } + + private void init() { + imageView = (ImageView) findViewById(R.id.imageView); + tvSpeed = (TextView) findViewById(R.id.tv_speed); + button = (Button) findViewById(R.id.button); + button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (!isHasSdCard()) { + Toast.makeText(mContext, R.string.sdcard_not_available, + Toast.LENGTH_LONG).show(); + return; + } + Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + // save pic in sdcard + Uri imageUri = Uri.fromFile(getTempImage()); + intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); + startActivityForResult(intent, TAKE_PHOTO_REQUEST_CODE); + + } + }); + Button bt_load = (Button) findViewById(R.id.bt_load); + bt_load.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + String assetPath = "pml_demo"; + String sdcardPath = Environment.getExternalStorageDirectory() + + File.separator + assetPath + File.separator + type; + PML.load(sdcardPath); + + } + }); + Button bt_clear = (Button) findViewById(R.id.bt_clear); + bt_clear.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + + PML.clear(); + + + } + }); + String assetPath = "pml_demo"; + String sdcardPath = Environment.getExternalStorageDirectory() + + File.separator + assetPath; + copyFilesFromAssets(this, assetPath, sdcardPath); + + + } + + public void copyFilesFromAssets(Context context, String oldPath, String newPath) { + try { + String[] fileNames = context.getAssets().list(oldPath); + if (fileNames.length > 0) { + // directory + File file = new File(newPath); + file.mkdirs(); + // copy recursivelyC + for (String fileName : fileNames) { + copyFilesFromAssets(context, oldPath + "/" + fileName, + newPath + "/" + fileName); + } + } else { + // file + InputStream is = context.getAssets().open(oldPath); + FileOutputStream fos = new FileOutputStream(new File(newPath)); + byte[] buffer = new byte[1024]; + int byteCount; + while ((byteCount = is.read(buffer)) != -1) { + fos.write(buffer, 0, byteCount); + } + fos.flush(); + is.close(); + fos.close(); + } + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + public File getTempImage() { + if (Environment.getExternalStorageState().equals( + Environment.MEDIA_MOUNTED)) { + File tempFile = new File(Environment.getExternalStorageDirectory(), "temp.jpg"); + try { + tempFile.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + } + + return tempFile; + } + return null; + } + + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + switch (requestCode) { + case TAKE_PHOTO_REQUEST_CODE: + if (resultCode == RESULT_OK) { + DetectionTask detectionTask = new DetectionTask(); + detectionTask.execute(getTempImage().getPath()); + } + break; + default: + break; + } + } + + /** + * draw rect on imageView + * + * @param bitmap + * @param predicted + * @param viewWidth + * @param viewHeight + */ + private void drawRect(Bitmap bitmap, float[] predicted, int viewWidth, int viewHeight) { + + Canvas canvas = new Canvas(bitmap); + canvas.drawBitmap(bitmap, 0, 0, null); + if (type == googlenet) { + Paint paint = new Paint(); + paint.setColor(Color.RED); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(3.0f); + float x1 = 0; + float x2 = 0; + float y1 = 0; + float y2 = 0; + + // the googlenet result sequence is (left top right top bottom) + x1 = (predicted[0] * viewWidth / 224); + y1 = (predicted[1] * viewHeight / 224); + x2 = (predicted[2] * viewWidth / 224); + y2 = (predicted[3] * viewHeight / 224); + + + canvas.drawRect(x1, y1, x2, y2, paint); + } + + + imageView.setImageBitmap(bitmap); + + } + + float getMaxIndex(float[] predicted) { + float max = 0; + int index = 0; + for (int i = 0; i < predicted.length; i++) { + if (predicted[i] > max) { + max = predicted[i]; + index = i; + } + } + return index; + } + + public float[] getScaledMatrix(Bitmap bitmap, int desWidth, + int desHeight) { + float[] dataBuf = new float[3 * desWidth * desHeight]; + int rIndex; + int gIndex; + int bIndex; + int[] pixels = new int[desWidth * desHeight]; + Bitmap bm = Bitmap.createScaledBitmap(bitmap, desWidth, desHeight, false); + bm.getPixels(pixels, 0, desWidth, 0, 0, desWidth, desHeight); + int j = 0; + int k = 0; + for (int i = 0; i < pixels.length; i ++) { + int clr = pixels[i]; + j = i / desHeight; + k = i % desWidth; + rIndex = j * desWidth + k; + gIndex = rIndex + desHeight * desWidth; + bIndex = gIndex + desHeight * desWidth; + dataBuf[rIndex] = (float)((clr & 0x00ff0000)>> 16) -148; + dataBuf[gIndex] = (float)((clr & 0x0000ff00)>> 8) - 148; + dataBuf[bIndex] = (float)((clr & 0x000000ff)) -148; + + } + if (bm.isRecycled()) { + bm.recycle(); + } + return dataBuf; + + + } + /** + * check whether sdcard is mounted + * + * @return + */ + public boolean isHasSdCard() { + if (Environment.getExternalStorageState().equals( + Environment.MEDIA_MOUNTED)) { + return true; + } else { + return false; + } + } + + public void dumpData(float[] results, String filename) { + try { + File writename = new File(filename); + writename.createNewFile(); + BufferedWriter out = new BufferedWriter(new FileWriter(writename)); + for (float result : results) { + out.write(result + " "); + } + out.flush(); + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + /** + * scale bitmap in case of OOM + * + * @param ctx + * @param filePath + * @return + */ + public Bitmap getScaleBitmap(Context ctx, String filePath) { + BitmapFactory.Options opt = new BitmapFactory.Options(); + opt.inJustDecodeBounds = true; + BitmapFactory.decodeFile(filePath, opt); + + int bmpWidth = opt.outWidth; + int bmpHeight = opt.outHeight; + + int maxSize = 500; + + opt.inSampleSize = 1; + while (true) { + if (bmpWidth / opt.inSampleSize < maxSize || bmpHeight / opt.inSampleSize < maxSize) { + break; + } + opt.inSampleSize *= 2; + } + opt.inJustDecodeBounds = false; + Bitmap bmp = BitmapFactory.decodeFile(filePath, opt); + return bmp; + } + + @Override + public void onBackPressed() { + super.onBackPressed(); + Log.d("mdl", "mdl clear"); + // clear mdl + PML.clear(); + + } + + class DetectionTask extends AsyncTask { + private long time; + + public DetectionTask() { + super(); + } + + @Override + protected void onPreExecute() { + super.onPreExecute(); + if (type == googlenet) { + inputSize = 224; + } + } + + @Override + protected void onPostExecute(float[] result) { + super.onPostExecute(result); + try { + Bitmap src = Bitmap.createScaledBitmap(bmp, imageView.getWidth(), + imageView.getHeight(), false); + drawRect(src, result, imageView.getWidth(), imageView.getHeight()); + tvSpeed.setText("detection cost:" + time + "ms"); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + protected void onProgressUpdate(Void... values) { + super.onProgressUpdate(values); + } + + @Override + protected void onCancelled() { + super.onCancelled(); + } + + @Override + protected float[] doInBackground(String... strings) { + bmp = getScaleBitmap(mContext, strings[0]); + float[] inputData = getScaledMatrix(bmp, inputSize, inputSize); + float[] result = null; + try { + long start = System.currentTimeMillis(); + result = PML.predict(inputData); + long end = System.currentTimeMillis(); + time = end - start; + + } catch (Exception e) { + e.printStackTrace(); + } + return result; + } + } +} diff --git a/demo/android/PaddleMobile_Android/app/src/main/java/com/baidu/paddle/PML.java b/demo/android/PaddleMobile_Android/app/src/main/java/com/baidu/paddle/PML.java new file mode 100644 index 0000000000000000000000000000000000000000..7649d4c081223bace01b806d1eb7dca57129ed7c --- /dev/null +++ b/demo/android/PaddleMobile_Android/app/src/main/java/com/baidu/paddle/PML.java @@ -0,0 +1,23 @@ +package com.baidu.paddle; + +public class PML { + /** + * Load + * @param modelPath + * @return + */ + public static native boolean load(String modelPath); + + + /** + * object detection + * + * @param buf + * @return + */ + public static native float[] predict(float[] buf); + + + public static native void clear(); + +} diff --git a/demo/android/PaddleMobile_Android/app/src/main/res/drawable/ic_launcher_background.xml b/demo/android/PaddleMobile_Android/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000000000000000000000000000000000000..d5fccc538c179838bfdce779c26eebb4fa0b5ce9 --- /dev/null +++ b/demo/android/PaddleMobile_Android/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/android/PaddleMobile_Android/app/src/main/res/layout/main_activity.xml b/demo/android/PaddleMobile_Android/app/src/main/res/layout/main_activity.xml new file mode 100644 index 0000000000000000000000000000000000000000..6653b182adbfc6d82d50d313db8c88789c0b027b --- /dev/null +++ b/demo/android/PaddleMobile_Android/app/src/main/res/layout/main_activity.xml @@ -0,0 +1,55 @@ + + + + +