(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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/xml/settings.xml b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/xml/settings.xml
new file mode 100644
index 0000000000..4c9177acbc
--- /dev/null
+++ b/PaddleLite-android-demo/mask_detection_demo/app/src/main/res/xml/settings.xml
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PaddleLite-android-demo/mask_detection_demo/app/src/test/java/com/baidu/paddle/lite/demo/mask_detection/ExampleUnitTest.java b/PaddleLite-android-demo/mask_detection_demo/app/src/test/java/com/baidu/paddle/lite/demo/mask_detection/ExampleUnitTest.java
new file mode 100644
index 0000000000..6ab9328ce0
--- /dev/null
+++ b/PaddleLite-android-demo/mask_detection_demo/app/src/test/java/com/baidu/paddle/lite/demo/mask_detection/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.baidu.paddle.lite.demo.mask_detection;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/PaddleLite-android-demo/mask_detection_demo/build.gradle b/PaddleLite-android-demo/mask_detection_demo/build.gradle
new file mode 100644
index 0000000000..fafc1b970b
--- /dev/null
+++ b/PaddleLite-android-demo/mask_detection_demo/build.gradle
@@ -0,0 +1,27 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ google()
+ jcenter()
+
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.4.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/PaddleLite-android-demo/mask_detection_demo/gradle.properties b/PaddleLite-android-demo/mask_detection_demo/gradle.properties
new file mode 100644
index 0000000000..82618cecb4
--- /dev/null
+++ b/PaddleLite-android-demo/mask_detection_demo/gradle.properties
@@ -0,0 +1,15 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+
+
diff --git a/PaddleLite-android-demo/mask_detection_demo/gradle/wrapper/gradle-wrapper.jar b/PaddleLite-android-demo/mask_detection_demo/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000000..f6b961fd5a
Binary files /dev/null and b/PaddleLite-android-demo/mask_detection_demo/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/PaddleLite-android-demo/mask_detection_demo/gradle/wrapper/gradle-wrapper.properties b/PaddleLite-android-demo/mask_detection_demo/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000000..578b5482ad
--- /dev/null
+++ b/PaddleLite-android-demo/mask_detection_demo/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Thu Aug 22 15:05:37 CST 2019
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
diff --git a/PaddleLite-android-demo/mask_detection_demo/gradlew b/PaddleLite-android-demo/mask_detection_demo/gradlew
new file mode 100755
index 0000000000..cccdd3d517
--- /dev/null
+++ b/PaddleLite-android-demo/mask_detection_demo/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/PaddleLite-android-demo/mask_detection_demo/gradlew.bat b/PaddleLite-android-demo/mask_detection_demo/gradlew.bat
new file mode 100644
index 0000000000..e95643d6a2
--- /dev/null
+++ b/PaddleLite-android-demo/mask_detection_demo/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/PaddleLite-android-demo/mask_detection_demo/settings.gradle b/PaddleLite-android-demo/mask_detection_demo/settings.gradle
new file mode 100644
index 0000000000..e7b4def49c
--- /dev/null
+++ b/PaddleLite-android-demo/mask_detection_demo/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/README.md b/README.md
index ecfa538de5..6d29dd191d 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,7 @@
* 基于MobileNetV1-SSD的目标检测;
* 基于Ultra-Light-Fast-Generic-Face-Detector-1MB的人脸检测;
* 基于DeeplabV3+MobilNetV2的人像分割;
+ * 基于视频流的人脸检测+口罩识别;
* ARMLinux示例:
* 基于MobileNetV1的图像分类;
* 基于MobileNetV1-SSD的目标检测;
@@ -152,6 +153,16 @@ $ git clone https://github.com/PaddlePaddle/Paddle-Lite-Demo
待支持
+ * 基于视频流的人脸检测+口罩识别
+
+ - CPU预测结果(测试环境:华为mate30)
+
+ ![android_mask_detection_cpu](https://paddlelite-demo.bj.bcebos.com/doc/android_mask_detection_cpu.jpg)
+
+ - NPU预测结果
+
+ 待支持
+
* ARMLinux
* 基于MobileNetV1的图像分类