Эх сурвалжийг харах

add lite/android sdk&demo

Channingss 5 жил өмнө
parent
commit
0d80449815
59 өөрчлөгдсөн 3708 нэмэгдсэн , 0 устгасан
  1. 13 0
      deploy/lite/android/demo/.gitignore
  2. 1 0
      deploy/lite/android/demo/app/.gitignore
  3. 119 0
      deploy/lite/android/demo/app/build.gradle
  4. 21 0
      deploy/lite/android/demo/app/proguard-rules.pro
  5. 32 0
      deploy/lite/android/demo/app/src/androidTest/java/com/baidu/paddlex/lite/demo/ExampleInstrumentedTest.java
  6. 28 0
      deploy/lite/android/demo/app/src/main/AndroidManifest.xml
  7. 126 0
      deploy/lite/android/demo/app/src/main/java/com/baidu/paddlex/lite/demo/AppCompatPreferenceActivity.java
  8. 466 0
      deploy/lite/android/demo/app/src/main/java/com/baidu/paddlex/lite/demo/MainActivity.java
  9. 158 0
      deploy/lite/android/demo/app/src/main/java/com/baidu/paddlex/lite/demo/SettingsActivity.java
  10. 34 0
      deploy/lite/android/demo/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
  11. BIN
      deploy/lite/android/demo/app/src/main/res/drawable/face.jpg
  12. 170 0
      deploy/lite/android/demo/app/src/main/res/drawable/ic_launcher_background.xml
  13. 112 0
      deploy/lite/android/demo/app/src/main/res/layout/activity_main.xml
  14. 21 0
      deploy/lite/android/demo/app/src/main/res/menu/menu_action_options.xml
  15. 5 0
      deploy/lite/android/demo/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
  16. 5 0
      deploy/lite/android/demo/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
  17. BIN
      deploy/lite/android/demo/app/src/main/res/mipmap-hdpi/ic_launcher.png
  18. BIN
      deploy/lite/android/demo/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
  19. BIN
      deploy/lite/android/demo/app/src/main/res/mipmap-mdpi/ic_launcher.png
  20. BIN
      deploy/lite/android/demo/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
  21. BIN
      deploy/lite/android/demo/app/src/main/res/mipmap-xhdpi/ic_launcher.png
  22. BIN
      deploy/lite/android/demo/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
  23. BIN
      deploy/lite/android/demo/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
  24. BIN
      deploy/lite/android/demo/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
  25. BIN
      deploy/lite/android/demo/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  26. BIN
      deploy/lite/android/demo/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
  27. 39 0
      deploy/lite/android/demo/app/src/main/res/values/arrays.xml
  28. 6 0
      deploy/lite/android/demo/app/src/main/res/values/colors.xml
  29. 16 0
      deploy/lite/android/demo/app/src/main/res/values/strings.xml
  30. 25 0
      deploy/lite/android/demo/app/src/main/res/values/styles.xml
  31. 49 0
      deploy/lite/android/demo/app/src/main/res/xml/settings.xml
  32. 17 0
      deploy/lite/android/demo/app/src/test/java/com/baidu/paddlex/lite/demo/ExampleUnitTest.java
  33. 27 0
      deploy/lite/android/demo/build.gradle
  34. 15 0
      deploy/lite/android/demo/gradle.properties
  35. BIN
      deploy/lite/android/demo/gradle/wrapper/gradle-wrapper.jar
  36. 6 0
      deploy/lite/android/demo/gradle/wrapper/gradle-wrapper.properties
  37. 172 0
      deploy/lite/android/demo/gradlew
  38. 84 0
      deploy/lite/android/demo/gradlew.bat
  39. 245 0
      deploy/lite/android/demo/import-summary.txt
  40. 1 0
      deploy/lite/android/demo/settings.gradle
  41. 1 0
      deploy/lite/android/sdk/.gitignore
  42. 163 0
      deploy/lite/android/sdk/build.gradle
  43. 0 0
      deploy/lite/android/sdk/consumer-rules.pro
  44. 7 0
      deploy/lite/android/sdk/local.properties
  45. 21 0
      deploy/lite/android/sdk/proguard-rules.pro
  46. 36 0
      deploy/lite/android/sdk/src/androidTest/java/com/example/paddlex/ExampleInstrumentedTest.java
  47. 1 0
      deploy/lite/android/sdk/src/main/AndroidManifest.xml
  48. 409 0
      deploy/lite/android/sdk/src/main/java/com/baidu/paddlex/Predictor.java
  49. 132 0
      deploy/lite/android/sdk/src/main/java/com/baidu/paddlex/Utils.java
  50. 162 0
      deploy/lite/android/sdk/src/main/java/com/baidu/paddlex/config/ConfigParser.java
  51. 52 0
      deploy/lite/android/sdk/src/main/java/com/baidu/paddlex/postprocess/ClsResult.java
  52. 76 0
      deploy/lite/android/sdk/src/main/java/com/baidu/paddlex/postprocess/DetResult.java
  53. 23 0
      deploy/lite/android/sdk/src/main/java/com/baidu/paddlex/postprocess/Result.java
  54. 72 0
      deploy/lite/android/sdk/src/main/java/com/baidu/paddlex/postprocess/SegResult.java
  55. 86 0
      deploy/lite/android/sdk/src/main/java/com/baidu/paddlex/preprocess/ImageBlob.java
  56. 286 0
      deploy/lite/android/sdk/src/main/java/com/baidu/paddlex/preprocess/Transforms.java
  57. 148 0
      deploy/lite/android/sdk/src/main/java/com/baidu/paddlex/visual/Visualize.java
  58. 3 0
      deploy/lite/android/sdk/src/main/res/values/strings.xml
  59. 17 0
      deploy/lite/android/sdk/src/test/java/com/example/paddlex/ExampleUnitTest.java

+ 13 - 0
deploy/lite/android/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

+ 1 - 0
deploy/lite/android/demo/app/.gitignore

@@ -0,0 +1 @@
+/build

+ 119 - 0
deploy/lite/android/demo/app/build.gradle

@@ -0,0 +1,119 @@
+import java.security.MessageDigest
+
+apply plugin: 'com.android.application'
+
+android {
+    compileSdkVersion 28
+    defaultConfig {
+        applicationId "com.baidu.paddlex.lite.demo"
+        minSdkVersion 15
+        targetSdkVersion 28
+        versionCode 1
+        versionName "1.0"
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+}
+
+dependencies {
+    implementation fileTree(include: ['*.aar'], 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 paddlexAndroidSdk = 'https://bj.bcebos.com/paddlex/deploy/lite/paddlex_lite_11cbd50e.tar.gz'
+
+task downloadAndExtractPaddleXAndroidSdk(type: DefaultTask) {
+    doFirst {
+        println "Downloading and extracting PaddleX Android SDK"}
+    doLast {
+        // Prepare cache folder for sdk
+        if (!file("cache").exists()) {
+            mkdir "cache"
+        }
+        // Generate cache name for sdk
+        MessageDigest messageDigest = MessageDigest.getInstance('MD5')
+        messageDigest.update(paddlexAndroidSdk.bytes)
+        String cacheName = new BigInteger(1, messageDigest.digest()).toString(32)
+        // Download sdk
+        if (!file("cache/${cacheName}.tar.gz").exists()) {
+            ant.get(src: paddlexAndroidSdk, dest: file("cache/${cacheName}.tar.gz"))
+        }
+        // Unpack sdk
+        copy {
+            from tarTree("cache/${cacheName}.tar.gz")
+            into "cache/${cacheName}"
+        }
+        // Copy sdk
+        if (!file("libs/paddlex.aar").exists()) {
+            copy {
+                from "cache/${cacheName}/paddlex.aar"
+                into "libs"
+            }
+        }
+    }
+}
+
+preBuild.dependsOn downloadAndExtractPaddleXAndroidSdk
+
+def paddleXLiteModel = 'https://bj.bcebos.com/paddlex/deploy/lite/mobilenetv2_imagenet_lite2.6.1.tar.gz'
+task downloadAndExtractPaddleXLiteModel(type: DefaultTask) {
+    doFirst {
+        println "Downloading and extracting PaddleX Android SDK"}
+
+    doLast {
+        // Prepare cache folder for model
+        if (!file("cache").exists()) {
+            mkdir "cache"
+        }
+        // Generate cache name for model
+        MessageDigest messageDigest = MessageDigest.getInstance('MD5')
+        messageDigest.update(paddleXLiteModel.bytes)
+        String cacheName = new BigInteger(1, messageDigest.digest()).toString(32)
+        // Download sdk
+        if (!file("cache/${cacheName}.tar.gz").exists()) {
+            ant.get(src: paddleXLiteModel, dest: file("cache/${cacheName}.tar.gz"))
+        }
+
+        // Unpack model
+        copy {
+            from tarTree("cache/${cacheName}.tar.gz")
+            into "cache/${cacheName}"
+        }
+
+        // Copy model.nb
+        if (!file("src/main/assets/model/model.nb").exists()) {
+            copy {
+                from "cache/${cacheName}/model.nb"
+                into "src/main/assets/model/"
+            }
+        }
+        // Copy config file model.yml
+        if (!file("src/main/assets/config/model.yml").exists()) {
+            copy {
+                from "cache/${cacheName}/model.yml"
+                into "src/main/assets/config/"
+            }
+        }
+        // Copy config file model.yml
+        if (!file("src/main/assets/images/test.jpg").exists()) {
+            copy {
+                from "cache/${cacheName}/test.jpg"
+                into "src/main/assets/images/"
+            }
+        }
+    }
+
+}
+
+preBuild.dependsOn downloadAndExtractPaddleXLiteModel

+ 21 - 0
deploy/lite/android/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

+ 32 - 0
deploy/lite/android/demo/app/src/androidTest/java/com/baidu/paddlex/lite/demo/ExampleInstrumentedTest.java

@@ -0,0 +1,32 @@
+package com.baidu.paddlex.lite.demo;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.baidu.paddlex.config.ConfigParser;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+    @Test
+    public void useAppContext() throws IOException {
+        // Context of the app under test.
+        Context appContext = InstrumentationRegistry.getTargetContext();
+        AssetManager ass = appContext.getAssets();
+        assertEquals("com.baidu.paddlex.lite.demo", appContext.getPackageName());
+    }
+}

+ 28 - 0
deploy/lite/android/demo/app/src/main/AndroidManifest.xml

@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.baidu.paddlex.lite.demo">
+
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.CAMERA" />
+
+    <application
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:roundIcon="@mipmap/ic_launcher_round"
+        android:supportsRtl="true"
+        android:largeHeap="true"
+        android:theme="@style/AppTheme">
+        <activity android:name="com.baidu.paddlex.lite.demo.MainActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:name="com.baidu.paddlex.lite.demo.SettingsActivity"
+            android:label="Settings"></activity>
+    </application>
+
+</manifest>

+ 126 - 0
deploy/lite/android/demo/app/src/main/java/com/baidu/paddlex/lite/demo/AppCompatPreferenceActivity.java

@@ -0,0 +1,126 @@
+// Copyright (c) 2020 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.
+
+package com.baidu.paddlex.lite.demo;
+
+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 android.preference.PreferenceActivity} which implements and proxies the necessary calls
+ * to be used with AppCompat.
+ * <p>
+ * This technique can be used with an {@link android.app.Activity} class, not just
+ * {@link android.preference.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;
+    }
+}

+ 466 - 0
deploy/lite/android/demo/app/src/main/java/com/baidu/paddlex/lite/demo/MainActivity.java

