diff --git a/PaddleLite-android-demo/mask_detection_demo/.gitignore b/PaddleLite-android-demo/mask_detection_demo/.gitignore new file mode 100644 index 0000000000..2b75303ac5 --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/.gitignore @@ -0,0 +1,13 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/PaddleLite-android-demo/mask_detection_demo/.idea/encodings.xml b/PaddleLite-android-demo/mask_detection_demo/.idea/encodings.xml new file mode 100644 index 0000000000..15a15b218a --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/PaddleLite-android-demo/mask_detection_demo/.idea/gradle.xml b/PaddleLite-android-demo/mask_detection_demo/.idea/gradle.xml new file mode 100644 index 0000000000..2996d53125 --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/.idea/gradle.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/PaddleLite-android-demo/mask_detection_demo/.idea/misc.xml b/PaddleLite-android-demo/mask_detection_demo/.idea/misc.xml new file mode 100644 index 0000000000..0d45e8dac2 --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/PaddleLite-android-demo/mask_detection_demo/.idea/runConfigurations.xml b/PaddleLite-android-demo/mask_detection_demo/.idea/runConfigurations.xml new file mode 100644 index 0000000000..7f68460d8b --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/PaddleLite-android-demo/mask_detection_demo/.idea/vcs.xml b/PaddleLite-android-demo/mask_detection_demo/.idea/vcs.xml new file mode 100644 index 0000000000..b2bdec2d71 --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/PaddleLite-android-demo/mask_detection_demo/app/.gitignore b/PaddleLite-android-demo/mask_detection_demo/app/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/PaddleLite-android-demo/mask_detection_demo/app/build.gradle b/PaddleLite-android-demo/mask_detection_demo/app/build.gradle new file mode 100644 index 0000000000..04abf67aad --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/build.gradle @@ -0,0 +1,95 @@ +import java.security.MessageDigest + +apply plugin: 'com.android.application' + +android { + compileSdkVersion 28 + defaultConfig { + applicationId "com.baidu.paddle.lite.demo.mask_detection" + minSdkVersion 15 + targetSdkVersion 28 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + externalNativeBuild { + cmake { + arguments '-DANDROID_PLATFORM=android-23', '-DANDROID_STL=c++_shared', "-DANDROID_TOOLCHAIN=" + abiFilters 'armeabi-v7a', 'arm64-v8a' + cppFlags "-std=c++11" + } + } + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + externalNativeBuild { + cmake { + path "src/main/cpp/CMakeLists.txt" + version "3.10.2" + } + } +} + +dependencies { + implementation fileTree(include: ['*.jar'], dir: 'libs') + implementation 'com.android.support:appcompat-v7:28.0.0' + implementation 'com.android.support.constraint:constraint-layout:1.1.3' + implementation 'com.android.support:design:28.0.0' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'com.android.support.test:runner:1.0.2' + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' +} + +def archives = [ + [ + 'src' : 'https://paddlelite-demo.bj.bcebos.com/libs/android/paddle_lite_libs_v2_3_0.tar.gz', + 'dest': 'PaddleLite' + ], + [ + 'src' : 'https://paddlelite-demo.bj.bcebos.com/libs/android/opencv-4.2.0-android-sdk.tar.gz', + 'dest': 'OpenCV' + ], + [ + 'src' : 'https://paddlelite-demo.bj.bcebos.com/models/pyramidbox_lite_fp32_for_cpu_v2_3_0.tar.gz', + 'dest' : 'src/main/assets/models/pyramidbox_lite_for_cpu' + ], + [ + 'src' : 'https://paddlelite-demo.bj.bcebos.com/models/mask_detector_fp32_128_128_for_cpu_v2_3_0.tar.gz', + 'dest' : 'src/main/assets/models/mask_detector_for_cpu' + ] +] + +task downloadAndExtractArchives(type: DefaultTask) { + doFirst { + println "Downloading and extracting archives including libs and models" + } + doLast { + // Prepare cache folder for archives + String cachePath = "cache" + if (!file("${cachePath}").exists()) { + mkdir "${cachePath}" + } + archives.eachWithIndex { archive, index -> + MessageDigest messageDigest = MessageDigest.getInstance('MD5') + messageDigest.update(archive.src.bytes) + String cacheName = new BigInteger(1, messageDigest.digest()).toString(32) + // Download the target archive if not exists + boolean copyFiles = !file("${archive.dest}").exists() + if (!file("${cachePath}/${cacheName}.tar.gz").exists()) { + ant.get(src: archive.src, dest: file("${cachePath}/${cacheName}.tar.gz")) + copyFiles = true; // force to copy files from the latest archive files + } + // Extract the target archive if its dest path does not exists + if (copyFiles) { + copy { + from tarTree("${cachePath}/${cacheName}.tar.gz") + into "${archive.dest}" + } + } + } + } +} +preBuild.dependsOn downloadAndExtractArchives diff --git a/PaddleLite-android-demo/mask_detection_demo/app/proguard-rules.pro b/PaddleLite-android-demo/mask_detection_demo/app/proguard-rules.pro new file mode 100644 index 0000000000..f1b424510d --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/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/PaddleLite-android-demo/mask_detection_demo/app/src/androidTest/java/com/baidu/paddle/lite/demo/mask_detection/ExampleInstrumentedTest.java b/PaddleLite-android-demo/mask_detection_demo/app/src/androidTest/java/com/baidu/paddle/lite/demo/mask_detection/ExampleInstrumentedTest.java new file mode 100644 index 0000000000..660a2d3abb --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/androidTest/java/com/baidu/paddle/lite/demo/mask_detection/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.baidu.paddle.lite.demo.mask_detection; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("com.baidu.paddle.lite.demo", appContext.getPackageName()); + } +} diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/AndroidManifest.xml b/PaddleLite-android-demo/mask_detection_demo/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..85fc00f4b8 --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/AndroidManifest.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/cpp/CMakeLists.txt b/PaddleLite-android-demo/mask_detection_demo/app/src/main/cpp/CMakeLists.txt new file mode 100644 index 0000000000..3dda100fb1 --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/cpp/CMakeLists.txt @@ -0,0 +1,170 @@ +# For more information about using CMake with Android Studio, read the +# documentation: https://d.android.com/studio/projects/add-native-code.html + +# Sets the minimum version of CMake required to build the native library. + +cmake_minimum_required(VERSION 3.4.1) + +# Creates and names a library, sets it as either STATIC or SHARED, and provides +# the relative paths to its source code. You can define multiple libraries, and +# CMake builds them for you. Gradle automatically packages shared libraries with +# your APK. + +set(PaddleLite_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../PaddleLite") +include_directories(${PaddleLite_DIR}/cxx/include) + +set(OpenCV_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../OpenCV/sdk/native/jni") +find_package(OpenCV REQUIRED) +message(STATUS "OpenCV libraries: ${OpenCV_LIBS}") +include_directories(${OpenCV_INCLUDE_DIRS}) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp") +set(CMAKE_CXX_FLAGS + "${CMAKE_CXX_FLAGS} -ffast-math -Ofast -Os -DNDEBUG -fno-exceptions -fomit-frame-pointer -fno-asynchronous-unwind-tables -fno-unwind-tables" +) +set(CMAKE_CXX_FLAGS + "${CMAKE_CXX_FLAGS} -fvisibility=hidden -fvisibility-inlines-hidden -fdata-sections -ffunction-sections" +) +set(CMAKE_SHARED_LINKER_FLAGS + "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections -Wl,-z,nocopyreloc") + +add_library( + # Sets the name of the library. + Native + # Sets the library as a shared library. + SHARED + # Provides a relative path to your source file(s). + Native.cc Pipeline.cc Utils.cc) + +find_library( + # Sets the name of the path variable. + log-lib + # Specifies the name of the NDK library that you want CMake to locate. + log) + +add_library( + # Sets the name of the library. + paddle_light_api_shared + # Sets the library as a shared library. + SHARED + # Provides a relative path to your source file(s). + IMPORTED) + +set_target_properties( + # Specifies the target library. + paddle_light_api_shared + # Specifies the parameter you want to define. + PROPERTIES + IMPORTED_LOCATION + ${PaddleLite_DIR}/cxx/libs/${ANDROID_ABI}/libpaddle_light_api_shared.so + # Provides the path to the library you want to import. +) + +# Comment the followings if libpaddle_light_api_shared.so is not fit for NPU +add_library( + # Sets the name of the library. + hiai + # Sets the library as a shared library. + SHARED + # Provides a relative path to your source file(s). + IMPORTED) + +set_target_properties( + # Specifies the target library. + hiai + # Specifies the parameter you want to define. + PROPERTIES IMPORTED_LOCATION + ${PaddleLite_DIR}/cxx/libs/${ANDROID_ABI}/libhiai.so + # Provides the path to the library you want to import. +) + +add_library( + # Sets the name of the library. + hiai_ir + # Sets the library as a shared library. + SHARED + # Provides a relative path to your source file(s). + IMPORTED) + +set_target_properties( + # Specifies the target library. + hiai_ir + # Specifies the parameter you want to define. + PROPERTIES IMPORTED_LOCATION + ${PaddleLite_DIR}/cxx/libs/${ANDROID_ABI}/libhiai_ir.so + # Provides the path to the library you want to import. +) + +add_library( + # Sets the name of the library. + hiai_ir_build + # Sets the library as a shared library. + SHARED + # Provides a relative path to your source file(s). + IMPORTED) + +set_target_properties( + # Specifies the target library. + hiai_ir_build + # Specifies the parameter you want to define. + PROPERTIES IMPORTED_LOCATION + ${PaddleLite_DIR}/cxx/libs/${ANDROID_ABI}/libhiai_ir_build.so + # Provides the path to the library you want to import. +) + +# Specifies libraries CMake should link to your target library. You can link +# multiple libraries, such as libraries you define in this build script, +# prebuilt third-party libraries, or system libraries. + +target_link_libraries( + # Specifies the target library. + Native + paddle_light_api_shared + ${OpenCV_LIBS} + GLESv2 + EGL + ${log-lib} + hiai + hiai_ir + hiai_ir_build) + +add_custom_command( + TARGET Native + POST_BUILD + COMMAND + ${CMAKE_COMMAND} -E copy + ${PaddleLite_DIR}/cxx/libs/${ANDROID_ABI}/libc++_shared.so + ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libc++_shared.so) + +add_custom_command( + TARGET Native + POST_BUILD + COMMAND + ${CMAKE_COMMAND} -E copy + ${PaddleLite_DIR}/cxx/libs/${ANDROID_ABI}/libpaddle_light_api_shared.so + ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libpaddle_light_api_shared.so) + +# Comment the followings if libpaddle_light_api_shared.so is not fit for NPU +add_custom_command( + TARGET Native + POST_BUILD + COMMAND + ${CMAKE_COMMAND} -E copy + ${PaddleLite_DIR}/cxx/libs/${ANDROID_ABI}/libhiai.so + ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libhiai.so) + +add_custom_command( + TARGET Native + POST_BUILD + COMMAND + ${CMAKE_COMMAND} -E copy + ${PaddleLite_DIR}/cxx/libs/${ANDROID_ABI}/libhiai_ir.so + ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libhiai_ir.so) + +add_custom_command( + TARGET Native + POST_BUILD + COMMAND + ${CMAKE_COMMAND} -E copy + ${PaddleLite_DIR}/cxx/libs/${ANDROID_ABI}/libhiai_ir_build.so + ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libhiai_ir_build.so) diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/cpp/Native.cc b/PaddleLite-android-demo/mask_detection_demo/app/src/main/cpp/Native.cc new file mode 100644 index 0000000000..b32f135c07 --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/cpp/Native.cc @@ -0,0 +1,89 @@ +// Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "Native.h" +#include "Pipeline.h" + +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: com_baidu_paddle_lite_demo_mask_detection_Native + * Method: nativeInit + * Signature: + * (Ljava/lang/String;ILjava/lang/String;F[F[FFLjava/lang/String;ILjava/lang/String;II[F[F)J + */ +JNIEXPORT jlong JNICALL +Java_com_baidu_paddle_lite_demo_mask_1detection_Native_nativeInit( + JNIEnv *env, jclass thiz, jstring jfdtModelDir, jint fdtCPUThreadNum, + jstring jfdtCPUPowerMode, jfloat fdtInputScale, jfloatArray jfdtInputMean, + jfloatArray jfdtInputStd, jfloat fdtScoreThreshold, jstring jmclModelDir, + jint mclCPUThreadNum, jstring jmclCPUPowerMode, jint mclInputWidth, + jint mclInputHeight, jfloatArray jmclInputMean, jfloatArray jmclInputStd) { + std::string fdtModelDir = jstring_to_cpp_string(env, jfdtModelDir); + std::string fdtCPUPowerMode = jstring_to_cpp_string(env, jfdtCPUPowerMode); + std::vector fdtInputMean = + jfloatarray_to_float_vector(env, jfdtInputMean); + std::vector fdtInputStd = + jfloatarray_to_float_vector(env, jfdtInputStd); + std::string mclModelDir = jstring_to_cpp_string(env, jmclModelDir); + std::string mclCPUPowerMode = jstring_to_cpp_string(env, jmclCPUPowerMode); + std::vector mclInputMean = + jfloatarray_to_float_vector(env, jmclInputMean); + std::vector mclInputStd = + jfloatarray_to_float_vector(env, jmclInputStd); + return reinterpret_cast( + new Pipeline(fdtModelDir, fdtCPUThreadNum, fdtCPUPowerMode, fdtInputScale, + fdtInputMean, fdtInputStd, fdtScoreThreshold, mclModelDir, + mclCPUThreadNum, mclCPUPowerMode, mclInputWidth, + mclInputHeight, mclInputMean, mclInputStd)); +} + +/* + * Class: com_baidu_paddle_lite_demo_mask_detection_Native + * Method: nativeRelease + * Signature: (J)Z + */ +JNIEXPORT jboolean JNICALL +Java_com_baidu_paddle_lite_demo_mask_1detection_Native_nativeRelease( + JNIEnv *env, jclass thiz, jlong ctx) { + if (ctx == 0) { + return JNI_FALSE; + } + Pipeline *pipeline = reinterpret_cast(ctx); + delete pipeline; + return JNI_TRUE; +} + +/* + * Class: com_baidu_paddle_lite_demo_mask_detection_Native + * Method: nativeProcess + * Signature: (JIIIILjava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL +Java_com_baidu_paddle_lite_demo_mask_1detection_Native_nativeProcess( + JNIEnv *env, jclass thiz, jlong ctx, jint inTextureId, jint outTextureId, + jint textureWidth, jint textureHeight, jstring jsavedImagePath) { + if (ctx == 0) { + return JNI_FALSE; + } + std::string savedImagePath = jstring_to_cpp_string(env, jsavedImagePath); + Pipeline *pipeline = reinterpret_cast(ctx); + return pipeline->Process(inTextureId, outTextureId, textureWidth, + textureHeight, savedImagePath); +} + +#ifdef __cplusplus +} +#endif diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/cpp/Native.h b/PaddleLite-android-demo/mask_detection_demo/app/src/main/cpp/Native.h new file mode 100644 index 0000000000..d595b8ea2c --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/cpp/Native.h @@ -0,0 +1,116 @@ +// Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include +#include + +inline std::string jstring_to_cpp_string(JNIEnv *env, jstring jstr) { + // In java, a unicode char will be encoded using 2 bytes (utf16). + // so jstring will contain characters utf16. std::string in c++ is + // essentially a string of bytes, not characters, so if we want to + // pass jstring from JNI to c++, we have convert utf16 to bytes. + if (!jstr) { + return ""; + } + const jclass stringClass = env->GetObjectClass(jstr); + const jmethodID getBytes = + env->GetMethodID(stringClass, "getBytes", "(Ljava/lang/String;)[B"); + const jbyteArray stringJbytes = (jbyteArray)env->CallObjectMethod( + jstr, getBytes, env->NewStringUTF("UTF-8")); + + size_t length = (size_t)env->GetArrayLength(stringJbytes); + jbyte *pBytes = env->GetByteArrayElements(stringJbytes, NULL); + + std::string ret = std::string(reinterpret_cast(pBytes), length); + env->ReleaseByteArrayElements(stringJbytes, pBytes, JNI_ABORT); + + env->DeleteLocalRef(stringJbytes); + env->DeleteLocalRef(stringClass); + return ret; +} + +inline jstring cpp_string_to_jstring(JNIEnv *env, std::string str) { + auto *data = str.c_str(); + jclass strClass = env->FindClass("java/lang/String"); + jmethodID strClassInitMethodID = + env->GetMethodID(strClass, "", "([BLjava/lang/String;)V"); + + jbyteArray bytes = env->NewByteArray(strlen(data)); + env->SetByteArrayRegion(bytes, 0, strlen(data), + reinterpret_cast(data)); + + jstring encoding = env->NewStringUTF("UTF-8"); + jstring res = (jstring)( + env->NewObject(strClass, strClassInitMethodID, bytes, encoding)); + + env->DeleteLocalRef(strClass); + env->DeleteLocalRef(encoding); + env->DeleteLocalRef(bytes); + + return res; +} + +inline jfloatArray cpp_array_to_jfloatarray(JNIEnv *env, const float *buf, + int64_t len) { + jfloatArray result = env->NewFloatArray(len); + env->SetFloatArrayRegion(result, 0, len, buf); + return result; +} + +inline jintArray cpp_array_to_jintarray(JNIEnv *env, const int *buf, + int64_t len) { + jintArray result = env->NewIntArray(len); + env->SetIntArrayRegion(result, 0, len, buf); + return result; +} + +inline jbyteArray cpp_array_to_jbytearray(JNIEnv *env, const int8_t *buf, + int64_t len) { + jbyteArray result = env->NewByteArray(len); + env->SetByteArrayRegion(result, 0, len, buf); + return result; +} + +inline jlongArray int64_vector_to_jlongarray(JNIEnv *env, + const std::vector &vec) { + jlongArray result = env->NewLongArray(vec.size()); + jlong *buf = new jlong[vec.size()]; + for (size_t i = 0; i < vec.size(); ++i) { + buf[i] = (jlong)vec[i]; + } + env->SetLongArrayRegion(result, 0, vec.size(), buf); + delete[] buf; + return result; +} + +inline std::vector jlongarray_to_int64_vector(JNIEnv *env, + jlongArray data) { + int data_size = env->GetArrayLength(data); + jlong *data_ptr = env->GetLongArrayElements(data, nullptr); + std::vector data_vec(data_ptr, data_ptr + data_size); + env->ReleaseLongArrayElements(data, data_ptr, 0); + return data_vec; +} + +inline std::vector jfloatarray_to_float_vector(JNIEnv *env, + jfloatArray data) { + int data_size = env->GetArrayLength(data); + jfloat *data_ptr = env->GetFloatArrayElements(data, nullptr); + std::vector data_vec(data_ptr, data_ptr + data_size); + env->ReleaseFloatArrayElements(data, data_ptr, 0); + return data_vec; +} diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/cpp/Pipeline.cc b/PaddleLite-android-demo/mask_detection_demo/app/src/main/cpp/Pipeline.cc new file mode 100644 index 0000000000..c0d1695f12 --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/cpp/Pipeline.cc @@ -0,0 +1,342 @@ +// Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "Pipeline.h" + +FaceDetector::FaceDetector(const std::string &modelDir, const int cpuThreadNum, + const std::string &cpuPowerMode, float inputScale, + const std::vector &inputMean, + const std::vector &inputStd, + float scoreThreshold) + : inputScale_(inputScale), inputMean_(inputMean), inputStd_(inputStd), + scoreThreshold_(scoreThreshold) { + paddle::lite_api::MobileConfig config; + config.set_model_from_file(modelDir + "/model.nb"); + config.set_threads(cpuThreadNum); + config.set_power_mode(ParsePowerMode(cpuPowerMode)); + predictor_ = + paddle::lite_api::CreatePaddlePredictor( + config); +} + +void FaceDetector::Preprocess(const cv::Mat &rgbaImage) { + auto t = GetCurrentTime(); + cv::Mat resizedRGBAImage; + cv::resize(rgbaImage, resizedRGBAImage, cv::Size(), inputScale_, inputScale_, + cv::INTER_CUBIC); + cv::Mat resizedBGRImage; + cv::cvtColor(resizedRGBAImage, resizedBGRImage, cv::COLOR_RGBA2BGR); + resizedBGRImage.convertTo(resizedBGRImage, CV_32FC3, 1.0 / 255.0f); + std::vector inputShape = {1, 3, resizedBGRImage.rows, + resizedBGRImage.cols}; + // Prepare input tensor + auto inputTensor = predictor_->GetInput(0); + inputTensor->Resize(inputShape); + auto inputData = inputTensor->mutable_data(); + NHWC2NCHW(reinterpret_cast(resizedBGRImage.data), inputData, + inputMean_.data(), inputStd_.data(), inputShape[3], inputShape[2]); +} + +void FaceDetector::Postprocess(const cv::Mat &rgbaImage, + std::vector *faces) { + int imageWidth = rgbaImage.cols; + int imageHeight = rgbaImage.rows; + // Get output tensor + auto outputTensor = predictor_->GetOutput(2); + auto outputData = outputTensor->data(); + auto outputShape = outputTensor->shape(); + int outputSize = ShapeProduction(outputShape); + faces->clear(); + for (int i = 0; i < outputSize; i += 6) { + // Class id + float class_id = outputData[i]; + // Confidence score + float score = outputData[i + 1]; + int left = outputData[i + 2] * imageWidth; + int top = outputData[i + 3] * imageHeight; + int right = outputData[i + 4] * imageWidth; + int bottom = outputData[i + 5] * imageHeight; + int width = right - left; + int height = bottom - top; + if (score > scoreThreshold_) { + Face face; + face.roi = cv::Rect(left, top, width, height) & + cv::Rect(0, 0, imageWidth - 1, imageHeight - 1); + faces->push_back(face); + } + } +} + +void FaceDetector::Predict(const cv::Mat &rgbImage, std::vector *faces, + double *preprocessTime, double *predictTime, + double *postprocessTime) { + auto t = GetCurrentTime(); + + t = GetCurrentTime(); + Preprocess(rgbImage); + *preprocessTime = GetElapsedTime(t); + LOGD("Face detector postprocess costs %f ms", *preprocessTime); + + t = GetCurrentTime(); + predictor_->Run(); + *predictTime = GetElapsedTime(t); + LOGD("Face detector predict costs %f ms", *predictTime); + + t = GetCurrentTime(); + Postprocess(rgbImage, faces); + *postprocessTime = GetElapsedTime(t); + LOGD("Face detector postprocess costs %f ms", *postprocessTime); +} + +MaskClassifier::MaskClassifier(const std::string &modelDir, + const int cpuThreadNum, + const std::string &cpuPowerMode, int inputWidth, + int inputHeight, + const std::vector &inputMean, + const std::vector &inputStd) + : inputWidth_(inputWidth), inputHeight_(inputHeight), inputMean_(inputMean), + inputStd_(inputStd) { + paddle::lite_api::MobileConfig config; + config.set_model_from_file(modelDir + "/model.nb"); + config.set_threads(cpuThreadNum); + config.set_power_mode(ParsePowerMode(cpuPowerMode)); + predictor_ = + paddle::lite_api::CreatePaddlePredictor( + config); +} + +void MaskClassifier::Preprocess(const cv::Mat &rgbImage, + const std::vector &faces) { + // Prepare input tensor + auto inputTensor = predictor_->GetInput(0); + int batchSize = faces.size(); + std::vector inputShape = {batchSize, 3, inputHeight_, inputWidth_}; + inputTensor->Resize(inputShape); + auto inputData = inputTensor->mutable_data(); + for (int i = 0; i < batchSize; i++) { + // Adjust the face region to improve the accuracy according to the aspect + // ratio of input image of the target model + int cx = faces[i].roi.x + faces[i].roi.width / 2.0f; + int cy = faces[i].roi.y + faces[i].roi.height / 2.0f; + int w = faces[i].roi.width; + int h = faces[i].roi.height; + float roiAspectRatio = + static_cast(faces[i].roi.width) / faces[i].roi.height; + float inputAspectRatio = static_cast(inputShape[3]) / inputShape[2]; + if (fabs(roiAspectRatio - inputAspectRatio) > 1e-5) { + float widthRatio = static_cast(faces[i].roi.width) / inputShape[3]; + float heightRatio = + static_cast(faces[i].roi.height) / inputShape[2]; + if (widthRatio > heightRatio) { + h = w / inputAspectRatio; + } else { + w = h * inputAspectRatio; + } + } + cv::Mat resizedRGBAImage( + rgbImage, cv::Rect(cx - w / 2, cy - h / 2, w, h) & + cv::Rect(0, 0, rgbImage.cols - 1, rgbImage.rows - 1)); + cv::resize(resizedRGBAImage, resizedRGBAImage, + cv::Size(inputShape[3], inputShape[2]), 0.0f, 0.0f, + cv::INTER_CUBIC); + cv::Mat resizedBGRImage; + cv::cvtColor(resizedRGBAImage, resizedBGRImage, cv::COLOR_RGBA2BGR); + resizedBGRImage.convertTo(resizedBGRImage, CV_32FC3, 1.0 / 255.0f); + NHWC2NCHW(reinterpret_cast(resizedBGRImage.data), inputData, + inputMean_.data(), inputStd_.data(), inputShape[3], + inputShape[2]); + inputData += inputShape[1] * inputShape[2] * inputShape[3]; + } +} + +void MaskClassifier::Postprocess(std::vector *faces) { + auto outputTensor = predictor_->GetOutput(0); + auto outputData = outputTensor->data(); + auto outputShape = outputTensor->shape(); + int outputSize = ShapeProduction(outputShape); + int batchSize = faces->size(); + int classNum = outputSize / batchSize; + for (int i = 0; i < batchSize; i++) { + (*faces)[i].classid = 0; + (*faces)[i].confidence = *(outputData++); + for (int j = 1; j < classNum; j++) { + auto confidence = *(outputData++); + if (confidence > (*faces)[i].confidence) { + (*faces)[i].classid = j; + (*faces)[i].confidence = confidence; + } + } + } +} + +void MaskClassifier::Predict(const cv::Mat &rgbImage, std::vector *faces, + double *preprocessTime, double *predictTime, + double *postprocessTime) { + auto t = GetCurrentTime(); + + t = GetCurrentTime(); + Preprocess(rgbImage, *faces); + *preprocessTime = GetElapsedTime(t); + LOGD("Mask classifier postprocess costs %f ms", *preprocessTime); + + t = GetCurrentTime(); + predictor_->Run(); + *predictTime = GetElapsedTime(t); + LOGD("Mask classifier predict costs %f ms", *predictTime); + + t = GetCurrentTime(); + Postprocess(faces); + *postprocessTime = GetElapsedTime(t); + LOGD("Mask classifier postprocess costs %f ms", *postprocessTime); +} + +Pipeline::Pipeline(const std::string &fdtModelDir, const int fdtCPUThreadNum, + const std::string &fdtCPUPowerMode, float fdtInputScale, + const std::vector &fdtInputMean, + const std::vector &fdtInputStd, + float detScoreThreshold, const std::string &mclModelDir, + const int mclCPUThreadNum, + const std::string &mclCPUPowerMode, int mclInputWidth, + int mclInputHeight, const std::vector &mclInputMean, + const std::vector &mclInputStd) { + faceDetector_.reset(new FaceDetector( + fdtModelDir, fdtCPUThreadNum, fdtCPUPowerMode, fdtInputScale, + fdtInputMean, fdtInputStd, detScoreThreshold)); + maskClassifier_.reset(new MaskClassifier( + mclModelDir, mclCPUThreadNum, mclCPUPowerMode, mclInputWidth, + mclInputHeight, mclInputMean, mclInputStd)); +} + +void Pipeline::VisualizeResults(const std::vector &faces, + cv::Mat *rgbaImage) { + for (int i = 0; i < faces.size(); i++) { + auto roi = faces[i].roi; + // Configure color and text size + cv::Scalar color; + std::string text; + if (faces[i].classid == 1) { + text = "MASK: "; + color = cv::Scalar(0, 255, 0); + } else { + text = "NO MASK: "; + color = cv::Scalar(255, 0, 0); + } + text += std::to_string(static_cast(faces[i].confidence * 100)) + "%"; + int font_face = cv::FONT_HERSHEY_PLAIN; + double font_scale = 1.f; + float thickness = 1; + cv::Size text_size = + cv::getTextSize(text, font_face, font_scale, thickness, nullptr); + font_scale = faces[i].roi.width * font_scale / text_size.width; + text_size = + cv::getTextSize(text, font_face, font_scale, thickness, nullptr); + // Draw roi object, text and background + cv::rectangle(*rgbaImage, faces[i].roi, color, 2); + cv::rectangle( + *rgbaImage, + cv::Point2d(faces[i].roi.x, + faces[i].roi.y - round(text_size.height * 1.25f)), + cv::Point2d(faces[i].roi.x + faces[i].roi.width, faces[i].roi.y), color, + -1); + cv::putText(*rgbaImage, text, cv::Point2d(faces[i].roi.x, faces[i].roi.y), + font_face, font_scale, cv::Scalar(255, 255, 255), thickness); + } +} + +void Pipeline::VisualizeStatus(double readGLFBOTime, double writeGLTextureTime, + double fdtPreprocessTime, double fdtPredictTime, + double fdtPostprocessTime, + double mclPreprocessTime, double mclPredictTime, + double mclPostprocessTime, cv::Mat *rgbaImage) { + char text[255]; + cv::Scalar color = cv::Scalar(255, 255, 255); + int font_face = cv::FONT_HERSHEY_PLAIN; + double font_scale = 1.f; + float thickness = 1; + sprintf(text, "Read GLFBO time: %.1f ms", readGLFBOTime); + cv::Size text_size = + cv::getTextSize(text, font_face, font_scale, thickness, nullptr); + text_size.height *= 1.25f; + cv::Point2d offset(10, text_size.height + 15); + cv::putText(*rgbaImage, text, offset, font_face, font_scale, color, + thickness); + sprintf(text, "Write GLTexture time: %.1f ms", writeGLTextureTime); + offset.y += text_size.height; + cv::putText(*rgbaImage, text, offset, font_face, font_scale, color, + thickness); + // Face detector + sprintf(text, "FDT preprocess time: %.1f ms", fdtPreprocessTime); + offset.y += text_size.height; + cv::putText(*rgbaImage, text, offset, font_face, font_scale, color, + thickness); + sprintf(text, "FDT predict time: %.1f ms", fdtPredictTime); + offset.y += text_size.height; + cv::putText(*rgbaImage, text, offset, font_face, font_scale, color, + thickness); + sprintf(text, "FDT postprocess time: %.1f ms", fdtPostprocessTime); + offset.y += text_size.height; + cv::putText(*rgbaImage, text, offset, font_face, font_scale, color, + thickness); + // Mask classification + sprintf(text, "MCL preprocess time: %.1f ms", mclPreprocessTime); + offset.y += text_size.height; + cv::putText(*rgbaImage, text, offset, font_face, font_scale, color, + thickness); + sprintf(text, "MCL predict time: %.1f ms", mclPredictTime); + offset.y += text_size.height; + cv::putText(*rgbaImage, text, offset, font_face, font_scale, color, + thickness); + sprintf(text, "MCL postprocess time: %.1f ms", mclPostprocessTime); + offset.y += text_size.height; + cv::putText(*rgbaImage, text, offset, font_face, font_scale, color, + thickness); +} + +bool Pipeline::Process(int inTexureId, int outTextureId, int textureWidth, + int textureHeight, std::string savedImagePath) { + double readGLFBOTime = 0, writeGLTextureTime = 0; + double fdtPreprocessTime = 0, fdtPredictTime = 0, fdtPostprocessTime = 0; + double mclPreprocessTime = 0, mclPredictTime = 0, mclPostprocessTime = 0; + + cv::Mat rgbaImage; + CreateRGBAImageFromGLFBOTexture(textureWidth, textureHeight, &rgbaImage, + &readGLFBOTime); + + // Stage1: Face detection + std::vector faces; + faceDetector_->Predict(rgbaImage, &faces, &fdtPreprocessTime, &fdtPredictTime, + &fdtPostprocessTime); + if (faces.size() > 0) { + // Stage2: Mask wearing classification + maskClassifier_->Predict(rgbaImage, &faces, &mclPreprocessTime, + &mclPredictTime, &mclPostprocessTime); + // Stage3: Visualize results + VisualizeResults(faces, &rgbaImage); + } + + // Visualize the status(performace data) to origin image + VisualizeStatus(readGLFBOTime, writeGLTextureTime, fdtPreprocessTime, + fdtPredictTime, fdtPostprocessTime, mclPreprocessTime, + mclPredictTime, mclPostprocessTime, &rgbaImage); + + // Dump modified image if savedImagePath is set + if (!savedImagePath.empty()) { + cv::Mat bgrImage; + cv::cvtColor(rgbaImage, bgrImage, cv::COLOR_RGBA2BGR); + imwrite(savedImagePath, bgrImage); + } + + WriteRGBAImageBackToGLTexture(rgbaImage, outTextureId, &writeGLTextureTime); + return true; +} diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/cpp/Pipeline.h b/PaddleLite-android-demo/mask_detection_demo/app/src/main/cpp/Pipeline.h new file mode 100644 index 0000000000..1207109b01 --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/cpp/Pipeline.h @@ -0,0 +1,136 @@ +// Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "Utils.h" +#include "paddle_api.h" +#include +#include +#include +#include +#include +#include +#include +#include + +struct Face { + // Detection result: face rectangle + cv::Rect roi; + // Classification result: confidence + float confidence; + // Classification result : class id + int classid; +}; + +class FaceDetector { +public: + explicit FaceDetector(const std::string &modelDir, const int cpuThreadNum, + const std::string &cpuPowerMode, float inputScale, + const std::vector &inputMean, + const std::vector &inputStd, + float scoreThreshold); + + void Predict(const cv::Mat &rgbImage, std::vector *faces, + double *preprocessTime, double *predictTime, + double *postprocessTime); + +private: + void Preprocess(const cv::Mat &rgbImage); + void Postprocess(const cv::Mat &rgbImage, std::vector *faces); + +private: + float inputScale_; + std::vector inputMean_; + std::vector inputStd_; + float scoreThreshold_; + std::shared_ptr predictor_; +}; + +class MaskClassifier { +public: + explicit MaskClassifier(const std::string &modelDir, const int cpuThreadNum, + const std::string &cpuPowerMode, int inputWidth, + int inputHeight, const std::vector &inputMean, + const std::vector &inputStd); + + void Predict(const cv::Mat &rgbImage, std::vector *faces, + double *preprocessTime, double *predictTime, + double *postprocessTime); + +private: + void Preprocess(const cv::Mat &rgbImage, const std::vector &faces); + void Postprocess(std::vector *faces); + +private: + int inputWidth_; + int inputHeight_; + std::vector inputMean_; + std::vector inputStd_; + std::shared_ptr predictor_; +}; + +class Pipeline { +public: + Pipeline(const std::string &fdtModelDir, const int fdtCPUThreadNum, + const std::string &detCPUPowerMode, float fdtInputScale, + const std::vector &fdtInputMean, + const std::vector &fdtInputStd, float fdtScoreThreshold, + const std::string &mclModelDir, const int mclCPUThreadNum, + const std::string &mclCPUPowerMode, int mclInputWidth, + int mclInputHeight, const std::vector &mclInputMean, + const std::vector &mclInputStd); + + bool Process(int inTextureId, int outTextureId, int textureWidth, + int textureHeight, std::string savedImagePath); + +private: + // Read pixels from FBO texture to CV image + void CreateRGBAImageFromGLFBOTexture(int textureWidth, int textureHeight, + cv::Mat *rgbaImage, + double *readGLFBOTime) { + auto t = GetCurrentTime(); + rgbaImage->create(textureHeight, textureWidth, CV_8UC4); + glReadPixels(0, 0, textureWidth, textureHeight, GL_RGBA, GL_UNSIGNED_BYTE, + rgbaImage->data); + *readGLFBOTime = GetElapsedTime(t); + LOGD("Read from FBO texture costs %f ms", *readGLFBOTime); + } + + // Write back to texture2D + void WriteRGBAImageBackToGLTexture(const cv::Mat &rgbaImage, int textureId, + double *writeGLTextureTime) { + auto t = GetCurrentTime(); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, textureId); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rgbaImage.cols, rgbaImage.rows, + GL_RGBA, GL_UNSIGNED_BYTE, rgbaImage.data); + *writeGLTextureTime = GetElapsedTime(t); + LOGD("Write back to texture2D costs %f ms", *writeGLTextureTime); + } + + // Visualize the results to origin image + void VisualizeResults(const std::vector &faces, cv::Mat *rgbaImage); + + // Visualize the status(performace data) to origin image + void VisualizeStatus(double readGLFBOTime, double writeGLTextureTime, + double fdtPreprocessTime, double fdtPredictTime, + double fdtPostprocessTime, double mclPreprocessTime, + double mclPredictTime, double mclPostprocessTime, + cv::Mat *rgbaImage); + +private: + std::shared_ptr faceDetector_; + std::shared_ptr maskClassifier_; +}; diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/cpp/Utils.cc b/PaddleLite-android-demo/mask_detection_demo/app/src/main/cpp/Utils.cc new file mode 100644 index 0000000000..8b10f663cb --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/cpp/Utils.cc @@ -0,0 +1,59 @@ +// Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "Utils.h" +#include + +int64_t ShapeProduction(const std::vector &shape) { + int64_t res = 1; + for (auto i : shape) + res *= i; + return res; +} + +void NHWC2NCHW(const float *src, float *dst, const float *mean, + const float *std, int width, int height) { + int size = height * width; + float32x4_t vmean0 = vdupq_n_f32(mean ? mean[0] : 0.0f); + float32x4_t vmean1 = vdupq_n_f32(mean ? mean[1] : 0.0f); + float32x4_t vmean2 = vdupq_n_f32(mean ? mean[2] : 0.0f); + float32x4_t vscale0 = vdupq_n_f32(std ? (1.0f / std[0]) : 1.0f); + float32x4_t vscale1 = vdupq_n_f32(std ? (1.0f / std[1]) : 1.0f); + float32x4_t vscale2 = vdupq_n_f32(std ? (1.0f / std[2]) : 1.0f); + float *dst_c0 = dst; + float *dst_c1 = dst + size; + float *dst_c2 = dst + size * 2; + int i = 0; + for (; i < size - 3; i += 4) { + float32x4x3_t vin3 = vld3q_f32(src); + float32x4_t vsub0 = vsubq_f32(vin3.val[0], vmean0); + float32x4_t vsub1 = vsubq_f32(vin3.val[1], vmean1); + float32x4_t vsub2 = vsubq_f32(vin3.val[2], vmean2); + float32x4_t vs0 = vmulq_f32(vsub0, vscale0); + float32x4_t vs1 = vmulq_f32(vsub1, vscale1); + float32x4_t vs2 = vmulq_f32(vsub2, vscale2); + vst1q_f32(dst_c0, vs0); + vst1q_f32(dst_c1, vs1); + vst1q_f32(dst_c2, vs2); + src += 12; + dst_c0 += 4; + dst_c1 += 4; + dst_c2 += 4; + } + for (; i < size; i++) { + *(dst_c0++) = (*(src++) - mean[0]) / std[0]; + *(dst_c1++) = (*(src++) - mean[1]) / std[1]; + *(dst_c2++) = (*(src++) - mean[2]) / std[2]; + } +} diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/cpp/Utils.h b/PaddleLite-android-demo/mask_detection_demo/app/src/main/cpp/Utils.h new file mode 100644 index 0000000000..492de7443f --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/cpp/Utils.h @@ -0,0 +1,89 @@ +// Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "paddle_api.h" +#include +#include +#include +#include + +#define TAG "JNI" +#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__) +#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__) +#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, TAG, __VA_ARGS__) + +int64_t ShapeProduction(const std::vector &shape); + +template +bool ReadFile(const std::string &path, std::vector *data) { + std::ifstream file(path, std::ifstream::binary); + if (file) { + file.seekg(0, file.end); + int size = file.tellg(); + LOGD("file size=%lld\n", size); + data->resize(size / sizeof(T)); + file.seekg(0, file.beg); + file.read(reinterpret_cast(data->data()), size); + file.close(); + return true; + } else { + LOGE("Can't read file from %s\n", path.c_str()); + } + return false; +} + +template +bool WriteFile(const std::string &path, const std::vector &data) { + std::ofstream file{path, std::ios::binary}; + if (!file.is_open()) { + LOGE("Can't write file to %s\n", path.c_str()); + return false; + } + file.write(reinterpret_cast(data.data()), + data.size() * sizeof(T)); + file.close(); + return true; +} + +inline int64_t GetCurrentTime() { + struct timeval time; + gettimeofday(&time, NULL); + return 1000000LL * (int64_t)time.tv_sec + (int64_t)time.tv_usec; +} + +inline double GetElapsedTime(int64_t time) { + return (GetCurrentTime() - time) / 1000.0f; +} + +inline paddle::lite_api::PowerMode ParsePowerMode(std::string mode) { + if (mode == "LITE_POWER_HIGH") { + return paddle::lite_api::LITE_POWER_HIGH; + } else if (mode == "LITE_POWER_LOW") { + return paddle::lite_api::LITE_POWER_LOW; + } else if (mode == "LITE_POWER_FULL") { + return paddle::lite_api::LITE_POWER_FULL; + } else if (mode == "LITE_POWER_RAND_HIGH") { + return paddle::lite_api::LITE_POWER_RAND_HIGH; + } else if (mode == "LITE_POWER_RAND_LOW") { + return paddle::lite_api::LITE_POWER_RAND_LOW; + } + return paddle::lite_api::LITE_POWER_NO_BIND; +} + +void NHWC2NCHW(const float *src, float *dst, const float *mean, + const float *std, int width, int height); diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/java/com/baidu/paddle/lite/demo/common/AppCompatPreferenceActivity.java b/PaddleLite-android-demo/mask_detection_demo/app/src/main/java/com/baidu/paddle/lite/demo/common/AppCompatPreferenceActivity.java new file mode 100644 index 0000000000..51d716b457 --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/java/com/baidu/paddle/lite/demo/common/AppCompatPreferenceActivity.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.baidu.paddle.lite.demo.common; + +import android.content.res.Configuration; +import android.os.Bundle; +import android.preference.PreferenceActivity; +import android.support.annotation.LayoutRes; +import android.support.annotation.Nullable; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatDelegate; +import android.support.v7.widget.Toolbar; +import android.view.MenuInflater; +import android.view.View; +import android.view.ViewGroup; + +/** + * A {@link PreferenceActivity} which implements and proxies the necessary calls + * to be used with AppCompat. + *

+ * This technique can be used with an {@link android.app.Activity} class, not just + * {@link PreferenceActivity}. + */ +public abstract class AppCompatPreferenceActivity extends PreferenceActivity { + private AppCompatDelegate mDelegate; + + @Override + protected void onCreate(Bundle savedInstanceState) { + getDelegate().installViewFactory(); + getDelegate().onCreate(savedInstanceState); + super.onCreate(savedInstanceState); + } + + @Override + protected void onPostCreate(Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + getDelegate().onPostCreate(savedInstanceState); + } + + public ActionBar getSupportActionBar() { + return getDelegate().getSupportActionBar(); + } + + public void setSupportActionBar(@Nullable Toolbar toolbar) { + getDelegate().setSupportActionBar(toolbar); + } + + @Override + public MenuInflater getMenuInflater() { + return getDelegate().getMenuInflater(); + } + + @Override + public void setContentView(@LayoutRes int layoutResID) { + getDelegate().setContentView(layoutResID); + } + + @Override + public void setContentView(View view) { + getDelegate().setContentView(view); + } + + @Override + public void setContentView(View view, ViewGroup.LayoutParams params) { + getDelegate().setContentView(view, params); + } + + @Override + public void addContentView(View view, ViewGroup.LayoutParams params) { + getDelegate().addContentView(view, params); + } + + @Override + protected void onPostResume() { + super.onPostResume(); + getDelegate().onPostResume(); + } + + @Override + protected void onTitleChanged(CharSequence title, int color) { + super.onTitleChanged(title, color); + getDelegate().setTitle(title); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + getDelegate().onConfigurationChanged(newConfig); + } + + @Override + protected void onStop() { + super.onStop(); + getDelegate().onStop(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + getDelegate().onDestroy(); + } + + public void invalidateOptionsMenu() { + getDelegate().invalidateOptionsMenu(); + } + + private AppCompatDelegate getDelegate() { + if (mDelegate == null) { + mDelegate = AppCompatDelegate.create(this, null); + } + return mDelegate; + } +} diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/java/com/baidu/paddle/lite/demo/common/CameraSurfaceView.java b/PaddleLite-android-demo/mask_detection_demo/app/src/main/java/com/baidu/paddle/lite/demo/common/CameraSurfaceView.java new file mode 100644 index 0000000000..24b1ad1cc7 --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/java/com/baidu/paddle/lite/demo/common/CameraSurfaceView.java @@ -0,0 +1,319 @@ +package com.baidu.paddle.lite.demo.common; + +import android.content.Context; +import android.graphics.SurfaceTexture; +import android.hardware.Camera; +import android.hardware.Camera.CameraInfo; +import android.hardware.Camera.Size; +import android.opengl.GLES11Ext; +import android.opengl.GLES20; +import android.opengl.GLSurfaceView; +import android.opengl.GLSurfaceView.Renderer; +import android.opengl.Matrix; +import android.util.AttributeSet; +import android.util.Log; + +import com.baidu.paddle.lite.demo.mask_detection.MainActivity; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.util.List; + +public class CameraSurfaceView extends GLSurfaceView implements Renderer, + SurfaceTexture.OnFrameAvailableListener { + private static final String TAG = MainActivity.class.getSimpleName(); + + public static final int EXPECTED_PREVIEW_WIDTH = 1280; + public static final int EXPECTED_PREVIEW_HEIGHT = 720; + + + protected int numberOfCameras; + protected int selectedCameraId; + protected boolean disableCamera = false; + protected Camera camera; + + protected Context context; + protected SurfaceTexture surfaceTexture; + protected int surfaceWidth = 0; + protected int surfaceHeight = 0; + protected int textureWidth = 0; + protected int textureHeight = 0; + + // In order to manipulate the camera preview data and render the modified one + // to the screen, three textures are created and the data flow is shown as following: + // previewdata->camTextureId->fboTexureId->drawTexureId->framebuffer + protected int[] fbo = {0}; + protected int[] camTextureId = {0}; + protected int[] fboTexureId = {0}; + protected int[] drawTexureId = {0}; + + private final String vss = "" + + "attribute vec2 vPosition;\n" + + "attribute vec2 vTexCoord;\n" + "varying vec2 texCoord;\n" + + "void main() {\n" + " texCoord = vTexCoord;\n" + + " gl_Position = vec4 (vPosition.x, vPosition.y, 0.0, 1.0);\n" + + "}"; + + private final String fssCam2FBO = "" + + "#extension GL_OES_EGL_image_external : require\n" + + "precision mediump float;\n" + + "uniform samplerExternalOES sTexture;\n" + + "varying vec2 texCoord;\n" + + "void main() {\n" + + " gl_FragColor = texture2D(sTexture,texCoord);\n" + "}"; + + private final String fssTex2Screen = "" + + "precision mediump float;\n" + + "uniform sampler2D sTexture;\n" + + "varying vec2 texCoord;\n" + + "void main() {\n" + + " gl_FragColor = texture2D(sTexture,texCoord);\n" + "}"; + + private final float vertexCoords[] = { + -1, -1, + -1, 1, + 1, -1, + 1, 1}; + private float textureCoords[] = { + 0, 1, + 0, 0, + 1, 1, + 1, 0}; + + private FloatBuffer vertexCoordsBuffer; + private FloatBuffer textureCoordsBuffer; + + private int progCam2FBO = -1; + private int progTex2Screen = -1; + private int vcCam2FBO; + private int tcCam2FBO; + private int vcTex2Screen; + private int tcTex2Screen; + + public interface OnTextureChangedListener { + public boolean onTextureChanged(int inTextureId, int outTextureId, int textureWidth, int textureHeight); + } + + private OnTextureChangedListener onTextureChangedListener = null; + + public void setOnTextureChangedListener(OnTextureChangedListener listener) { + onTextureChangedListener = listener; + } + + public CameraSurfaceView(Context ctx, AttributeSet attrs) { + super(ctx, attrs); + context = ctx; + setEGLContextClientVersion(2); + setRenderer(this); + setRenderMode(RENDERMODE_WHEN_DIRTY); + + // Find the total number of available cameras and the ID of the default camera + numberOfCameras = Camera.getNumberOfCameras(); + CameraInfo cameraInfo = new CameraInfo(); + for (int i = 0; i < numberOfCameras; i++) { + Camera.getCameraInfo(i, cameraInfo); + if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) { + selectedCameraId = i; + } + } + } + + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + // Create OES texture for storing camera preview data(YUV format) + GLES20.glGenTextures(1, camTextureId, 0); + GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, camTextureId[0]); + GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); + GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST); + surfaceTexture = new SurfaceTexture(camTextureId[0]); + surfaceTexture.setOnFrameAvailableListener(this); + + // Prepare vertex and texture coordinates + int bytes = vertexCoords.length * Float.SIZE / Byte.SIZE; + vertexCoordsBuffer = ByteBuffer.allocateDirect(bytes).order(ByteOrder.nativeOrder()).asFloatBuffer(); + textureCoordsBuffer = ByteBuffer.allocateDirect(bytes).order(ByteOrder.nativeOrder()).asFloatBuffer(); + vertexCoordsBuffer.put(vertexCoords).position(0); + textureCoordsBuffer.put(textureCoords).position(0); + + // Create vertex and fragment shaders + // camTextureId->fboTexureId + progCam2FBO = Utils.createShaderProgram(vss, fssCam2FBO); + vcCam2FBO = GLES20.glGetAttribLocation(progCam2FBO, "vPosition"); + tcCam2FBO = GLES20.glGetAttribLocation(progCam2FBO, "vTexCoord"); + GLES20.glEnableVertexAttribArray(vcCam2FBO); + GLES20.glEnableVertexAttribArray(tcCam2FBO); + // fboTexureId/drawTexureId -> screen + progTex2Screen = Utils.createShaderProgram(vss, fssTex2Screen); + vcTex2Screen = GLES20.glGetAttribLocation(progTex2Screen, "vPosition"); + tcTex2Screen = GLES20.glGetAttribLocation(progTex2Screen, "vTexCoord"); + GLES20.glEnableVertexAttribArray(vcTex2Screen); + GLES20.glEnableVertexAttribArray(tcTex2Screen); + } + + @Override + public void onSurfaceChanged(GL10 gl, int width, int height) { + surfaceWidth = width; + surfaceHeight = height; + openCamera(); + } + + @Override + public void onDrawFrame(GL10 gl) { + if (surfaceTexture == null) return; + + GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); + surfaceTexture.updateTexImage(); + float matrix[] = new float[16]; + surfaceTexture.getTransformMatrix(matrix); + + // camTextureId->fboTexureId + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fbo[0]); + GLES20.glViewport(0, 0, textureWidth, textureHeight); + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); + GLES20.glUseProgram(progCam2FBO); + GLES20.glVertexAttribPointer(vcCam2FBO, 2, GLES20.GL_FLOAT, false, 4 * 2, vertexCoordsBuffer); + textureCoordsBuffer.clear(); + textureCoordsBuffer.put(transformTextureCoordinates(textureCoords, matrix)); + textureCoordsBuffer.position(0); + GLES20.glVertexAttribPointer(tcCam2FBO, 2, GLES20.GL_FLOAT, false, 4 * 2, textureCoordsBuffer); + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, camTextureId[0]); + GLES20.glUniform1i(GLES20.glGetUniformLocation(progCam2FBO, "sTexture"), 0); + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + GLES20.glFlush(); + + // Check if the draw texture is set + int targetTexureId = fboTexureId[0]; + if (onTextureChangedListener != null) { + boolean modified = onTextureChangedListener.onTextureChanged(fboTexureId[0], drawTexureId[0], + textureWidth, textureHeight); + if (modified) { + targetTexureId = drawTexureId[0]; + } + } + + // fboTexureId/drawTexureId->Screen + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); + GLES20.glViewport(0, 0, surfaceWidth, surfaceHeight); + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); + GLES20.glUseProgram(progTex2Screen); + GLES20.glVertexAttribPointer(vcTex2Screen, 2, GLES20.GL_FLOAT, false, 4 * 2, vertexCoordsBuffer); + textureCoordsBuffer.clear(); + textureCoordsBuffer.put(textureCoords); + textureCoordsBuffer.position(0); + GLES20.glVertexAttribPointer(tcTex2Screen, 2, GLES20.GL_FLOAT, false, 4 * 2, textureCoordsBuffer); + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, targetTexureId); + GLES20.glUniform1i(GLES20.glGetUniformLocation(progTex2Screen, "sTexture"), 0); + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + GLES20.glFlush(); + } + + private float[] transformTextureCoordinates(float[] coords, float[] matrix) { + float[] result = new float[coords.length]; + float[] vt = new float[4]; + for (int i = 0; i < coords.length; i += 2) { + float[] v = {coords[i], coords[i + 1], 0, 1}; + Matrix.multiplyMV(vt, 0, matrix, 0, v, 0); + result[i] = vt[0]; + result[i + 1] = vt[1]; + } + return result; + } + + @Override + public void onResume() { + super.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + releaseCamera(); + } + + @Override + public void onFrameAvailable(SurfaceTexture surfaceTexture) { + requestRender(); + } + + public void disableCamera() { + disableCamera = true; + } + + public void switchCamera() { + releaseCamera(); + selectedCameraId = (selectedCameraId + 1) % numberOfCameras; + openCamera(); + } + + public void openCamera() { + if (disableCamera) return; + camera = Camera.open(selectedCameraId); + List supportedPreviewSizes = camera.getParameters().getSupportedPreviewSizes(); + Size previewSize = Utils.getOptimalPreviewSize(supportedPreviewSizes, EXPECTED_PREVIEW_WIDTH, + EXPECTED_PREVIEW_HEIGHT); + Camera.Parameters parameters = camera.getParameters(); + parameters.setPreviewSize(previewSize.width, previewSize.height); + if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) { + parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); + } + camera.setParameters(parameters); + int degree = Utils.getCameraDisplayOrientation(context, selectedCameraId); + camera.setDisplayOrientation(degree); + boolean rotate = degree == 90 || degree == 270; + textureWidth = rotate ? previewSize.height : previewSize.width; + textureHeight = rotate ? previewSize.width : previewSize.height; + // Destroy FBO and draw textures + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); + GLES20.glDeleteFramebuffers(1, fbo, 0); + GLES20.glDeleteTextures(1, drawTexureId, 0); + GLES20.glDeleteTextures(1, fboTexureId, 0); + // Normal texture for storing modified camera preview data(RGBA format) + GLES20.glGenTextures(1, drawTexureId, 0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, drawTexureId[0]); + GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, textureWidth, textureHeight, 0, + GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST); + // FBO texture for storing camera preview data(RGBA format) + GLES20.glGenTextures(1, fboTexureId, 0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, fboTexureId[0]); + GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, textureWidth, textureHeight, 0, + GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST); + // Generate FBO and bind to FBO texture + GLES20.glGenFramebuffers(1, fbo, 0); + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fbo[0]); + GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, + fboTexureId[0], 0); + try { + camera.setPreviewTexture(surfaceTexture); + } catch (IOException exception) { + Log.e(TAG, "IOException caused by setPreviewDisplay()", exception); + } + camera.startPreview(); + } + + public void releaseCamera() { + if (camera != null) { + camera.setPreviewCallback(null); + camera.stopPreview(); + camera.release(); + camera = null; + } + } +} diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/java/com/baidu/paddle/lite/demo/common/Utils.java b/PaddleLite-android-demo/mask_detection_demo/app/src/main/java/com/baidu/paddle/lite/demo/common/Utils.java new file mode 100644 index 0000000000..4a6197c928 --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/java/com/baidu/paddle/lite/demo/common/Utils.java @@ -0,0 +1,219 @@ +package com.baidu.paddle.lite.demo.common; + +import android.content.Context; +import android.content.res.Resources; +import android.hardware.Camera; +import android.opengl.GLES20; +import android.os.Environment; +import android.util.Log; +import android.view.Surface; +import android.view.WindowManager; + +import java.io.*; +import java.util.List; + +public class Utils { + private static final String TAG = Utils.class.getSimpleName(); + + public static void copyFileFromAssets(Context appCtx, String srcPath, String dstPath) { + if (srcPath.isEmpty() || dstPath.isEmpty()) { + return; + } + InputStream is = null; + OutputStream os = null; + try { + is = new BufferedInputStream(appCtx.getAssets().open(srcPath)); + os = new BufferedOutputStream(new FileOutputStream(new File(dstPath))); + byte[] buffer = new byte[1024]; + int length = 0; + while ((length = is.read(buffer)) != -1) { + os.write(buffer, 0, length); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + os.close(); + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + public static void copyDirectoryFromAssets(Context appCtx, String srcDir, String dstDir) { + if (srcDir.isEmpty() || dstDir.isEmpty()) { + return; + } + try { + if (!new File(dstDir).exists()) { + new File(dstDir).mkdirs(); + } + for (String fileName : appCtx.getAssets().list(srcDir)) { + String srcSubPath = srcDir + File.separator + fileName; + String dstSubPath = dstDir + File.separator + fileName; + if (new File(srcSubPath).isDirectory()) { + copyDirectoryFromAssets(appCtx, srcSubPath, dstSubPath); + } else { + copyFileFromAssets(appCtx, srcSubPath, dstSubPath); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static float[] parseFloatsFromString(String string, String delimiter) { + String[] pieces = string.trim().toLowerCase().split(delimiter); + float[] floats = new float[pieces.length]; + for (int i = 0; i < pieces.length; i++) { + floats[i] = Float.parseFloat(pieces[i].trim()); + } + return floats; + } + + public static long[] parseLongsFromString(String string, String delimiter) { + String[] pieces = string.trim().toLowerCase().split(delimiter); + long[] longs = new long[pieces.length]; + for (int i = 0; i < pieces.length; i++) { + longs[i] = Long.parseLong(pieces[i].trim()); + } + return longs; + } + + public static String getSDCardDirectory() { + return Environment.getExternalStorageDirectory().getAbsolutePath(); + } + + public static String getDCIMDirectory() { + return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath(); + } + + public static Camera.Size getOptimalPreviewSize(List sizes, int w, int h) { + final double ASPECT_TOLERANCE = 0.1; + double targetRatio = (double) w / h; + if (sizes == null) return null; + + Camera.Size optimalSize = null; + double minDiff = Double.MAX_VALUE; + + int targetHeight = h; + + // Try to find an size match aspect ratio and size + for (Camera.Size size : sizes) { + double ratio = (double) size.width / size.height; + if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue; + if (Math.abs(size.height - targetHeight) < minDiff) { + optimalSize = size; + minDiff = Math.abs(size.height - targetHeight); + } + } + + // Cannot find the one match the aspect ratio, ignore the requirement + if (optimalSize == null) { + minDiff = Double.MAX_VALUE; + for (Camera.Size size : sizes) { + if (Math.abs(size.height - targetHeight) < minDiff) { + optimalSize = size; + minDiff = Math.abs(size.height - targetHeight); + } + } + } + return optimalSize; + } + + public static int getScreenWidth() { + return Resources.getSystem().getDisplayMetrics().widthPixels; + } + + public static int getScreenHeight() { + return Resources.getSystem().getDisplayMetrics().heightPixels; + } + + public static int getCameraDisplayOrientation(Context context, int cameraId) { + android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo(); + android.hardware.Camera.getCameraInfo(cameraId, info); + WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + int rotation = wm.getDefaultDisplay().getRotation(); + int degrees = 0; + switch (rotation) { + case Surface.ROTATION_0: + degrees = 0; + break; + case Surface.ROTATION_90: + degrees = 90; + break; + case Surface.ROTATION_180: + degrees = 180; + break; + case Surface.ROTATION_270: + degrees = 270; + break; + } + int result; + if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { + result = (info.orientation + degrees) % 360; + result = (360 - result) % 360; // compensate the mirror + } else { + // back-facing + result = (info.orientation - degrees + 360) % 360; + } + return result; + } + + public static int createShaderProgram(String vss, String fss) { + int vshader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER); + GLES20.glShaderSource(vshader, vss); + GLES20.glCompileShader(vshader); + int[] status = new int[1]; + GLES20.glGetShaderiv(vshader, GLES20.GL_COMPILE_STATUS, status, 0); + if (status[0] == 0) { + Log.e(TAG, GLES20.glGetShaderInfoLog(vshader)); + GLES20.glDeleteShader(vshader); + vshader = 0; + return 0; + } + + int fshader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER); + GLES20.glShaderSource(fshader, fss); + GLES20.glCompileShader(fshader); + GLES20.glGetShaderiv(fshader, GLES20.GL_COMPILE_STATUS, status, 0); + if (status[0] == 0) { + Log.e(TAG, GLES20.glGetShaderInfoLog(fshader)); + GLES20.glDeleteShader(vshader); + GLES20.glDeleteShader(fshader); + fshader = 0; + return 0; + } + + int program = GLES20.glCreateProgram(); + GLES20.glAttachShader(program, vshader); + GLES20.glAttachShader(program, fshader); + GLES20.glLinkProgram(program); + GLES20.glDeleteShader(vshader); + GLES20.glDeleteShader(fshader); + GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, status, 0); + if (status[0] == 0) { + Log.e(TAG, GLES20.glGetProgramInfoLog(program)); + program = 0; + return 0; + } + GLES20.glValidateProgram(program); + GLES20.glGetProgramiv(program, GLES20.GL_VALIDATE_STATUS, status, 0); + if (status[0] == 0) { + Log.e(TAG, GLES20.glGetProgramInfoLog(program)); + GLES20.glDeleteProgram(program); + program = 0; + return 0; + } + + return program; + } + + public static boolean isSupportedNPU() { + String hardware = android.os.Build.HARDWARE; + return hardware.equalsIgnoreCase("kirin810") || hardware.equalsIgnoreCase("kirin990"); + } +} diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/java/com/baidu/paddle/lite/demo/mask_detection/MainActivity.java b/PaddleLite-android-demo/mask_detection_demo/app/src/main/java/com/baidu/paddle/lite/demo/mask_detection/MainActivity.java new file mode 100644 index 0000000000..6c264a0c28 --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/java/com/baidu/paddle/lite/demo/mask_detection/MainActivity.java @@ -0,0 +1,207 @@ +package com.baidu.paddle.lite.demo.mask_detection; + +import android.Manifest; +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.graphics.*; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.support.annotation.NonNull; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; +import android.util.Log; +import android.view.*; +import android.widget.*; + +import com.baidu.paddle.lite.demo.common.CameraSurfaceView; +import com.baidu.paddle.lite.demo.common.Utils; + +import java.io.File; +import java.nio.file.attribute.FileTime; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class MainActivity extends Activity implements View.OnClickListener, CameraSurfaceView.OnTextureChangedListener { + private static final String TAG = MainActivity.class.getSimpleName(); + + CameraSurfaceView svPreview; + TextView tvStatus; + ImageButton btnSwitch; + ImageButton btnShutter; + ImageButton btnSettings; + + String savedImagePath = ""; + int lastFrameIndex = 0; + long lastFrameTime; + + Native predictor = new Native(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Fullscreen + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); + + setContentView(R.layout.activity_main); + + // Clear all setting items to avoid app crashing due to the incorrect settings + initSettings(); + + // Init the camera preview and UI components + initView(); + + // Check and request CAMERA and WRITE_EXTERNAL_STORAGE permissions + if (!checkAllPermissions()) { + requestAllPermissions(); + } + } + + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.btn_switch: + svPreview.switchCamera(); + break; + case R.id.btn_shutter: + SimpleDateFormat date = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss"); + synchronized (this) { + savedImagePath = Utils.getDCIMDirectory() + File.separator + date.format(new Date()).toString() + ".png"; + } + Toast.makeText(MainActivity.this, "Save snapshot to " + savedImagePath, Toast.LENGTH_SHORT).show(); + break; + case R.id.btn_settings: + startActivity(new Intent(MainActivity.this, SettingsActivity.class)); + break; + } + } + + @Override + public boolean onTextureChanged(int inTextureId, int outTextureId, int textureWidth, int textureHeight) { + String savedImagePath = ""; + synchronized (this) { + savedImagePath = MainActivity.this.savedImagePath; + } + boolean modified = predictor.process(inTextureId, outTextureId, textureWidth, textureHeight, savedImagePath); + if (!savedImagePath.isEmpty()) { + synchronized (this) { + MainActivity.this.savedImagePath = ""; + } + } + lastFrameIndex++; + if (lastFrameIndex >= 30) { + final int fps = (int) (lastFrameIndex * 1e9 / (System.nanoTime() - lastFrameTime)); + runOnUiThread(new Runnable() { + public void run() { + tvStatus.setText(Integer.toString(fps) + "fps"); + } + }); + lastFrameIndex = 0; + lastFrameTime = System.nanoTime(); + } + return modified; + } + + @Override + protected void onResume() { + super.onResume(); + // Reload settings and re-initialize the predictor + checkAndUpdateSettings(); + // Open camera until the permissions have been granted + if (!checkAllPermissions()) { + svPreview.disableCamera(); + } + svPreview.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + svPreview.onPause(); + } + + @Override + protected void onDestroy() { + if (predictor != null) { + predictor.release(); + } + super.onDestroy(); + } + + public void initView() { + svPreview = (CameraSurfaceView) findViewById(R.id.sv_preview); + svPreview.setOnTextureChangedListener(this); + tvStatus = (TextView) findViewById(R.id.tv_status); + btnSwitch = (ImageButton) findViewById(R.id.btn_switch); + btnSwitch.setOnClickListener(this); + btnShutter = (ImageButton) findViewById(R.id.btn_shutter); + btnShutter.setOnClickListener(this); + btnSettings = (ImageButton) findViewById(R.id.btn_settings); + btnSettings.setOnClickListener(this); + } + + public void initSettings() { + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.clear(); + editor.commit(); + } + + public void checkAndUpdateSettings() { + if (SettingsActivity.checkAndUpdateSettings(this)) { + String fdtRealModelDir = getCacheDir() + "/" + SettingsActivity.fdtModelDir; + Utils.copyDirectoryFromAssets(this, SettingsActivity.fdtModelDir, fdtRealModelDir); + String mclRealModelDir = getCacheDir() + "/" + SettingsActivity.mclModelDir; + Utils.copyDirectoryFromAssets(this, SettingsActivity.mclModelDir, mclRealModelDir); + predictor.init( + fdtRealModelDir, + SettingsActivity.fdtCPUThreadNum, + SettingsActivity.fdtCPUPowerMode, + SettingsActivity.fdtInputScale, + SettingsActivity.fdtInputMean, + SettingsActivity.fdtInputStd, + SettingsActivity.fdtScoreThreshold, + mclRealModelDir, + SettingsActivity.mclCPUThreadNum, + SettingsActivity.mclCPUPowerMode, + SettingsActivity.mclInputWidth, + SettingsActivity.mclInputHeight, + SettingsActivity.mclInputMean, + SettingsActivity.mclInputStd); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, + @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (grantResults[0] != PackageManager.PERMISSION_GRANTED || grantResults[1] != PackageManager.PERMISSION_GRANTED) { + new AlertDialog.Builder(MainActivity.this) + .setTitle("Permission denied") + .setMessage("Click to force quit the app, then open Settings->Apps & notifications->Target " + + "App->Permissions to grant all of the permissions.") + .setCancelable(false) + .setPositiveButton("Exit", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + MainActivity.this.finish(); + } + }).show(); + } + } + + private void requestAllPermissions() { + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, + Manifest.permission.CAMERA}, 0); + } + + private boolean checkAllPermissions() { + return ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED + && ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED; + } +} diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/java/com/baidu/paddle/lite/demo/mask_detection/Native.java b/PaddleLite-android-demo/mask_detection_demo/app/src/main/java/com/baidu/paddle/lite/demo/mask_detection/Native.java new file mode 100644 index 0000000000..3409215cc1 --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/java/com/baidu/paddle/lite/demo/mask_detection/Native.java @@ -0,0 +1,76 @@ +package com.baidu.paddle.lite.demo.mask_detection; + +import android.content.Context; + +public class Native { + static { + System.loadLibrary("Native"); + } + + private long ctx = 0; + + public boolean init(String fdtModelDir, + int fdtCPUThreadNum, + String fdtCPUPowerMode, + float fdtInputScale, + float[] fdtInputMean, + float[] fdtInputStd, + float fdtScoreThreshold, + String mclModelDir, + int mclCPUThreadNum, + String mclCPUPowerMode, + int mclInputWidth, + int mclInputHeight, + float[] mclInputMean, + float[] mclInputStd) { + ctx = nativeInit( + fdtModelDir, + fdtCPUThreadNum, + fdtCPUPowerMode, + fdtInputScale, + fdtInputMean, + fdtInputStd, + fdtScoreThreshold, + mclModelDir, + mclCPUThreadNum, + mclCPUPowerMode, + mclInputWidth, + mclInputHeight, + mclInputMean, + mclInputStd); + return ctx == 0; + } + + public boolean release() { + if (ctx == 0) { + return false; + } + return nativeRelease(ctx); + } + + public boolean process(int inTextureId, int outTextureId, int textureWidth, int textureHeight, String savedImagePath) { + if (ctx == 0) { + return false; + } + return nativeProcess(ctx, inTextureId, outTextureId, textureWidth, textureHeight, savedImagePath); + } + + public static native long nativeInit(String fdtModelDir, + int fdtCPUThreadNum, + String fdtCPUPowerMode, + float fdtInputScale, + float[] fdtInputMean, + float[] fdtInputStd, + float fdtScoreThreshold, + String mclModelDir, + int mclCPUThreadNum, + String mclCPUPowerMode, + int mclInputWidth, + int mclInputHeight, + float[] mclInputMean, + float[] mclInputStd); + + public static native boolean nativeRelease(long ctx); + + public static native boolean nativeProcess(long ctx, int inTextureId, int outTextureId, int textureWidth, int textureHeight, String savedImagePath); +} diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/java/com/baidu/paddle/lite/demo/mask_detection/SettingsActivity.java b/PaddleLite-android-demo/mask_detection_demo/app/src/main/java/com/baidu/paddle/lite/demo/mask_detection/SettingsActivity.java new file mode 100644 index 0000000000..b3cbc57287 --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/java/com/baidu/paddle/lite/demo/mask_detection/SettingsActivity.java @@ -0,0 +1,371 @@ +package com.baidu.paddle.lite.demo.mask_detection; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.preference.EditTextPreference; +import android.preference.ListPreference; +import android.preference.PreferenceManager; +import android.support.v7.app.ActionBar; + +import com.baidu.paddle.lite.demo.common.AppCompatPreferenceActivity; +import com.baidu.paddle.lite.demo.common.Utils; + +import java.util.ArrayList; +import java.util.List; + +public class SettingsActivity extends AppCompatPreferenceActivity implements SharedPreferences.OnSharedPreferenceChangeListener { + private static final String TAG = SettingsActivity.class.getSimpleName(); + + // Face detector + static public int fdtSelectedModelIdx = 0; + static public String fdtModelDir = ""; + static public int fdtCPUThreadNum = 0; + static public String fdtCPUPowerMode = ""; + static public float fdtInputScale = 0.f; + static public float[] fdtInputMean = new float[]{}; + static public float[] fdtInputStd = new float[]{}; + static public float fdtScoreThreshold = 0.f; + + ListPreference lpFdtChoosePreInstalledModel = null; + EditTextPreference etFdtModelDir = null; + ListPreference lpFdtCPUThreadNum = null; + ListPreference lpFdtCPUPowerMode = null; + EditTextPreference etFdtInputScale = null; + EditTextPreference etFdtInputMean = null; + EditTextPreference etFdtInputStd = null; + EditTextPreference etFdtScoreThreshold = null; + + List fdtPreInstalledModelDirs = null; + List fdtPreInstalledCPUThreadNums = null; + List fdtPreInstalledCPUPowerModes = null; + List fdtPreInstalledInputScales = null; + List fdtPreInstalledInputMeans = null; + List fdtPreInstalledInputStds = null; + List fdtPreInstalledScoreThresholds = null; + + // Mask classifier + static public int mclSelectedModelIdx = 0; + static public String mclModelDir = ""; + static public int mclCPUThreadNum = 0; + static public String mclCPUPowerMode = ""; + static public int mclInputWidth = 0; + static public int mclInputHeight = 0; + static public float[] mclInputMean = new float[]{}; + static public float[] mclInputStd = new float[]{}; + + ListPreference lpMclChoosePreInstalledModel = null; + EditTextPreference etMclModelDir = null; + ListPreference lpMclCPUThreadNum = null; + ListPreference lpMclCPUPowerMode = null; + EditTextPreference etMclInputWidth = null; + EditTextPreference etMclInputHeight = null; + EditTextPreference etMclInputMean = null; + EditTextPreference etMclInputStd = null; + + List mclPreInstalledModelDirs = null; + List mclPreInstalledCPUThreadNums = null; + List mclPreInstalledCPUPowerModes = null; + List mclPreInstalledInputWidths = null; + List mclPreInstalledInputHeights = null; + List mclPreInstalledInputMeans = null; + List mclPreInstalledInputStds = null; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.settings); + ActionBar supportActionBar = getSupportActionBar(); + if (supportActionBar != null) { + supportActionBar.setDisplayHomeAsUpEnabled(true); + } + + // Face detector + // Initialize pre-installed models + fdtPreInstalledModelDirs = new ArrayList(); + fdtPreInstalledCPUThreadNums = new ArrayList(); + fdtPreInstalledCPUPowerModes = new ArrayList(); + fdtPreInstalledInputScales = new ArrayList(); + fdtPreInstalledInputMeans = new ArrayList(); + fdtPreInstalledInputStds = new ArrayList(); + fdtPreInstalledScoreThresholds = new ArrayList(); + fdtPreInstalledModelDirs.add(getString(R.string.FDT_MODEL_DIR_DEFAULT)); + fdtPreInstalledCPUThreadNums.add(getString(R.string.FDT_CPU_THREAD_NUM_DEFAULT)); + fdtPreInstalledCPUPowerModes.add(getString(R.string.FDT_CPU_POWER_MODE_DEFAULT)); + fdtPreInstalledInputScales.add(getString(R.string.FDT_INPUT_SCALE_DEFAULT)); + fdtPreInstalledInputMeans.add(getString(R.string.FDT_INPUT_MEAN_DEFAULT)); + fdtPreInstalledInputStds.add(getString(R.string.FDT_INPUT_STD_DEFAULT)); + fdtPreInstalledScoreThresholds.add(getString(R.string.FDT_SCORE_THRESHOLD_DEFAULT)); + // Setup UI components + lpFdtChoosePreInstalledModel = + (ListPreference) findPreference(getString(R.string.FDT_CHOOSE_PRE_INSTALLED_MODEL_KEY)); + String[] fdtPreInstalledModelNames = new String[fdtPreInstalledModelDirs.size()]; + for (int i = 0; i < fdtPreInstalledModelDirs.size(); i++) { + fdtPreInstalledModelNames[i] = + fdtPreInstalledModelDirs.get(i).substring(fdtPreInstalledModelDirs.get(i).lastIndexOf("/") + 1); + } + lpFdtChoosePreInstalledModel.setEntries(fdtPreInstalledModelNames); + lpFdtChoosePreInstalledModel.setEntryValues(fdtPreInstalledModelDirs.toArray(new String[fdtPreInstalledModelDirs.size()])); + lpFdtCPUThreadNum = + (ListPreference) findPreference(getString(R.string.FDT_CPU_THREAD_NUM_KEY)); + lpFdtCPUPowerMode = + (ListPreference) findPreference(getString(R.string.FDT_CPU_POWER_MODE_KEY)); + etFdtModelDir = (EditTextPreference) findPreference(getString(R.string.FDT_MODEL_DIR_KEY)); + etFdtModelDir.setTitle("Model dir (SDCard: " + Utils.getSDCardDirectory() + ")"); + etFdtInputScale = (EditTextPreference) findPreference(getString(R.string.FDT_INPUT_SCALE_KEY)); + etFdtInputMean = (EditTextPreference) findPreference(getString(R.string.FDT_INPUT_MEAN_KEY)); + etFdtInputStd = (EditTextPreference) findPreference(getString(R.string.FDT_INPUT_STD_KEY)); + etFdtScoreThreshold = (EditTextPreference) findPreference(getString(R.string.FDT_SCORE_THRESHOLD_KEY)); + + // Mask classifier + // Initialize pre-installed models + mclPreInstalledModelDirs = new ArrayList(); + mclPreInstalledCPUThreadNums = new ArrayList(); + mclPreInstalledCPUPowerModes = new ArrayList(); + mclPreInstalledInputWidths = new ArrayList(); + mclPreInstalledInputHeights = new ArrayList(); + mclPreInstalledInputMeans = new ArrayList(); + mclPreInstalledInputStds = new ArrayList(); + mclPreInstalledModelDirs.add(getString(R.string.MCL_MODEL_DIR_DEFAULT)); + mclPreInstalledCPUThreadNums.add(getString(R.string.MCL_CPU_THREAD_NUM_DEFAULT)); + mclPreInstalledCPUPowerModes.add(getString(R.string.MCL_CPU_POWER_MODE_DEFAULT)); + mclPreInstalledInputWidths.add(getString(R.string.MCL_INPUT_WIDTH_DEFAULT)); + mclPreInstalledInputHeights.add(getString(R.string.MCL_INPUT_HEIGHT_DEFAULT)); + mclPreInstalledInputMeans.add(getString(R.string.MCL_INPUT_MEAN_DEFAULT)); + mclPreInstalledInputStds.add(getString(R.string.MCL_INPUT_STD_DEFAULT)); + // Setup UI components + lpMclChoosePreInstalledModel = + (ListPreference) findPreference(getString(R.string.MCL_CHOOSE_PRE_INSTALLED_MODEL_KEY)); + String[] mclPreInstalledModelNames = new String[mclPreInstalledModelDirs.size()]; + for (int i = 0; i < mclPreInstalledModelDirs.size(); i++) { + mclPreInstalledModelNames[i] = + mclPreInstalledModelDirs.get(i).substring(mclPreInstalledModelDirs.get(i).lastIndexOf("/") + 1); + } + lpMclChoosePreInstalledModel.setEntries(mclPreInstalledModelNames); + lpMclChoosePreInstalledModel.setEntryValues(mclPreInstalledModelDirs.toArray(new String[mclPreInstalledModelDirs.size()])); + lpMclCPUThreadNum = + (ListPreference) findPreference(getString(R.string.MCL_CPU_THREAD_NUM_KEY)); + lpMclCPUPowerMode = + (ListPreference) findPreference(getString(R.string.MCL_CPU_POWER_MODE_KEY)); + etMclModelDir = (EditTextPreference) findPreference(getString(R.string.MCL_MODEL_DIR_KEY)); + etMclModelDir.setTitle("Model dir (SDCard: " + Utils.getSDCardDirectory() + ")"); + etMclInputWidth = (EditTextPreference) findPreference(getString(R.string.MCL_INPUT_WIDTH_KEY)); + etMclInputHeight = (EditTextPreference) findPreference(getString(R.string.MCL_INPUT_HEIGHT_KEY)); + etMclInputMean = (EditTextPreference) findPreference(getString(R.string.MCL_INPUT_MEAN_KEY)); + etMclInputStd = (EditTextPreference) findPreference(getString(R.string.MCL_INPUT_STD_KEY)); + } + + private void reloadSettingsAndUpdateUI() { + SharedPreferences sharedPreferences = getPreferenceScreen().getSharedPreferences(); + + // Face detector + String selectedModelPath = sharedPreferences.getString(getString(R.string.FDT_CHOOSE_PRE_INSTALLED_MODEL_KEY), + getString(R.string.FDT_MODEL_DIR_DEFAULT)); + int selectedModelIdx = lpFdtChoosePreInstalledModel.findIndexOfValue(selectedModelPath); + if (selectedModelIdx >= 0 && selectedModelIdx < fdtPreInstalledModelDirs.size() && selectedModelIdx != fdtSelectedModelIdx) { + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putString(getString(R.string.FDT_MODEL_DIR_KEY), fdtPreInstalledModelDirs.get(selectedModelIdx)); + editor.putString(getString(R.string.FDT_CPU_THREAD_NUM_KEY), fdtPreInstalledCPUThreadNums.get(selectedModelIdx)); + editor.putString(getString(R.string.FDT_CPU_POWER_MODE_KEY), fdtPreInstalledCPUPowerModes.get(selectedModelIdx)); + editor.putString(getString(R.string.FDT_INPUT_SCALE_KEY), fdtPreInstalledInputScales.get(selectedModelIdx)); + editor.putString(getString(R.string.FDT_INPUT_MEAN_KEY), fdtPreInstalledInputMeans.get(selectedModelIdx)); + editor.putString(getString(R.string.FDT_INPUT_STD_KEY), fdtPreInstalledInputStds.get(selectedModelIdx)); + editor.putString(getString(R.string.FDT_SCORE_THRESHOLD_KEY), fdtPreInstalledScoreThresholds.get(selectedModelIdx)); + editor.commit(); + lpFdtChoosePreInstalledModel.setSummary(fdtSelectedModelIdx); + fdtSelectedModelIdx = selectedModelIdx; + } + + String modelDir = sharedPreferences.getString(getString(R.string.FDT_MODEL_DIR_KEY), + getString(R.string.FDT_MODEL_DIR_DEFAULT)); + String cpuThreadNum = sharedPreferences.getString(getString(R.string.FDT_CPU_THREAD_NUM_KEY), + getString(R.string.FDT_CPU_THREAD_NUM_DEFAULT)); + String cpuPowerMode = sharedPreferences.getString(getString(R.string.FDT_CPU_POWER_MODE_KEY), + getString(R.string.FDT_CPU_POWER_MODE_DEFAULT)); + String inputScale = sharedPreferences.getString(getString(R.string.FDT_INPUT_SCALE_KEY), + getString(R.string.FDT_INPUT_SCALE_DEFAULT)); + String inputMean = sharedPreferences.getString(getString(R.string.FDT_INPUT_MEAN_KEY), + getString(R.string.FDT_INPUT_MEAN_DEFAULT)); + String inputStd = sharedPreferences.getString(getString(R.string.FDT_INPUT_STD_KEY), + getString(R.string.FDT_INPUT_STD_DEFAULT)); + String scoreThreshold = sharedPreferences.getString(getString(R.string.FDT_SCORE_THRESHOLD_KEY), + getString(R.string.FDT_SCORE_THRESHOLD_DEFAULT)); + + etFdtModelDir.setSummary(modelDir); + lpFdtCPUThreadNum.setValue(cpuThreadNum); + lpFdtCPUThreadNum.setSummary(cpuThreadNum); + lpFdtCPUPowerMode.setValue(cpuPowerMode); + lpFdtCPUPowerMode.setSummary(cpuPowerMode); + etFdtInputScale.setSummary(inputScale); + etFdtInputMean.setSummary(inputMean); + etFdtInputMean.setText(inputMean); + etFdtInputStd.setSummary(inputStd); + etFdtInputStd.setText(inputStd); + etFdtScoreThreshold.setSummary(scoreThreshold); + + // Mask classifier + selectedModelPath = sharedPreferences.getString(getString(R.string.MCL_CHOOSE_PRE_INSTALLED_MODEL_KEY), + getString(R.string.MCL_MODEL_DIR_DEFAULT)); + selectedModelIdx = lpMclChoosePreInstalledModel.findIndexOfValue(selectedModelPath); + if (selectedModelIdx >= 0 && selectedModelIdx < mclPreInstalledModelDirs.size() && selectedModelIdx != mclSelectedModelIdx) { + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putString(getString(R.string.MCL_MODEL_DIR_KEY), mclPreInstalledModelDirs.get(selectedModelIdx)); + editor.putString(getString(R.string.MCL_CPU_THREAD_NUM_KEY), mclPreInstalledCPUThreadNums.get(selectedModelIdx)); + editor.putString(getString(R.string.MCL_CPU_POWER_MODE_KEY), mclPreInstalledCPUPowerModes.get(selectedModelIdx)); + editor.putString(getString(R.string.MCL_INPUT_WIDTH_KEY), mclPreInstalledInputWidths.get(selectedModelIdx)); + editor.putString(getString(R.string.MCL_INPUT_HEIGHT_KEY), mclPreInstalledInputHeights.get(selectedModelIdx)); + editor.putString(getString(R.string.MCL_INPUT_MEAN_KEY), mclPreInstalledInputMeans.get(selectedModelIdx)); + editor.putString(getString(R.string.MCL_INPUT_STD_KEY), mclPreInstalledInputStds.get(selectedModelIdx)); + editor.commit(); + lpMclChoosePreInstalledModel.setSummary(mclSelectedModelIdx); + } + + modelDir = sharedPreferences.getString(getString(R.string.MCL_MODEL_DIR_KEY), + getString(R.string.MCL_MODEL_DIR_DEFAULT)); + cpuThreadNum = sharedPreferences.getString(getString(R.string.MCL_CPU_THREAD_NUM_KEY), + getString(R.string.MCL_CPU_THREAD_NUM_DEFAULT)); + cpuPowerMode = sharedPreferences.getString(getString(R.string.MCL_CPU_POWER_MODE_KEY), + getString(R.string.MCL_CPU_POWER_MODE_DEFAULT)); + String inputWidth = sharedPreferences.getString(getString(R.string.MCL_INPUT_WIDTH_KEY), + getString(R.string.MCL_INPUT_WIDTH_DEFAULT)); + String inputHeight = sharedPreferences.getString(getString(R.string.MCL_INPUT_HEIGHT_KEY), + getString(R.string.MCL_INPUT_HEIGHT_DEFAULT)); + inputMean = sharedPreferences.getString(getString(R.string.MCL_INPUT_MEAN_KEY), + getString(R.string.MCL_INPUT_MEAN_DEFAULT)); + inputStd = sharedPreferences.getString(getString(R.string.MCL_INPUT_STD_KEY), + getString(R.string.MCL_INPUT_STD_DEFAULT)); + + etMclModelDir.setSummary(modelDir); + lpMclCPUThreadNum.setValue(cpuThreadNum); + lpMclCPUThreadNum.setSummary(cpuThreadNum); + lpMclCPUPowerMode.setValue(cpuPowerMode); + lpMclCPUPowerMode.setSummary(cpuPowerMode); + etMclInputWidth.setSummary(inputWidth); + etMclInputHeight.setSummary(inputHeight); + etMclInputMean.setSummary(inputMean); + etMclInputMean.setText(inputMean); + etMclInputStd.setSummary(inputStd); + etMclInputStd.setText(inputStd); + } + + static boolean checkAndUpdateSettings(Context ctx) { + boolean settingsChanged = false; + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(ctx); + + // Face detector + String modelDir = sharedPreferences.getString(ctx.getString(R.string.FDT_MODEL_DIR_KEY), + ctx.getString(R.string.FDT_MODEL_DIR_DEFAULT)); + settingsChanged |= !fdtModelDir.equalsIgnoreCase(modelDir); + fdtModelDir = modelDir; + + String cpuThreadNum = sharedPreferences.getString(ctx.getString(R.string.FDT_CPU_THREAD_NUM_KEY), + ctx.getString(R.string.FDT_CPU_THREAD_NUM_DEFAULT)); + settingsChanged |= fdtCPUThreadNum != Integer.parseInt(cpuThreadNum); + fdtCPUThreadNum = Integer.parseInt(cpuThreadNum); + + String cpuPowerMode = sharedPreferences.getString(ctx.getString(R.string.FDT_CPU_POWER_MODE_KEY), + ctx.getString(R.string.FDT_CPU_POWER_MODE_DEFAULT)); + settingsChanged |= !fdtCPUPowerMode.equalsIgnoreCase(cpuPowerMode); + fdtCPUPowerMode = cpuPowerMode; + + String inputScale = sharedPreferences.getString(ctx.getString(R.string.FDT_INPUT_SCALE_KEY), + ctx.getString(R.string.FDT_INPUT_SCALE_DEFAULT)); + settingsChanged |= fdtInputScale != Float.parseFloat(inputScale); + fdtInputScale = Float.parseFloat(inputScale); + + String inputMean = sharedPreferences.getString(ctx.getString(R.string.FDT_INPUT_MEAN_KEY), + ctx.getString(R.string.FDT_INPUT_MEAN_DEFAULT)); + float[] input_mean = Utils.parseFloatsFromString(inputMean, ","); + settingsChanged |= input_mean.length != fdtInputMean.length; + if (!settingsChanged) { + for (int i = 0; i < input_mean.length; i++) { + settingsChanged |= input_mean[i] != fdtInputMean[i]; + } + } + fdtInputMean = input_mean; + + String inputStd = sharedPreferences.getString(ctx.getString(R.string.FDT_INPUT_STD_KEY), + ctx.getString(R.string.FDT_INPUT_STD_DEFAULT)); + float[] input_std = Utils.parseFloatsFromString(inputStd, ","); + settingsChanged |= input_std.length != fdtInputStd.length; + if (!settingsChanged) { + for (int i = 0; i < input_std.length; i++) { + settingsChanged |= input_std[i] != fdtInputStd[i]; + } + } + fdtInputStd = input_std; + + String scoreThreshold = sharedPreferences.getString(ctx.getString(R.string.FDT_SCORE_THRESHOLD_KEY), + ctx.getString(R.string.FDT_SCORE_THRESHOLD_DEFAULT)); + settingsChanged |= fdtScoreThreshold != Float.parseFloat(scoreThreshold); + fdtScoreThreshold = Float.parseFloat(scoreThreshold); + + // Mask classifier + modelDir = sharedPreferences.getString(ctx.getString(R.string.MCL_MODEL_DIR_KEY), + ctx.getString(R.string.MCL_MODEL_DIR_DEFAULT)); + settingsChanged |= !fdtModelDir.equalsIgnoreCase(modelDir); + mclModelDir = modelDir; + + cpuThreadNum = sharedPreferences.getString(ctx.getString(R.string.MCL_CPU_THREAD_NUM_KEY), + ctx.getString(R.string.MCL_CPU_THREAD_NUM_DEFAULT)); + settingsChanged |= mclCPUThreadNum != Integer.parseInt(cpuThreadNum); + mclCPUThreadNum = Integer.parseInt(cpuThreadNum); + + cpuPowerMode = sharedPreferences.getString(ctx.getString(R.string.MCL_CPU_POWER_MODE_KEY), + ctx.getString(R.string.MCL_CPU_POWER_MODE_DEFAULT)); + settingsChanged |= !mclCPUPowerMode.equalsIgnoreCase(cpuPowerMode); + mclCPUPowerMode = cpuPowerMode; + + String inputWidth = sharedPreferences.getString(ctx.getString(R.string.MCL_INPUT_WIDTH_KEY), + ctx.getString(R.string.MCL_INPUT_WIDTH_DEFAULT)); + settingsChanged |= mclInputWidth != Integer.parseInt(inputWidth); + mclInputWidth = Integer.parseInt(inputWidth); + + String inputHeight = sharedPreferences.getString(ctx.getString(R.string.MCL_INPUT_HEIGHT_KEY), + ctx.getString(R.string.MCL_INPUT_HEIGHT_DEFAULT)); + settingsChanged |= mclInputHeight != Integer.parseInt(inputHeight); + mclInputHeight = Integer.parseInt(inputHeight); + + inputMean = sharedPreferences.getString(ctx.getString(R.string.MCL_INPUT_MEAN_KEY), + ctx.getString(R.string.MCL_INPUT_MEAN_DEFAULT)); + input_mean = Utils.parseFloatsFromString(inputMean, ","); + settingsChanged |= input_mean.length != mclInputMean.length; + if (!settingsChanged) { + for (int i = 0; i < input_mean.length; i++) { + settingsChanged |= input_mean[i] != mclInputMean[i]; + } + } + mclInputMean = input_mean; + + inputStd = sharedPreferences.getString(ctx.getString(R.string.MCL_INPUT_STD_KEY), + ctx.getString(R.string.MCL_INPUT_STD_DEFAULT)); + input_std = Utils.parseFloatsFromString(inputStd, ","); + settingsChanged |= input_std.length != mclInputStd.length; + if (!settingsChanged) { + for (int i = 0; i < input_std.length; i++) { + settingsChanged |= input_std[i] != fdtInputStd[i]; + } + } + mclInputStd = input_std; + return settingsChanged; + } + + + @Override + protected void onResume() { + super.onResume(); + getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this); + reloadSettingsAndUpdateUI(); + } + + @Override + protected void onPause() { + super.onPause(); + getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this); + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + reloadSettingsAndUpdateUI(); + } +} diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000000..1f6bb29060 --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable-xxhdpi-v4/btn_switch_default.png b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable-xxhdpi-v4/btn_switch_default.png new file mode 100644 index 0000000000..b9e66c7f60 Binary files /dev/null and b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable-xxhdpi-v4/btn_switch_default.png differ diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable-xxhdpi-v4/btn_switch_pressed.png b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable-xxhdpi-v4/btn_switch_pressed.png new file mode 100644 index 0000000000..9544133bda Binary files /dev/null and b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable-xxhdpi-v4/btn_switch_pressed.png differ diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable/btn_settings.xml b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable/btn_settings.xml new file mode 100644 index 0000000000..917897b999 --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable/btn_settings.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable/btn_settings_default.xml b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable/btn_settings_default.xml new file mode 100644 index 0000000000..e19589a97e --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable/btn_settings_default.xml @@ -0,0 +1,13 @@ + + + + diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable/btn_settings_pressed.xml b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable/btn_settings_pressed.xml new file mode 100644 index 0000000000..c4af2a042d --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable/btn_settings_pressed.xml @@ -0,0 +1,13 @@ + + + + diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable/btn_shutter.xml b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable/btn_shutter.xml new file mode 100644 index 0000000000..4f9826d3ae --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable/btn_shutter.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable/btn_shutter_default.xml b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable/btn_shutter_default.xml new file mode 100644 index 0000000000..234ca014a7 --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable/btn_shutter_default.xml @@ -0,0 +1,17 @@ + + + + + diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable/btn_shutter_pressed.xml b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable/btn_shutter_pressed.xml new file mode 100644 index 0000000000..accc7acedb --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable/btn_shutter_pressed.xml @@ -0,0 +1,17 @@ + + + + + diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable/btn_switch.xml b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable/btn_switch.xml new file mode 100644 index 0000000000..691e8c2e97 --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable/btn_switch.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable/ic_launcher_background.xml b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000000..0d025f9bf6 --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/layout-land/activity_main.xml b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/layout-land/activity_main.xml new file mode 100644 index 0000000000..871e4f0c0d --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/layout-land/activity_main.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/layout/activity_main.xml b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000000..939e7192cf --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000000..eca70cfe52 --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000000..eca70cfe52 --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-hdpi/ic_launcher.png b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..898f3ed59a Binary files /dev/null and b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000000..dffca3601e Binary files /dev/null and b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-mdpi/ic_launcher.png b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..64ba76f75e Binary files /dev/null and b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000000..dae5e08234 Binary files /dev/null and b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..e5ed46597e Binary files /dev/null and b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000..14ed0af350 Binary files /dev/null and b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..b0907cac3b Binary files /dev/null and b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000..d8ae031549 Binary files /dev/null and b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..2c18de9e66 Binary files /dev/null and b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000..beed3cdd2c Binary files /dev/null and b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/values/arrays.xml b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/values/arrays.xml new file mode 100644 index 0000000000..8c99734d14 --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/values/arrays.xml @@ -0,0 +1,31 @@ + + + + 1 threads + 2 threads + 4 threads + 8 threads + + + 1 + 2 + 4 + 8 + + + HIGH(only big cores) + LOW(only LITTLE cores) + FULL(all cores) + NO_BIND(depends on system) + RAND_HIGH + RAND_LOW + + + LITE_POWER_HIGH + LITE_POWER_LOW + LITE_POWER_FULL + LITE_POWER_NO_BIND + LITE_POWER_RAND_HIGH + LITE_POWER_RAND_LOW + + \ No newline at end of file diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/values/colors.xml b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/values/colors.xml new file mode 100644 index 0000000000..d948fbaf11 --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #008577 + #00574B + #D81B60 + #FF000000 + #00000000 + #00000000 + #FFFFFFFF + diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/values/dimens.xml b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000000..2df89499da --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/values/dimens.xml @@ -0,0 +1,17 @@ + + + 26dp + 36dp + 34dp + 60dp + 16dp + 67dp + 67dp + 56dp + 56dp + 46dp + 46dp + 32dp + 24dp + 16dp + diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/values/strings.xml b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/values/strings.xml new file mode 100644 index 0000000000..97464641c6 --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/values/strings.xml @@ -0,0 +1,38 @@ + + Mask Detection + + FDT_CHOOSE_INSTALLED_MODEL_KEY + FDT_ENABLE_CUSTOM_SETTINGS_KEY + FDT_MODEL_DIR_KEY + FDT_CPU_THREAD_NUM_KEY + FDT_CPU_POWER_MODE_KEY + FDT_INPUT_SCALE_KEY + FDT_INPUT_MEAN_KEY + FDT_INPUT_STD_KEY + FDT_SCORE_THRESHOLD_KEY + models/pyramidbox_lite_for_cpu + 1 + LITE_POWER_HIGH + 0.25 + 0.407843,0.694118,0.482353 + 0.5,0.5,0.5 + 0.7 + + MCL_CHOOSE_INSTALLED_MODEL_KEY + MCL_ENABLE_CUSTOM_SETTINGS_KEY + MCL_MODEL_DIR_KEY + MCL_CPU_THREAD_NUM_KEY + MCL_CPU_POWER_MODE_KEY + MCL_INPUT_WIDTH_KEY + MCL_INPUT_HEIGHT_KEY + MCL_INPUT_MEAN_KEY + MCL_INPUT_STD_KEY + models/mask_detector_for_cpu + 1 + LITE_POWER_HIGH + 128 + 128 + 0.5,0.5,0.5 + 1.0,1.0,1.0 + + diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/values/styles.xml b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/values/styles.xml new file mode 100644 index 0000000000..853262016a --- /dev/null +++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/values/styles.xml @@ -0,0 +1,25 @@ + + + + + + + + + +