(env, j_str));
+ }
+ }
+
+ cv::Mat c_bgr;
+ // From ARGB Bitmap to BGR
+ if (!fastdeploy::jni::ARGB888Bitmap2BGR(env, argb8888_bitmap, &c_bgr)) {
+ return JNI_FALSE;
+ }
+ cv::Mat c_vis_im;
+ if (!c_labels.empty()) {
+ c_vis_im = fastdeploy::vision::VisDetection(
+ c_bgr, c_result, c_labels, score_threshold, line_size, font_size);
+ } else {
+ c_vis_im = fastdeploy::vision::VisDetection(
+ c_bgr, c_result, score_threshold, line_size, font_size);
+ }
+ // Rendering to bitmap
+ if (!fastdeploy::jni::BGR2ARGB888Bitmap(env, argb8888_bitmap, c_vis_im)) {
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/FDModelTag.java b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/FDModelTag.java
new file mode 100644
index 000000000..19ee71cd3
--- /dev/null
+++ b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/FDModelTag.java
@@ -0,0 +1,8 @@
+package com.baidu.paddle.fastdeploy;
+
+public enum FDModelTag {
+ UNKNOWN,
+ VISION_DETECTION_PICODET,
+ VISION_DETECTION_PPYOLOE,
+ VISION_CLASSIFICATION_PPCLS
+}
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/FastDeployInitializer.java b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/FastDeployInitializer.java
new file mode 100644
index 000000000..e69ac3036
--- /dev/null
+++ b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/FastDeployInitializer.java
@@ -0,0 +1,22 @@
+package com.baidu.paddle.fastdeploy;
+
+/**
+ * Initializer for FastDeploy. The initialization methods are called by package
+ * classes only. Public users don't have to call them. Public users can get
+ * FastDeploy information constants such as JNI lib name in this class.
+ */
+public class FastDeployInitializer {
+ /** name of C++ JNI lib */
+ public final static String JNI_LIB_NAME = "fastdeploy_jni";
+
+ /**
+ * loads the C++ JNI lib. We only call it in our package, so it shouldn't be
+ * visible to public users.
+ *
+ * @return true if initialize successfully.
+ */
+ public static boolean init() {
+ System.loadLibrary(JNI_LIB_NAME);
+ return true;
+ }
+}
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/LitePowerMode.java b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/LitePowerMode.java
new file mode 100644
index 000000000..4e0330da0
--- /dev/null
+++ b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/LitePowerMode.java
@@ -0,0 +1,10 @@
+package com.baidu.paddle.fastdeploy;
+
+public enum LitePowerMode {
+ LITE_POWER_HIGH,
+ LITE_POWER_LOW,
+ LITE_POWER_FULL,
+ LITE_POWER_NO_BIND,
+ LITE_POWER_RAND_HIGH,
+ LITE_POWER_RAND_LOW
+}
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/RuntimeOption.java b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/RuntimeOption.java
new file mode 100644
index 000000000..471673f59
--- /dev/null
+++ b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/RuntimeOption.java
@@ -0,0 +1,64 @@
+package com.baidu.paddle.fastdeploy;
+
+public class RuntimeOption {
+ public int mCpuThreadNum = 1;
+ public boolean mEnableLiteFp16 = false;
+ public boolean mEnableRecordTimeOfRuntime = false;
+ public LitePowerMode mLitePowerMode = LitePowerMode.LITE_POWER_NO_BIND;
+ public String mLiteOptimizedModelDir = "";
+
+ public RuntimeOption() {
+ mCpuThreadNum = 1;
+ mEnableLiteFp16 = false;
+ mEnableRecordTimeOfRuntime = false;
+ mLitePowerMode = LitePowerMode.LITE_POWER_NO_BIND;
+ mLiteOptimizedModelDir = "";
+ }
+
+ public void enableLiteFp16() {
+ mEnableLiteFp16 = true;
+ }
+
+ public void disableLiteFP16() {
+ mEnableLiteFp16 = false;
+ }
+
+ public void setCpuThreadNum(int threadNum) {
+ mCpuThreadNum = threadNum;
+ }
+
+ public void setLitePowerMode(LitePowerMode mode) {
+ mLitePowerMode = mode;
+ }
+
+ public void setLitePowerMode(String modeStr) {
+ mLitePowerMode = parseLitePowerModeFromString(modeStr);
+ }
+
+ public void setLiteOptimizedModelDir(String modelDir) {
+ mLiteOptimizedModelDir = modelDir;
+ }
+
+ public void enableRecordTimeOfRuntime() {
+ mEnableRecordTimeOfRuntime = true;
+ }
+
+ // Helpers: parse lite power mode from string
+ public static LitePowerMode parseLitePowerModeFromString(String modeStr) {
+ if (modeStr.equalsIgnoreCase("LITE_POWER_HIGH")) {
+ return LitePowerMode.LITE_POWER_HIGH;
+ } else if (modeStr.equalsIgnoreCase("LITE_POWER_LOW")) {
+ return LitePowerMode.LITE_POWER_LOW;
+ } else if (modeStr.equalsIgnoreCase("LITE_POWER_FULL")) {
+ return LitePowerMode.LITE_POWER_FULL;
+ } else if (modeStr.equalsIgnoreCase("LITE_POWER_NO_BIND")) {
+ return LitePowerMode.LITE_POWER_NO_BIND;
+ } else if (modeStr.equalsIgnoreCase("LITE_POWER_RAND_HIGH")) {
+ return LitePowerMode.LITE_POWER_RAND_HIGH;
+ } else if (modeStr.equalsIgnoreCase("LITE_POWER_RAND_LOW")) {
+ return LitePowerMode.LITE_POWER_RAND_LOW;
+ } else {
+ return LitePowerMode.LITE_POWER_NO_BIND;
+ }
+ }
+}
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/common/ActionBarLayout.java b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/common/ActionBarLayout.java
new file mode 100644
index 000000000..3b5d2df91
--- /dev/null
+++ b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/common/ActionBarLayout.java
@@ -0,0 +1,33 @@
+package com.baidu.paddle.fastdeploy.common;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+import android.widget.RelativeLayout;
+
+
+public class ActionBarLayout extends RelativeLayout {
+ private int layoutHeight = 150;
+
+ public ActionBarLayout(Context context) {
+ super(context);
+ }
+
+ public ActionBarLayout(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public ActionBarLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ setMeasuredDimension(width, layoutHeight);
+ setBackgroundColor(Color.BLACK);
+ setAlpha(0.9f);
+ }
+}
\ No newline at end of file
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/common/AppCompatPreferenceActivity.java b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/common/AppCompatPreferenceActivity.java
new file mode 100644
index 000000000..265f75f01
--- /dev/null
+++ b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/common/AppCompatPreferenceActivity.java
@@ -0,0 +1,111 @@
+package com.baidu.paddle.fastdeploy.common;
+
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.Nullable;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatDelegate;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A {@link PreferenceActivity} which implements and proxies the necessary calls
+ * to be used with AppCompat.
+ *
+ * This technique can be used with an {@link android.app.Activity} class, not just
+ * {@link PreferenceActivity}.
+ */
+public abstract class AppCompatPreferenceActivity extends PreferenceActivity {
+ private AppCompatDelegate mDelegate;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ getDelegate().installViewFactory();
+ getDelegate().onCreate(savedInstanceState);
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ super.onPostCreate(savedInstanceState);
+ getDelegate().onPostCreate(savedInstanceState);
+ }
+
+ public ActionBar getSupportActionBar() {
+ return getDelegate().getSupportActionBar();
+ }
+
+ public void setSupportActionBar(@Nullable Toolbar toolbar) {
+ getDelegate().setSupportActionBar(toolbar);
+ }
+
+ @Override
+ public MenuInflater getMenuInflater() {
+ return getDelegate().getMenuInflater();
+ }
+
+ @Override
+ public void setContentView(@LayoutRes int layoutResID) {
+ getDelegate().setContentView(layoutResID);
+ }
+
+ @Override
+ public void setContentView(View view) {
+ getDelegate().setContentView(view);
+ }
+
+ @Override
+ public void setContentView(View view, ViewGroup.LayoutParams params) {
+ getDelegate().setContentView(view, params);
+ }
+
+ @Override
+ public void addContentView(View view, ViewGroup.LayoutParams params) {
+ getDelegate().addContentView(view, params);
+ }
+
+ @Override
+ protected void onPostResume() {
+ super.onPostResume();
+ getDelegate().onPostResume();
+ }
+
+ @Override
+ protected void onTitleChanged(CharSequence title, int color) {
+ super.onTitleChanged(title, color);
+ getDelegate().setTitle(title);
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ getDelegate().onConfigurationChanged(newConfig);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ getDelegate().onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ getDelegate().onDestroy();
+ }
+
+ public void invalidateOptionsMenu() {
+ getDelegate().invalidateOptionsMenu();
+ }
+
+ private AppCompatDelegate getDelegate() {
+ if (mDelegate == null) {
+ mDelegate = AppCompatDelegate.create(this, null);
+ }
+ return mDelegate;
+ }
+}
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/common/CameraSurfaceView.java b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/common/CameraSurfaceView.java
new file mode 100644
index 000000000..dec602b7f
--- /dev/null
+++ b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/common/CameraSurfaceView.java
@@ -0,0 +1,329 @@
+package com.baidu.paddle.fastdeploy.common;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.SurfaceTexture;
+import android.hardware.Camera;
+import android.hardware.Camera.CameraInfo;
+import android.hardware.Camera.Size;
+import android.opengl.GLES11Ext;
+import android.opengl.GLES20;
+import android.opengl.GLSurfaceView;
+import android.opengl.GLSurfaceView.Renderer;
+import android.opengl.GLUtils;
+import android.opengl.Matrix;
+import android.util.AttributeSet;
+import android.util.Log;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.util.List;
+
+public class CameraSurfaceView extends GLSurfaceView implements Renderer,
+ SurfaceTexture.OnFrameAvailableListener {
+ private static final String TAG = CameraSurfaceView.class.getSimpleName();
+
+ public static final int EXPECTED_PREVIEW_WIDTH = 1280;
+ public static final int EXPECTED_PREVIEW_HEIGHT = 720;
+
+
+ protected int numberOfCameras;
+ protected int selectedCameraId;
+ protected boolean disableCamera = false;
+ protected Camera camera;
+
+ protected Context context;
+ protected SurfaceTexture surfaceTexture;
+ protected int surfaceWidth = 0;
+ protected int surfaceHeight = 0;
+ protected int textureWidth = 0;
+ protected int textureHeight = 0;
+
+ // In order to manipulate the camera preview data and render the modified one
+ // to the screen, three textures are created and the data flow is shown as following:
+ // previewdata->camTextureId->fboTexureId->drawTexureId->framebuffer
+ protected int[] fbo = {0};
+ protected int[] camTextureId = {0};
+ protected int[] fboTexureId = {0};
+ protected int[] drawTexureId = {0};
+
+ private final String vss = ""
+ + "attribute vec2 vPosition;\n"
+ + "attribute vec2 vTexCoord;\n" + "varying vec2 texCoord;\n"
+ + "void main() {\n" + " texCoord = vTexCoord;\n"
+ + " gl_Position = vec4 (vPosition.x, vPosition.y, 0.0, 1.0);\n"
+ + "}";
+
+ private final String fssCam2FBO = ""
+ + "#extension GL_OES_EGL_image_external : require\n"
+ + "precision mediump float;\n"
+ + "uniform samplerExternalOES sTexture;\n"
+ + "varying vec2 texCoord;\n"
+ + "void main() {\n"
+ + " gl_FragColor = texture2D(sTexture,texCoord);\n" + "}";
+
+ private final String fssTex2Screen = ""
+ + "precision mediump float;\n"
+ + "uniform sampler2D sTexture;\n"
+ + "varying vec2 texCoord;\n"
+ + "void main() {\n"
+ + " gl_FragColor = texture2D(sTexture,texCoord);\n" + "}";
+
+ private final float[] vertexCoords = {
+ -1, -1,
+ -1, 1,
+ 1, -1,
+ 1, 1};
+ private float[] textureCoords = {
+ 0, 1,
+ 0, 0,
+ 1, 1,
+ 1, 0};
+
+ private FloatBuffer vertexCoordsBuffer;
+ private FloatBuffer textureCoordsBuffer;
+
+ private int progCam2FBO = -1;
+ private int progTex2Screen = -1;
+ private int vcCam2FBO;
+ private int tcCam2FBO;
+ private int vcTex2Screen;
+ private int tcTex2Screen;
+
+ public interface OnTextureChangedListener {
+ boolean onTextureChanged(Bitmap ARGB8888ImageBitmap);
+ }
+
+ private OnTextureChangedListener onTextureChangedListener = null;
+
+ public void setOnTextureChangedListener(OnTextureChangedListener listener) {
+ onTextureChangedListener = listener;
+ }
+
+ public CameraSurfaceView(Context ctx, AttributeSet attrs) {
+ super(ctx, attrs);
+ context = ctx;
+ setEGLContextClientVersion(2);
+ setRenderer(this);
+ setRenderMode(RENDERMODE_WHEN_DIRTY);
+
+ // Find the total number of available cameras and the ID of the default camera
+ numberOfCameras = Camera.getNumberOfCameras();
+ CameraInfo cameraInfo = new CameraInfo();
+ for (int i = 0; i < numberOfCameras; i++) {
+ Camera.getCameraInfo(i, cameraInfo);
+ if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
+ selectedCameraId = i;
+ }
+ }
+ }
+
+ @Override
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ // Create OES texture for storing camera preview data(YUV format)
+ GLES20.glGenTextures(1, camTextureId, 0);
+ GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, camTextureId[0]);
+ GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
+ GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
+ surfaceTexture = new SurfaceTexture(camTextureId[0]);
+ surfaceTexture.setOnFrameAvailableListener(this);
+
+ // Prepare vertex and texture coordinates
+ int bytes = vertexCoords.length * Float.SIZE / Byte.SIZE;
+ vertexCoordsBuffer = ByteBuffer.allocateDirect(bytes).order(ByteOrder.nativeOrder()).asFloatBuffer();
+ textureCoordsBuffer = ByteBuffer.allocateDirect(bytes).order(ByteOrder.nativeOrder()).asFloatBuffer();
+ vertexCoordsBuffer.put(vertexCoords).position(0);
+ textureCoordsBuffer.put(textureCoords).position(0);
+
+ // Create vertex and fragment shaders
+ // camTextureId->fboTexureId
+ progCam2FBO = Utils.createShaderProgram(vss, fssCam2FBO);
+ vcCam2FBO = GLES20.glGetAttribLocation(progCam2FBO, "vPosition");
+ tcCam2FBO = GLES20.glGetAttribLocation(progCam2FBO, "vTexCoord");
+ GLES20.glEnableVertexAttribArray(vcCam2FBO);
+ GLES20.glEnableVertexAttribArray(tcCam2FBO);
+ // fboTexureId/drawTexureId -> screen
+ progTex2Screen = Utils.createShaderProgram(vss, fssTex2Screen);
+ vcTex2Screen = GLES20.glGetAttribLocation(progTex2Screen, "vPosition");
+ tcTex2Screen = GLES20.glGetAttribLocation(progTex2Screen, "vTexCoord");
+ GLES20.glEnableVertexAttribArray(vcTex2Screen);
+ GLES20.glEnableVertexAttribArray(tcTex2Screen);
+ }
+
+ @Override
+ public void onSurfaceChanged(GL10 gl, int width, int height) {
+ surfaceWidth = width;
+ surfaceHeight = height;
+ openCamera();
+ }
+
+ @Override
+ public void onDrawFrame(GL10 gl) {
+ if (surfaceTexture == null) return;
+
+ GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
+ surfaceTexture.updateTexImage();
+ float[] matrix = new float[16];
+ surfaceTexture.getTransformMatrix(matrix);
+
+ // camTextureId->fboTexureId
+ GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fbo[0]);
+ GLES20.glViewport(0, 0, textureWidth, textureHeight);
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+ GLES20.glUseProgram(progCam2FBO);
+ GLES20.glVertexAttribPointer(vcCam2FBO, 2, GLES20.GL_FLOAT, false, 4 * 2, vertexCoordsBuffer);
+ textureCoordsBuffer.clear();
+ textureCoordsBuffer.put(transformTextureCoordinates(textureCoords, matrix));
+ textureCoordsBuffer.position(0);
+ GLES20.glVertexAttribPointer(tcCam2FBO, 2, GLES20.GL_FLOAT, false, 4 * 2, textureCoordsBuffer);
+ GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
+ GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, camTextureId[0]);
+ GLES20.glUniform1i(GLES20.glGetUniformLocation(progCam2FBO, "sTexture"), 0);
+ GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
+ GLES20.glFlush();
+
+ // Check if the draw texture is set
+ int targetTexureId = fboTexureId[0];
+ if (onTextureChangedListener != null) {
+ // Read pixels of FBO to a bitmap
+ ByteBuffer pixelBuffer = ByteBuffer.allocate(textureWidth * textureHeight * 4);
+ GLES20.glReadPixels(0, 0, textureWidth, textureHeight, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, pixelBuffer);
+ Bitmap ARGB8888ImageBitmap = Bitmap.createBitmap(textureWidth, textureHeight, Bitmap.Config.ARGB_8888);
+ ARGB8888ImageBitmap.copyPixelsFromBuffer(pixelBuffer);
+ boolean modified = onTextureChangedListener.onTextureChanged(ARGB8888ImageBitmap);
+ if (modified) {
+ targetTexureId = drawTexureId[0];
+ // Update a bitmap to the GL texture if modified
+ GLES20.glActiveTexture(targetTexureId);
+ // GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, targetTexureId);
+ GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, targetTexureId);
+ GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, ARGB8888ImageBitmap, 0);
+ }
+ ARGB8888ImageBitmap.recycle();
+ }
+
+ // fboTexureId/drawTexureId->Screen
+ GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
+ GLES20.glViewport(0, 0, surfaceWidth, surfaceHeight);
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+ GLES20.glUseProgram(progTex2Screen);
+ GLES20.glVertexAttribPointer(vcTex2Screen, 2, GLES20.GL_FLOAT, false, 4 * 2, vertexCoordsBuffer);
+ textureCoordsBuffer.clear();
+ textureCoordsBuffer.put(textureCoords);
+ textureCoordsBuffer.position(0);
+ GLES20.glVertexAttribPointer(tcTex2Screen, 2, GLES20.GL_FLOAT, false, 4 * 2, textureCoordsBuffer);
+ GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, targetTexureId);
+ GLES20.glUniform1i(GLES20.glGetUniformLocation(progTex2Screen, "sTexture"), 0);
+ GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
+ GLES20.glFlush();
+ }
+
+ private float[] transformTextureCoordinates(float[] coords, float[] matrix) {
+ float[] result = new float[coords.length];
+ float[] vt = new float[4];
+ for (int i = 0; i < coords.length; i += 2) {
+ float[] v = {coords[i], coords[i + 1], 0, 1};
+ Matrix.multiplyMV(vt, 0, matrix, 0, v, 0);
+ result[i] = vt[0];
+ result[i + 1] = vt[1];
+ }
+ return result;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ releaseCamera();
+ }
+
+ @Override
+ public void onFrameAvailable(SurfaceTexture surfaceTexture) {
+ requestRender();
+ }
+
+ public void disableCamera() {
+ disableCamera = true;
+ }
+
+ public void switchCamera() {
+ releaseCamera();
+ selectedCameraId = (selectedCameraId + 1) % numberOfCameras;
+ openCamera();
+ }
+
+ public void openCamera() {
+ if (disableCamera) return;
+ camera = Camera.open(selectedCameraId);
+ List supportedPreviewSizes = camera.getParameters().getSupportedPreviewSizes();
+ Size previewSize = Utils.getOptimalPreviewSize(supportedPreviewSizes, EXPECTED_PREVIEW_WIDTH,
+ EXPECTED_PREVIEW_HEIGHT);
+ Camera.Parameters parameters = camera.getParameters();
+ parameters.setPreviewSize(previewSize.width, previewSize.height);
+ if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
+ parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
+ }
+ camera.setParameters(parameters);
+ int degree = Utils.getCameraDisplayOrientation(context, selectedCameraId);
+ camera.setDisplayOrientation(degree);
+ boolean rotate = degree == 90 || degree == 270;
+ textureWidth = rotate ? previewSize.height : previewSize.width;
+ textureHeight = rotate ? previewSize.width : previewSize.height;
+ // Destroy FBO and draw textures
+ GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
+ GLES20.glDeleteFramebuffers(1, fbo, 0);
+ GLES20.glDeleteTextures(1, drawTexureId, 0);
+ GLES20.glDeleteTextures(1, fboTexureId, 0);
+ // Normal texture for storing modified camera preview data(RGBA format)
+ GLES20.glGenTextures(1, drawTexureId, 0);
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, drawTexureId[0]);
+ GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, textureWidth, textureHeight, 0,
+ GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
+ // FBO texture for storing camera preview data(RGBA format)
+ GLES20.glGenTextures(1, fboTexureId, 0);
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, fboTexureId[0]);
+ GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, textureWidth, textureHeight, 0,
+ GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
+ // Generate FBO and bind to FBO texture
+ GLES20.glGenFramebuffers(1, fbo, 0);
+ GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fbo[0]);
+ GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D,
+ fboTexureId[0], 0);
+ try {
+ camera.setPreviewTexture(surfaceTexture);
+ } catch (IOException exception) {
+ Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
+ }
+ camera.startPreview();
+ }
+
+ public void releaseCamera() {
+ if (camera != null) {
+ camera.setPreviewCallback(null);
+ camera.stopPreview();
+ camera.release();
+ camera = null;
+ }
+ }
+}
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/common/Utils.java b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/common/Utils.java
new file mode 100644
index 000000000..3428bc1f1
--- /dev/null
+++ b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/common/Utils.java
@@ -0,0 +1,237 @@
+package com.baidu.paddle.fastdeploy.common;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.hardware.Camera;
+import android.opengl.GLES20;
+import android.os.Environment;
+import android.util.Log;
+import android.view.Surface;
+import android.view.WindowManager;
+
+import java.io.*;
+import java.util.List;
+
+public class Utils {
+ private static final String TAG = Utils.class.getSimpleName();
+
+ public static void RecursiveCreateDirectories(String fileDir) {
+ String[] fileDirs = fileDir.split("\\/");
+ String topPath = "";
+ for (int i = 0; i < fileDirs.length; i++) {
+ topPath += "/" + fileDirs[i];
+ File file = new File(topPath);
+ if (file.exists()) {
+ continue;
+ } else {
+ file.mkdir();
+ }
+ }
+ }
+
+ public static void copyFileFromAssets(Context appCtx, String srcPath, String dstPath) {
+ if (srcPath.isEmpty() || dstPath.isEmpty()) {
+ return;
+ }
+ String dstDir = dstPath.substring(0, dstPath.lastIndexOf('/'));
+ if (dstDir.length() > 0) {
+ RecursiveCreateDirectories(dstDir);
+ }
+ InputStream is = null;
+ OutputStream os = null;
+ try {
+ is = new BufferedInputStream(appCtx.getAssets().open(srcPath));
+ os = new BufferedOutputStream(new FileOutputStream(new File(dstPath)));
+ byte[] buffer = new byte[1024];
+ int length = 0;
+ while ((length = is.read(buffer)) != -1) {
+ os.write(buffer, 0, length);
+ }
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ os.close();
+ is.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public static void copyDirectoryFromAssets(Context appCtx, String srcDir, String dstDir) {
+ if (srcDir.isEmpty() || dstDir.isEmpty()) {
+ return;
+ }
+ try {
+ if (!new File(dstDir).exists()) {
+ new File(dstDir).mkdirs();
+ }
+ for (String fileName : appCtx.getAssets().list(srcDir)) {
+ String srcSubPath = srcDir + File.separator + fileName;
+ String dstSubPath = dstDir + File.separator + fileName;
+ if (new File(srcSubPath).isDirectory()) {
+ copyDirectoryFromAssets(appCtx, srcSubPath, dstSubPath);
+ } else {
+ copyFileFromAssets(appCtx, srcSubPath, dstSubPath);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static float[] parseFloatsFromString(String string, String delimiter) {
+ String[] pieces = string.trim().toLowerCase().split(delimiter);
+ float[] floats = new float[pieces.length];
+ for (int i = 0; i < pieces.length; i++) {
+ floats[i] = Float.parseFloat(pieces[i].trim());
+ }
+ return floats;
+ }
+
+ public static long[] parseLongsFromString(String string, String delimiter) {
+ String[] pieces = string.trim().toLowerCase().split(delimiter);
+ long[] longs = new long[pieces.length];
+ for (int i = 0; i < pieces.length; i++) {
+ longs[i] = Long.parseLong(pieces[i].trim());
+ }
+ return longs;
+ }
+
+ public static String getSDCardDirectory() {
+ return Environment.getExternalStorageDirectory().getAbsolutePath();
+ }
+
+ public static String getDCIMDirectory() {
+ return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath();
+ }
+
+ public static Camera.Size getOptimalPreviewSize(List sizes, int w, int h) {
+ final double ASPECT_TOLERANCE = 0.1;
+ double targetRatio = (double) w / h;
+ if (sizes == null) return null;
+
+ Camera.Size optimalSize = null;
+ double minDiff = Double.MAX_VALUE;
+
+ int targetHeight = h;
+
+ // Try to find an size match aspect ratio and size
+ for (Camera.Size size : sizes) {
+ double ratio = (double) size.width / size.height;
+ if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
+ if (Math.abs(size.height - targetHeight) < minDiff) {
+ optimalSize = size;
+ minDiff = Math.abs(size.height - targetHeight);
+ }
+ }
+
+ // Cannot find the one match the aspect ratio, ignore the requirement
+ if (optimalSize == null) {
+ minDiff = Double.MAX_VALUE;
+ for (Camera.Size size : sizes) {
+ if (Math.abs(size.height - targetHeight) < minDiff) {
+ optimalSize = size;
+ minDiff = Math.abs(size.height - targetHeight);
+ }
+ }
+ }
+ return optimalSize;
+ }
+
+ public static int getScreenWidth() {
+ return Resources.getSystem().getDisplayMetrics().widthPixels;
+ }
+
+ public static int getScreenHeight() {
+ return Resources.getSystem().getDisplayMetrics().heightPixels;
+ }
+
+ public static int getCameraDisplayOrientation(Context context, int cameraId) {
+ android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
+ android.hardware.Camera.getCameraInfo(cameraId, info);
+ WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ int rotation = wm.getDefaultDisplay().getRotation();
+ int degrees = 0;
+ switch (rotation) {
+ case Surface.ROTATION_0:
+ degrees = 0;
+ break;
+ case Surface.ROTATION_90:
+ degrees = 90;
+ break;
+ case Surface.ROTATION_180:
+ degrees = 180;
+ break;
+ case Surface.ROTATION_270:
+ degrees = 270;
+ break;
+ }
+ int result;
+ if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
+ result = (info.orientation + degrees) % 360;
+ result = (360 - result) % 360; // compensate the mirror
+ } else {
+ // back-facing
+ result = (info.orientation - degrees + 360) % 360;
+ }
+ return result;
+ }
+
+ public static int createShaderProgram(String vss, String fss) {
+ int vshader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
+ GLES20.glShaderSource(vshader, vss);
+ GLES20.glCompileShader(vshader);
+ int[] status = new int[1];
+ GLES20.glGetShaderiv(vshader, GLES20.GL_COMPILE_STATUS, status, 0);
+ if (status[0] == 0) {
+ Log.e(TAG, GLES20.glGetShaderInfoLog(vshader));
+ GLES20.glDeleteShader(vshader);
+ vshader = 0;
+ return 0;
+ }
+
+ int fshader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
+ GLES20.glShaderSource(fshader, fss);
+ GLES20.glCompileShader(fshader);
+ GLES20.glGetShaderiv(fshader, GLES20.GL_COMPILE_STATUS, status, 0);
+ if (status[0] == 0) {
+ Log.e(TAG, GLES20.glGetShaderInfoLog(fshader));
+ GLES20.glDeleteShader(vshader);
+ GLES20.glDeleteShader(fshader);
+ fshader = 0;
+ return 0;
+ }
+
+ int program = GLES20.glCreateProgram();
+ GLES20.glAttachShader(program, vshader);
+ GLES20.glAttachShader(program, fshader);
+ GLES20.glLinkProgram(program);
+ GLES20.glDeleteShader(vshader);
+ GLES20.glDeleteShader(fshader);
+ GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, status, 0);
+ if (status[0] == 0) {
+ Log.e(TAG, GLES20.glGetProgramInfoLog(program));
+ program = 0;
+ return 0;
+ }
+ GLES20.glValidateProgram(program);
+ GLES20.glGetProgramiv(program, GLES20.GL_VALIDATE_STATUS, status, 0);
+ if (status[0] == 0) {
+ Log.e(TAG, GLES20.glGetProgramInfoLog(program));
+ GLES20.glDeleteProgram(program);
+ program = 0;
+ return 0;
+ }
+
+ return program;
+ }
+
+ public static boolean isSupportedNPU() {
+ String hardware = android.os.Build.HARDWARE;
+ return hardware.equalsIgnoreCase("kirin810") || hardware.equalsIgnoreCase("kirin990");
+ }
+}
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/examples/MainActivity.java b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/examples/MainActivity.java
new file mode 100644
index 000000000..5c1d9d98d
--- /dev/null
+++ b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/examples/MainActivity.java
@@ -0,0 +1,251 @@
+package com.baidu.paddle.fastdeploy.examples;
+
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.graphics.*;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.support.annotation.NonNull;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
+import android.util.Log;
+import android.view.*;
+import android.widget.*;
+
+import com.baidu.paddle.fastdeploy.RuntimeOption;
+import com.baidu.paddle.fastdeploy.common.CameraSurfaceView;
+import com.baidu.paddle.fastdeploy.common.Utils;
+import com.baidu.paddle.fastdeploy.examples.R;
+import com.baidu.paddle.fastdeploy.vision.DetectionResult;
+import com.baidu.paddle.fastdeploy.vision.detection.PicoDet;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public class MainActivity extends Activity implements View.OnClickListener, CameraSurfaceView.OnTextureChangedListener {
+ private static final String TAG = MainActivity.class.getSimpleName();
+
+ CameraSurfaceView svPreview;
+ TextView tvStatus;
+ ImageButton btnSwitch;
+ ImageButton btnShutter;
+ ImageButton btnSettings;
+ ImageView realtimeToggleButton;
+ boolean isRealtimeStatusRunning = false;
+ ImageView backInPreview;
+
+ String savedImagePath = "result.jpg";
+ int lastFrameIndex = 0;
+ long lastFrameTime;
+
+ // Call 'init' and 'release' manually later
+ PicoDet predictor = new PicoDet();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Fullscreen
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
+
+ setContentView(R.layout.activity_main);
+
+ // Clear all setting items to avoid app crashing due to the incorrect settings
+ initSettings();
+
+ // Init the camera preview and UI components
+ initView();
+
+ // Check and request CAMERA and WRITE_EXTERNAL_STORAGE permissions
+ if (!checkAllPermissions()) {
+ requestAllPermissions();
+ }
+ }
+
+ @SuppressLint("NonConstantResourceId")
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.btn_switch:
+ svPreview.switchCamera();
+ break;
+ case R.id.btn_shutter:
+ @SuppressLint("SimpleDateFormat")
+ SimpleDateFormat date = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss");
+ synchronized (this) {
+ savedImagePath = Utils.getDCIMDirectory() + File.separator + date.format(new Date()).toString() + ".png";
+ }
+ Toast.makeText(MainActivity.this, "Save snapshot to " + savedImagePath, Toast.LENGTH_SHORT).show();
+ break;
+ case R.id.btn_settings:
+ startActivity(new Intent(MainActivity.this, SettingsActivity.class));
+ break;
+ case R.id.realtime_toggle_btn:
+ toggleRealtimeStyle();
+ break;
+ case R.id.back_in_preview:
+ finish();
+ break;
+ }
+ }
+
+ private void toggleRealtimeStyle() {
+ if (isRealtimeStatusRunning) {
+ isRealtimeStatusRunning = false;
+ realtimeToggleButton.setImageResource(R.drawable.realtime_stop_btn);
+ svPreview.setOnTextureChangedListener(this);
+ tvStatus.setVisibility(View.VISIBLE);
+ } else {
+ isRealtimeStatusRunning = true;
+ realtimeToggleButton.setImageResource(R.drawable.realtime_start_btn);
+ tvStatus.setVisibility(View.GONE);
+ svPreview.setOnTextureChangedListener(new CameraSurfaceView.OnTextureChangedListener() {
+ @Override
+ public boolean onTextureChanged(Bitmap ARGB8888ImageBitmap) {
+ return false;
+ }
+ });
+ }
+ }
+
+ @Override
+ public boolean onTextureChanged(Bitmap ARGB8888ImageBitmap) {
+ String savedImagePath = "";
+ synchronized (this) {
+ savedImagePath = MainActivity.this.savedImagePath;
+ }
+ boolean modified = false;
+ DetectionResult result = predictor.predict(
+ ARGB8888ImageBitmap, savedImagePath, SettingsActivity.scoreThreshold);
+ modified = result.initialized();
+ if (!savedImagePath.isEmpty()) {
+ synchronized (this) {
+ MainActivity.this.savedImagePath = "result.jpg";
+ }
+ }
+ lastFrameIndex++;
+ if (lastFrameIndex >= 30) {
+ final int fps = (int) (lastFrameIndex * 1e9 / (System.nanoTime() - lastFrameTime));
+ runOnUiThread(new Runnable() {
+ @SuppressLint("SetTextI18n")
+ public void run() {
+ tvStatus.setText(Integer.toString(fps) + "fps");
+ }
+ });
+ lastFrameIndex = 0;
+ lastFrameTime = System.nanoTime();
+ }
+ return modified;
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ // Reload settings and re-initialize the predictor
+ checkAndUpdateSettings();
+ // Open camera until the permissions have been granted
+ if (!checkAllPermissions()) {
+ svPreview.disableCamera();
+ }
+ svPreview.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ svPreview.onPause();
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (predictor != null) {
+ predictor.release();
+ }
+ super.onDestroy();
+ }
+
+ public void initView() {
+ svPreview = (CameraSurfaceView) findViewById(R.id.sv_preview);
+ svPreview.setOnTextureChangedListener(this);
+ tvStatus = (TextView) findViewById(R.id.tv_status);
+ btnSwitch = (ImageButton) findViewById(R.id.btn_switch);
+ btnSwitch.setOnClickListener(this);
+ btnShutter = (ImageButton) findViewById(R.id.btn_shutter);
+ btnShutter.setOnClickListener(this);
+ btnSettings = (ImageButton) findViewById(R.id.btn_settings);
+ btnSettings.setOnClickListener(this);
+ realtimeToggleButton = findViewById(R.id.realtime_toggle_btn);
+ realtimeToggleButton.setOnClickListener(this);
+ backInPreview = findViewById(R.id.back_in_preview);
+ backInPreview.setOnClickListener(this);
+ }
+
+ @SuppressLint("ApplySharedPref")
+ public void initSettings() {
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+ SharedPreferences.Editor editor = sharedPreferences.edit();
+ editor.clear();
+ editor.commit();
+ SettingsActivity.resetSettings();
+ }
+
+ public void checkAndUpdateSettings() {
+ if (SettingsActivity.checkAndUpdateSettings(this)) {
+ String realModelDir = getCacheDir() + "/" + SettingsActivity.modelDir;
+ Utils.copyDirectoryFromAssets(this, SettingsActivity.modelDir, realModelDir);
+ String realLabelPath = getCacheDir() + "/" + SettingsActivity.labelPath;
+ Utils.copyFileFromAssets(this, SettingsActivity.labelPath, realLabelPath);
+
+ String modelFile = realModelDir + "/" + "model.pdmodel";
+ String paramsFile = realModelDir + "/" + "model.pdiparams";
+ String configFile = realModelDir + "/" + "infer_cfg.yml";
+ String labelFile = realLabelPath;
+ RuntimeOption option = new RuntimeOption();
+ option.setCpuThreadNum(SettingsActivity.cpuThreadNum);
+ option.setLitePowerMode(SettingsActivity.cpuPowerMode);
+ option.enableRecordTimeOfRuntime();
+ if (Boolean.parseBoolean(SettingsActivity.enableLiteFp16)) {
+ option.enableLiteFp16();
+ }
+ predictor.init(modelFile, paramsFile, configFile, labelFile, option);
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
+ @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ if (grantResults[0] != PackageManager.PERMISSION_GRANTED || grantResults[1] != PackageManager.PERMISSION_GRANTED) {
+ new AlertDialog.Builder(MainActivity.this)
+ .setTitle("Permission denied")
+ .setMessage("Click to force quit the app, then open Settings->Apps & notifications->Target " +
+ "App->Permissions to grant all of the permissions.")
+ .setCancelable(false)
+ .setPositiveButton("Exit", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ MainActivity.this.finish();
+ }
+ }).show();
+ }
+ }
+
+ private void requestAllPermissions() {
+ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ Manifest.permission.CAMERA}, 0);
+ }
+
+ private boolean checkAllPermissions() {
+ return ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
+ && ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED;
+ }
+}
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/examples/SettingsActivity.java b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/examples/SettingsActivity.java
new file mode 100644
index 000000000..738f7fc01
--- /dev/null
+++ b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/examples/SettingsActivity.java
@@ -0,0 +1,199 @@
+package com.baidu.paddle.fastdeploy.examples;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.preference.EditTextPreference;
+import android.preference.ListPreference;
+import android.preference.PreferenceManager;
+import android.support.v7.app.ActionBar;
+import android.util.Log;
+
+import com.baidu.paddle.fastdeploy.common.AppCompatPreferenceActivity;
+import com.baidu.paddle.fastdeploy.common.Utils;
+import com.baidu.paddle.fastdeploy.examples.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class SettingsActivity extends AppCompatPreferenceActivity implements
+ SharedPreferences.OnSharedPreferenceChangeListener {
+ private static final String TAG = SettingsActivity.class.getSimpleName();
+
+ static public int selectedModelIdx = -1;
+ static public String modelDir = "";
+ static public String labelPath = "";
+ static public int cpuThreadNum = 2;
+ static public String cpuPowerMode = "";
+ static public float scoreThreshold = 0.4f;
+ static public String enableLiteFp16 = "true";
+
+ ListPreference lpChoosePreInstalledModel = null;
+ EditTextPreference etModelDir = null;
+ EditTextPreference etLabelPath = null;
+ ListPreference lpCPUThreadNum = null;
+ ListPreference lpCPUPowerMode = null;
+ EditTextPreference etScoreThreshold = null;
+ ListPreference lpEnableLiteFp16 = null;
+
+ List preInstalledModelDirs = null;
+ List preInstalledLabelPaths = null;
+ List preInstalledCPUThreadNums = null;
+ List preInstalledCPUPowerModes = null;
+ List preInstalledScoreThresholds = null;
+ List preInstalledEnableLiteFp16s = null;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.settings);
+ ActionBar supportActionBar = getSupportActionBar();
+ if (supportActionBar != null) {
+ supportActionBar.setDisplayHomeAsUpEnabled(true);
+ }
+
+ // Initialize pre-installed models
+ preInstalledModelDirs = new ArrayList();
+ preInstalledLabelPaths = new ArrayList();
+ preInstalledCPUThreadNums = new ArrayList();
+ preInstalledCPUPowerModes = new ArrayList();
+ preInstalledScoreThresholds = new ArrayList();
+ preInstalledEnableLiteFp16s = new ArrayList();
+ preInstalledModelDirs.add(getString(R.string.MODEL_DIR_DEFAULT));
+ preInstalledLabelPaths.add(getString(R.string.LABEL_PATH_DEFAULT));
+ preInstalledCPUThreadNums.add(getString(R.string.CPU_THREAD_NUM_DEFAULT));
+ preInstalledCPUPowerModes.add(getString(R.string.CPU_POWER_MODE_DEFAULT));
+ preInstalledScoreThresholds.add(getString(R.string.SCORE_THRESHOLD_DEFAULT));
+ preInstalledEnableLiteFp16s.add(getString(R.string.ENABLE_LITE_FP16_MODE_DEFAULT));
+
+ // Setup UI components
+ lpChoosePreInstalledModel =
+ (ListPreference) findPreference(getString(R.string.CHOOSE_PRE_INSTALLED_MODEL_KEY));
+ String[] preInstalledModelNames = new String[preInstalledModelDirs.size()];
+ for (int i = 0; i < preInstalledModelDirs.size(); i++) {
+ preInstalledModelNames[i] = preInstalledModelDirs.get(i).substring(preInstalledModelDirs.get(i).lastIndexOf("/") + 1);
+ }
+ lpChoosePreInstalledModel.setEntries(preInstalledModelNames);
+ lpChoosePreInstalledModel.setEntryValues(preInstalledModelDirs.toArray(new String[preInstalledModelDirs.size()]));
+ lpCPUThreadNum = (ListPreference) findPreference(getString(R.string.CPU_THREAD_NUM_KEY));
+ lpCPUPowerMode = (ListPreference) findPreference(getString(R.string.CPU_POWER_MODE_KEY));
+ etModelDir = (EditTextPreference) findPreference(getString(R.string.MODEL_DIR_KEY));
+ etModelDir.setTitle("Model dir (SDCard: " + Utils.getSDCardDirectory() + ")");
+ etLabelPath = (EditTextPreference) findPreference(getString(R.string.LABEL_PATH_KEY));
+ etLabelPath.setTitle("Label path (SDCard: " + Utils.getSDCardDirectory() + ")");
+ etScoreThreshold = (EditTextPreference) findPreference(getString(R.string.SCORE_THRESHOLD_KEY));
+ lpEnableLiteFp16 = (ListPreference) findPreference(getString(R.string.ENABLE_LITE_FP16_MODE_KEY));
+ }
+
+ @SuppressLint("ApplySharedPref")
+ private void reloadSettingsAndUpdateUI() {
+ SharedPreferences sharedPreferences = getPreferenceScreen().getSharedPreferences();
+
+ String selected_model_dir = sharedPreferences.getString(getString(R.string.CHOOSE_PRE_INSTALLED_MODEL_KEY),
+ getString(R.string.MODEL_DIR_DEFAULT));
+ int selected_model_idx = lpChoosePreInstalledModel.findIndexOfValue(selected_model_dir);
+ if (selected_model_idx >= 0 && selected_model_idx < preInstalledModelDirs.size() && selected_model_idx != selectedModelIdx) {
+ SharedPreferences.Editor editor = sharedPreferences.edit();
+ editor.putString(getString(R.string.MODEL_DIR_KEY), preInstalledModelDirs.get(selected_model_idx));
+ editor.putString(getString(R.string.LABEL_PATH_KEY), preInstalledLabelPaths.get(selected_model_idx));
+ editor.putString(getString(R.string.CPU_THREAD_NUM_KEY), preInstalledCPUThreadNums.get(selected_model_idx));
+ editor.putString(getString(R.string.CPU_POWER_MODE_KEY), preInstalledCPUPowerModes.get(selected_model_idx));
+ editor.putString(getString(R.string.SCORE_THRESHOLD_KEY), preInstalledScoreThresholds.get(selected_model_idx));
+ editor.putString(getString(R.string.ENABLE_LITE_FP16_MODE_DEFAULT), preInstalledEnableLiteFp16s.get(selected_model_idx));
+ editor.commit();
+ lpChoosePreInstalledModel.setSummary(selected_model_dir);
+ selectedModelIdx = selected_model_idx;
+ }
+
+ String model_dir = sharedPreferences.getString(getString(R.string.MODEL_DIR_KEY),
+ getString(R.string.MODEL_DIR_DEFAULT));
+ String label_path = sharedPreferences.getString(getString(R.string.LABEL_PATH_KEY),
+ getString(R.string.LABEL_PATH_DEFAULT));
+ String cpu_thread_num = sharedPreferences.getString(getString(R.string.CPU_THREAD_NUM_KEY),
+ getString(R.string.CPU_THREAD_NUM_DEFAULT));
+ String cpu_power_mode = sharedPreferences.getString(getString(R.string.CPU_POWER_MODE_KEY),
+ getString(R.string.CPU_POWER_MODE_DEFAULT));
+ String score_threshold = sharedPreferences.getString(getString(R.string.SCORE_THRESHOLD_KEY),
+ getString(R.string.SCORE_THRESHOLD_DEFAULT));
+ String enable_lite_fp16 = sharedPreferences.getString(getString(R.string.ENABLE_LITE_FP16_MODE_KEY),
+ getString(R.string.ENABLE_LITE_FP16_MODE_DEFAULT));
+
+ etModelDir.setSummary(model_dir);
+ etLabelPath.setSummary(label_path);
+ lpCPUThreadNum.setValue(cpu_thread_num);
+ lpCPUThreadNum.setSummary(cpu_thread_num);
+ lpCPUPowerMode.setValue(cpu_power_mode);
+ lpCPUPowerMode.setSummary(cpu_power_mode);
+ etScoreThreshold.setSummary(score_threshold);
+ etScoreThreshold.setText(score_threshold);
+ lpEnableLiteFp16.setValue(enable_lite_fp16);
+ lpEnableLiteFp16.setSummary(enable_lite_fp16);
+
+ }
+
+ static boolean checkAndUpdateSettings(Context ctx) {
+ boolean settingsChanged = false;
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(ctx);
+
+ String model_dir = sharedPreferences.getString(ctx.getString(R.string.MODEL_DIR_KEY),
+ ctx.getString(R.string.MODEL_DIR_DEFAULT));
+ settingsChanged |= !modelDir.equalsIgnoreCase(model_dir);
+ modelDir = model_dir;
+
+ String label_path = sharedPreferences.getString(ctx.getString(R.string.LABEL_PATH_KEY),
+ ctx.getString(R.string.LABEL_PATH_DEFAULT));
+ settingsChanged |= !labelPath.equalsIgnoreCase(label_path);
+ labelPath = label_path;
+
+ String cpu_thread_num = sharedPreferences.getString(ctx.getString(R.string.CPU_THREAD_NUM_KEY),
+ ctx.getString(R.string.CPU_THREAD_NUM_DEFAULT));
+ settingsChanged |= cpuThreadNum != Integer.parseInt(cpu_thread_num);
+ cpuThreadNum = Integer.parseInt(cpu_thread_num);
+
+ String cpu_power_mode = sharedPreferences.getString(ctx.getString(R.string.CPU_POWER_MODE_KEY),
+ ctx.getString(R.string.CPU_POWER_MODE_DEFAULT));
+ settingsChanged |= !cpuPowerMode.equalsIgnoreCase(cpu_power_mode);
+ cpuPowerMode = cpu_power_mode;
+
+ String score_threshold = sharedPreferences.getString(ctx.getString(R.string.SCORE_THRESHOLD_KEY),
+ ctx.getString(R.string.SCORE_THRESHOLD_DEFAULT));
+ settingsChanged |= scoreThreshold != Float.parseFloat(score_threshold);
+ scoreThreshold = Float.parseFloat(score_threshold);
+
+ String enable_lite_fp16 = sharedPreferences.getString(ctx.getString(R.string.ENABLE_LITE_FP16_MODE_KEY),
+ ctx.getString(R.string.ENABLE_LITE_FP16_MODE_DEFAULT));
+ settingsChanged |= !enableLiteFp16.equalsIgnoreCase(enable_lite_fp16);
+ enableLiteFp16 = enable_lite_fp16;
+
+ return settingsChanged;
+ }
+
+ static void resetSettings() {
+ selectedModelIdx = -1;
+ modelDir = "";
+ labelPath = "";
+ cpuThreadNum = 2;
+ cpuPowerMode = "";
+ scoreThreshold = 0.4f;
+ enableLiteFp16 = "true";
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
+ reloadSettingsAndUpdateUI();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ reloadSettingsAndUpdateUI();
+ }
+}
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/vision/ClassifyResult.java b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/vision/ClassifyResult.java
new file mode 100644
index 000000000..7e6e55bd0
--- /dev/null
+++ b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/vision/ClassifyResult.java
@@ -0,0 +1,51 @@
+package com.baidu.paddle.fastdeploy.vision;
+
+import android.support.annotation.NonNull;
+
+public class ClassifyResult {
+ public float[] mScores; // [n]
+ public int[] mLabelIds; // [n]
+ public boolean mInitialized = false;
+
+ public ClassifyResult() {
+ mInitialized = false;
+ }
+
+ public ClassifyResult(long nativeResultContext) {
+ mInitialized = copyAllFromNativeContext(nativeResultContext);
+ }
+
+ public boolean initialized() {
+ return mInitialized;
+ }
+
+ private void setScores(@NonNull float[] scoresBuffer) {
+ if (scoresBuffer.length > 0) {
+ mScores = scoresBuffer.clone();
+ }
+ }
+
+ private void setLabelIds(@NonNull int[] labelIdsBuffer) {
+ if (labelIdsBuffer.length > 0) {
+ mLabelIds = labelIdsBuffer.clone();
+ }
+ }
+
+ private boolean copyAllFromNativeContext(long nativeResultContext) {
+ if (nativeResultContext == 0) {
+ return false;
+ }
+ setScores(copyScoresFromNative(nativeResultContext));
+ setLabelIds(copyLabelIdsFromNative(nativeResultContext));
+ // WARN: must release ctx.
+ return releaseNative(nativeResultContext);
+ }
+
+ // Fetch native buffers from native context.
+ private static native float[] copyScoresFromNative(long nativeResultContext);
+
+ private static native int[] copyLabelIdsFromNative(long nativeResultContext);
+
+ private static native boolean releaseNative(long nativeResultContext);
+
+}
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/vision/DetectionResult.java b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/vision/DetectionResult.java
new file mode 100644
index 000000000..f658241ce
--- /dev/null
+++ b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/vision/DetectionResult.java
@@ -0,0 +1,80 @@
+package com.baidu.paddle.fastdeploy.vision;
+
+import android.support.annotation.NonNull;
+
+import java.util.Arrays;
+
+import com.baidu.paddle.fastdeploy.FastDeployInitializer;
+
+public class DetectionResult {
+ // Not support MaskRCNN now.
+ public float[][] mBoxes; // [n,4]
+ public float[] mScores; // [n]
+ public int[] mLabelIds; // [n]
+ public boolean mInitialized = false;
+
+ public DetectionResult() {
+ mInitialized = false;
+ }
+
+ public DetectionResult(long nativeResultContext) {
+ mInitialized = copyAllFromNativeContext(nativeResultContext);
+ }
+
+ public boolean initialized() {
+ return mInitialized;
+ }
+
+ // Setup results from native buffers.
+ private boolean copyAllFromNativeContext(long nativeResultContext) {
+ if (nativeResultContext == 0) {
+ return false;
+ }
+ if (copyBoxesNumFromNative(nativeResultContext) > 0) {
+ setBoxes(copyBoxesFromNative(nativeResultContext));
+ setScores(copyScoresFromNative(nativeResultContext));
+ setLabelIds(copyLabelIdsFromNative(nativeResultContext));
+ }
+ // WARN: must release ctx.
+ return releaseNative(nativeResultContext);
+ }
+
+ private void setBoxes(@NonNull float[] boxesBuffer) {
+ int boxesNum = boxesBuffer.length / 4;
+ if (boxesNum > 0) {
+ mBoxes = new float[boxesNum][4];
+ for (int i = 0; i < boxesNum; ++i) {
+ mBoxes[i] = Arrays.copyOfRange(
+ boxesBuffer, i * 4, (i + 1) * 4);
+ }
+ }
+ }
+
+ private void setScores(@NonNull float[] scoresBuffer) {
+ if (scoresBuffer.length > 0) {
+ mScores = scoresBuffer.clone();
+ }
+ }
+
+ private void setLabelIds(@NonNull int[] labelIdsBuffer) {
+ if (labelIdsBuffer.length > 0) {
+ mLabelIds = labelIdsBuffer.clone();
+ }
+ }
+
+ // Fetch native buffers from native context.
+ private static native int copyBoxesNumFromNative(long nativeResultContext);
+
+ private static native float[] copyBoxesFromNative(long nativeResultContext);
+
+ private static native float[] copyScoresFromNative(long nativeResultContext);
+
+ private static native int[] copyLabelIdsFromNative(long nativeResultContext);
+
+ private static native boolean releaseNative(long nativeResultContext);
+
+ // Initializes at the beginning.
+ static {
+ FastDeployInitializer.init();
+ }
+}
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/vision/Visualize.java b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/vision/Visualize.java
new file mode 100644
index 000000000..0c9a13a3b
--- /dev/null
+++ b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/vision/Visualize.java
@@ -0,0 +1,85 @@
+package com.baidu.paddle.fastdeploy.vision;
+
+import android.graphics.Bitmap;
+
+import com.baidu.paddle.fastdeploy.FastDeployInitializer;
+
+
+public class Visualize {
+ // TODO(qiuyanjun):
+ // VisClassification, VisSegmentation, VisMatting, VisOcr, ...
+
+ // Visualize DetectionResult without labels
+ public static boolean visDetection(Bitmap ARGB8888Bitmap,
+ DetectionResult result) {
+ return visDetectionNative(
+ ARGB8888Bitmap,
+ result.mBoxes,
+ result.mScores,
+ result.mLabelIds,
+ 0.f, 1, 0.5f,
+ new String[]{});
+ }
+
+ public static boolean visDetection(Bitmap ARGB8888Bitmap,
+ DetectionResult result,
+ float score_threshold,
+ int line_size,
+ float font_size) {
+ return visDetectionNative(
+ ARGB8888Bitmap,
+ result.mBoxes,
+ result.mScores,
+ result.mLabelIds,
+ score_threshold,
+ line_size,
+ font_size,
+ new String[]{});
+ }
+
+ // Visualize DetectionResult with labels
+ public static boolean visDetection(Bitmap ARGB8888Bitmap,
+ DetectionResult result,
+ String[] labels) {
+ return visDetectionNative(
+ ARGB8888Bitmap,
+ result.mBoxes,
+ result.mScores,
+ result.mLabelIds,
+ 0.f, 1, 0.5f,
+ labels);
+ }
+
+ public static boolean visDetection(Bitmap ARGB8888Bitmap,
+ DetectionResult result,
+ float score_threshold,
+ int line_size,
+ float font_size,
+ String[] labels) {
+ return visDetectionNative(
+ ARGB8888Bitmap,
+ result.mBoxes,
+ result.mScores,
+ result.mLabelIds,
+ score_threshold,
+ line_size,
+ font_size,
+ labels);
+ }
+
+ // VisDetection in native
+ public static native boolean visDetectionNative(Bitmap ARGB8888Bitmap,
+ float[][] boxes,
+ float[] scores,
+ int[] labelIds,
+ float score_threshold,
+ int line_size,
+ float font_size,
+ String[] labels);
+
+
+ /* Initializes at the beginning */
+ static {
+ FastDeployInitializer.init();
+ }
+}
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/vision/classification/PaddleClasModel.java b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/vision/classification/PaddleClasModel.java
new file mode 100644
index 000000000..f0524604e
--- /dev/null
+++ b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/vision/classification/PaddleClasModel.java
@@ -0,0 +1,158 @@
+package com.baidu.paddle.fastdeploy.vision.classification;
+
+import android.graphics.Bitmap;
+
+import com.baidu.paddle.fastdeploy.FastDeployInitializer;
+import com.baidu.paddle.fastdeploy.RuntimeOption;
+import com.baidu.paddle.fastdeploy.vision.ClassifyResult;
+
+public class PaddleClasModel {
+ protected long mNativeModelContext = 0; // Context from native.
+ protected boolean mInitialized = false;
+
+ public PaddleClasModel() {
+ mInitialized = false;
+ }
+
+ // Constructor without label file
+ public PaddleClasModel(String modelFile,
+ String paramsFile,
+ String configFile,
+ RuntimeOption option) {
+ init_(modelFile, paramsFile, configFile, "", option);
+ }
+
+ // Constructor with label file
+ public PaddleClasModel(String modelFile,
+ String paramsFile,
+ String configFile,
+ String labelFile,
+ RuntimeOption option) {
+ init_(modelFile, paramsFile, configFile, labelFile, option);
+ }
+
+ // Call init manually without label file
+ public boolean init(String modelFile,
+ String paramsFile,
+ String configFile,
+ RuntimeOption option) {
+ return init_(modelFile, paramsFile, configFile, "", option);
+ }
+
+ // Call init manually with label file
+ public boolean init(String modelFile,
+ String paramsFile,
+ String configFile,
+ String labelFile,
+ RuntimeOption option) {
+ return init_(modelFile, paramsFile, configFile, labelFile, option);
+ }
+
+
+ public boolean release() {
+ mInitialized = false;
+ if (mNativeModelContext == 0) {
+ return false;
+ }
+ return releaseNative(mNativeModelContext);
+ }
+
+ public boolean initialized() {
+ return mInitialized;
+ }
+
+ // Predict without image saving and bitmap rendering.
+ public ClassifyResult predict(Bitmap ARGB8888Bitmap) {
+ if (mNativeModelContext == 0) {
+ return new ClassifyResult();
+ }
+ // Only support ARGB8888 bitmap in native now.
+ return new ClassifyResult(predictNative(
+ mNativeModelContext, ARGB8888Bitmap, false,
+ "", 0.f, false));
+ }
+
+ // Predict with image saving and bitmap rendering (will cost more times)
+ public ClassifyResult predict(Bitmap ARGB8888Bitmap,
+ String savedImagePath,
+ float scoreThreshold) {
+ // scoreThreshold is for visualizing only.
+ if (mNativeModelContext == 0) {
+ return new ClassifyResult();
+ }
+ // Only support ARGB8888 bitmap in native now.
+ return new ClassifyResult(predictNative(
+ mNativeModelContext, ARGB8888Bitmap, true,
+ savedImagePath, scoreThreshold, true));
+ }
+
+ // Internal init_ method
+ private boolean init_(String modelFile,
+ String paramsFile,
+ String configFile,
+ String labelFile,
+ RuntimeOption option) {
+ if (!mInitialized) {
+ mNativeModelContext = bindNative(
+ modelFile,
+ paramsFile,
+ configFile,
+ option.mCpuThreadNum,
+ option.mEnableLiteFp16,
+ option.mLitePowerMode.ordinal(),
+ option.mLiteOptimizedModelDir,
+ option.mEnableRecordTimeOfRuntime, labelFile);
+ if (mNativeModelContext != 0) {
+ mInitialized = true;
+ }
+ return mInitialized;
+ } else {
+ // release current native context and bind a new one.
+ if (release()) {
+ mNativeModelContext = bindNative(
+ modelFile,
+ paramsFile,
+ configFile,
+ option.mCpuThreadNum,
+ option.mEnableLiteFp16,
+ option.mLitePowerMode.ordinal(),
+ option.mLiteOptimizedModelDir,
+ option.mEnableRecordTimeOfRuntime, labelFile);
+ if (mNativeModelContext != 0) {
+ mInitialized = true;
+ }
+ return mInitialized;
+ }
+ return false;
+ }
+ }
+
+
+ // Bind predictor from native context.
+ private static native long bindNative(String modelFile,
+ String paramsFile,
+ String configFile,
+ int cpuNumThread,
+ boolean enableLiteFp16,
+ int litePowerMode,
+ String liteOptimizedModelDir,
+ boolean enableRecordTimeOfRuntime,
+ String labelFile);
+
+ // Call prediction from native context.
+ private static native long predictNative(long nativeModelContext,
+ Bitmap ARGB8888Bitmap,
+ boolean saved,
+ String savedImagePath,
+ float scoreThreshold,
+ boolean rendering);
+
+ // Release buffers allocated in native context.
+ private static native boolean releaseNative(long nativeModelContext);
+
+ // Initializes at the beginning.
+ static {
+ FastDeployInitializer.init();
+ }
+
+}
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/vision/detection/PicoDet.java b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/vision/detection/PicoDet.java
new file mode 100644
index 000000000..bbd52eece
--- /dev/null
+++ b/examples/vision/detection/paddledetection/android/app/src/main/java/com/baidu/paddle/fastdeploy/vision/detection/PicoDet.java
@@ -0,0 +1,156 @@
+package com.baidu.paddle.fastdeploy.vision.detection;
+
+import android.graphics.Bitmap;
+
+import com.baidu.paddle.fastdeploy.FastDeployInitializer;
+import com.baidu.paddle.fastdeploy.RuntimeOption;
+import com.baidu.paddle.fastdeploy.vision.DetectionResult;
+
+public class PicoDet {
+ protected long mNativeModelContext = 0; // Context from native.
+ protected boolean mInitialized = false;
+
+ public PicoDet() {
+ mInitialized = false;
+ }
+
+ // Constructor without label file
+ public PicoDet(String modelFile,
+ String paramsFile,
+ String configFile,
+ RuntimeOption option) {
+ init_(modelFile, paramsFile, configFile, "", option);
+ }
+
+ // Constructor with label file
+ public PicoDet(String modelFile,
+ String paramsFile,
+ String configFile,
+ String labelFile,
+ RuntimeOption option) {
+ init_(modelFile, paramsFile, configFile, labelFile, option);
+ }
+
+ // Call init manually without label file
+ public boolean init(String modelFile,
+ String paramsFile,
+ String configFile,
+ RuntimeOption option) {
+ return init_(modelFile, paramsFile, configFile, "", option);
+ }
+
+ // Call init manually with label file
+ public boolean init(String modelFile,
+ String paramsFile,
+ String configFile,
+ String labelFile,
+ RuntimeOption option) {
+ return init_(modelFile, paramsFile, configFile, labelFile, option);
+ }
+
+ public boolean release() {
+ mInitialized = false;
+ if (mNativeModelContext == 0) {
+ return false;
+ }
+ return releaseNative(mNativeModelContext);
+ }
+
+ public boolean initialized() {
+ return mInitialized;
+ }
+
+ // Predict without image saving and bitmap rendering.
+ public DetectionResult predict(Bitmap ARGB8888Bitmap) {
+ if (mNativeModelContext == 0) {
+ return new DetectionResult();
+ }
+ // Only support ARGB8888 bitmap in native now.
+ return new DetectionResult(predictNative(
+ mNativeModelContext, ARGB8888Bitmap, false,
+ "", 0.f, false));
+ }
+
+ // Predict with image saving and bitmap rendering (will cost more times)
+ public DetectionResult predict(Bitmap ARGB8888Bitmap,
+ String savedImagePath,
+ float scoreThreshold) {
+ // scoreThreshold is for visualizing only.
+ if (mNativeModelContext == 0) {
+ return new DetectionResult();
+ }
+ // Only support ARGB8888 bitmap in native now.
+ return new DetectionResult(predictNative(
+ mNativeModelContext, ARGB8888Bitmap, true,
+ savedImagePath, scoreThreshold, true));
+ }
+
+
+ private boolean init_(String modelFile,
+ String paramsFile,
+ String configFile,
+ String labelFile,
+ RuntimeOption option) {
+ if (!mInitialized) {
+ mNativeModelContext = bindNative(
+ modelFile,
+ paramsFile,
+ configFile,
+ option.mCpuThreadNum,
+ option.mEnableLiteFp16,
+ option.mLitePowerMode.ordinal(),
+ option.mLiteOptimizedModelDir,
+ option.mEnableRecordTimeOfRuntime, labelFile);
+ if (mNativeModelContext != 0) {
+ mInitialized = true;
+ }
+ return mInitialized;
+ } else {
+ // release current native context and bind a new one.
+ if (release()) {
+ mNativeModelContext = bindNative(
+ modelFile,
+ paramsFile,
+ configFile,
+ option.mCpuThreadNum,
+ option.mEnableLiteFp16,
+ option.mLitePowerMode.ordinal(),
+ option.mLiteOptimizedModelDir,
+ option.mEnableRecordTimeOfRuntime, labelFile);
+ if (mNativeModelContext != 0) {
+ mInitialized = true;
+ }
+ return mInitialized;
+ }
+ return false;
+ }
+ }
+
+ // Bind predictor from native context.
+ private static native long bindNative(String modelFile,
+ String paramsFile,
+ String configFile,
+ int cpuNumThread,
+ boolean enableLiteFp16,
+ int litePowerMode,
+ String liteOptimizedModelDir,
+ boolean enableRecordTimeOfRuntime,
+ String labelFile);
+
+ // Call prediction from native context.
+ private static native long predictNative(long nativeModelContext,
+ Bitmap ARGB8888Bitmap,
+ boolean saved,
+ String savedImagePath,
+ float scoreThreshold,
+ boolean rendering);
+
+ // Release buffers allocated in native context.
+ private static native boolean releaseNative(long nativeModelContext);
+
+ // Initializes at the beginning.
+ static {
+ FastDeployInitializer.init();
+ }
+}
+
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/action_button_layer.xml b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/action_button_layer.xml
new file mode 100644
index 000000000..a0d2e76bf
--- /dev/null
+++ b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/action_button_layer.xml
@@ -0,0 +1,14 @@
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/album_btn.xml b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/album_btn.xml
new file mode 100644
index 000000000..26d01c584
--- /dev/null
+++ b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/album_btn.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 000000000..1f6bb2906
--- /dev/null
+++ b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/realtime_start_btn.xml b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/realtime_start_btn.xml
new file mode 100644
index 000000000..664134453
--- /dev/null
+++ b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/realtime_start_btn.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/realtime_stop_btn.xml b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/realtime_stop_btn.xml
new file mode 100644
index 000000000..8869a1b2b
--- /dev/null
+++ b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/realtime_stop_btn.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/result_page_border_section_bk.xml b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/result_page_border_section_bk.xml
new file mode 100644
index 000000000..bd068f169
--- /dev/null
+++ b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/result_page_border_section_bk.xml
@@ -0,0 +1,12 @@
+
+
+ -
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/round_corner_btn.xml b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/round_corner_btn.xml
new file mode 100644
index 000000000..c5dcc45d5
--- /dev/null
+++ b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/round_corner_btn.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/seekbar_progress_realtime.xml b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/seekbar_progress_realtime.xml
new file mode 100644
index 000000000..b349d15a6
--- /dev/null
+++ b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/seekbar_progress_realtime.xml
@@ -0,0 +1,18 @@
+
+
+
+
+ -
+
+
+
+ -
+
+
+
+
+
+
+
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/seekbar_progress_result.xml b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/seekbar_progress_result.xml
new file mode 100644
index 000000000..17cb68ed8
--- /dev/null
+++ b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/seekbar_progress_result.xml
@@ -0,0 +1,18 @@
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+
+
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/seekbar_thumb.xml b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/seekbar_thumb.xml
new file mode 100644
index 000000000..96bd95e0a
--- /dev/null
+++ b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/seekbar_thumb.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/seekbar_thumb_shape.xml b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/seekbar_thumb_shape.xml
new file mode 100644
index 000000000..26d033b6d
--- /dev/null
+++ b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/seekbar_thumb_shape.xml
@@ -0,0 +1,26 @@
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/switch_side_btn.xml b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/switch_side_btn.xml
new file mode 100644
index 000000000..b9b2edfb6
--- /dev/null
+++ b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/switch_side_btn.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/take_picture_btn.xml b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/take_picture_btn.xml
new file mode 100644
index 000000000..4966675c3
--- /dev/null
+++ b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-v24/take_picture_btn.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-xhdpi/album.png b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-xhdpi/album.png
new file mode 100644
index 0000000000000000000000000000000000000000..3a6fdedaee3cce52cf376ecb9977ea750a6014df
GIT binary patch
literal 10284
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
literal 0
HcmV?d00001
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-xhdpi/album_pressed.png b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-xhdpi/album_pressed.png
new file mode 100644
index 0000000000000000000000000000000000000000..aa873424ebb9921081bbb9618875fc410bf9c84d
GIT binary patch
literal 9982
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
NwC?G`E7hMw{|}{%Ewlgt
literal 0
HcmV?d00001
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-xhdpi/back_btn.png b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-xhdpi/back_btn.png
new file mode 100644
index 0000000000000000000000000000000000000000..ff121e85f5614dfd022f39627028af825a46d683
GIT binary patch
literal 455
zcmV;&0XY7NP)Px$fk{L`R7ee_)juf2aTv$(yX$hI{E2}ivam=p$Rx_3WKfbxN~EM@k(5%BO-WfU
zWl@q%HpwItU5P~`e@e=q$e-iq{oFle{yooApZi!ouPf;*5^-D*1ZCuuOqv~5>
zrA!dR1lbdTyC*fFAx1H>N#tHgV`xMM43|aVK1sV3na&VF@JshukwHbI#;r&f<8Or)
ztVj#Mn<8sgqz>RwksUf78e&vIt`s?>^DIaa!;~UtbcqGYWq6>-4P9kH>^Eu$f78R5Cr_+ovj*@l7te|F0k6$6}HucYmJGEE%xis4oBj002ovPDHLkV1m`y!3qEX
literal 0
HcmV?d00001
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-xhdpi/more_menu.png b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-xhdpi/more_menu.png
new file mode 100644
index 0000000000000000000000000000000000000000..edf9f3ccced5afeb71d9516d93ea19f26c7d9984
GIT binary patch
literal 414
zcmV;P0b%}$P)0`;fA>{1X_Il!ymh=^7u{{Rl_|GL>6mPM;1WY$RjBH$w
zWyNH^^e|SD$^D5?A`~MKi>Dn*gkl6@@w7vUP>et<-f}zi4a_uOC8db_zyJUM07*qo
IM6N<$f|TdAY5)KL
literal 0
HcmV?d00001
diff --git a/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-xhdpi/realtime_start.png b/examples/vision/detection/paddledetection/android/app/src/main/res/drawable-xhdpi/realtime_start.png
new file mode 100644
index 0000000000000000000000000000000000000000..94ab0817247bfa462d539237441cdc5795f1fdb0
GIT binary patch
literal 6166
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