@@ -0,0 +1,466 @@
+// Copyright (c) 2020 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.
+
+package com.baidu.paddlex.lite.demo;
+
+import android.Manifest;
+import android.app.ProgressDialog;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.preference.PreferenceManager;
+import android.provider.MediaStore;
+import android.support.annotation.NonNull;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.AppCompatActivity;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+import com.baidu.paddlex.Predictor;
+import com.baidu.paddlex.Utils;
+import com.baidu.paddlex.config.ConfigParser;
+import com.baidu.paddlex.postprocess.ClsResult;
+import com.baidu.paddlex.postprocess.DetResult;
+import com.baidu.paddlex.postprocess.SegResult;
+import com.baidu.paddlex.visual.Visualize;
+import org.opencv.core.Mat;
+import org.opencv.imgcodecs.Imgcodecs;
+import org.opencv.imgproc.Imgproc;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class MainActivity extends AppCompatActivity {
+    public static final int OPEN_GALLERY_REQUEST_CODE = 0;
+    public static final int TAKE_PHOTO_REQUEST_CODE = 1;
+    public static final int REQUEST_LOAD_MODEL = 0;
+    public static final int REQUEST_RUN_MODEL = 1;
+    public static final int RESPONSE_LOAD_MODEL_SUCCESSED = 0;
+    public static final int RESPONSE_LOAD_MODEL_FAILED = 1;
+    public static final int RESPONSE_RUN_MODEL_SUCCESSED = 2;
+    public static final int RESPONSE_RUN_MODEL_FAILED = 3;
+    private static final String TAG = MainActivity.class.getSimpleName();
+    protected ProgressDialog pbLoadModel = null;
+    protected ProgressDialog pbRunModel = null;
+
+    protected Handler receiver = null; // receive messages from worker thread
+    protected Handler sender = null; // send command to worker thread
+    protected HandlerThread worker = null; // worker thread to load&run model
+
+    protected TextView tvInputSetting;
+    protected ImageView ivInputImage;
+    protected TextView tvOutputResult;
+    protected TextView tvInferenceTime;
+    private Button predictButton;
+    protected String testImagePathFromAsset;
+    protected String testYamlPathFromAsset;
+    protected String testModelPathFromAsset;
+
+    // Predictor
+    protected Predictor predictor = new Predictor();
+    // model config
+    protected ConfigParser configParser = new ConfigParser();
+    // Visualize
+    protected Visualize visualize = new Visualize();
+    // Predict Mat of Opencv
+    protected Mat predictMat;
+
+
+
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+        receiver = new Handler() {
+            @Override
+            public void handleMessage(Message msg) {
+                switch (msg.what) {
+                    case RESPONSE_LOAD_MODEL_SUCCESSED:
+                        pbLoadModel.dismiss();
+                        Toast.makeText(MainActivity.this, "Load model successfully!", Toast.LENGTH_SHORT).show();
+                        break;
+                    case RESPONSE_LOAD_MODEL_FAILED:
+                        pbLoadModel.dismiss();
+                        Toast.makeText(MainActivity.this, "Load model failed!", Toast.LENGTH_SHORT).show();
+                        break;
+                    case RESPONSE_RUN_MODEL_SUCCESSED:
+                        pbRunModel.dismiss();
+                        onRunModelSuccessed();
+                        break;
+                    case RESPONSE_RUN_MODEL_FAILED:
+                        pbRunModel.dismiss();
+                        Toast.makeText(MainActivity.this, "Run model failed!", Toast.LENGTH_SHORT).show();
+                        onRunModelFailed();
+                        break;
+                    default:
+                        break;
+                }
+            }
+        };
+        worker = new HandlerThread("Predictor Worker");
+        worker.start();
+        sender = new Handler(worker.getLooper()) {
+            public void handleMessage(Message msg) {
+                switch (msg.what) {
+                    case REQUEST_LOAD_MODEL:
+                        // load model and reload test image
+                        if (onLoadModel()) {
+                            receiver.sendEmptyMessage(RESPONSE_LOAD_MODEL_SUCCESSED);
+                        } else {
+                            receiver.sendEmptyMessage(RESPONSE_LOAD_MODEL_FAILED);
+                        }
+                        break;
+                    case REQUEST_RUN_MODEL:
+                        // run model if model is loaded
+                        if (onRunModel()) {
+                            receiver.sendEmptyMessage(RESPONSE_RUN_MODEL_SUCCESSED);
+                        } else {
+                            receiver.sendEmptyMessage(RESPONSE_RUN_MODEL_FAILED);
+                        }
+                        break;
+                    default:
+                        break;
+                }
+            }
+        };
+
+        tvInputSetting = findViewById(R.id.tv_input_setting);
+        ivInputImage = findViewById(R.id.iv_input_image);
+        predictButton = findViewById(R.id.iv_predict_button);
+        tvInferenceTime = findViewById(R.id.tv_inference_time);
+        tvOutputResult = findViewById(R.id.tv_output_result);
+        tvInputSetting.setMovementMethod(ScrollingMovementMethod.getInstance());
+        tvOutputResult.setMovementMethod(ScrollingMovementMethod.getInstance());
+        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+        String image_path = sharedPreferences.getString(getString(R.string.IMAGE_PATH_KEY),
+                getString(R.string.IMAGE_PATH_DEFAULT));
+        Utils.initialOpencv();
+        loadTestImageFromAsset(image_path);
+        predictButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if(predictor.isLoaded()){
+                    onLoadModelSuccessed();
+                }
+            }
+        });
+
+    }
+
+    public boolean onLoadModel() {
+        return predictor.init(configParser);
+    }
+
+    public boolean onRunModel() {
+        return predictor.isLoaded() && predictor.predict();
+    }
+
+    public void onRunModelFailed() {
+    }
+
+    public void loadModel() {
+        pbLoadModel = ProgressDialog.show(this, "", "Loading model...", false, false);
+        sender.sendEmptyMessage(REQUEST_LOAD_MODEL);
+    }
+
+    public void runModel() {
+        pbRunModel = ProgressDialog.show(this, "", "Running model...", false, false);
+        sender.sendEmptyMessage(REQUEST_RUN_MODEL);
+    }
+
+    public void onLoadModelSuccessed() {
+        if (predictMat != null && predictor.isLoaded()) {
+            int w = predictMat.width();
+            int h = predictMat.height();
+            int c = predictMat.channels();
+            predictor.setInputMat(predictMat);
+            runModel();
+        }
+    }
+
+    public void onRunModelSuccessed() {
+        // obtain results and update UI
+        tvInferenceTime.setText("Inference time: " + predictor.getInferenceTime() + " ms");
+
+        if (configParser.getModelType().equalsIgnoreCase("segmenter")) {
+            SegResult segResult = predictor.getSegResult();
+            Mat maskMat = visualize.draw(segResult, predictMat.clone(), predictor.getImageBlob(), 1);
+            Imgproc.cvtColor(maskMat, maskMat, Imgproc.COLOR_BGRA2RGBA);
+            Bitmap outputImage = Bitmap.createBitmap(maskMat.width(), maskMat.height(), Bitmap.Config.ARGB_8888);
+            org.opencv.android.Utils.matToBitmap(maskMat, outputImage);
+            if (outputImage != null) {
+                ivInputImage.setImageBitmap(outputImage);
+            }
+        } else if (configParser.getModelType().equalsIgnoreCase("detector")) {
+            DetResult detResult = predictor.getDetResult();
+            Mat roiMat  = visualize.draw(detResult,  predictMat.clone());
+            Imgproc.cvtColor(roiMat, roiMat, Imgproc.COLOR_BGR2RGB);
+            Bitmap outputImage = Bitmap.createBitmap(roiMat.width(),roiMat.height(), Bitmap.Config.ARGB_8888);
+            org.opencv.android.Utils.matToBitmap(roiMat,outputImage);
+            if (outputImage != null) {
+                ivInputImage.setImageBitmap(outputImage);
+            }
+        } else if (configParser.getModelType().equalsIgnoreCase("classifier")) {
+            ClsResult clsResult = predictor.getClsResult();
+            if (configParser.getLabeList().size() > 0) {
+                String outputResult = "Top1: " + clsResult.getCategory() + " - " + String.format("%.3f", clsResult.getScore());
+                tvOutputResult.setText(outputResult);
+                tvOutputResult.scrollTo(0, 0);
+            }
+        }
+    }
+
+    public void onMatChanged(Mat mat) {
+        this.predictMat = mat.clone();
+    }
+
+    public void onImageChanged(Bitmap image) {
+        ivInputImage.setImageBitmap(image);
+        tvOutputResult.setText("");
+        tvInferenceTime.setText("Inference time: -- ms");
+    }
+
+    public void onSettingsClicked() {
+        startActivity(new Intent(MainActivity.this, SettingsActivity.class));
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.menu_action_options, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case android.R.id.home:
+                finish();
+                break;
+            case R.id.open_gallery:
+                if (requestAllPermissions()) {
+                    openGallery();
+                }
+                break;
+            case R.id.take_photo:
+                if (requestAllPermissions()) {
+                    takePhoto();
+                }
+                break;
+            case R.id.settings:
+                if (requestAllPermissions()) {
+                    // make sure we have SDCard r&w permissions to load model from SDCard
+                    onSettingsClicked();
+                }
+                break;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @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) {
+            Toast.makeText(this, "Permission Denied", Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        if (resultCode == RESULT_OK && data != null) {
+            switch (requestCode) {
+                case OPEN_GALLERY_REQUEST_CODE:
+                    try {
+                        ContentResolver resolver = getContentResolver();
+                        Uri uri = data.getData();
+                        Bitmap image = MediaStore.Images.Media.getBitmap(resolver, uri);
+                        String[] proj = {MediaStore.Images.Media.DATA};
+                        Cursor cursor = managedQuery(uri, proj, null, null, null);
+                        cursor.moveToFirst();
+                        int columnIndex = cursor.getColumnIndex(proj[0]);
+                        String imgDecodableString = cursor.getString(columnIndex);
+                        File file = new File(imgDecodableString);
+                        Mat mat = Imgcodecs.imread(file.getAbsolutePath(),Imgcodecs.IMREAD_COLOR);
+                        onImageChanged(image);
+                        onMatChanged(mat);
+                    } catch (IOException e) {
+                        Log.e(TAG, e.toString());
+                    }
+                    break;
+                case TAKE_PHOTO_REQUEST_CODE:
+                    Bitmap image = (Bitmap) data.getParcelableExtra("data");
+                    Mat mat = new Mat();
+                    org.opencv.android.Utils.bitmapToMat(image, mat);
+                    Imgproc.cvtColor(mat, mat, Imgproc.COLOR_RGBA2BGR);
+                    onImageChanged(image);
+                    onMatChanged(mat);
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    private boolean requestAllPermissions() {
+        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
+                != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this,
+                Manifest.permission.CAMERA)
+                != PackageManager.PERMISSION_GRANTED) {
+            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,
+                            Manifest.permission.CAMERA},
+                    0);
+            return false;
+        }
+        return true;
+    }
+
+    private void openGallery() {
+        Intent intent = new Intent(Intent.ACTION_PICK, null);
+        intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
+        startActivityForResult(intent, OPEN_GALLERY_REQUEST_CODE);
+    }
+
+    private void takePhoto() {
+        Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
+        if (takePhotoIntent.resolveActivity(getPackageManager()) != null) {
+            startActivityForResult(takePhotoIntent, TAKE_PHOTO_REQUEST_CODE);
+        }
+    }
+
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        boolean isLoaded = predictor.isLoaded();
+        menu.findItem(R.id.open_gallery).setEnabled(isLoaded);
+        menu.findItem(R.id.take_photo).setEnabled(isLoaded);
+        return super.onPrepareOptionsMenu(menu);
+    }
+
+    @Override
+    protected void onResume() {
+        Log.i(TAG, "begin onResume");
+        super.onResume();
+        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+        boolean settingsChanged = false;
+        boolean testImageChanged = false;
+        String modelPath = sharedPreferences.getString(getString(R.string.MODEL_PATH_KEY),
+                getString(R.string.MODEL_PATH_DEFAULT));
+        settingsChanged |= !modelPath.equalsIgnoreCase(testModelPathFromAsset);
+        String yamlPath = sharedPreferences.getString(getString(R.string.YAML_PATH_KEY),
+                getString(R.string.YAML_PATH_DEFAULT));
+        settingsChanged |= !yamlPath.equalsIgnoreCase(testYamlPathFromAsset);
+        int cpuThreadNum = Integer.parseInt(sharedPreferences.getString(getString(R.string.CPU_THREAD_NUM_KEY),
+                getString(R.string.CPU_THREAD_NUM_DEFAULT)));
+        settingsChanged |= cpuThreadNum != configParser.getCpuThreadNum();
+        String cpuPowerMode = sharedPreferences.getString(getString(R.string.CPU_POWER_MODE_KEY),
+                getString(R.string.CPU_POWER_MODE_DEFAULT));
+        settingsChanged |= !cpuPowerMode.equalsIgnoreCase(configParser.getCpuPowerMode());
+        String imagePath = sharedPreferences.getString(getString(R.string.IMAGE_PATH_KEY),
+                getString(R.string.IMAGE_PATH_DEFAULT));
+        testImageChanged |= !imagePath.equalsIgnoreCase(testImagePathFromAsset);
+
+        testYamlPathFromAsset = yamlPath;
+        testModelPathFromAsset = modelPath;
+        if (settingsChanged) {
+            try {
+                String realModelPath = modelPath;
+                if (!modelPath.substring(0, 1).equals("/")) {
+                    String modelFileName = Utils.getFileNameFromString(modelPath);
+                    realModelPath = this.getCacheDir() + File.separator + modelFileName;
+                    Utils.copyFileFromAssets(this, modelPath, realModelPath);
+                }
+                String realYamlPath = yamlPath;
+                if (!yamlPath.substring(0, 1).equals("/")) {
+                    String yamlFileName = Utils.getFileNameFromString(yamlPath);
+                    realYamlPath = this.getCacheDir() + File.separator + yamlFileName;
+                    Utils.copyFileFromAssets(this, yamlPath, realYamlPath);
+                }
+                configParser.init(realModelPath, realYamlPath, cpuThreadNum, cpuPowerMode);
+                visualize.init(configParser.getNumClasses());
+            } catch (IOException e) {
+                e.printStackTrace();
+                Toast.makeText(MainActivity.this, "Load config failed!", Toast.LENGTH_SHORT).show();
+            }
+            // update UI
+            tvInputSetting.setText("Model: " + configParser.getModel()+ "\n" + "CPU" +
+                    " Thread Num: " + Integer.toString(configParser.getCpuThreadNum()) + "\n" + "CPU Power Mode: " + configParser.getCpuPowerMode());
+            tvInputSetting.scrollTo(0, 0);
+            // reload model if configure has been changed
+            loadModel();
+        }
+
+        if (testImageChanged){
+            loadTestImageFromAsset(imagePath);
+        }
+    }
+
+    public void loadTestImageFromAsset(String imagePath){
+        if (imagePath.isEmpty()) {
+            return;
+        }
+        // read test image file from custom file_paths if the first character of mode file_paths is '/', otherwise read test
+        // image file from assets
+        testImagePathFromAsset = imagePath;
+        if (!imagePath.substring(0, 1).equals("/")) {
+            InputStream imageStream = null;
+            try {
+                imageStream = getAssets().open(imagePath);
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+            onImageChanged(BitmapFactory.decodeStream(imageStream));
+            String realPath;
+            String imageFileName = Utils.getFileNameFromString(imagePath);
+            realPath = this.getCacheDir() + File.separator + imageFileName;
+            Utils.copyFileFromAssets(this, imagePath, realPath);
+            onMatChanged(Imgcodecs.imread(realPath, Imgcodecs.IMREAD_COLOR));
+        } else {
+            if (!new File(imagePath).exists()) {
+                return;
+            }
+            onMatChanged(Imgcodecs.imread(imagePath, Imgcodecs.IMREAD_COLOR));
+            onImageChanged( BitmapFactory.decodeFile(imagePath));
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        if (predictor != null) {
+            predictor.releaseModel();
+        }
+        worker.quit();
+        super.onDestroy();
+    }
+}

+ 158 - 0
deploy/lite/android/demo/app/src/main/java/com/baidu/paddlex/lite/demo/SettingsActivity.java

@@ -0,0 +1,158 @@
+// Copyright (c) 2020 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.
+
+package com.baidu.paddlex.lite.demo;
+
+import com.baidu.paddlex.Utils;
+
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.EditTextPreference;
+import android.preference.ListPreference;
+import android.support.v7.app.ActionBar;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class SettingsActivity extends AppCompatPreferenceActivity implements SharedPreferences.OnSharedPreferenceChangeListener {
+    ListPreference lpChoosePreInstalledModel = null;
+    CheckBoxPreference cbEnableCustomSettings = null;
+    EditTextPreference etModelPath = null;
+    EditTextPreference etYamlPath = null;
+    EditTextPreference etImagePath = null;
+    ListPreference lpCPUThreadNum = null;
+    ListPreference lpCPUPowerMode = null;
+
+    List<String> preInstalledModelPaths = null;
+    List<String> preInstalledYamlPaths = null;
+    List<String> preInstalledImagePaths = null;
+    List<String> preInstalledCPUThreadNums = null;
+    List<String> preInstalledCPUPowerModes = null;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        addPreferencesFromResource(R.xml.settings);
+        ActionBar supportActionBar = getSupportActionBar();
+        if (supportActionBar != null) {
+            supportActionBar.setDisplayHomeAsUpEnabled(true);
+        }
+
+        // initialized pre-installed models
+        preInstalledModelPaths = new ArrayList<String>();
+        preInstalledYamlPaths = new ArrayList<String>();
+        preInstalledImagePaths = new ArrayList<String>();
+        preInstalledCPUThreadNums = new ArrayList<String>();
+        preInstalledCPUPowerModes = new ArrayList<String>();
+        preInstalledModelPaths.add(getString(R.string.MODEL_PATH_DEFAULT));
+        preInstalledYamlPaths.add(getString(R.string.YAML_PATH_DEFAULT));
+        preInstalledImagePaths.add(getString(R.string.IMAGE_PATH_DEFAULT));
+        preInstalledCPUThreadNums.add(getString(R.string.CPU_THREAD_NUM_DEFAULT));
+        preInstalledCPUPowerModes.add(getString(R.string.CPU_POWER_MODE_DEFAULT));
+        // initialize UI components
+        lpChoosePreInstalledModel =
+                (ListPreference) findPreference(getString(R.string.CHOOSE_PRE_INSTALLED_MODEL_KEY));
+        String[] preInstalledModelNames = new String[preInstalledModelPaths.size()];
+        for (int i = 0; i < preInstalledModelPaths.size(); i++) {
+            preInstalledModelNames[i] =
+                    preInstalledModelPaths.get(i).substring(preInstalledModelPaths.get(i).lastIndexOf("/") + 1);
+        }
+        lpChoosePreInstalledModel.setEntries(preInstalledModelNames);
+        lpChoosePreInstalledModel.setEntryValues(preInstalledModelPaths.toArray(new String[preInstalledModelPaths.size()]));
+        cbEnableCustomSettings =
+                (CheckBoxPreference) findPreference(getString(R.string.ENABLE_CUSTOM_SETTINGS_KEY));
+        etModelPath = (EditTextPreference) findPreference(getString(R.string.MODEL_PATH_KEY));
+        etModelPath.setTitle("Model Path (SDCard: " + Utils.getSDCardDirectory() + ")");
+        etYamlPath = (EditTextPreference) findPreference(getString(R.string.YAML_PATH_KEY));
+        etImagePath = (EditTextPreference) findPreference(getString(R.string.IMAGE_PATH_KEY));
+        lpCPUThreadNum =
+                (ListPreference) findPreference(getString(R.string.CPU_THREAD_NUM_KEY));
+        lpCPUPowerMode =
+                (ListPreference) findPreference(getString(R.string.CPU_POWER_MODE_KEY));
+    }
+
+    private void reloadPreferenceAndUpdateUI() {
+        SharedPreferences sharedPreferences = getPreferenceScreen().getSharedPreferences();
+        boolean enableCustomSettings =
+                sharedPreferences.getBoolean(getString(R.string.ENABLE_CUSTOM_SETTINGS_KEY), false);
+        String modelPath = sharedPreferences.getString(getString(R.string.CHOOSE_PRE_INSTALLED_MODEL_KEY),
+                getString(R.string.MODEL_PATH_DEFAULT));
+        int modelIdx = lpChoosePreInstalledModel.findIndexOfValue(modelPath);
+        if (modelIdx >= 0 && modelIdx < preInstalledModelPaths.size()) {
+            if (!enableCustomSettings) {
+                SharedPreferences.Editor editor = sharedPreferences.edit();
+                editor.putString(getString(R.string.MODEL_PATH_KEY), preInstalledModelPaths.get(modelIdx));
+                editor.putString(getString(R.string.YAML_PATH_KEY), preInstalledYamlPaths.get(modelIdx));
+                editor.putString(getString(R.string.IMAGE_PATH_KEY), preInstalledImagePaths.get(modelIdx));
+                editor.putString(getString(R.string.CPU_THREAD_NUM_KEY), preInstalledCPUThreadNums.get(modelIdx));
+                editor.putString(getString(R.string.CPU_POWER_MODE_KEY), preInstalledCPUPowerModes.get(modelIdx));
+                editor.commit();
+            }
+            lpChoosePreInstalledModel.setSummary(modelPath);
+        }
+
+        cbEnableCustomSettings.setChecked(enableCustomSettings);
+        etModelPath.setEnabled(enableCustomSettings);
+        etYamlPath.setEnabled(enableCustomSettings);
+        etImagePath.setEnabled(enableCustomSettings);
+        lpCPUThreadNum.setEnabled(enableCustomSettings);
+        lpCPUPowerMode.setEnabled(enableCustomSettings);
+        modelPath = sharedPreferences.getString(getString(R.string.MODEL_PATH_KEY),
+                getString(R.string.MODEL_PATH_DEFAULT));
+        String YamlPath = sharedPreferences.getString(getString(R.string.YAML_PATH_KEY),
+                getString(R.string.YAML_PATH_DEFAULT));
+        String imagePath = sharedPreferences.getString(getString(R.string.IMAGE_PATH_KEY),
+                getString(R.string.IMAGE_PATH_DEFAULT));
+        String cpuThreadNum = sharedPreferences.getString(getString(R.string.CPU_THREAD_NUM_KEY),
+                getString(R.string.CPU_THREAD_NUM_DEFAULT));
+        String cpuPowerMode = sharedPreferences.getString(getString(R.string.CPU_POWER_MODE_KEY),
+                getString(R.string.CPU_POWER_MODE_DEFAULT));
+
+        etModelPath.setSummary(modelPath);
+        etModelPath.setText(modelPath);
+        etYamlPath.setSummary(YamlPath);
+        etYamlPath.setText(YamlPath);
+        etImagePath.setSummary(imagePath);
+        etImagePath.setText(imagePath);
+        lpCPUThreadNum.setValue(cpuThreadNum);
+        lpCPUThreadNum.setSummary(cpuThreadNum);
+        lpCPUPowerMode.setValue(cpuPowerMode);
+        lpCPUPowerMode.setSummary(cpuPowerMode);
+
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
+        reloadPreferenceAndUpdateUI();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
+    }
+
+    @Override
+    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+        if (key.equals(getString(R.string.CHOOSE_PRE_INSTALLED_MODEL_KEY))) {
+            SharedPreferences.Editor editor = sharedPreferences.edit();
+            editor.putBoolean(getString(R.string.ENABLE_CUSTOM_SETTINGS_KEY), false);
+            editor.commit();
+        }
+        reloadPreferenceAndUpdateUI();
+    }
+}

+ 34 - 0
deploy/lite/android/demo/app/src/main/res/drawable-v24/ic_launcher_foreground.xml

@@ -0,0 +1,34 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+    <path
+        android:fillType="evenOdd"
+        android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
+        android:strokeWidth="1"
+        android:strokeColor="#00000000">
+        <aapt:attr name="android:fillColor">
+            <gradient
+                android:endX="78.5885"
+                android:endY="90.9159"
+                android:startX="48.7653"
+                android:startY="61.0927"
+                android:type="linear">
+                <item
+                    android:color="#44000000"
+                    android:offset="0.0" />
+                <item
+                    android:color="#00000000"
+                    android:offset="1.0" />
+            </gradient>
+        </aapt:attr>
+    </path>
+    <path
+        android:fillColor="#FFFFFF"
+        android:fillType="nonZero"
+        android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
+        android:strokeWidth="1"
+        android:strokeColor="#00000000" />
+</vector>

BIN
deploy/lite/android/demo/app/src/main/res/drawable/face.jpg


+ 170 - 0
deploy/lite/android/demo/app/src/main/res/drawable/ic_launcher_background.xml

@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+    <path
+        android:fillColor="#008577"
+        android:pathData="M0,0h108v108h-108z" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M9,0L9,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,0L19,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,0L29,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,0L39,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,0L49,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,0L59,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,0L69,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,0L79,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M89,0L89,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M99,0L99,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,9L108,9"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,19L108,19"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,29L108,29"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,39L108,39"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,49L108,49"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,59L108,59"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,69L108,69"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,79L108,79"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,89L108,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,99L108,99"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,29L89,29"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,39L89,39"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,49L89,49"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,59L89,59"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,69L89,69"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,79L89,79"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,19L29,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,19L39,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,19L49,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,19L59,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,19L69,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,19L79,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+</vector>

+ 112 - 0
deploy/lite/android/demo/app/src/main/res/layout/activity_main.xml

@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context="com.baidu.paddlex.lite.demo.MainActivity">
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <LinearLayout
+            android:id="@+id/v_input_info"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentTop="true"
+            android:orientation="vertical">
+
+            <TextView
+                android:id="@+id/tv_input_setting"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="12dp"
+                android:layout_marginTop="10dp"
+                android:layout_marginRight="12dp"
+                android:layout_marginBottom="5dp"
+                android:lineSpacingExtra="4dp"
+                android:maxLines="6"
+                android:scrollbars="vertical"
+                android:singleLine="false"
+                android:text="" />
+
+        </LinearLayout>
+
+        <RelativeLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_above="@+id/v_output_info"
+            android:layout_below="@+id/v_input_info">
+
+            <ImageView
+                android:id="@+id/iv_input_image"
+                android:layout_width="400dp"
+                android:layout_height="400dp"
+                android:layout_centerInParent="true"
+                android:layout_marginLeft="12dp"
+                android:layout_marginTop="5dp"
+                android:layout_marginRight="12dp"
+                android:layout_marginBottom="5dp"
+                android:adjustViewBounds="true"
+                android:scaleType="fitCenter" />
+        </RelativeLayout>
+
+
+        <RelativeLayout
+            android:id="@+id/v_output_info"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            android:layout_centerHorizontal="true">
+
+            <TextView
+                android:id="@+id/tv_output_result"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentTop="true"
+                android:layout_centerHorizontal="true"
+                android:layout_centerVertical="true"
+                android:layout_marginLeft="12dp"
+                android:layout_marginTop="5dp"
+                android:layout_marginRight="12dp"
+                android:layout_marginBottom="5dp"
+                android:lineSpacingExtra="5dp"
+                android:maxLines="5"
+                android:scrollbars="vertical"
+                android:singleLine="false"
+                android:text=""
+                android:textAlignment="center" />
+
+            <TextView
+                android:id="@+id/tv_inference_time"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/tv_output_result"
+                android:layout_centerHorizontal="true"
+                android:layout_centerVertical="true"
+                android:layout_marginLeft="12dp"
+                android:layout_marginTop="5dp"
+                android:layout_marginRight="12dp"
+                android:layout_marginBottom="10dp"
+                android:text=""
+                android:textAlignment="center" />
+
+            <Button
+                android:id="@+id/iv_predict_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/tv_inference_time"
+                android:layout_centerHorizontal="true"
+                android:layout_centerVertical="true"
+                android:layout_marginLeft="12dp"
+                android:layout_marginTop="5dp"
+                android:layout_marginRight="12dp"
+                android:layout_marginBottom="10dp"
+                android:text="Predict"
+                android:textAlignment="center" />/>
+
+        </RelativeLayout>
+
+    </RelativeLayout>
+
+</android.support.constraint.ConstraintLayout>

+ 21 - 0
deploy/lite/android/demo/app/src/main/res/menu/menu_action_options.xml

@@ -0,0 +1,21 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+    <group android:id="@+id/pick_image">
+        <item
+            android:id="@+id/open_gallery"
+            android:title="Open Gallery"
+            app:showAsAction="withText" />
+
+        <item
+            android:id="@+id/take_photo"
+            android:title="Take Photo"
+            app:showAsAction="withText" />
+    </group>
+
+    <group>
+        <item
+            android:id="@+id/settings"
+            android:title="Settings..."
+            app:showAsAction="withText" />
+    </group>
+</menu>

+ 5 - 0
deploy/lite/android/demo/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/ic_launcher_background" />
+    <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>

+ 5 - 0
deploy/lite/android/demo/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/ic_launcher_background" />
+    <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>

BIN
deploy/lite/android/demo/app/src/main/res/mipmap-hdpi/ic_launcher.png


BIN
deploy/lite/android/demo/app/src/main/res/mipmap-hdpi/ic_launcher_round.png


BIN
deploy/lite/android/demo/app/src/main/res/mipmap-mdpi/ic_launcher.png


BIN
deploy/lite/android/demo/app/src/main/res/mipmap-mdpi/ic_launcher_round.png


BIN
deploy/lite/android/demo/app/src/main/res/mipmap-xhdpi/ic_launcher.png


BIN
deploy/lite/android/demo/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png


BIN
deploy/lite/android/demo/app/src/main/res/mipmap-xxhdpi/ic_launcher.png


BIN
deploy/lite/android/demo/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png


BIN
deploy/lite/android/demo/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png


BIN
deploy/lite/android/demo/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png


+ 39 - 0
deploy/lite/android/demo/app/src/main/res/values/arrays.xml

@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string-array name="cpu_thread_num_entries">
+        <item>1 threads</item>
+        <item>2 threads</item>
+        <item>4 threads</item>
+        <item>8 threads</item>
+    </string-array>
+    <string-array name="cpu_thread_num_values">
+        <item>1</item>
+        <item>2</item>
+        <item>4</item>
+        <item>8</item>
+    </string-array>
+    <string-array name="cpu_power_mode_entries">
+        <item>HIGH(only big cores)</item>
+        <item>LOW(only LITTLE cores)</item>
+        <item>FULL(all cores)</item>
+        <item>NO_BIND(depends on system)</item>
+        <item>RAND_HIGH</item>
+        <item>RAND_LOW</item>
+    </string-array>
+    <string-array name="cpu_power_mode_values">
+        <item>LITE_POWER_HIGH</item>
+        <item>LITE_POWER_LOW</item>
+        <item>LITE_POWER_FULL</item>
+        <item>LITE_POWER_NO_BIND</item>
+        <item>LITE_POWER_RAND_HIGH</item>
+        <item>LITE_POWER_RAND_LOW</item>
+    </string-array>
+    <string-array name="input_color_format_entries">
+        <item>BGR color format</item>
+        <item>RGB color format</item>
+    </string-array>
+    <string-array name="input_color_format_values">
+        <item>BGR</item>
+        <item>RGB</item>
+    </string-array>
+</resources>

+ 6 - 0
deploy/lite/android/demo/app/src/main/res/values/colors.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="colorPrimary">#008577</color>
+    <color name="colorPrimaryDark">#00574B</color>
+    <color name="colorAccent">#D81B60</color>
+</resources>

+ 16 - 0
deploy/lite/android/demo/app/src/main/res/values/strings.xml

@@ -0,0 +1,16 @@
+<resources>
+<string name="app_name">PaddleX Demo</string>
+<!-- settings -->
+<string name="CHOOSE_PRE_INSTALLED_MODEL_KEY">CHOOSE_PRE_INSTALLED_MODEL_KEY</string>
+<string name="ENABLE_CUSTOM_SETTINGS_KEY">ENABLE_CUSTOM_SETTINGS_KEY</string>
+<string name="MODEL_PATH_KEY">MODEL_PATH_KEY</string>
+<string name="YAML_PATH_KEY">YAML_PATH_KEY</string>
+<string name="IMAGE_PATH_KEY">IMAGE_PATH_KEY</string>
+<string name="CPU_POWER_MODE_KEY">CPU_POWER_MODE_KEY</string>
+<string name="CPU_THREAD_NUM_KEY">CPU_THREAD_NUM_KEY</string>
+<string name="MODEL_PATH_DEFAULT">model/model.nb</string>
+<string name="YAML_PATH_DEFAULT">config/model.yml</string>
+<string name="IMAGE_PATH_DEFAULT">images/test.jpg</string>
+<string name="CPU_THREAD_NUM_DEFAULT">1</string>
+<string name="CPU_POWER_MODE_DEFAULT">LITE_POWER_HIGH</string>
+</resources>

+ 25 - 0
deploy/lite/android/demo/app/src/main/res/values/styles.xml

@@ -0,0 +1,25 @@
+<resources>
+
+    <!-- Base application theme. -->
+    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+        <!-- Customize your theme here. -->
+        <item name="colorPrimary">@color/colorPrimary</item>
+        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+        <item name="colorAccent">@color/colorAccent</item>
+        <item name="actionOverflowMenuStyle">@style/OverflowMenuStyle</item>
+    </style>
+
+    <style name="OverflowMenuStyle" parent="Widget.AppCompat.Light.PopupMenu.Overflow">
+        <item name="overlapAnchor">false</item>
+    </style>
+
+    <style name="AppTheme.NoActionBar">
+        <item name="windowActionBar">false</item>
+        <item name="windowNoTitle">true</item>
+    </style>
+
+    <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+    <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
+
+</resources>

+ 49 - 0
deploy/lite/android/demo/app/src/main/res/xml/settings.xml

@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+    <PreferenceCategory android:title="Model Settings">
+        <ListPreference
+            android:defaultValue="@string/MODEL_PATH_DEFAULT"
+            android:key="@string/CHOOSE_PRE_INSTALLED_MODEL_KEY"
+            android:negativeButtonText="@null"
+            android:positiveButtonText="@null"
+            android:title="Choose pre-installed models" />
+        <CheckBoxPreference
+            android:defaultValue="false"
+            android:key="@string/ENABLE_CUSTOM_SETTINGS_KEY"
+            android:summaryOff="Disable"
+            android:summaryOn="Enable"
+            android:title="Enable custom settings" />
+        <EditTextPreference
+            android:defaultValue="@string/MODEL_PATH_DEFAULT"
+            android:key="@string/MODEL_PATH_KEY"
+            android:title="Model Path" />
+        <EditTextPreference
+            android:defaultValue="@string/YAML_PATH_DEFAULT"
+            android:key="@string/YAML_PATH_KEY"
+            android:title="Yaml Path" />
+    </PreferenceCategory>
+    <PreferenceCategory android:title="Image Settings">
+        <EditTextPreference
+            android:defaultValue="@string/IMAGE_PATH_DEFAULT"
+            android:key="@string/IMAGE_PATH_KEY"
+            android:title="Image Path" />
+    </PreferenceCategory>
+    <PreferenceCategory android:title="CPU Settings">
+        <ListPreference
+            android:defaultValue="@string/CPU_THREAD_NUM_DEFAULT"
+            android:entries="@array/cpu_thread_num_entries"
+            android:entryValues="@array/cpu_thread_num_values"
+            android:key="@string/CPU_THREAD_NUM_KEY"
+            android:negativeButtonText="@null"
+            android:positiveButtonText="@null"
+            android:title="CPU Thread Num" />
+        <ListPreference
+            android:defaultValue="@string/CPU_POWER_MODE_DEFAULT"
+            android:entries="@array/cpu_power_mode_entries"
+            android:entryValues="@array/cpu_power_mode_values"
+            android:key="@string/CPU_POWER_MODE_KEY"
+            android:negativeButtonText="@null"
+            android:positiveButtonText="@null"
+            android:title="CPU Power Mode" />
+    </PreferenceCategory>
+</PreferenceScreen>

+ 17 - 0
deploy/lite/android/demo/app/src/test/java/com/baidu/paddlex/lite/demo/ExampleUnitTest.java

@@ -0,0 +1,17 @@
+package com.baidu.paddlex.lite.demo;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+public class ExampleUnitTest {
+    @Test
+    public void addition_isCorrect() {
+        assertEquals(4, 2 + 2);
+    }
+}

+ 27 - 0
deploy/lite/android/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
+}

+ 15 - 0
deploy/lite/android/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
+
+

BIN
deploy/lite/android/demo/gradle/wrapper/gradle-wrapper.jar


+ 6 - 0
deploy/lite/android/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

+ 172 - 0
deploy/lite/android/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_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_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_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" "$@"

+ 84 - 0
deploy/lite/android/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

+ 245 - 0
deploy/lite/android/demo/import-summary.txt

@@ -0,0 +1,245 @@
+ECLIPSE ANDROID PROJECT IMPORT SUMMARY
+======================================
+
+Ignored Files:
+--------------
+The following files were *not* copied into the new Gradle project; you
+should evaluate whether these are still needed in your project and if
+so manually move them:
+
+* javadoc/
+* javadoc/allclasses-frame.html
+* javadoc/allclasses-noframe.html
+* javadoc/constant-values.html
+* javadoc/help-doc.html
+* javadoc/index-all.html
+* javadoc/index.html
+* javadoc/org/
+* javadoc/org/opencv/
+* javadoc/org/opencv/android/
+* javadoc/org/opencv/android/BaseLoaderCallback.html
+* javadoc/org/opencv/android/Camera2Renderer.html
+* javadoc/org/opencv/android/CameraBridgeViewBase.CvCameraViewFrame.html
+* javadoc/org/opencv/android/CameraBridgeViewBase.CvCameraViewListener.html
+* javadoc/org/opencv/android/CameraBridgeViewBase.CvCameraViewListener2.html
+* javadoc/org/opencv/android/CameraBridgeViewBase.ListItemAccessor.html
+* javadoc/org/opencv/android/CameraBridgeViewBase.html
+* javadoc/org/opencv/android/CameraGLRendererBase.html
+* javadoc/org/opencv/android/CameraGLSurfaceView.CameraTextureListener.html
+* javadoc/org/opencv/android/CameraGLSurfaceView.html
+* javadoc/org/opencv/android/CameraRenderer.html
+* javadoc/org/opencv/android/FpsMeter.html
+* javadoc/org/opencv/android/InstallCallbackInterface.html
+* javadoc/org/opencv/android/JavaCamera2View.html
+* javadoc/org/opencv/android/JavaCameraView.JavaCameraSizeAccessor.html
+* javadoc/org/opencv/android/JavaCameraView.html
+* javadoc/org/opencv/android/LoaderCallbackInterface.html
+* javadoc/org/opencv/android/OpenCVLoader.html
+* javadoc/org/opencv/android/Utils.html
+* javadoc/org/opencv/android/package-frame.html
+* javadoc/org/opencv/android/package-summary.html
+* javadoc/org/opencv/android/package-tree.html
+* javadoc/org/opencv/calib3d/
+* javadoc/org/opencv/calib3d/Calib3d.html
+* javadoc/org/opencv/calib3d/StereoBM.html
+* javadoc/org/opencv/calib3d/StereoMatcher.html
+* javadoc/org/opencv/calib3d/StereoSGBM.html
+* javadoc/org/opencv/calib3d/package-frame.html
+* javadoc/org/opencv/calib3d/package-summary.html
+* javadoc/org/opencv/calib3d/package-tree.html
+* javadoc/org/opencv/core/
+* javadoc/org/opencv/core/Algorithm.html
+* javadoc/org/opencv/core/Core.MinMaxLocResult.html
+* javadoc/org/opencv/core/Core.html
+* javadoc/org/opencv/core/CvException.html
+* javadoc/org/opencv/core/CvType.html
+* javadoc/org/opencv/core/DMatch.html
+* javadoc/org/opencv/core/KeyPoint.html
+* javadoc/org/opencv/core/Mat.html
+* javadoc/org/opencv/core/MatOfByte.html
+* javadoc/org/opencv/core/MatOfDMatch.html
+* javadoc/org/opencv/core/MatOfDouble.html
+* javadoc/org/opencv/core/MatOfFloat.html
+* javadoc/org/opencv/core/MatOfFloat4.html
+* javadoc/org/opencv/core/MatOfFloat6.html
+* javadoc/org/opencv/core/MatOfInt.html
+* javadoc/org/opencv/core/MatOfInt4.html
+* javadoc/org/opencv/core/MatOfKeyPoint.html
+* javadoc/org/opencv/core/MatOfPoint.html
+* javadoc/org/opencv/core/MatOfPoint2f.html
+* javadoc/org/opencv/core/MatOfPoint3.html
+* javadoc/org/opencv/core/MatOfPoint3f.html
+* javadoc/org/opencv/core/MatOfRect.html
+* javadoc/org/opencv/core/MatOfRect2d.html
+* javadoc/org/opencv/core/MatOfRotatedRect.html
+* javadoc/org/opencv/core/Point.html
+* javadoc/org/opencv/core/Point3.html
+* javadoc/org/opencv/core/Range.html
+* javadoc/org/opencv/core/Rect.html
+* javadoc/org/opencv/core/Rect2d.html
+* javadoc/org/opencv/core/RotatedRect.html
+* javadoc/org/opencv/core/Scalar.html
+* javadoc/org/opencv/core/Size.html
+* javadoc/org/opencv/core/TermCriteria.html
+* javadoc/org/opencv/core/TickMeter.html
+* javadoc/org/opencv/core/package-frame.html
+* javadoc/org/opencv/core/package-summary.html
+* javadoc/org/opencv/core/package-tree.html
+* javadoc/org/opencv/dnn/
+* javadoc/org/opencv/dnn/DictValue.html
+* javadoc/org/opencv/dnn/Dnn.html
+* javadoc/org/opencv/dnn/Layer.html
+* javadoc/org/opencv/dnn/Net.html
+* javadoc/org/opencv/dnn/package-frame.html
+* javadoc/org/opencv/dnn/package-summary.html
+* javadoc/org/opencv/dnn/package-tree.html
+* javadoc/org/opencv/features2d/
+* javadoc/org/opencv/features2d/AKAZE.html
+* javadoc/org/opencv/features2d/AgastFeatureDetector.html
+* javadoc/org/opencv/features2d/BFMatcher.html
+* javadoc/org/opencv/features2d/BOWImgDescriptorExtractor.html
+* javadoc/org/opencv/features2d/BOWKMeansTrainer.html
+* javadoc/org/opencv/features2d/BOWTrainer.html
+* javadoc/org/opencv/features2d/BRISK.html
+* javadoc/org/opencv/features2d/DescriptorMatcher.html
+* javadoc/org/opencv/features2d/FastFeatureDetector.html
+* javadoc/org/opencv/features2d/Feature2D.html
+* javadoc/org/opencv/features2d/Features2d.html
+* javadoc/org/opencv/features2d/FlannBasedMatcher.html
+* javadoc/org/opencv/features2d/GFTTDetector.html
+* javadoc/org/opencv/features2d/KAZE.html
+* javadoc/org/opencv/features2d/MSER.html
+* javadoc/org/opencv/features2d/ORB.html
+* javadoc/org/opencv/features2d/Params.html
+* javadoc/org/opencv/features2d/package-frame.html
+* javadoc/org/opencv/features2d/package-summary.html
+* javadoc/org/opencv/features2d/package-tree.html
+* javadoc/org/opencv/imgcodecs/
+* javadoc/org/opencv/imgcodecs/Imgcodecs.html
+* javadoc/org/opencv/imgcodecs/package-frame.html
+* javadoc/org/opencv/imgcodecs/package-summary.html
+* javadoc/org/opencv/imgcodecs/package-tree.html
+* javadoc/org/opencv/imgproc/
+* javadoc/org/opencv/imgproc/CLAHE.html
+* javadoc/org/opencv/imgproc/Imgproc.html
+* javadoc/org/opencv/imgproc/LineSegmentDetector.html
+* javadoc/org/opencv/imgproc/Moments.html
+* javadoc/org/opencv/imgproc/Subdiv2D.html
+* javadoc/org/opencv/imgproc/package-frame.html
+* javadoc/org/opencv/imgproc/package-summary.html
+* javadoc/org/opencv/imgproc/package-tree.html
+* javadoc/org/opencv/ml/
+* javadoc/org/opencv/ml/ANN_MLP.html
+* javadoc/org/opencv/ml/ANN_MLP_ANNEAL.html
+* javadoc/org/opencv/ml/Boost.html
+* javadoc/org/opencv/ml/DTrees.html
+* javadoc/org/opencv/ml/EM.html
+* javadoc/org/opencv/ml/KNearest.html
+* javadoc/org/opencv/ml/LogisticRegression.html
+* javadoc/org/opencv/ml/Ml.html
+* javadoc/org/opencv/ml/NormalBayesClassifier.html
+* javadoc/org/opencv/ml/ParamGrid.html
+* javadoc/org/opencv/ml/RTrees.html
+* javadoc/org/opencv/ml/SVM.html
+* javadoc/org/opencv/ml/SVMSGD.html
+* javadoc/org/opencv/ml/StatModel.html
+* javadoc/org/opencv/ml/TrainData.html
+* javadoc/org/opencv/ml/package-frame.html
+* javadoc/org/opencv/ml/package-summary.html
+* javadoc/org/opencv/ml/package-tree.html
+* javadoc/org/opencv/objdetect/
+* javadoc/org/opencv/objdetect/BaseCascadeClassifier.html
+* javadoc/org/opencv/objdetect/CascadeClassifier.html
+* javadoc/org/opencv/objdetect/HOGDescriptor.html
+* javadoc/org/opencv/objdetect/Objdetect.html
+* javadoc/org/opencv/objdetect/QRCodeDetector.html
+* javadoc/org/opencv/objdetect/package-frame.html
+* javadoc/org/opencv/objdetect/package-summary.html
+* javadoc/org/opencv/objdetect/package-tree.html
+* javadoc/org/opencv/osgi/
+* javadoc/org/opencv/osgi/OpenCVInterface.html
+* javadoc/org/opencv/osgi/OpenCVNativeLoader.html
+* javadoc/org/opencv/osgi/package-frame.html
+* javadoc/org/opencv/osgi/package-summary.html
+* javadoc/org/opencv/osgi/package-tree.html
+* javadoc/org/opencv/photo/
+* javadoc/org/opencv/photo/AlignExposures.html
+* javadoc/org/opencv/photo/AlignMTB.html
+* javadoc/org/opencv/photo/CalibrateCRF.html
+* javadoc/org/opencv/photo/CalibrateDebevec.html
+* javadoc/org/opencv/photo/CalibrateRobertson.html
+* javadoc/org/opencv/photo/MergeDebevec.html
+* javadoc/org/opencv/photo/MergeExposures.html
+* javadoc/org/opencv/photo/MergeMertens.html
+* javadoc/org/opencv/photo/MergeRobertson.html
+* javadoc/org/opencv/photo/Photo.html
+* javadoc/org/opencv/photo/Tonemap.html
+* javadoc/org/opencv/photo/TonemapDrago.html
+* javadoc/org/opencv/photo/TonemapMantiuk.html
+* javadoc/org/opencv/photo/TonemapReinhard.html
+* javadoc/org/opencv/photo/package-frame.html
+* javadoc/org/opencv/photo/package-summary.html
+* javadoc/org/opencv/photo/package-tree.html
+* javadoc/org/opencv/utils/
+* javadoc/org/opencv/utils/Converters.html
+* javadoc/org/opencv/utils/package-frame.html
+* javadoc/org/opencv/utils/package-summary.html
+* javadoc/org/opencv/utils/package-tree.html
+* javadoc/org/opencv/video/
+* javadoc/org/opencv/video/BackgroundSubtractor.html
+* javadoc/org/opencv/video/BackgroundSubtractorKNN.html
+* javadoc/org/opencv/video/BackgroundSubtractorMOG2.html
+* javadoc/org/opencv/video/DenseOpticalFlow.html
+* javadoc/org/opencv/video/DualTVL1OpticalFlow.html
+* javadoc/org/opencv/video/FarnebackOpticalFlow.html
+* javadoc/org/opencv/video/KalmanFilter.html
+* javadoc/org/opencv/video/SparseOpticalFlow.html
+* javadoc/org/opencv/video/SparsePyrLKOpticalFlow.html
+* javadoc/org/opencv/video/Video.html
+* javadoc/org/opencv/video/package-frame.html
+* javadoc/org/opencv/video/package-summary.html
+* javadoc/org/opencv/video/package-tree.html
+* javadoc/org/opencv/videoio/
+* javadoc/org/opencv/videoio/VideoCapture.html
+* javadoc/org/opencv/videoio/VideoWriter.html
+* javadoc/org/opencv/videoio/Videoio.html
+* javadoc/org/opencv/videoio/package-frame.html
+* javadoc/org/opencv/videoio/package-summary.html
+* javadoc/org/opencv/videoio/package-tree.html
+* javadoc/overview-frame.html
+* javadoc/overview-summary.html
+* javadoc/overview-tree.html
+* javadoc/package-list
+* javadoc/resources/
+* javadoc/resources/background.gif
+* javadoc/resources/tab.gif
+* javadoc/resources/titlebar.gif
+* javadoc/resources/titlebar_end.gif
+* javadoc/serialized-form.html
+* javadoc/stylesheet.css
+
+Moved Files:
+------------
+Android Gradle projects use a different directory structure than ADT
+Eclipse projects. Here's how the projects were restructured:
+
+* AndroidManifest.xml => openCVLibrary346/src/main/AndroidManifest.xml
+* lint.xml => openCVLibrary346/lint.xml
+* res/ => openCVLibrary346/src/main/res/
+* src/ => openCVLibrary346/src/main/java/
+* src/org/opencv/engine/OpenCVEngineInterface.aidl => openCVLibrary346/src/main/aidl/org/opencv/engine/OpenCVEngineInterface.aidl
+
+Next Steps:
+-----------
+You can now build the project. The Gradle project needs network
+connectivity to download dependencies.
+
+Bugs:
+-----
+If for some reason your project does not build, and you determine that
+it is due to a bug or limitation of the Eclipse to Gradle importer,
+please file a bug at http://b.android.com with category
+Component-Tools.
+
+(This import summary is for your information only, and can be deleted
+after import once you are satisfied with the results.)

+ 1 - 0
deploy/lite/android/demo/settings.gradle

@@ -0,0 +1 @@
+include ':app'

+ 1 - 0
deploy/lite/android/sdk/.gitignore

@@ -0,0 +1 @@
+/build

+ 163 - 0
deploy/lite/android/sdk/build.gradle

@@ -0,0 +1,163 @@
+import java.security.MessageDigest
+
+apply plugin: 'com.android.library'
+
+android {
+    compileSdkVersion 28
+    buildToolsVersion "29.0.2"
+    defaultConfig {
+        minSdkVersion 15
+        targetSdkVersion 28
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+        consumerProguardFiles 'consumer-rules.pro'
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+}
+
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar','*.aar'])
+    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 'androidx.test.ext:junit:1.1.1'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+}
+
+
+def paddleLiteLibs = 'https://bj.bcebos.com/paddlex/deploy/lite/paddle_lite_version_11cbd50e.tar.gz'
+task downloadAndExtractPaddleLiteLibs(type: DefaultTask) {
+    doFirst {
+        println "Downloading and extracting Paddle Lite libs"
+    }
+    doLast {
+        // Prepare cache folder for libs
+        if (!file("cache").exists()) {
+            mkdir "cache"
+        }
+        // Generate cache name for libs
+        MessageDigest messageDigest = MessageDigest.getInstance('MD5')
+        messageDigest.update(paddleLiteLibs.bytes)
+        String cacheName = new BigInteger(1, messageDigest.digest()).toString(32)
+        // Download libs
+        if (!file("cache/${cacheName}.tar.gz").exists()) {
+            ant.get(src: paddleLiteLibs, dest: file("cache/${cacheName}.tar.gz"))
+        }
+        // Unpack libs
+        copy {
+            from tarTree("cache/${cacheName}.tar.gz")
+            into "cache/${cacheName}"
+        }
+        // Copy PaddlePredictor.jar
+        if (!file("libs/PaddlePredictor.jar").exists()) {
+            copy {
+                from "cache/${cacheName}/PaddlePredictor.jar"
+                into "libs"
+            }
+        }
+        // Copy libpaddle_lite_jni.so for armeabi-v7a and arm64-v8a
+        if (!file("src/main/jniLibs/armeabi-v7a/libpaddle_lite_jni.so").exists()) {
+            copy {
+                from "cache/${cacheName}/libs/armeabi-v7a/"
+                into "src/main/jniLibs/armeabi-v7a"
+            }
+        }
+        if (!file("src/main/jniLibs/arm64-v8a/libpaddle_lite_jni.so").exists()) {
+            copy {
+                from "cache/${cacheName}/libs/arm64-v8a/"
+                into "src/main/jniLibs/arm64-v8a"
+            }
+        }
+    }
+}
+preBuild.dependsOn downloadAndExtractPaddleLiteLibs
+
+def snakeYamlLibs = 'https://bj.bcebos.com/paddlex/deploy/lite/snakeyaml-1.18-android.tar.gz'
+task downloadAndExtractSnakeYamlLibs(type: DefaultTask) {
+    doFirst {
+        println "Downloading and extracting snake yaml sdk"
+    }
+    doLast {
+        // Prepare cache folder for sdk
+        if (!file("cache").exists()) {
+            mkdir "cache"
+        }
+        // Generate cache name for sdk
+        MessageDigest messageDigest = MessageDigest.getInstance('MD5')
+        messageDigest.update(snakeYamlLibs.bytes)
+        String cacheName = new BigInteger(1, messageDigest.digest()).toString(32)
+        // Download libs
+        if (!file("cache/${cacheName}.tar.gz").exists()) {
+            ant.get(src: snakeYamlLibs, dest: file("cache/${cacheName}.tar.gz"))
+        }
+        // Unpack libs
+        copy {
+            from tarTree("cache/${cacheName}.tar.gz")
+            into "cache/${cacheName}"
+        }
+        // Copy .jar
+        if (!file("libs/snakeyaml-1.18-android.jar").exists()) {
+            copy {
+                from "cache/${cacheName}/snakeyaml-1.18-android.jar"
+                into "libs"
+            }
+        }
+    }
+}
+preBuild.dependsOn downloadAndExtractSnakeYamlLibs
+
+def opencvLibs = 'https://bj.bcebos.com/paddlex/deploy/lite/opencv-3.4.6-android.tar.gz'
+task downloadAndExtractOpencvLibs(type: DefaultTask) {
+    doFirst {
+        println "Downloading and extracting opencv sdk"
+    }
+    doLast {
+        // Prepare cache folder for sdk
+        if (!file("cache").exists()) {
+            mkdir "cache"
+        }
+        // Generate cache name for sdk
+        MessageDigest messageDigest = MessageDigest.getInstance('MD5')
+        messageDigest.update(opencvLibs.bytes)
+        String cacheName = new BigInteger(1, messageDigest.digest()).toString(32)
+        // Download libs
+        if (!file("cache/${cacheName}.tar.gz").exists()) {
+            ant.get(src: opencvLibs, dest: file("cache/${cacheName}.tar.gz"))
+        }
+        // Unpack libs
+        copy {
+            from tarTree("cache/${cacheName}.tar.gz")
+            into "cache/${cacheName}"
+        }
+        // Copy .jar
+        if (!file("libs/opencv346.jar").exists()) {
+            copy {
+                from "cache/${cacheName}/opencv346.jar"
+                into "libs"
+            }
+        }
+        // Copy .so for armeabi-v7a and arm64-v8a
+        if (!file("src/main/jniLibs/armeabi-v7a/libopencv_java3.so").exists()) {
+            copy {
+                from "cache/${cacheName}/libs/armeabi-v7a/"
+                into "src/main/jniLibs/armeabi-v7a"
+            }
+        }
+        if (!file("src/main/jniLibs/arm64-v8a/libopencv_java3.so").exists()) {
+            copy {
+                from "cache/${cacheName}/libs/arm64-v8a/"
+                into "src/main/jniLibs/arm64-v8a"
+            }
+        }
+    }
+}
+
+preBuild.dependsOn downloadAndExtractOpencvLibs

+ 0 - 0
deploy/lite/android/sdk/consumer-rules.pro


+ 7 - 0
deploy/lite/android/sdk/local.properties

@@ -0,0 +1,7 @@
+## This file must *NOT* be checked into Version Control Systems,
+# as it contains information specific to your local configuration.
+#
+# Location of the SDK. This is only used by Gradle.
+# For customization when using a Version Control System, please read the
+# header note.
+#Tue Jun 16 10:08:04 CST 2020

+ 21 - 0
deploy/lite/android/sdk/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

+ 36 - 0
deploy/lite/android/sdk/src/androidTest/java/com/example/paddlex/ExampleInstrumentedTest.java

@@ -0,0 +1,36 @@
+package com.example.paddlex;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+
+import com.baidu.paddlex.config.ConfigParser;
+
+import org.json.JSONException;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+    @Test
+    public void useAppContext() throws IOException, JSONException {
+        // Context of the app under test.
+        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        AssetManager ass =  appContext.getAssets();
+    }
+}

+ 1 - 0
deploy/lite/android/sdk/src/main/AndroidManifest.xml

@@ -0,0 +1 @@
+<manifest package="com.example.paddlex" />

+ 409 - 0
deploy/lite/android/sdk/src/main/java/com/baidu/paddlex/Predictor.java

@@ -0,0 +1,409 @@
+// Copyright (c) 2020 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.
+
+package com.baidu.paddlex;
+import android.util.Log;
+import com.baidu.paddle.lite.MobileConfig;
+import com.baidu.paddle.lite.PaddlePredictor;
+import com.baidu.paddle.lite.PowerMode;
+import com.baidu.paddle.lite.Tensor;
+import com.baidu.paddlex.config.ConfigParser;
+import com.baidu.paddlex.postprocess.ClsResult;
+import com.baidu.paddlex.postprocess.DetResult;
+import com.baidu.paddlex.postprocess.Result;
+import com.baidu.paddlex.postprocess.SegResult;
+import com.baidu.paddlex.preprocess.ImageBlob;
+import com.baidu.paddlex.preprocess.Transforms;
+import java.util.Date;
+import org.opencv.core.Mat;
+
+public class Predictor {
+    private static final String TAG = Predictor.class.getSimpleName();
+    protected boolean isLoaded = false;
+    protected int warmupIterNum = 0;
+    protected int inferIterNum = 1;
+    protected int cpuThreadNum = 1;
+    protected String cpuPowerMode = "LITE_POWER_HIGH";
+    protected String modelPath = "";
+    protected String modelName = "";
+    protected float inferenceTime = 0;
+    protected float preprocessTime = 0;
+    protected float postprocessTime = 0;
+    protected PaddlePredictor paddlePredictor = null;
+    protected ImageBlob imageBlob = new ImageBlob();
+    protected Transforms transforms = new Transforms();
+    protected ConfigParser configParser = new ConfigParser();
+    protected Mat inputMat;
+    protected Result result;
+
+    public Predictor() {
+        super();
+    }
+
+    public boolean init(String modelPath, int cpuThreadNum, String cpuPowerMode) {
+        if (configParser.getModelType().equalsIgnoreCase("classifier")) {
+            result = new ClsResult();
+        } else if (configParser.getModelType().equalsIgnoreCase("detector")) {
+            result = new DetResult();
+        } else if (configParser.getModelType().equalsIgnoreCase("segmenter")) {
+            result = new SegResult();
+        } else {
+            Log.i(TAG, "model type: " + configParser.getModelType() + " is not support! Only support: 'classifier' or 'detector' or 'segmenter'");
+        }
+        isLoaded = loadModel(modelPath, cpuThreadNum, cpuPowerMode);
+        return isLoaded;
+    }
+
+    public boolean init(ConfigParser configParser) {
+        this.configParser = configParser;
+        init(configParser.getModelPath(), configParser.getCpuThreadNum(), configParser.getCpuPowerMode());
+        transforms.loadConfig(configParser.getTransformsList(), configParser.getTransformsMode());
+        if (!isLoaded()) {
+            return false;
+        }
+        Log.i(TAG, configParser.toString());
+        return isLoaded;
+    }
+
+    public boolean predict() {
+        this.imageBlob.clear();
+        this.imageBlob = transforms.run(inputMat, imageBlob);
+        if (configParser.getModelType().equalsIgnoreCase("classifier")) {
+            runModel((ClsResult) result);
+        } else if (configParser.getModelType().equalsIgnoreCase("detector")) {
+            runModel((DetResult) result);
+        } else if (configParser.getModelType().equalsIgnoreCase("segmenter")) {
+            runModel((SegResult) result);
+        }
+        return true;
+    }
+
+    private boolean runModel(DetResult detReult) {
+        // set input shape & data
+        Tensor imTensor = getInput(0);
+        imTensor.resize(imageBlob.getNewImageSize());
+        imTensor.setData(imageBlob.getImageData());
+        if (configParser.getModel().equalsIgnoreCase("YOLOv3")) {
+            Tensor imSizeTensor = getInput(1);
+            long[] imSize = {1, 2};
+            imSizeTensor.resize(imSize);
+            imSizeTensor.setData(new int[]{(int) imageBlob.getOriImageSize()[2], (int) imageBlob.getOriImageSize()[3]});
+        } else if (configParser.getModel().equalsIgnoreCase("FasterRCNN")) {
+            Tensor imInfoTensor = getInput(1);
+            long[] imInfo = {1, 3};
+            imInfoTensor.resize(imInfo);
+            imInfoTensor.setData(new float[]{imageBlob.getNewImageSize()[2], imageBlob.getNewImageSize()[3], imageBlob.getScale()});
+
+            Tensor imShapeTensor = getInput(2);
+            long[] imShape = {1, 3};
+            imShapeTensor.resize(imShape);
+            imShapeTensor.setData(new float[]{imageBlob.getOriImageSize()[2], imageBlob.getOriImageSize()[3], 1});
+        }
+        // run model
+        runModel();
+        // Fetch output tensor
+        Tensor outputTensor = getOutput(0);
+        float[] output = outputTensor.getFloatData();
+        long[] outputShape = outputTensor.shape();
+        long outputSize = 1;
+        for (long s : outputShape) {
+            outputSize *= s;
+        }
+        int num_boxes = (int) (outputSize / 6);
+        for (int i = 0; i < num_boxes; i++) {
+            DetResult.Box box = detReult.new Box();
+            box.setCategoryId((int) output[i * 6]);
+            box.setCategory(configParser.getLabeList().get(box.getCategoryId()));
+            box.setScore(output[i * 6 + 1]);
+            float xmin = output[i * 6 + 2];
+            float ymin = output[i * 6 + 3];
+            float xmax = output[i * 6 + 4];
+            float ymax = output[i * 6 + 5];
+            box.setCoordinate(new float[]{xmin, ymin, xmax, ymax});
+            detReult.getBoxes().add(box);
+        }
+        return true;
+    }
+
+    private boolean runModel(SegResult segReult) {
+        // set input shape & data
+        Tensor imTensor = getInput(0);
+        imTensor.resize(imageBlob.getNewImageSize());
+        imTensor.setData(imageBlob.getImageData());
+        // run model
+        runModel();
+        Tensor labelTensor = getOutput(0);
+        // Fetch output tensor
+        long[] labelData = labelTensor.getLongData();
+        segReult.getMask().setLabelShape(labelTensor.shape());
+        long labelSize = 1;
+        for (long s : segReult.getMask().getLabelShape()) {
+            labelSize *= s;
+        }
+        segReult.getMask().setLabelData(labelData);
+
+        Tensor scoreTensor = getOutput(1);
+        float[] scoreData = scoreTensor.getFloatData();
+        segReult.getMask().setScoreShape(scoreTensor.shape());
+        segReult.getMask().setScoreData(scoreData);
+        return true;
+    }
+
+    private boolean runModel(ClsResult clsReult) {
+        // set input shape & data
+        Tensor imTensor = getInput(0);
+        imTensor.resize(imageBlob.getNewImageSize());
+        imTensor.setData(imageBlob.getImageData());
+        // run model
+        runModel();
+        // Fetch output tensor
+        Tensor outputTensor = getOutput(0);
+        long[] outputShape = outputTensor.shape();
+        long outputSize = 1;
+        for (long s : outputShape) {
+            outputSize *= s;
+        }
+        int max_index = 0; // Top3 indices
+        float max_score = 0; // Top3 scores
+        for (int i = 0; i < outputSize; i++) {
+            float tmp = outputTensor.getFloatData()[i];
+            if (tmp > max_score) {
+                max_index = i;
+                max_score = tmp;
+            }
+        }
+        clsReult.setCategoryId(max_index);
+        clsReult.setCategory(configParser.getLabeList().get(max_index));
+        clsReult.setScore(max_score);
+        return true;
+    }
+
+    private boolean loadModel(String modelPath, int cpuThreadNum, String cpuPowerMode) {
+        // release model if exists
+        releaseModel();
+        // load model
+        if (modelPath.isEmpty()) {
+            return false;
+        }
+        MobileConfig config = new MobileConfig();
+        config.setModelFromFile(modelPath);
+        config.setThreads(cpuThreadNum);
+        if (cpuPowerMode.equalsIgnoreCase("LITE_POWER_HIGH")) {
+            config.setPowerMode(PowerMode.LITE_POWER_HIGH);
+        } else if (cpuPowerMode.equalsIgnoreCase("LITE_POWER_LOW")) {
+            config.setPowerMode(PowerMode.LITE_POWER_LOW);
+        } else if (cpuPowerMode.equalsIgnoreCase("LITE_POWER_FULL")) {
+            config.setPowerMode(PowerMode.LITE_POWER_FULL);
+        } else if (cpuPowerMode.equalsIgnoreCase("LITE_POWER_NO_BIND")) {
+            config.setPowerMode(PowerMode.LITE_POWER_NO_BIND);
+        } else if (cpuPowerMode.equalsIgnoreCase("LITE_POWER_RAND_HIGH")) {
+            config.setPowerMode(PowerMode.LITE_POWER_RAND_HIGH);
+        } else if (cpuPowerMode.equalsIgnoreCase("LITE_POWER_RAND_LOW")) {
+            config.setPowerMode(PowerMode.LITE_POWER_RAND_LOW);
+        } else {
+            Log.e(TAG, "unknown cpu power mode!");
+            return false;
+        }
+        paddlePredictor = PaddlePredictor.createPaddlePredictor(config);
+        this.cpuThreadNum = cpuThreadNum;
+        this.cpuPowerMode = cpuPowerMode;
+        this.modelPath = modelPath;
+        this.modelName = configParser.getModel();
+        return true;
+    }
+
+    private boolean runModel() {
+        if (!isLoaded()) {
+            return false;
+        }
+        // warm up
+        for (int i = 0; i < warmupIterNum; i++) {
+            paddlePredictor.run();
+        }
+        Date start = new Date();
+        // inference
+        for (int i = 0; i < inferIterNum; i++) {
+            paddlePredictor.run();
+        }
+        Date end = new Date();
+        inferenceTime = (end.getTime() - start.getTime()) / (float) inferIterNum;
+        return true;
+    }
+
+    public void releaseModel() {
+        paddlePredictor = null;
+        isLoaded = false;
+        cpuThreadNum = 1;
+        cpuPowerMode = "LITE_POWER_HIGH";
+        modelPath = "";
+        modelName = "";
+    }
+
+    public boolean isLoaded() {
+        return paddlePredictor != null && isLoaded;
+    }
+
+    public void setLoaded(boolean loaded) {
+        isLoaded = loaded;
+    }
+
+    public int getWarmupIterNum() {
+        return warmupIterNum;
+    }
+
+    public void setWarmupIterNum(int warmupIterNum) {
+        this.warmupIterNum = warmupIterNum;
+    }
+
+    public int getInferIterNum() {
+        return inferIterNum;
+    }
+
+    public void setInferIterNum(int inferIterNum) {
+        this.inferIterNum = inferIterNum;
+    }
+
+    public float getInferenceTime() {
+        return inferenceTime;
+    }
+
+    public void setInferenceTime(float inferenceTime) {
+        this.inferenceTime = inferenceTime;
+    }
+
+    public int getCpuThreadNum() {
+        return cpuThreadNum;
+    }
+
+    public void setCpuThreadNum(int cpuThreadNum) {
+        this.cpuThreadNum = cpuThreadNum;
+    }
+
+    public String getCpuPowerMode() {
+        return cpuPowerMode;
+    }
+
+    public void setCpuPowerMode(String cpuPowerMode) {
+        this.cpuPowerMode = cpuPowerMode;
+    }
+
+    public String getModelPath() {
+        return modelPath;
+    }
+
+    public void setModelPath(String modelPath) {
+        this.modelPath = modelPath;
+    }
+
+    public String getModelName() {
+        return modelName;
+    }
+
+    public void setModelName(String modelName) {
+        this.modelName = modelName;
+    }
+
+    public Result getResult() {
+        return result;
+    }
+
+    public void setResult(Result result) {
+        this.result = result;
+    }
+
+    public PaddlePredictor getPaddlePredictor() {
+        return paddlePredictor;
+    }
+
+    public void setPaddlePredictor(PaddlePredictor paddlePredictor) {
+        this.paddlePredictor = paddlePredictor;
+    }
+
+    public float getPreprocessTime() {
+        return preprocessTime;
+    }
+
+    public void setPreprocessTime(float preprocessTime) {
+        this.preprocessTime = preprocessTime;
+    }
+
+    public float getPostprocessTime() {
+        return postprocessTime;
+    }
+
+    public void setPostprocessTime(float postprocessTime) {
+        this.postprocessTime = postprocessTime;
+    }
+
+    public void setConfigParser(ConfigParser configParser) {
+        this.configParser = configParser;
+    }
+
+    public Mat getInputMat() {
+        return inputMat;
+    }
+
+    public void setInputMat(Mat inputMat) {
+        Mat copyMat = new Mat();
+        inputMat.copyTo(copyMat);
+        this.inputMat = copyMat;
+    }
+
+    public DetResult getDetResult() {
+        if (result.getType() != "det") {
+            Log.e(TAG, "this model_type is not detector");
+            return null;
+        }
+        return (DetResult) result;
+    }
+
+    public SegResult getSegResult() {
+        if (result.getType() != "seg") {
+            Log.e(TAG, "this model_type is not segmeter");
+            return null;
+        }
+        return (SegResult) result;
+    }
+
+    public ClsResult getClsResult() {
+        if (result.getType() != "cls") {
+            Log.e(TAG, "this model_type is not classifier");
+            return null;
+        }
+        return (ClsResult) result;
+    }
+
+    public ImageBlob getImageBlob() {
+        return imageBlob;
+    }
+
+    public void setImageBlob(ImageBlob imageBlob) {
+        this.imageBlob = imageBlob;
+    }
+
+    public Tensor getInput(int idx) {
+        if (!isLoaded()) {
+            return null;
+        }
+        return paddlePredictor.getInput(idx);
+    }
+
+    public Tensor getOutput(int idx) {
+        if (!isLoaded()) {
+            return null;
+        }
+        return paddlePredictor.getOutput(idx);
+    }
+
+}

+ 132 - 0
deploy/lite/android/sdk/src/main/java/com/baidu/paddlex/Utils.java

@@ -0,0 +1,132 @@
+// Copyright (c) 2020 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.
+
+package com.baidu.paddlex;
+
+import android.content.Context;
+import android.os.Environment;
+
+import org.opencv.android.OpenCVLoader;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+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;
+                copyFileFromAssets(appCtx, srcSubPath, dstSubPath);
+                if (new File(srcSubPath).isDirectory()) {
+                    copyDirectoryFromAssets(appCtx, srcSubPath, dstSubPath);
+                } else {
+                    copyFileFromAssets(appCtx, srcSubPath, dstSubPath);
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public static String getFileNameFromString(String srcDir) {
+        if (srcDir.isEmpty()) {
+            return null;
+        }
+        try {
+            String fileName = srcDir.substring(srcDir.lastIndexOf("/") + 1);
+            return fileName;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    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 boolean isSupportedNPU() {
+        String hardware = android.os.Build.HARDWARE;
+        return hardware.equalsIgnoreCase("kirin810") || hardware.equalsIgnoreCase("kirin990");
+    }
+
+    public static boolean initialOpencv() {
+        if (!OpenCVLoader.initDebug()) {
+            return false;
+        }
+        return true;
+    }
+
+}

+ 162 - 0
deploy/lite/android/sdk/src/main/java/com/baidu/paddlex/config/ConfigParser.java

@@ -0,0 +1,162 @@
+// Copyright (c) 2020 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.
+
+package com.baidu.paddlex.config;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+
+import org.yaml.snakeyaml.Yaml;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class ConfigParser {
+    protected String model = "";
+    protected List<String> labeList = new ArrayList<>();
+    protected int numClasses = 0;
+    protected String modelType = "";
+    protected String transformsMode = "RGB";
+    protected List transformsList = new ArrayList();
+    protected String modelPath = "";
+    protected int cpuThreadNum = 1;
+    protected String cpuPowerMode = "";
+    protected String yamlPath = "";
+
+    public void init(String modelPath, String yamlPath, int cpuThreadNum,
+                     String cpuPowerMode) throws IOException {
+
+        this.modelPath = modelPath;
+        this.cpuThreadNum = cpuThreadNum;
+        this.cpuPowerMode = cpuPowerMode;
+        this.yamlPath = yamlPath;
+        InputStream ymlStream = new FileInputStream(new File(yamlPath));
+        Yaml yml = new Yaml();
+        HashMap yml_map = (HashMap) yml.load(ymlStream);
+        model = (String) yml_map.get("Model");
+        if (yml_map.containsKey("TransformsMode")) {
+            transformsMode = (String) yml_map.get("TransformsMode");
+        }
+        HashMap _Attributes = (HashMap) yml_map.get("_Attributes");
+        // parser label_list
+        labeList = (List<String>) _Attributes.get("labels");
+        numClasses = (int) _Attributes.get("num_classes");
+        // parser model_type(classifier, segmenter, detector)
+        modelType = (String) _Attributes.get("model_type");
+        // parser Transforms
+        transformsList = (List) yml_map.get("Transforms");
+
+    }
+
+    @Override
+    public String toString() {
+        return "ConfigParser{" +
+                "model='" + model + '\'' +
+                ", labeList=" + labeList +
+                ", numClasses=" + numClasses +
+                ", modelType='" + modelType + '\'' +
+                ", transformsMode='" + transformsMode + '\'' +
+                ", transformsList=" + transformsList +
+                ", modelPath='" + modelPath + '\'' +
+                ", cpuThreadNum=" + cpuThreadNum +
+                ", cpuPowerMode='" + cpuPowerMode + '\'' +
+                ", yamlPath='" + yamlPath + '\'' +
+                '}';
+    }
+
+    public int getNumClasses() {
+        return numClasses;
+    }
+
+    public void setNumClasses(int numClasses) {
+        this.numClasses = numClasses;
+    }
+
+    public List<String> getLabeList() {
+        return labeList;
+    }
+
+    public void setLabeList(List<String> labeList) {
+        this.labeList = labeList;
+    }
+
+    public String getModelType() {
+        return modelType;
+    }
+
+    public void setModelType(String modelType) {
+        this.modelType = modelType;
+    }
+
+    public List getTransformsList() {
+        return transformsList;
+    }
+
+    public void setTransformsList(List transformsList) {
+        this.transformsList = transformsList;
+    }
+
+    public String getModel() {
+        return model;
+    }
+
+    public void setModel(String model) {
+        this.model = model;
+    }
+
+    public String getTransformsMode() {
+        return transformsMode;
+    }
+
+    public void setTransformsMode(String transformsMode) {
+        this.transformsMode = transformsMode;
+    }
+
+    public String getModelPath() {
+        return modelPath;
+    }
+
+    public void setModelPath(String modelPath) {
+        this.modelPath = modelPath;
+    }
+
+    public int getCpuThreadNum() {
+        return cpuThreadNum;
+    }
+
+    public void setCpuThreadNum(int cpuThreadNum) {
+        this.cpuThreadNum = cpuThreadNum;
+    }
+
+    public String getCpuPowerMode() {
+        return cpuPowerMode;
+    }
+
+    public void setCpuPowerMode(String cpuPowerMode) {
+        this.cpuPowerMode = cpuPowerMode;
+    }
+
+    public String getYamlPath() {
+        return yamlPath;
+    }
+
+    public void setYamlPath(String yamlPath) {
+        this.yamlPath = yamlPath;
+    }
+}

+ 52 - 0
deploy/lite/android/sdk/src/main/java/com/baidu/paddlex/postprocess/ClsResult.java

@@ -0,0 +1,52 @@
+// Copyright (c) 2020 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.
+
+package com.baidu.paddlex.postprocess;
+
+public class ClsResult extends Result {
+    static String type = "cls";
+    protected int categoryId;
+    protected String category;
+    protected float score;
+
+    public int getCategoryId() {
+        return categoryId;
+    }
+
+    public void setCategoryId(int categoryId) {
+        this.categoryId = categoryId;
+    }
+
+    public String getCategory() {
+        return category;
+    }
+
+    public void setCategory(String category) {
+        this.category = category;
+    }
+
+    public double getScore() {
+        return score;
+    }
+
+    public void setScore(float score) {
+        this.score = score;
+    }
+
+    @Override
+    public String getType() {
+        return type;
+    }
+
+}

+ 76 - 0
deploy/lite/android/sdk/src/main/java/com/baidu/paddlex/postprocess/DetResult.java

@@ -0,0 +1,76 @@
+// Copyright (c) 2020 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.
+
+package com.baidu.paddlex.postprocess;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DetResult extends Result {
+    static String type = "det";
+    protected List<Box> boxes = new ArrayList<Box>();
+
+    public List<Box> getBoxes() {
+        return boxes;
+    }
+
+    public void setBoxes(List<Box> boxes) {
+        this.boxes = boxes;
+    }
+
+    @Override
+    public String getType() {
+        return type;
+    }
+
+    public class Box {
+        protected int categoryId;
+        protected String category;
+        protected float score;
+        protected float[] coordinate = new float[4];
+
+        public int getCategoryId() {
+            return categoryId;
+        }
+
+        public void setCategoryId(int category_id) {
+            this.categoryId = category_id;
+        }
+
+        public String getCategory() {
+            return category;
+        }
+
+        public void setCategory(String category) {
+            this.category = category;
+        }
+
+        public float getScore() {
+            return score;
+        }
+
+        public void setScore(float score) {
+            this.score = score;
+        }
+
+        public float[] getCoordinate() {
+            return coordinate;
+        }
+
+        public void setCoordinate(float[] coordinate) {
+            this.coordinate = coordinate;
+        }
+    }
+
+}

+ 23 - 0
deploy/lite/android/sdk/src/main/java/com/baidu/paddlex/postprocess/Result.java

@@ -0,0 +1,23 @@
+// Copyright (c) 2020 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.
+
+package com.baidu.paddlex.postprocess;
+
+public class Result {
+    static String type = "base";
+
+    public String getType() {
+        return type;
+    }
+}

+ 72 - 0
deploy/lite/android/sdk/src/main/java/com/baidu/paddlex/postprocess/SegResult.java

@@ -0,0 +1,72 @@
+// Copyright (c) 2020 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.
+
+package com.baidu.paddlex.postprocess;
+
+public class SegResult extends Result {
+    static String type = "seg";
+    protected Mask mask = new Mask();
+
+    public Mask getMask() {
+        return mask;
+    }
+
+    public void setMask(Mask mask) {
+        this.mask = mask;
+    }
+
+    @Override
+    public String getType() {
+        return type;
+    }
+
+    public class Mask {
+        protected float[] scoreData;
+        protected long[] labelData;
+        protected long[] labelShape = new long[4];
+        protected long[] scoreShape = new long[4];
+
+        public float[] getScoreData() {
+            return scoreData;
+        }
+
+        public void setScoreData(float[] score_data) {
+            this.scoreData = score_data;
+        }
+
+        public long[] getLabelData() {
+            return labelData;
+        }
+
+        public void setLabelData(long[] label_data) {
+            this.labelData = label_data;
+        }
+
+        public long[] getLabelShape() {
+            return labelShape;
+        }
+
+        public void setLabelShape(long[] labelShape) {
+            this.labelShape = labelShape;
+        }
+
+        public long[] getScoreShape() {
+            return scoreShape;
+        }
+
+        public void setScoreShape(long[] scoreShape) {
+            this.scoreShape = scoreShape;
+        }
+    }
+}

+ 86 - 0
deploy/lite/android/sdk/src/main/java/com/baidu/paddlex/preprocess/ImageBlob.java

@@ -0,0 +1,86 @@
+// Copyright (c) 2020 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.
+
+package com.baidu.paddlex.preprocess;
+
+import java.util.LinkedHashMap;
+
+public class ImageBlob {
+    // Original image height and width
+    private long[] oriImageSize = new long[]{1, 3, -1, -1};
+    // Newest image height and width after process
+    private long[] newImageSize = new long[]{1, 3, -1, -1};
+    // Reshape order, Image height and width before resize
+    private LinkedHashMap<String, int[]> reshapeInfo = new LinkedHashMap<String, int[]>();
+    // Resize scale
+    private float scale = 1;
+    // Buffer for image data after preprocessing
+    private float[] imageData;
+
+    public void clear() {
+        oriImageSize = new long[]{1, 3, -1, -1};
+        newImageSize = new long[]{1, 3, -1, -1};
+        reshapeInfo.clear();
+        imageData = null;
+    }
+
+    public long[] getOriImageSize() {
+        return oriImageSize;
+    }
+
+    public void setOriImageSize(long[] oriImageSize) {
+        this.oriImageSize = oriImageSize;
+    }
+
+    public void setOriImageSize(long dim, int idx) {
+        this.oriImageSize[idx] = dim;
+    }
+
+    public long[] getNewImageSize() {
+        return newImageSize;
+    }
+
+    public void setNewImageSize(long[] newImageSize) {
+        this.newImageSize = newImageSize;
+    }
+
+    public void setNewImageSize(long dim, int idx) {
+        this.newImageSize[idx] = dim;
+    }
+
+
+    public LinkedHashMap<String, int[]> getReshapeInfo() {
+        return reshapeInfo;
+    }
+
+    public void setReshapeInfo(LinkedHashMap<String, int[]> reshapeInfo) {
+        this.reshapeInfo = reshapeInfo;
+    }
+
+    public float getScale() {
+        return scale;
+    }
+
+    public void setScale(float scale) {
+        this.scale = scale;
+    }
+
+    public float[] getImageData() {
+        return imageData;
+    }
+
+    public void setImageData(float[] imageData) {
+        this.imageData = imageData;
+    }
+}

+ 286 - 0
deploy/lite/android/sdk/src/main/java/com/baidu/paddlex/preprocess/Transforms.java

@@ -0,0 +1,286 @@
+// Copyright (c) 2020 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.
+
+package com.baidu.paddlex.preprocess;
+import android.util.Log;
+import org.opencv.android.OpenCVLoader;
+import org.opencv.core.Core;
+import org.opencv.core.CvType;
+import org.opencv.core.Mat;
+import org.opencv.core.Rect;
+import org.opencv.core.Scalar;
+import org.opencv.core.Size;
+import org.opencv.imgproc.Imgproc;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class Transforms {
+    private static final String TAG = Transforms.class.getSimpleName();
+    private List<transformOp> transformOps = new ArrayList<transformOp>();
+    private String transformsMode = "RGB";
+	private HashMap<String, Integer> interpMap = new HashMap<String, Integer>(){{
+        put("LINEAR", Imgproc.INTER_LINEAR);
+        put("NEAREST", Imgproc.INTER_NEAREST);
+        put("AREA", Imgproc.INTER_AREA);
+        put("CUBIC", Imgproc.INTER_CUBIC);
+        put("LANCZOS4", Imgproc.INTER_LANCZOS4);
+        }
+    };
+
+    public void loadConfig(List transforms_list, String transformsMode) {
+        if (!OpenCVLoader.initDebug()) {
+            Log.e(TAG,"OpenCV Loadding failed.");
+        }
+        this.transformsMode = transformsMode;
+        for (int i = 0; i < transforms_list.size(); i++) {
+            HashMap transform_op = (HashMap) (transforms_list.get(i));
+            if (transform_op.containsKey("ResizeByShort")) {
+                HashMap info = (HashMap) transform_op.get("ResizeByShort");
+                ResizeByShort resizeByShort = new ResizeByShort();
+                resizeByShort.max_size = (int)info.get("max_size");
+                resizeByShort.short_size = (int)info.get("short_size");
+                if (info.containsKey("interp")) {
+                    resizeByShort.interp = (String) info.get("interp");
+                }
+                transformOps.add(resizeByShort);
+            } else if (transform_op.containsKey("ResizeByLong")) {
+                HashMap info = (HashMap) transform_op.get("ResizeByLong");
+                ResizeByLong resizeByLong = new ResizeByLong();
+                resizeByLong.long_size = (int)info.get("long_size");
+                if (info.containsKey("interp")) {
+                    resizeByLong.interp = (String) info.get("interp");
+                }
+                transformOps.add(resizeByLong);
+
+            } else if (transform_op.containsKey("CenterCrop")) {
+                HashMap info = (HashMap) transform_op.get("CenterCrop");
+                CenterCrop centerCrop = new CenterCrop();
+                if (info.get("crop_size") instanceof Integer) {
+                    centerCrop.cropHeight = (int) info.get("crop_size");
+                    centerCrop.cropWidth = (int) info.get("crop_size");
+                } else {
+                    centerCrop.cropWidth = ((List<Integer>) info.get("crop_size")).get(0);
+                    centerCrop.cropHeight = ((List<Integer>) info.get("crop_size")).get(1);
+                }
+                transformOps.add(centerCrop);
+            } else if (transform_op.containsKey("Normalize")) {
+                HashMap<String, List<Float>> info = (HashMap<String, List<Float>>) transform_op.get("Normalize");
+                Normalize normalize = new Normalize();
+                normalize.mean = info.get("mean").toArray(new Double[info.get("mean").size()]);
+                normalize.std = info.get("std").toArray(new Double[info.get("std").size()]);
+                transformOps.add(normalize);
+            } else if (transform_op.containsKey("Resize")) {
+                HashMap info = (HashMap) transform_op.get("Resize");
+                Resize resize = new Resize();
+                if (info.get("target_size") instanceof Integer) {
+                    resize.width = (int) info.get("target_size");
+                    resize.height = (int) info.get("target_size");
+                } else {
+                    resize.width = ((List<Integer>) info.get("target_size")).get(0);
+                    resize.height = ((List<Integer>) info.get("target_size")).get(1);
+                }
+                if (info.containsKey("interp")) {
+                    resize.interp = (String) info.get("interp");
+                }
+                transformOps.add(resize);
+            } else if (transform_op.containsKey("Padding")) {
+                HashMap info = (HashMap) transform_op.get("Padding");
+                Padding padding = new Padding();
+                if (info.containsKey("coarsest_stride")) {
+                    padding.coarsest_stride = (int) info.get("coarsest_stride");
+                }
+                if (info.containsKey("target_size")) {
+                    if (info.get("target_size") instanceof Integer) {
+                        padding.width = (int) info.get("target_size");
+                        padding.height = (int) info.get("target_size");
+                    } else {
+                        padding.width = ((List<Integer>) info.get("target_size")).get(0);
+                        padding.height = ((List<Integer>) info.get("target_size")).get(1);
+                    }
+                }
+                transformOps.add(padding);
+            }
+        }
+    }
+
+    public ImageBlob run(Mat inputMat, ImageBlob imageBlob) {
+        imageBlob.setOriImageSize(inputMat.height(),2);
+        imageBlob.setOriImageSize(inputMat.width(),3);
+        imageBlob.setNewImageSize(inputMat.height(),2);
+        imageBlob.setNewImageSize(inputMat.width(),3);
+
+        if(transformsMode.equalsIgnoreCase("RGB")){
+            Imgproc.cvtColor(inputMat, inputMat, Imgproc.COLOR_BGR2RGB);
+        }else if(!transformsMode.equalsIgnoreCase("BGR")){
+            Log.e(TAG, "transformsMode only support RGB or BGR");
+        }
+        inputMat.convertTo(inputMat, CvType.CV_32FC(3));
+
+        for (transformOp op : transformOps) {
+            inputMat = op.run(inputMat, imageBlob);
+        }
+
+        int w = inputMat.width();
+        int h = inputMat.height();
+        int c = inputMat.channels();
+        imageBlob.setImageData(new float[w * h * c]);
+        int[] channelStride = new int[]{w * h, w * h * 2};
+        for (int y = 0; y < h; y++) {
+            for (int x = 0;
+                 x < w; x++) {
+                double[] color = inputMat.get(y, x);
+                imageBlob.getImageData()[y * w + x]  =  (float) (color[0]);
+                imageBlob.getImageData()[y * w + x +  channelStride[0]] = (float) (color[1]);
+                imageBlob.getImageData()[y * w + x +  channelStride[1]] = (float) (color[2]);
+            }
+        }
+        return imageBlob;
+    }
+
+    private class transformOp {
+        public Mat run(Mat inputMat, ImageBlob data) {
+            return inputMat;
+        }
+    }
+
+    private class ResizeByShort extends transformOp {
+        private int max_size;
+        private int short_size;
+        private String interp = "LINEAR";
+
+        public Mat run(Mat inputMat, ImageBlob imageBlob) {
+            int origin_w = inputMat.width();
+            int origin_h = inputMat.height();
+            imageBlob.getReshapeInfo().put("resize", new int[]{origin_w, origin_h});
+            int im_size_max = Math.max(origin_w, origin_h);
+            int im_size_min = Math.min(origin_w, origin_h);
+            float scale = (float) (short_size) / (float) (im_size_min);
+            if (max_size > 0) {
+                if (Math.round(scale * im_size_max) > max_size) {
+                    scale = (float) (max_size) / (float) (im_size_max);
+                }
+            }
+            int width = Math.round(scale * origin_w);
+            int height = Math.round(scale * origin_h);
+            Size sz = new Size(width, height);
+            Imgproc.resize(inputMat, inputMat, sz,0,0, interpMap.get(interp));
+            imageBlob.setNewImageSize(inputMat.height(),2);
+            imageBlob.setNewImageSize(inputMat.width(),3);
+            imageBlob.setScale(scale);
+            return inputMat;
+        }
+    }
+
+    private class ResizeByLong extends transformOp {
+        private int long_size;
+        private String interp = "LINEAR";
+
+        public Mat run(Mat inputMat, ImageBlob imageBlob) {
+            int origin_w = inputMat.width();
+            int origin_h = inputMat.height();
+            imageBlob.getReshapeInfo().put("resize", new int[]{origin_w, origin_h});
+            int im_size_max = Math.max(origin_w, origin_h);
+            float scale = (float) (long_size) / (float) (im_size_max);
+            int width = Math.round(scale * origin_w);
+            int height = Math.round(scale * origin_h);
+            Size sz = new Size(width, height);
+            Imgproc.resize(inputMat, inputMat, sz,0,0, interpMap.get(interp));
+            imageBlob.setNewImageSize(inputMat.height(),2);
+            imageBlob.setNewImageSize(inputMat.width(),3);
+            imageBlob.setScale(scale);
+            return inputMat;
+        }
+    }
+
+    private class CenterCrop extends transformOp {
+        private int cropHeight;
+        private int cropWidth;
+
+        public Mat run(Mat inputMat, ImageBlob imageBlob) {
+            int origin_w = inputMat.width();
+            int origin_h = inputMat.height();
+            if (origin_h < cropHeight || origin_w < cropWidth) {
+                Log.e(TAG, "[CenterCrop] Image size less than crop size");
+            }
+            int offset_x, offset_y;
+            offset_x = (origin_w - cropWidth) / 2;
+            offset_y = (origin_h - cropHeight) / 2;
+            offset_x = Math.max(Math.min(offset_x, origin_w - cropWidth), 0);
+            offset_y = Math.max(Math.min(offset_y, origin_h - cropHeight), 0);
+            Rect crop_roi = new Rect(offset_x, offset_y, cropHeight, cropWidth);
+            inputMat = inputMat.submat(crop_roi);
+            imageBlob.setNewImageSize(inputMat.height(),2);
+            imageBlob.setNewImageSize(inputMat.width(),3);
+            return inputMat;
+        }
+    }
+
+    private class Resize extends transformOp {
+        private int height;
+        private int width;
+        private String interp = "LINEAR";
+
+        public Mat run(Mat inputMat, ImageBlob imageBlob) {
+            int origin_w = inputMat.width();
+            int origin_h = inputMat.height();
+            imageBlob.getReshapeInfo().put("resize", new int[]{origin_w, origin_h});
+            Size sz = new Size(width, height);
+            Imgproc.resize(inputMat, inputMat, sz,0,0,  interpMap.get(interp));
+            imageBlob.setNewImageSize(inputMat.height(),2);
+            imageBlob.setNewImageSize(inputMat.width(),3);
+            return inputMat;
+        }
+    }
+
+    private class Padding extends transformOp {
+        private double width;
+        private double height;
+        private double coarsest_stride;
+
+        public Mat run(Mat inputMat, ImageBlob imageBlob) {
+            int origin_w = inputMat.width();
+            int origin_h = inputMat.height();
+            imageBlob.getReshapeInfo().put("padding", new int[]{origin_w, origin_h});
+            double padding_w = 0;
+            double padding_h = 0;
+            if (width > 1 & height > 1) {
+                padding_w = width;
+                padding_h = height;
+            } else if (coarsest_stride > 1) {
+                padding_h = Math.ceil(origin_h / coarsest_stride) * coarsest_stride;
+                padding_w = Math.ceil(origin_w / coarsest_stride) * coarsest_stride;
+            }
+            imageBlob.setNewImageSize(inputMat.height(),2);
+            imageBlob.setNewImageSize(inputMat.width(),3);
+            Core.copyMakeBorder(inputMat, inputMat, 0, (int)padding_h, 0, (int)padding_w, Core.BORDER_CONSTANT, new Scalar(0));
+            return inputMat;
+        }
+    }
+
+    private class Normalize extends transformOp {
+        private Double[] mean = new Double[3];
+        private Double[] std = new Double[3];
+
+        public Mat run(Mat inputMat, ImageBlob imageBlob) {
+            inputMat.convertTo(inputMat, CvType.CV_32FC(3), 1/255.0);
+            Scalar meanScalar = new Scalar(mean[0], mean[1], mean[2]);
+            Scalar stdScalar = new Scalar(std[0], std[1], std[2]);
+            Core.subtract(inputMat, meanScalar, inputMat);
+            Core.divide(inputMat, stdScalar, inputMat);
+            return inputMat;
+        }
+    }
+}
+

+ 148 - 0
deploy/lite/android/sdk/src/main/java/com/baidu/paddlex/visual/Visualize.java

@@ -0,0 +1,148 @@
+// Copyright (c) 2020 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.
+
+package com.baidu.paddlex.visual;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.util.Log;
+
+import com.baidu.paddlex.postprocess.DetResult;
+import com.baidu.paddlex.postprocess.SegResult;
+import com.baidu.paddlex.preprocess.ImageBlob;
+
+import org.opencv.core.Core;
+import org.opencv.core.CvType;
+import org.opencv.core.Mat;
+import org.opencv.core.Point;
+import org.opencv.core.Rect;
+import org.opencv.core.Scalar;
+import org.opencv.core.Size;
+import org.opencv.imgproc.Imgproc;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+
+public class Visualize {
+    protected static final String TAG = Visualize.class.getSimpleName();
+    protected float detectConfidenceThreshold = (float) 0.5;
+    protected Scalar[] colormap = new Scalar[]{};
+
+    protected void generateColorMap(int num_class) {
+        this.colormap = new Scalar[num_class];
+        this.colormap[0] = new Scalar(0, 0, 0);
+        for (int i = 0; i < num_class; i++) {
+            int j = 0;
+            int lab = i;
+            while (lab > 0) {
+                int r = (((lab >> 0) & 1) << (7 - j));
+                int g = (((lab >> 1) & 1) << (7 - j));
+                int b = (((lab >> 2) & 1) << (7 - j));
+                this.colormap[i] = new Scalar(r, g, b);
+                ++j;
+                lab >>= 3;
+            }
+        }
+    }
+
+    public float getDetectConfidenceThreshold() {
+        return detectConfidenceThreshold;
+    }
+
+    public void setDetectConfidenceThreshold(float detectConfidenceThreshold) {
+        this.detectConfidenceThreshold = detectConfidenceThreshold;
+    }
+
+    public Scalar[] getColormap() {
+        return colormap;
+    }
+
+    public void setColormap(Scalar[] colormap) {
+        this.colormap = colormap;
+    }
+
+    public void init(int num_class) {
+        generateColorMap(num_class);
+    }
+
+    public Mat draw(DetResult result, Mat visualizeMat) {
+        Paint rectPaint = new Paint();
+        rectPaint.setStyle(Paint.Style.STROKE);
+        rectPaint.setStrokeWidth(2);
+        Paint txtPaint = new Paint();
+        txtPaint.setTextSize(15);
+        txtPaint.setAntiAlias(true);
+        for (DetResult.Box box : result.getBoxes()) {
+            if (box.getScore() < detectConfidenceThreshold) {
+                continue;
+            }
+
+            String text = box.getCategory() + ":" + String.valueOf(box.getScore()).substring(0, 4);
+            Scalar roiColor = colormap[box.getCategoryId()];
+            double font_scale = 0.5;
+            int thickness = 1;
+            int font_face = Core.FONT_HERSHEY_SIMPLEX;
+
+            Point roiXyMin = new Point(box.getCoordinate()[0],box.getCoordinate()[1]);
+            Point roiXyMax = new Point(box.getCoordinate()[2],box.getCoordinate()[3]);
+            Size text_size = Imgproc.getTextSize(text, font_face,font_scale, thickness,null);
+            Imgproc.rectangle(visualizeMat, roiXyMin, roiXyMax, roiColor,2);
+
+            Point textXyMin =  new Point(box.getCoordinate()[0],box.getCoordinate()[1]-text_size.height);
+            Point textXyMax = new Point(box.getCoordinate()[0]+text_size.width,box.getCoordinate()[1]);
+            Imgproc.rectangle(visualizeMat,textXyMin, textXyMax, roiColor,-1);
+            Imgproc.putText(visualizeMat,
+                    text,
+                    roiXyMin,
+                    font_face,
+                    font_scale,
+                    new Scalar(255, 255, 255));
+        }
+        return visualizeMat;
+    }
+
+    public Mat draw(SegResult result, Mat visualizeMat, ImageBlob imageBlob, int cutoutClass) {
+        int new_h = (int)imageBlob.getNewImageSize()[2];
+        int new_w = (int)imageBlob.getNewImageSize()[3];
+        Mat mask = new Mat(new_h, new_w, CvType.CV_8UC(1));
+
+        for  (int h = 0; h < new_h; h++) {
+            for  (int w = 0; w < new_w; w++){
+                mask.put(h , w, (1-result.getMask().getScoreData()[cutoutClass + h * new_h + w]) * 255);
+            }
+        }
+        ListIterator<Map.Entry<String, int[]>> reverseReshapeInfo = new ArrayList<Map.Entry<String, int[]>>(imageBlob.getReshapeInfo().entrySet()).listIterator(imageBlob.getReshapeInfo().size());
+        while (reverseReshapeInfo.hasPrevious()) {
+            Map.Entry<String, int[]> entry = reverseReshapeInfo.previous();
+            if (entry.getKey().equalsIgnoreCase("padding")) {
+                Rect crop_roi = new Rect(0, 0, entry.getValue()[0], entry.getValue()[1]);
+                mask = mask.submat(crop_roi);
+            } else if (entry.getKey().equalsIgnoreCase("resize")) {
+                Size sz = new Size(entry.getValue()[0], entry.getValue()[1]);
+                Imgproc.resize(mask, mask, sz,0,0,Imgproc.INTER_LINEAR);
+            }
+            Log.i(TAG, "postprocess operator: " + entry.getKey());
+            Log.i(TAG, "shape:: " + String.valueOf(mask.width()) + ","+ String.valueOf(mask.height()));
+        }
+
+        Mat dst  = new Mat();
+        List<Mat> listMat = Arrays.asList(visualizeMat, mask);
+        Core.merge(listMat, dst);
+
+        return dst;
+    }
+}

+ 3 - 0
deploy/lite/android/sdk/src/main/res/values/strings.xml

@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">PaddleX</string>
+</resources>

+ 17 - 0
deploy/lite/android/sdk/src/test/java/com/example/paddlex/ExampleUnitTest.java

@@ -0,0 +1,17 @@
+package com.example.paddlex;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+public class ExampleUnitTest {
+    @Test
+    public void addition_isCorrect() {
+        assertEquals(4, 2 + 2);
+    }
+}