& scale);
diff --git a/deploy/android_demo/app/src/main/java/com/baidu/paddle/lite/demo/ocr/AppCompatPreferenceActivity.java b/deploy/android_demo/app/src/main/java/com/baidu/paddle/lite/demo/ocr/AppCompatPreferenceActivity.java
new file mode 100644
index 000000000..397e4e39f
--- /dev/null
+++ b/deploy/android_demo/app/src/main/java/com/baidu/paddle/lite/demo/ocr/AppCompatPreferenceActivity.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.baidu.paddle.lite.demo.ocr;
+
+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/deploy/android_demo/app/src/main/java/com/baidu/paddle/lite/demo/ocr/MainActivity.java b/deploy/android_demo/app/src/main/java/com/baidu/paddle/lite/demo/ocr/MainActivity.java
new file mode 100644
index 000000000..b72d72df4
--- /dev/null
+++ b/deploy/android_demo/app/src/main/java/com/baidu/paddle/lite/demo/ocr/MainActivity.java
@@ -0,0 +1,414 @@
+package com.baidu.paddle.lite.demo.ocr;
+
+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.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class MainActivity extends AppCompatActivity {
+ private static final String TAG = MainActivity.class.getSimpleName();
+ 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;
+
+ 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
+
+ // UI components of object detection
+ protected TextView tvInputSetting;
+ protected ImageView ivInputImage;
+ protected TextView tvOutputResult;
+ protected TextView tvInferenceTime;
+
+ // Model settings of object detection
+ protected String modelPath = "";
+ protected String labelPath = "";
+ protected String imagePath = "";
+ protected int cpuThreadNum = 1;
+ protected String cpuPowerMode = "";
+ protected String inputColorFormat = "";
+ protected long[] inputShape = new long[]{};
+ protected float[] inputMean = new float[]{};
+ protected float[] inputStd = new float[]{};
+ protected float scoreThreshold = 0.1f;
+
+ protected Predictor predictor = new Predictor();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ // Clear all setting items to avoid app crashing due to the incorrect settings
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+ SharedPreferences.Editor editor = sharedPreferences.edit();
+ editor.clear();
+ editor.commit();
+
+ // Prepare the worker thread for mode loading and inference
+ receiver = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case RESPONSE_LOAD_MODEL_SUCCESSED:
+ pbLoadModel.dismiss();
+ onLoadModelSuccessed();
+ break;
+ case RESPONSE_LOAD_MODEL_FAILED:
+ pbLoadModel.dismiss();
+ Toast.makeText(MainActivity.this, "Load model failed!", Toast.LENGTH_SHORT).show();
+ onLoadModelFailed();
+ 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;
+ }
+ }
+ };
+
+ // Setup the UI components
+ tvInputSetting = findViewById(R.id.tv_input_setting);
+ ivInputImage = findViewById(R.id.iv_input_image);
+ tvInferenceTime = findViewById(R.id.tv_inference_time);
+ tvOutputResult = findViewById(R.id.tv_output_result);
+ tvInputSetting.setMovementMethod(ScrollingMovementMethod.getInstance());
+ tvOutputResult.setMovementMethod(ScrollingMovementMethod.getInstance());
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+ boolean settingsChanged = false;
+ String model_path = sharedPreferences.getString(getString(R.string.MODEL_PATH_KEY),
+ getString(R.string.MODEL_PATH_DEFAULT));
+ String label_path = sharedPreferences.getString(getString(R.string.LABEL_PATH_KEY),
+ getString(R.string.LABEL_PATH_DEFAULT));
+ String image_path = sharedPreferences.getString(getString(R.string.IMAGE_PATH_KEY),
+ getString(R.string.IMAGE_PATH_DEFAULT));
+ settingsChanged |= !model_path.equalsIgnoreCase(modelPath);
+ settingsChanged |= !label_path.equalsIgnoreCase(labelPath);
+ settingsChanged |= !image_path.equalsIgnoreCase(imagePath);
+ int cpu_thread_num = Integer.parseInt(sharedPreferences.getString(getString(R.string.CPU_THREAD_NUM_KEY),
+ getString(R.string.CPU_THREAD_NUM_DEFAULT)));
+ settingsChanged |= cpu_thread_num != cpuThreadNum;
+ String cpu_power_mode =
+ sharedPreferences.getString(getString(R.string.CPU_POWER_MODE_KEY),
+ getString(R.string.CPU_POWER_MODE_DEFAULT));
+ settingsChanged |= !cpu_power_mode.equalsIgnoreCase(cpuPowerMode);
+ String input_color_format =
+ sharedPreferences.getString(getString(R.string.INPUT_COLOR_FORMAT_KEY),
+ getString(R.string.INPUT_COLOR_FORMAT_DEFAULT));
+ settingsChanged |= !input_color_format.equalsIgnoreCase(inputColorFormat);
+ long[] input_shape =
+ Utils.parseLongsFromString(sharedPreferences.getString(getString(R.string.INPUT_SHAPE_KEY),
+ getString(R.string.INPUT_SHAPE_DEFAULT)), ",");
+ float[] input_mean =
+ Utils.parseFloatsFromString(sharedPreferences.getString(getString(R.string.INPUT_MEAN_KEY),
+ getString(R.string.INPUT_MEAN_DEFAULT)), ",");
+ float[] input_std =
+ Utils.parseFloatsFromString(sharedPreferences.getString(getString(R.string.INPUT_STD_KEY)
+ , getString(R.string.INPUT_STD_DEFAULT)), ",");
+ settingsChanged |= input_shape.length != inputShape.length;
+ settingsChanged |= input_mean.length != inputMean.length;
+ settingsChanged |= input_std.length != inputStd.length;
+ if (!settingsChanged) {
+ for (int i = 0; i < input_shape.length; i++) {
+ settingsChanged |= input_shape[i] != inputShape[i];
+ }
+ for (int i = 0; i < input_mean.length; i++) {
+ settingsChanged |= input_mean[i] != inputMean[i];
+ }
+ for (int i = 0; i < input_std.length; i++) {
+ settingsChanged |= input_std[i] != inputStd[i];
+ }
+ }
+ float score_threshold =
+ Float.parseFloat(sharedPreferences.getString(getString(R.string.SCORE_THRESHOLD_KEY),
+ getString(R.string.SCORE_THRESHOLD_DEFAULT)));
+ settingsChanged |= scoreThreshold != score_threshold;
+ if (settingsChanged) {
+ modelPath = model_path;
+ labelPath = label_path;
+ imagePath = image_path;
+ cpuThreadNum = cpu_thread_num;
+ cpuPowerMode = cpu_power_mode;
+ inputColorFormat = input_color_format;
+ inputShape = input_shape;
+ inputMean = input_mean;
+ inputStd = input_std;
+ scoreThreshold = score_threshold;
+ // Update UI
+ tvInputSetting.setText("Model: " + modelPath.substring(modelPath.lastIndexOf("/") + 1) + "\n" + "CPU" +
+ " Thread Num: " + Integer.toString(cpuThreadNum) + "\n" + "CPU Power Mode: " + cpuPowerMode);
+ tvInputSetting.scrollTo(0, 0);
+ // Reload model if configure has been changed
+ loadModel();
+ }
+ }
+
+ 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 boolean onLoadModel() {
+ return predictor.init(MainActivity.this, modelPath, labelPath, cpuThreadNum,
+ cpuPowerMode,
+ inputColorFormat,
+ inputShape, inputMean,
+ inputStd, scoreThreshold);
+ }
+
+ public boolean onRunModel() {
+ return predictor.isLoaded() && predictor.runModel();
+ }
+
+ public void onLoadModelSuccessed() {
+ // Load test image from path and run model
+ try {
+ if (imagePath.isEmpty()) {
+ return;
+ }
+ Bitmap image = null;
+ // Read test image file from custom path if the first character of mode path is '/', otherwise read test
+ // image file from assets
+ if (!imagePath.substring(0, 1).equals("/")) {
+ InputStream imageStream = getAssets().open(imagePath);
+ image = BitmapFactory.decodeStream(imageStream);
+ } else {
+ if (!new File(imagePath).exists()) {
+ return;
+ }
+ image = BitmapFactory.decodeFile(imagePath);
+ }
+ if (image != null && predictor.isLoaded()) {
+ predictor.setInputImage(image);
+ runModel();
+ }
+ } catch (IOException e) {
+ Toast.makeText(MainActivity.this, "Load image failed!", Toast.LENGTH_SHORT).show();
+ e.printStackTrace();
+ }
+ }
+
+ public void onLoadModelFailed() {
+ }
+
+ public void onRunModelSuccessed() {
+ // Obtain results and update UI
+ tvInferenceTime.setText("Inference time: " + predictor.inferenceTime() + " ms");
+ Bitmap outputImage = predictor.outputImage();
+ if (outputImage != null) {
+ ivInputImage.setImageBitmap(outputImage);
+ }
+ tvOutputResult.setText(predictor.outputResult());
+ tvOutputResult.scrollTo(0, 0);
+ }
+
+ public void onRunModelFailed() {
+ }
+
+ public void onImageChanged(Bitmap image) {
+ // Rerun model if users pick test image from gallery or camera
+ if (image != null && predictor.isLoaded()) {
+ predictor.setInputImage(image);
+ runModel();
+ }
+ }
+
+ 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;
+ }
+
+ 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
+ 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();
+ }
+ }
+
+ 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
+ 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();
+ onImageChanged(image);
+ } catch (IOException e) {
+ Log.e(TAG, e.toString());
+ }
+ break;
+ case TAKE_PHOTO_REQUEST_CODE:
+ Bundle extras = data.getExtras();
+ Bitmap image = (Bitmap) extras.get("data");
+ onImageChanged(image);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (predictor != null) {
+ predictor.releaseModel();
+ }
+ worker.quit();
+ super.onDestroy();
+ }
+}
diff --git a/deploy/android_demo/app/src/main/java/com/baidu/paddle/lite/demo/ocr/OCRPredictorNative.java b/deploy/android_demo/app/src/main/java/com/baidu/paddle/lite/demo/ocr/OCRPredictorNative.java
new file mode 100644
index 000000000..103d5d37a
--- /dev/null
+++ b/deploy/android_demo/app/src/main/java/com/baidu/paddle/lite/demo/ocr/OCRPredictorNative.java
@@ -0,0 +1,100 @@
+package com.baidu.paddle.lite.demo.ocr;
+
+import android.graphics.Bitmap;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class OCRPredictorNative {
+
+ private static final AtomicBoolean isSOLoaded = new AtomicBoolean();
+
+ public static void loadLibrary() throws RuntimeException {
+ if (!isSOLoaded.get() && isSOLoaded.compareAndSet(false, true)) {
+ try {
+ System.loadLibrary("Native");
+ } catch (Throwable e) {
+ RuntimeException exception = new RuntimeException(
+ "Load libNative.so failed, please check it exists in apk file.", e);
+ throw exception;
+ }
+ }
+ }
+
+ private Config config;
+
+ private long nativePointer = 0;
+
+ public OCRPredictorNative(Config config) {
+ this.config = config;
+ loadLibrary();
+ nativePointer = init(config.detModelFilename, config.recModelFilename,
+ config.cpuThreadNum, config.cpuPower);
+ Log.i("OCRPredictorNative", "load success " + nativePointer);
+
+ }
+
+ public void release(){
+ if (nativePointer != 0){
+ nativePointer = 0;
+ destory(nativePointer);
+ }
+ }
+
+ public ArrayList runImage(float[] inputData, int width, int height, int channels, Bitmap originalImage) {
+ Log.i("OCRPredictorNative", "begin to run image " + inputData.length + " " + width + " " + height);
+ float[] dims = new float[]{1, channels, height, width};
+ float[] rawResults = forward(nativePointer, inputData, dims, originalImage);
+ ArrayList results = postprocess(rawResults);
+ return results;
+ }
+
+ public static class Config {
+ public int cpuThreadNum;
+ public String cpuPower;
+ public String detModelFilename;
+ public String recModelFilename;
+
+ }
+
+ protected native long init(String detModelPath, String recModelPath, int threadNum, String cpuMode);
+
+ protected native float[] forward(long pointer, float[] buf, float[] ddims, Bitmap originalImage);
+
+ protected native void destory(long pointer);
+
+ private ArrayList postprocess(float[] raw) {
+ ArrayList results = new ArrayList();
+ int begin = 0;
+
+ while (begin < raw.length) {
+ int point_num = Math.round(raw[begin]);
+ int word_num = Math.round(raw[begin + 1]);
+ OcrResultModel model = parse(raw, begin + 2, point_num, word_num);
+ begin += 2 + 1 + point_num * 2 + word_num;
+ results.add(model);
+ }
+
+ return results;
+ }
+
+ private OcrResultModel parse(float[] raw, int begin, int pointNum, int wordNum) {
+ int current = begin;
+ OcrResultModel model = new OcrResultModel();
+ model.setConfidence(raw[current]);
+ current++;
+ for (int i = 0; i < pointNum; i++) {
+ model.addPoints(Math.round(raw[current + i * 2]), Math.round(raw[current + i * 2 + 1]));
+ }
+ current += (pointNum * 2);
+ for (int i = 0; i < wordNum; i++) {
+ int index = Math.round(raw[current + i]);
+ model.addWordIndex(index);
+ }
+ Log.i("OCRPredictorNative", "word finished " + wordNum);
+ return model;
+ }
+
+
+}
diff --git a/deploy/android_demo/app/src/main/java/com/baidu/paddle/lite/demo/ocr/OcrResultModel.java b/deploy/android_demo/app/src/main/java/com/baidu/paddle/lite/demo/ocr/OcrResultModel.java
new file mode 100644
index 000000000..9494574e0
--- /dev/null
+++ b/deploy/android_demo/app/src/main/java/com/baidu/paddle/lite/demo/ocr/OcrResultModel.java
@@ -0,0 +1,52 @@
+package com.baidu.paddle.lite.demo.ocr;
+
+import android.graphics.Point;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class OcrResultModel {
+ private List points;
+ private List wordIndex;
+ private String label;
+ private float confidence;
+
+ public OcrResultModel() {
+ super();
+ points = new ArrayList<>();
+ wordIndex = new ArrayList<>();
+ }
+
+ public void addPoints(int x, int y) {
+ Point point = new Point(x, y);
+ points.add(point);
+ }
+
+ public void addWordIndex(int index) {
+ wordIndex.add(index);
+ }
+
+ public List getPoints() {
+ return points;
+ }
+
+ public List getWordIndex() {
+ return wordIndex;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public float getConfidence() {
+ return confidence;
+ }
+
+ public void setConfidence(float confidence) {
+ this.confidence = confidence;
+ }
+}
diff --git a/deploy/android_demo/app/src/main/java/com/baidu/paddle/lite/demo/ocr/Predictor.java b/deploy/android_demo/app/src/main/java/com/baidu/paddle/lite/demo/ocr/Predictor.java
new file mode 100644
index 000000000..d491481e7
--- /dev/null
+++ b/deploy/android_demo/app/src/main/java/com/baidu/paddle/lite/demo/ocr/Predictor.java
@@ -0,0 +1,351 @@
+package com.baidu.paddle.lite.demo.ocr;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Point;
+import android.util.Log;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Vector;
+
+import static android.graphics.Color.*;
+
+public class Predictor {
+ private static final String TAG = Predictor.class.getSimpleName();
+ public boolean isLoaded = false;
+ public int warmupIterNum = 1;
+ public int inferIterNum = 1;
+ public int cpuThreadNum = 4;
+ public String cpuPowerMode = "LITE_POWER_HIGH";
+ public String modelPath = "";
+ public String modelName = "";
+ protected OCRPredictorNative paddlePredictor = null;
+ protected float inferenceTime = 0;
+ // Only for object detection
+ protected Vector wordLabels = new Vector();
+ protected String inputColorFormat = "BGR";
+ protected long[] inputShape = new long[]{1, 3, 960};
+ protected float[] inputMean = new float[]{0.485f, 0.456f, 0.406f};
+ protected float[] inputStd = new float[]{1.0f / 0.229f, 1.0f / 0.224f, 1.0f / 0.225f};
+ protected float scoreThreshold = 0.1f;
+ protected Bitmap inputImage = null;
+ protected Bitmap outputImage = null;
+ protected String outputResult = "";
+ protected float preprocessTime = 0;
+ protected float postprocessTime = 0;
+
+
+ public Predictor() {
+ }
+
+ public boolean init(Context appCtx, String modelPath, String labelPath, int cpuThreadNum, String cpuPowerMode,
+ String inputColorFormat,
+ long[] inputShape, float[] inputMean,
+ float[] inputStd, float scoreThreshold) {
+ if (inputShape.length != 3) {
+ Log.e(TAG, "Size of input shape should be: 3");
+ return false;
+ }
+ if (inputMean.length != inputShape[1]) {
+ Log.e(TAG, "Size of input mean should be: " + Long.toString(inputShape[1]));
+ return false;
+ }
+ if (inputStd.length != inputShape[1]) {
+ Log.e(TAG, "Size of input std should be: " + Long.toString(inputShape[1]));
+ return false;
+ }
+ if (inputShape[0] != 1) {
+ Log.e(TAG, "Only one batch is supported in the image classification demo, you can use any batch size in " +
+ "your Apps!");
+ return false;
+ }
+ if (inputShape[1] != 1 && inputShape[1] != 3) {
+ Log.e(TAG, "Only one/three channels are supported in the image classification demo, you can use any " +
+ "channel size in your Apps!");
+ return false;
+ }
+ if (!inputColorFormat.equalsIgnoreCase("BGR")) {
+ Log.e(TAG, "Only BGR color format is supported.");
+ return false;
+ }
+ isLoaded = loadModel(appCtx, modelPath, cpuThreadNum, cpuPowerMode);
+ if (!isLoaded) {
+ return false;
+ }
+ isLoaded = loadLabel(appCtx, labelPath);
+ if (!isLoaded) {
+ return false;
+ }
+ this.inputColorFormat = inputColorFormat;
+ this.inputShape = inputShape;
+ this.inputMean = inputMean;
+ this.inputStd = inputStd;
+ this.scoreThreshold = scoreThreshold;
+ return true;
+ }
+
+ protected boolean loadModel(Context appCtx, String modelPath, int cpuThreadNum, String cpuPowerMode) {
+ // Release model if exists
+ releaseModel();
+
+ // Load model
+ if (modelPath.isEmpty()) {
+ return false;
+ }
+ String realPath = modelPath;
+ if (!modelPath.substring(0, 1).equals("/")) {
+ // Read model files from custom path if the first character of mode path is '/'
+ // otherwise copy model to cache from assets
+ realPath = appCtx.getCacheDir() + "/" + modelPath;
+ Utils.copyDirectoryFromAssets(appCtx, modelPath, realPath);
+ }
+ if (realPath.isEmpty()) {
+ return false;
+ }
+
+ OCRPredictorNative.Config config = new OCRPredictorNative.Config();
+ config.cpuThreadNum = cpuThreadNum;
+ config.detModelFilename = realPath + File.separator + "ch_det_mv3_db_opt.nb";
+ config.recModelFilename = realPath + File.separator + "ch_rec_mv3_crnn_opt.nb";
+ Log.e("Predictor", "model path" + config.detModelFilename + " ; " + config.recModelFilename);
+ config.cpuPower = cpuPowerMode;
+ paddlePredictor = new OCRPredictorNative(config);
+
+ this.cpuThreadNum = cpuThreadNum;
+ this.cpuPowerMode = cpuPowerMode;
+ this.modelPath = realPath;
+ this.modelName = realPath.substring(realPath.lastIndexOf("/") + 1);
+ return true;
+ }
+
+ public void releaseModel() {
+ if (paddlePredictor != null){
+ paddlePredictor.release();
+ paddlePredictor = null;
+ }
+ isLoaded = false;
+ cpuThreadNum = 4;
+ cpuPowerMode = "LITE_POWER_HIGH";
+ modelPath = "";
+ modelName = "";
+ }
+
+ protected boolean loadLabel(Context appCtx, String labelPath) {
+ wordLabels.clear();
+ // Load word labels from file
+ try {
+ InputStream assetsInputStream = appCtx.getAssets().open(labelPath);
+ int available = assetsInputStream.available();
+ byte[] lines = new byte[available];
+ assetsInputStream.read(lines);
+ assetsInputStream.close();
+ String words = new String(lines);
+ String[] contents = words.split("\n");
+ for (String content : contents) {
+ wordLabels.add(content);
+ }
+ Log.i(TAG, "Word label size: " + wordLabels.size());
+ } catch (Exception e) {
+ Log.e(TAG, e.getMessage());
+ return false;
+ }
+ return true;
+ }
+
+
+ public boolean runModel() {
+ if (inputImage == null || !isLoaded()) {
+ return false;
+ }
+
+ // Pre-process image, and feed input tensor with pre-processed data
+
+ Bitmap scaleImage = Utils.resizeWithStep(inputImage, Long.valueOf(inputShape[2]).intValue(), 32);
+
+ Date start = new Date();
+ int channels = (int) inputShape[1];
+ int width = scaleImage.getWidth();
+ int height = scaleImage.getHeight();
+ float[] inputData = new float[channels * width * height];
+ if (channels == 3) {
+ int[] channelIdx = null;
+ if (inputColorFormat.equalsIgnoreCase("RGB")) {
+ channelIdx = new int[]{0, 1, 2};
+ } else if (inputColorFormat.equalsIgnoreCase("BGR")) {
+ channelIdx = new int[]{2, 1, 0};
+ } else {
+ Log.i(TAG, "Unknown color format " + inputColorFormat + ", only RGB and BGR color format is " +
+ "supported!");
+ return false;
+ }
+ int[] channelStride = new int[]{width * height, width * height * 2};
+ int p = scaleImage.getPixel(scaleImage.getWidth() - 1, scaleImage.getHeight() - 1);
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ int color = scaleImage.getPixel(x, y);
+ float[] rgb = new float[]{(float) red(color) / 255.0f, (float) green(color) / 255.0f,
+ (float) blue(color) / 255.0f};
+ inputData[y * width + x] = (rgb[channelIdx[0]] - inputMean[0]) / inputStd[0];
+ inputData[y * width + x + channelStride[0]] = (rgb[channelIdx[1]] - inputMean[1]) / inputStd[1];
+ inputData[y * width + x + channelStride[1]] = (rgb[channelIdx[2]] - inputMean[2]) / inputStd[2];
+
+ }
+ }
+ } else if (channels == 1) {
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ int color = inputImage.getPixel(x, y);
+ float gray = (float) (red(color) + green(color) + blue(color)) / 3.0f / 255.0f;
+ inputData[y * width + x] = (gray - inputMean[0]) / inputStd[0];
+ }
+ }
+ } else {
+ Log.i(TAG, "Unsupported channel size " + Integer.toString(channels) + ", only channel 1 and 3 is " +
+ "supported!");
+ return false;
+ }
+ float[] pixels = inputData;
+ Log.i(TAG, "pixels " + pixels[0] + " " + pixels[1] + " " + pixels[2] + " " + pixels[3]
+ + " " + pixels[pixels.length / 2] + " " + pixels[pixels.length / 2 + 1] + " " + pixels[pixels.length - 2] + " " + pixels[pixels.length - 1]);
+ Date end = new Date();
+ preprocessTime = (float) (end.getTime() - start.getTime());
+
+ // Warm up
+ for (int i = 0; i < warmupIterNum; i++) {
+ paddlePredictor.runImage(inputData, width, height, channels, inputImage);
+ }
+ warmupIterNum = 0; // 之后不要再warm了
+ // Run inference
+ start = new Date();
+ ArrayList results = paddlePredictor.runImage(inputData, width, height, channels, inputImage);
+ end = new Date();
+ inferenceTime = (end.getTime() - start.getTime()) / (float) inferIterNum;
+
+ results = postprocess(results);
+ Log.i(TAG, "[stat] Preprocess Time: " + preprocessTime
+ + " ; Inference Time: " + inferenceTime + " ;Box Size " + results.size());
+ drawResults(results);
+
+ return true;
+ }
+
+
+ public boolean isLoaded() {
+ return paddlePredictor != null && isLoaded;
+ }
+
+ public String modelPath() {
+ return modelPath;
+ }
+
+ public String modelName() {
+ return modelName;
+ }
+
+ public int cpuThreadNum() {
+ return cpuThreadNum;
+ }
+
+ public String cpuPowerMode() {
+ return cpuPowerMode;
+ }
+
+ public float inferenceTime() {
+ return inferenceTime;
+ }
+
+ public Bitmap inputImage() {
+ return inputImage;
+ }
+
+ public Bitmap outputImage() {
+ return outputImage;
+ }
+
+ public String outputResult() {
+ return outputResult;
+ }
+
+ public float preprocessTime() {
+ return preprocessTime;
+ }
+
+ public float postprocessTime() {
+ return postprocessTime;
+ }
+
+
+ public void setInputImage(Bitmap image) {
+ if (image == null) {
+ return;
+ }
+ // Scale image to the size of input tensor
+ Bitmap rgbaImage = image.copy(Bitmap.Config.ARGB_8888, true);
+ this.inputImage = rgbaImage;
+ }
+
+ private ArrayList postprocess(ArrayList results) {
+ for (OcrResultModel r : results) {
+ StringBuffer word = new StringBuffer();
+ for (int index : r.getWordIndex()) {
+ if (index >= 0 && index < wordLabels.size()) {
+ word.append(wordLabels.get(index));
+ } else {
+ Log.e(TAG, "Word index is not in label list:" + index);
+ word.append("×");
+ }
+ }
+ r.setLabel(word.toString());
+ }
+ return results;
+ }
+
+ private void drawResults(ArrayList results) {
+ StringBuffer outputResultSb = new StringBuffer("");
+ for (int i=0;i points = result.getPoints();
+ path.moveTo(points.get(0).x, points.get(0).y);
+ for (int i = points.size() - 1; i >= 0; i--) {
+ Point p = points.get(i);
+ path.lineTo(p.x, p.y);
+ }
+ canvas.drawPath(path, paint);
+ canvas.drawPath(path, paintFillAlpha);
+ }
+ }
+
+}
diff --git a/deploy/android_demo/app/src/main/java/com/baidu/paddle/lite/demo/ocr/SettingsActivity.java b/deploy/android_demo/app/src/main/java/com/baidu/paddle/lite/demo/ocr/SettingsActivity.java
new file mode 100644
index 000000000..28727e60a
--- /dev/null
+++ b/deploy/android_demo/app/src/main/java/com/baidu/paddle/lite/demo/ocr/SettingsActivity.java
@@ -0,0 +1,200 @@
+package com.baidu.paddle.lite.demo.ocr;
+
+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 etLabelPath = null;
+ EditTextPreference etImagePath = null;
+ ListPreference lpCPUThreadNum = null;
+ ListPreference lpCPUPowerMode = null;
+ ListPreference lpInputColorFormat = null;
+ EditTextPreference etInputShape = null;
+ EditTextPreference etInputMean = null;
+ EditTextPreference etInputStd = null;
+ EditTextPreference etScoreThreshold = null;
+
+ List preInstalledModelPaths = null;
+ List preInstalledLabelPaths = null;
+ List preInstalledImagePaths = null;
+ List preInstalledInputShapes = null;
+ List preInstalledCPUThreadNums = null;
+ List preInstalledCPUPowerModes = null;
+ List preInstalledInputColorFormats = null;
+ List preInstalledInputMeans = null;
+ List preInstalledInputStds = null;
+ List preInstalledScoreThresholds = 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();
+ preInstalledLabelPaths = new ArrayList();
+ preInstalledImagePaths = new ArrayList();
+ preInstalledInputShapes = new ArrayList();
+ preInstalledCPUThreadNums = new ArrayList();
+ preInstalledCPUPowerModes = new ArrayList();
+ preInstalledInputColorFormats = new ArrayList();
+ preInstalledInputMeans = new ArrayList();
+ preInstalledInputStds = new ArrayList();
+ preInstalledScoreThresholds = new ArrayList();
+ // Add ssd_mobilenet_v1_pascalvoc_for_cpu
+ preInstalledModelPaths.add(getString(R.string.MODEL_PATH_DEFAULT));
+ preInstalledLabelPaths.add(getString(R.string.LABEL_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));
+ preInstalledInputColorFormats.add(getString(R.string.INPUT_COLOR_FORMAT_DEFAULT));
+ preInstalledInputShapes.add(getString(R.string.INPUT_SHAPE_DEFAULT));
+ preInstalledInputMeans.add(getString(R.string.INPUT_MEAN_DEFAULT));
+ preInstalledInputStds.add(getString(R.string.INPUT_STD_DEFAULT));
+ preInstalledScoreThresholds.add(getString(R.string.SCORE_THRESHOLD_DEFAULT));
+
+ // Setup 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() + ")");
+ etLabelPath = (EditTextPreference) findPreference(getString(R.string.LABEL_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));
+ lpInputColorFormat =
+ (ListPreference) findPreference(getString(R.string.INPUT_COLOR_FORMAT_KEY));
+ etInputShape = (EditTextPreference) findPreference(getString(R.string.INPUT_SHAPE_KEY));
+ etInputMean = (EditTextPreference) findPreference(getString(R.string.INPUT_MEAN_KEY));
+ etInputStd = (EditTextPreference) findPreference(getString(R.string.INPUT_STD_KEY));
+ etScoreThreshold = (EditTextPreference) findPreference(getString(R.string.SCORE_THRESHOLD_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.LABEL_PATH_KEY), preInstalledLabelPaths.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.putString(getString(R.string.INPUT_COLOR_FORMAT_KEY),
+ preInstalledInputColorFormats.get(modelIdx));
+ editor.putString(getString(R.string.INPUT_SHAPE_KEY), preInstalledInputShapes.get(modelIdx));
+ editor.putString(getString(R.string.INPUT_MEAN_KEY), preInstalledInputMeans.get(modelIdx));
+ editor.putString(getString(R.string.INPUT_STD_KEY), preInstalledInputStds.get(modelIdx));
+ editor.putString(getString(R.string.SCORE_THRESHOLD_KEY),
+ preInstalledScoreThresholds.get(modelIdx));
+ editor.commit();
+ }
+ lpChoosePreInstalledModel.setSummary(modelPath);
+ }
+ cbEnableCustomSettings.setChecked(enableCustomSettings);
+ etModelPath.setEnabled(enableCustomSettings);
+ etLabelPath.setEnabled(enableCustomSettings);
+ etImagePath.setEnabled(enableCustomSettings);
+ lpCPUThreadNum.setEnabled(enableCustomSettings);
+ lpCPUPowerMode.setEnabled(enableCustomSettings);
+ lpInputColorFormat.setEnabled(enableCustomSettings);
+ etInputShape.setEnabled(enableCustomSettings);
+ etInputMean.setEnabled(enableCustomSettings);
+ etInputStd.setEnabled(enableCustomSettings);
+ etScoreThreshold.setEnabled(enableCustomSettings);
+ modelPath = sharedPreferences.getString(getString(R.string.MODEL_PATH_KEY),
+ getString(R.string.MODEL_PATH_DEFAULT));
+ String labelPath = sharedPreferences.getString(getString(R.string.LABEL_PATH_KEY),
+ getString(R.string.LABEL_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));
+ String inputColorFormat = sharedPreferences.getString(getString(R.string.INPUT_COLOR_FORMAT_KEY),
+ getString(R.string.INPUT_COLOR_FORMAT_DEFAULT));
+ String inputShape = sharedPreferences.getString(getString(R.string.INPUT_SHAPE_KEY),
+ getString(R.string.INPUT_SHAPE_DEFAULT));
+ String inputMean = sharedPreferences.getString(getString(R.string.INPUT_MEAN_KEY),
+ getString(R.string.INPUT_MEAN_DEFAULT));
+ String inputStd = sharedPreferences.getString(getString(R.string.INPUT_STD_KEY),
+ getString(R.string.INPUT_STD_DEFAULT));
+ String scoreThreshold = sharedPreferences.getString(getString(R.string.SCORE_THRESHOLD_KEY),
+ getString(R.string.SCORE_THRESHOLD_DEFAULT));
+ etModelPath.setSummary(modelPath);
+ etModelPath.setText(modelPath);
+ etLabelPath.setSummary(labelPath);
+ etLabelPath.setText(labelPath);
+ etImagePath.setSummary(imagePath);
+ etImagePath.setText(imagePath);
+ lpCPUThreadNum.setValue(cpuThreadNum);
+ lpCPUThreadNum.setSummary(cpuThreadNum);
+ lpCPUPowerMode.setValue(cpuPowerMode);
+ lpCPUPowerMode.setSummary(cpuPowerMode);
+ lpInputColorFormat.setValue(inputColorFormat);
+ lpInputColorFormat.setSummary(inputColorFormat);
+ etInputShape.setSummary(inputShape);
+ etInputShape.setText(inputShape);
+ etInputMean.setSummary(inputMean);
+ etInputMean.setText(inputMean);
+ etInputStd.setSummary(inputStd);
+ etInputStd.setText(inputStd);
+ etScoreThreshold.setText(scoreThreshold);
+ etScoreThreshold.setSummary(scoreThreshold);
+ }
+
+ @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();
+ }
+}
diff --git a/deploy/android_demo/app/src/main/java/com/baidu/paddle/lite/demo/ocr/Utils.java b/deploy/android_demo/app/src/main/java/com/baidu/paddle/lite/demo/ocr/Utils.java
new file mode 100644
index 000000000..905494334
--- /dev/null
+++ b/deploy/android_demo/app/src/main/java/com/baidu/paddle/lite/demo/ocr/Utils.java
@@ -0,0 +1,113 @@
+package com.baidu.paddle.lite.demo.ocr;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.Environment;
+
+import java.io.*;
+
+public class Utils {
+ private static final String TAG = Utils.class.getSimpleName();
+
+ public static void copyFileFromAssets(Context appCtx, String srcPath, String dstPath) {
+ if (srcPath.isEmpty() || dstPath.isEmpty()) {
+ return;
+ }
+ InputStream is = null;
+ OutputStream os = null;
+ try {
+ is = new BufferedInputStream(appCtx.getAssets().open(srcPath));
+ os = new BufferedOutputStream(new FileOutputStream(new File(dstPath)));
+ byte[] buffer = new byte[1024];
+ int length = 0;
+ while ((length = is.read(buffer)) != -1) {
+ os.write(buffer, 0, length);
+ }
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ os.close();
+ is.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public static void copyDirectoryFromAssets(Context appCtx, String srcDir, String dstDir) {
+ if (srcDir.isEmpty() || dstDir.isEmpty()) {
+ return;
+ }
+ try {
+ if (!new File(dstDir).exists()) {
+ new File(dstDir).mkdirs();
+ }
+ for (String fileName : appCtx.getAssets().list(srcDir)) {
+ String srcSubPath = srcDir + File.separator + fileName;
+ String dstSubPath = dstDir + File.separator + fileName;
+ if (new File(srcSubPath).isDirectory()) {
+ copyDirectoryFromAssets(appCtx, srcSubPath, dstSubPath);
+ } else {
+ copyFileFromAssets(appCtx, srcSubPath, dstSubPath);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static float[] parseFloatsFromString(String string, String delimiter) {
+ String[] pieces = string.trim().toLowerCase().split(delimiter);
+ float[] floats = new float[pieces.length];
+ for (int i = 0; i < pieces.length; i++) {
+ floats[i] = Float.parseFloat(pieces[i].trim());
+ }
+ return floats;
+ }
+
+ public static long[] parseLongsFromString(String string, String delimiter) {
+ String[] pieces = string.trim().toLowerCase().split(delimiter);
+ long[] longs = new long[pieces.length];
+ for (int i = 0; i < pieces.length; i++) {
+ longs[i] = Long.parseLong(pieces[i].trim());
+ }
+ return longs;
+ }
+
+ public static String getSDCardDirectory() {
+ return Environment.getExternalStorageDirectory().getAbsolutePath();
+ }
+
+ public static boolean isSupportedNPU() {
+ return false;
+ // String hardware = android.os.Build.HARDWARE;
+ // return hardware.equalsIgnoreCase("kirin810") || hardware.equalsIgnoreCase("kirin990");
+ }
+
+ public static Bitmap resizeWithStep(Bitmap bitmap, int maxLength, int step) {
+ int width = bitmap.getWidth();
+ int height = bitmap.getHeight();
+ int maxWH = Math.max(width, height);
+ float ratio = 1;
+ int newWidth = width;
+ int newHeight = height;
+ if (maxWH > maxLength) {
+ ratio = maxLength * 1.0f / maxWH;
+ newWidth = (int) Math.floor(ratio * width);
+ newHeight = (int) Math.floor(ratio * height);
+ }
+
+ newWidth = newWidth - newWidth % step;
+ if (newWidth == 0) {
+ newWidth = step;
+ }
+ newHeight = newHeight - newHeight % step;
+ if (newHeight == 0) {
+ newHeight = step;
+ }
+ return Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true);
+ }
+}
diff --git a/deploy/android_demo/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/deploy/android_demo/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 000000000..1f6bb2906
--- /dev/null
+++ b/deploy/android_demo/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/deploy/android_demo/app/src/main/res/drawable/ic_launcher_background.xml b/deploy/android_demo/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 000000000..0d025f9bf
--- /dev/null
+++ b/deploy/android_demo/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/deploy/android_demo/app/src/main/res/layout/activity_main.xml b/deploy/android_demo/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 000000000..98b9bc1a8
--- /dev/null
+++ b/deploy/android_demo/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/deploy/android_demo/app/src/main/res/menu/menu_action_options.xml b/deploy/android_demo/app/src/main/res/menu/menu_action_options.xml
new file mode 100644
index 000000000..fe74758ae
--- /dev/null
+++ b/deploy/android_demo/app/src/main/res/menu/menu_action_options.xml
@@ -0,0 +1,21 @@
+
diff --git a/deploy/android_demo/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/deploy/android_demo/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 000000000..eca70cfe5
--- /dev/null
+++ b/deploy/android_demo/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/deploy/android_demo/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/deploy/android_demo/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 000000000..eca70cfe5
--- /dev/null
+++ b/deploy/android_demo/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/deploy/android_demo/app/src/main/res/mipmap-hdpi/ic_launcher.png b/deploy/android_demo/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..898f3ed59
Binary files /dev/null and b/deploy/android_demo/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/deploy/android_demo/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/deploy/android_demo/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 000000000..dffca3601
Binary files /dev/null and b/deploy/android_demo/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/deploy/android_demo/app/src/main/res/mipmap-mdpi/ic_launcher.png b/deploy/android_demo/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..64ba76f75
Binary files /dev/null and b/deploy/android_demo/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/deploy/android_demo/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/deploy/android_demo/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 000000000..dae5e0823
Binary files /dev/null and b/deploy/android_demo/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/deploy/android_demo/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/deploy/android_demo/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..e5ed46597
Binary files /dev/null and b/deploy/android_demo/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/deploy/android_demo/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/deploy/android_demo/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..14ed0af35
Binary files /dev/null and b/deploy/android_demo/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/deploy/android_demo/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/deploy/android_demo/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..b0907cac3
Binary files /dev/null and b/deploy/android_demo/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/deploy/android_demo/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/deploy/android_demo/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..d8ae03154
Binary files /dev/null and b/deploy/android_demo/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/deploy/android_demo/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/deploy/android_demo/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..2c18de9e6
Binary files /dev/null and b/deploy/android_demo/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/deploy/android_demo/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/deploy/android_demo/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..beed3cdd2
Binary files /dev/null and b/deploy/android_demo/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/deploy/android_demo/app/src/main/res/values/arrays.xml b/deploy/android_demo/app/src/main/res/values/arrays.xml
new file mode 100644
index 000000000..8e08ad57d
--- /dev/null
+++ b/deploy/android_demo/app/src/main/res/values/arrays.xml
@@ -0,0 +1,39 @@
+
+
+
+ - 1 threads
+ - 2 threads
+ - 4 threads
+ - 8 threads
+
+
+ - 1
+ - 2
+ - 4
+ - 8
+
+
+ - HIGH(only big cores)
+ - LOW(only LITTLE cores)
+ - FULL(all cores)
+ - NO_BIND(depends on system)
+ - RAND_HIGH
+ - RAND_LOW
+
+
+ - LITE_POWER_HIGH
+ - LITE_POWER_LOW
+ - LITE_POWER_FULL
+ - LITE_POWER_NO_BIND
+ - LITE_POWER_RAND_HIGH
+ - LITE_POWER_RAND_LOW
+
+
+ - BGR color format
+ - RGB color format
+
+
+ - BGR
+ - RGB
+
+
\ No newline at end of file
diff --git a/deploy/android_demo/app/src/main/res/values/colors.xml b/deploy/android_demo/app/src/main/res/values/colors.xml
new file mode 100644
index 000000000..69b22338c
--- /dev/null
+++ b/deploy/android_demo/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #008577
+ #00574B
+ #D81B60
+
diff --git a/deploy/android_demo/app/src/main/res/values/strings.xml b/deploy/android_demo/app/src/main/res/values/strings.xml
new file mode 100644
index 000000000..f092ae403
--- /dev/null
+++ b/deploy/android_demo/app/src/main/res/values/strings.xml
@@ -0,0 +1,26 @@
+
+ OCR Chinese
+ CHOOSE_PRE_INSTALLED_MODEL_KEY
+ ENABLE_CUSTOM_SETTINGS_KEY
+ MODEL_PATH_KEY
+ LABEL_PATH_KEY
+ IMAGE_PATH_KEY
+ CPU_THREAD_NUM_KEY
+ CPU_POWER_MODE_KEY
+ INPUT_COLOR_FORMAT_KEY
+ INPUT_SHAPE_KEY
+ INPUT_MEAN_KEY
+ INPUT_STD_KEY
+ SCORE_THRESHOLD_KEY
+ models/ocr_v1_for_cpu
+ labels/ppocr_keys_v1.txt
+ images/5.jpg
+ 4
+ LITE_POWER_HIGH
+ BGR
+ 1,3,960
+ 0.485, 0.456, 0.406
+ 0.229,0.224,0.225
+ 0.1
+
+
diff --git a/deploy/android_demo/app/src/main/res/values/styles.xml b/deploy/android_demo/app/src/main/res/values/styles.xml
new file mode 100644
index 000000000..853262016
--- /dev/null
+++ b/deploy/android_demo/app/src/main/res/values/styles.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/deploy/android_demo/app/src/main/res/xml/settings.xml b/deploy/android_demo/app/src/main/res/xml/settings.xml
new file mode 100644
index 000000000..440f3e478
--- /dev/null
+++ b/deploy/android_demo/app/src/main/res/xml/settings.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/deploy/android_demo/app/src/test/java/com/baidu/paddle/lite/demo/ocr/ExampleUnitTest.java b/deploy/android_demo/app/src/test/java/com/baidu/paddle/lite/demo/ocr/ExampleUnitTest.java
new file mode 100644
index 000000000..d523a9a70
--- /dev/null
+++ b/deploy/android_demo/app/src/test/java/com/baidu/paddle/lite/demo/ocr/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.baidu.paddle.lite.demo.ocr;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/deploy/android_demo/build.gradle b/deploy/android_demo/build.gradle
new file mode 100644
index 000000000..fafc1b970
--- /dev/null
+++ b/deploy/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
+}
diff --git a/deploy/android_demo/gradle.properties b/deploy/android_demo/gradle.properties
new file mode 100644
index 000000000..82618cecb
--- /dev/null
+++ b/deploy/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
+
+
diff --git a/deploy/android_demo/gradle/wrapper/gradle-wrapper.jar b/deploy/android_demo/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..f6b961fd5
Binary files /dev/null and b/deploy/android_demo/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/deploy/android_demo/gradle/wrapper/gradle-wrapper.properties b/deploy/android_demo/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..578b5482a
--- /dev/null
+++ b/deploy/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
diff --git a/deploy/android_demo/gradlew b/deploy/android_demo/gradlew
new file mode 100644
index 000000000..cccdd3d51
--- /dev/null
+++ b/deploy/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_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/deploy/android_demo/gradlew.bat b/deploy/android_demo/gradlew.bat
new file mode 100644
index 000000000..f9553162f
--- /dev/null
+++ b/deploy/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
diff --git a/deploy/android_demo/settings.gradle b/deploy/android_demo/settings.gradle
new file mode 100644
index 000000000..e7b4def49
--- /dev/null
+++ b/deploy/android_demo/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/deploy/cpp_infer/CMakeLists.txt b/deploy/cpp_infer/CMakeLists.txt
new file mode 100644
index 000000000..1415e2cb8
--- /dev/null
+++ b/deploy/cpp_infer/CMakeLists.txt
@@ -0,0 +1,105 @@
+project(ocr_system CXX C)
+option(WITH_MKL "Compile demo with MKL/OpenBlas support, default use MKL." ON)
+option(WITH_GPU "Compile demo with GPU/CPU, default use CPU." OFF)
+option(WITH_STATIC_LIB "Compile demo with static/shared library, default use static." ON)
+option(USE_TENSORRT "Compile demo with TensorRT." OFF)
+
+
+macro(safe_set_static_flag)
+ foreach(flag_var
+ CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
+ CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
+ if(${flag_var} MATCHES "/MD")
+ string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
+ endif(${flag_var} MATCHES "/MD")
+ endforeach(flag_var)
+endmacro()
+
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g -fpermissive")
+set(CMAKE_STATIC_LIBRARY_PREFIX "")
+message("flags" ${CMAKE_CXX_FLAGS})
+set(CMAKE_CXX_FLAGS_RELEASE "-O3")
+
+if(NOT DEFINED PADDLE_LIB)
+ message(FATAL_ERROR "please set PADDLE_LIB with -DPADDLE_LIB=/path/paddle/lib")
+endif()
+if(NOT DEFINED DEMO_NAME)
+ message(FATAL_ERROR "please set DEMO_NAME with -DDEMO_NAME=demo_name")
+endif()
+
+
+set(OPENCV_DIR ${OPENCV_DIR})
+find_package(OpenCV REQUIRED PATHS ${OPENCV_DIR}/share/OpenCV NO_DEFAULT_PATH)
+include_directories(${OpenCV_INCLUDE_DIRS})
+
+include_directories("${PADDLE_LIB}/paddle/include")
+include_directories("${PADDLE_LIB}/third_party/install/protobuf/include")
+include_directories("${PADDLE_LIB}/third_party/install/glog/include")
+include_directories("${PADDLE_LIB}/third_party/install/gflags/include")
+include_directories("${PADDLE_LIB}/third_party/install/xxhash/include")
+include_directories("${PADDLE_LIB}/third_party/install/zlib/include")
+include_directories("${PADDLE_LIB}/third_party/boost")
+include_directories("${PADDLE_LIB}/third_party/eigen3")
+
+include_directories("${CMAKE_SOURCE_DIR}/")
+
+if (USE_TENSORRT AND WITH_GPU)
+ include_directories("${TENSORRT_ROOT}/include")
+ link_directories("${TENSORRT_ROOT}/lib")
+endif()
+
+link_directories("${PADDLE_LIB}/third_party/install/zlib/lib")
+
+link_directories("${PADDLE_LIB}/third_party/install/protobuf/lib")
+link_directories("${PADDLE_LIB}/third_party/install/glog/lib")
+link_directories("${PADDLE_LIB}/third_party/install/gflags/lib")
+link_directories("${PADDLE_LIB}/third_party/install/xxhash/lib")
+link_directories("${PADDLE_LIB}/paddle/lib")
+
+
+AUX_SOURCE_DIRECTORY(./src SRCS)
+add_executable(${DEMO_NAME} ${SRCS})
+
+if(WITH_MKL)
+ include_directories("${PADDLE_LIB}/third_party/install/mklml/include")
+ set(MATH_LIB ${PADDLE_LIB}/third_party/install/mklml/lib/libmklml_intel${CMAKE_SHARED_LIBRARY_SUFFIX}
+ ${PADDLE_LIB}/third_party/install/mklml/lib/libiomp5${CMAKE_SHARED_LIBRARY_SUFFIX})
+ set(MKLDNN_PATH "${PADDLE_LIB}/third_party/install/mkldnn")
+ if(EXISTS ${MKLDNN_PATH})
+ include_directories("${MKLDNN_PATH}/include")
+ set(MKLDNN_LIB ${MKLDNN_PATH}/lib/libmkldnn.so.0)
+ endif()
+else()
+ set(MATH_LIB ${PADDLE_LIB}/third_party/install/openblas/lib/libopenblas${CMAKE_STATIC_LIBRARY_SUFFIX})
+endif()
+
+# Note: libpaddle_inference_api.so/a must put before libpaddle_fluid.so/a
+if(WITH_STATIC_LIB)
+ set(DEPS
+ ${PADDLE_LIB}/paddle/lib/libpaddle_fluid${CMAKE_STATIC_LIBRARY_SUFFIX})
+else()
+ set(DEPS
+ ${PADDLE_LIB}/paddle/lib/libpaddle_fluid${CMAKE_SHARED_LIBRARY_SUFFIX})
+endif()
+
+set(EXTERNAL_LIB "-lrt -ldl -lpthread -lm")
+
+set(DEPS ${DEPS}
+ ${MATH_LIB} ${MKLDNN_LIB}
+ glog gflags protobuf z xxhash
+ ${EXTERNAL_LIB} ${OpenCV_LIBS})
+
+if(WITH_GPU)
+ if (USE_TENSORRT)
+ set(DEPS ${DEPS}
+ ${TENSORRT_ROOT}/lib/libnvinfer${CMAKE_SHARED_LIBRARY_SUFFIX})
+ set(DEPS ${DEPS}
+ ${TENSORRT_ROOT}/lib/libnvinfer_plugin${CMAKE_SHARED_LIBRARY_SUFFIX})
+ endif()
+ set(DEPS ${DEPS} ${CUDA_LIB}/libcudart${CMAKE_SHARED_LIBRARY_SUFFIX})
+ set(DEPS ${DEPS} ${CUDA_LIB}/libcudart${CMAKE_SHARED_LIBRARY_SUFFIX} )
+ set(DEPS ${DEPS} ${CUDA_LIB}/libcublas${CMAKE_SHARED_LIBRARY_SUFFIX} )
+ set(DEPS ${DEPS} ${CUDNN_LIB}/libcudnn${CMAKE_SHARED_LIBRARY_SUFFIX} )
+endif()
+
+target_link_libraries(${DEMO_NAME} ${DEPS})
diff --git a/deploy/cpp_infer/include/clipper.h b/deploy/cpp_infer/include/clipper.h
new file mode 100644
index 000000000..384a6cf44
--- /dev/null
+++ b/deploy/cpp_infer/include/clipper.h
@@ -0,0 +1,423 @@
+/*******************************************************************************
+* *
+* Author : Angus Johnson *
+* Version : 6.4.2 *
+* Date : 27 February 2017 *
+* Website : http://www.angusj.com *
+* Copyright : Angus Johnson 2010-2017 *
+* *
+* License: *
+* Use, modification & distribution is subject to Boost Software License Ver 1. *
+* http://www.boost.org/LICENSE_1_0.txt *
+* *
+* Attributions: *
+* The code in this library is an extension of Bala Vatti's clipping algorithm: *
+* "A generic solution to polygon clipping" *
+* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. *
+* http://portal.acm.org/citation.cfm?id=129906 *
+* *
+* Computer graphics and geometric modeling: implementation and algorithms *
+* By Max K. Agoston *
+* Springer; 1 edition (January 4, 2005) *
+* http://books.google.com/books?q=vatti+clipping+agoston *
+* *
+* See also: *
+* "Polygon Offsetting by Computing Winding Numbers" *
+* Paper no. DETC2005-85513 pp. 565-575 *
+* ASME 2005 International Design Engineering Technical Conferences *
+* and Computers and Information in Engineering Conference (IDETC/CIE2005) *
+* September 24-28, 2005 , Long Beach, California, USA *
+* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf *
+* *
+*******************************************************************************/
+
+#ifndef clipper_hpp
+#define clipper_hpp
+
+#define CLIPPER_VERSION "6.4.2"
+
+// use_int32: When enabled 32bit ints are used instead of 64bit ints. This
+// improve performance but coordinate values are limited to the range +/- 46340
+//#define use_int32
+
+// use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance.
+//#define use_xyz
+
+// use_lines: Enables line clipping. Adds a very minor cost to performance.
+#define use_lines
+
+// use_deprecated: Enables temporary support for the obsolete functions
+//#define use_deprecated
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace ClipperLib {
+
+enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };
+enum PolyType { ptSubject, ptClip };
+// By far the most widely used winding rules for polygon filling are
+// EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32)
+// Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL)
+// see http://glprogramming.com/red/chapter11.html
+enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
+
+#ifdef use_int32
+typedef int cInt;
+static cInt const loRange = 0x7FFF;
+static cInt const hiRange = 0x7FFF;
+#else
+typedef signed long long cInt;
+static cInt const loRange = 0x3FFFFFFF;
+static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL;
+typedef signed long long long64; // used by Int128 class
+typedef unsigned long long ulong64;
+
+#endif
+
+struct IntPoint {
+ cInt X;
+ cInt Y;
+#ifdef use_xyz
+ cInt Z;
+ IntPoint(cInt x = 0, cInt y = 0, cInt z = 0) : X(x), Y(y), Z(z){};
+#else
+ IntPoint(cInt x = 0, cInt y = 0) : X(x), Y(y){};
+#endif
+
+ friend inline bool operator==(const IntPoint &a, const IntPoint &b) {
+ return a.X == b.X && a.Y == b.Y;
+ }
+ friend inline bool operator!=(const IntPoint &a, const IntPoint &b) {
+ return a.X != b.X || a.Y != b.Y;
+ }
+};
+//------------------------------------------------------------------------------
+
+typedef std::vector Path;
+typedef std::vector Paths;
+
+inline Path &operator<<(Path &poly, const IntPoint &p) {
+ poly.push_back(p);
+ return poly;
+}
+inline Paths &operator<<(Paths &polys, const Path &p) {
+ polys.push_back(p);
+ return polys;
+}
+
+std::ostream &operator<<(std::ostream &s, const IntPoint &p);
+std::ostream &operator<<(std::ostream &s, const Path &p);
+std::ostream &operator<<(std::ostream &s, const Paths &p);
+
+struct DoublePoint {
+ double X;
+ double Y;
+ DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {}
+ DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {}
+};
+//------------------------------------------------------------------------------
+
+#ifdef use_xyz
+typedef void (*ZFillCallback)(IntPoint &e1bot, IntPoint &e1top, IntPoint &e2bot,
+ IntPoint &e2top, IntPoint &pt);
+#endif
+
+enum InitOptions {
+ ioReverseSolution = 1,
+ ioStrictlySimple = 2,
+ ioPreserveCollinear = 4
+};
+enum JoinType { jtSquare, jtRound, jtMiter };
+enum EndType {
+ etClosedPolygon,
+ etClosedLine,
+ etOpenButt,
+ etOpenSquare,
+ etOpenRound
+};
+
+class PolyNode;
+typedef std::vector PolyNodes;
+
+class PolyNode {
+public:
+ PolyNode();
+ virtual ~PolyNode(){};
+ Path Contour;
+ PolyNodes Childs;
+ PolyNode *Parent;
+ PolyNode *GetNext() const;
+ bool IsHole() const;
+ bool IsOpen() const;
+ int ChildCount() const;
+
+private:
+ // PolyNode& operator =(PolyNode& other);
+ unsigned Index; // node index in Parent.Childs
+ bool m_IsOpen;
+ JoinType m_jointype;
+ EndType m_endtype;
+ PolyNode *GetNextSiblingUp() const;
+ void AddChild(PolyNode &child);
+ friend class Clipper; // to access Index
+ friend class ClipperOffset;
+};
+
+class PolyTree : public PolyNode {
+public:
+ ~PolyTree() { Clear(); };
+ PolyNode *GetFirst() const;
+ void Clear();
+ int Total() const;
+
+private:
+ // PolyTree& operator =(PolyTree& other);
+ PolyNodes AllNodes;
+ friend class Clipper; // to access AllNodes
+};
+
+bool Orientation(const Path &poly);
+double Area(const Path &poly);
+int PointInPolygon(const IntPoint &pt, const Path &path);
+
+void SimplifyPolygon(const Path &in_poly, Paths &out_polys,
+ PolyFillType fillType = pftEvenOdd);
+void SimplifyPolygons(const Paths &in_polys, Paths &out_polys,
+ PolyFillType fillType = pftEvenOdd);
+void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd);
+
+void CleanPolygon(const Path &in_poly, Path &out_poly, double distance = 1.415);
+void CleanPolygon(Path &poly, double distance = 1.415);
+void CleanPolygons(const Paths &in_polys, Paths &out_polys,
+ double distance = 1.415);
+void CleanPolygons(Paths &polys, double distance = 1.415);
+
+void MinkowskiSum(const Path &pattern, const Path &path, Paths &solution,
+ bool pathIsClosed);
+void MinkowskiSum(const Path &pattern, const Paths &paths, Paths &solution,
+ bool pathIsClosed);
+void MinkowskiDiff(const Path &poly1, const Path &poly2, Paths &solution);
+
+void PolyTreeToPaths(const PolyTree &polytree, Paths &paths);
+void ClosedPathsFromPolyTree(const PolyTree &polytree, Paths &paths);
+void OpenPathsFromPolyTree(PolyTree &polytree, Paths &paths);
+
+void ReversePath(Path &p);
+void ReversePaths(Paths &p);
+
+struct IntRect {
+ cInt left;
+ cInt top;
+ cInt right;
+ cInt bottom;
+};
+
+// enums that are used internally ...
+enum EdgeSide { esLeft = 1, esRight = 2 };
+
+// forward declarations (for stuff used internally) ...
+struct TEdge;
+struct IntersectNode;
+struct LocalMinimum;
+struct OutPt;
+struct OutRec;
+struct Join;
+
+typedef std::vector PolyOutList;
+typedef std::vector EdgeList;
+typedef std::vector JoinList;
+typedef std::vector IntersectList;
+
+//------------------------------------------------------------------------------
+
+// ClipperBase is the ancestor to the Clipper class. It should not be
+// instantiated directly. This class simply abstracts the conversion of sets of
+// polygon coordinates into edge objects that are stored in a LocalMinima list.
+class ClipperBase {
+public:
+ ClipperBase();
+ virtual ~ClipperBase();
+ virtual bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed);
+ bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed);
+ virtual void Clear();
+ IntRect GetBounds();
+ bool PreserveCollinear() { return m_PreserveCollinear; };
+ void PreserveCollinear(bool value) { m_PreserveCollinear = value; };
+
+protected:
+ void DisposeLocalMinimaList();
+ TEdge *AddBoundsToLML(TEdge *e, bool IsClosed);
+ virtual void Reset();
+ TEdge *ProcessBound(TEdge *E, bool IsClockwise);
+ void InsertScanbeam(const cInt Y);
+ bool PopScanbeam(cInt &Y);
+ bool LocalMinimaPending();
+ bool PopLocalMinima(cInt Y, const LocalMinimum *&locMin);
+ OutRec *CreateOutRec();
+ void DisposeAllOutRecs();
+ void DisposeOutRec(PolyOutList::size_type index);
+ void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);
+ void DeleteFromAEL(TEdge *e);
+ void UpdateEdgeIntoAEL(TEdge *&e);
+
+ typedef std::vector MinimaList;
+ MinimaList::iterator m_CurrentLM;
+ MinimaList m_MinimaList;
+
+ bool m_UseFullRange;
+ EdgeList m_edges;
+ bool m_PreserveCollinear;
+ bool m_HasOpenPaths;
+ PolyOutList m_PolyOuts;
+ TEdge *m_ActiveEdges;
+
+ typedef std::priority_queue ScanbeamList;
+ ScanbeamList m_Scanbeam;
+};
+//------------------------------------------------------------------------------
+
+class Clipper : public virtual ClipperBase {
+public:
+ Clipper(int initOptions = 0);
+ bool Execute(ClipType clipType, Paths &solution,
+ PolyFillType fillType = pftEvenOdd);
+ bool Execute(ClipType clipType, Paths &solution, PolyFillType subjFillType,
+ PolyFillType clipFillType);
+ bool Execute(ClipType clipType, PolyTree &polytree,
+ PolyFillType fillType = pftEvenOdd);
+ bool Execute(ClipType clipType, PolyTree &polytree, PolyFillType subjFillType,
+ PolyFillType clipFillType);
+ bool ReverseSolution() { return m_ReverseOutput; };
+ void ReverseSolution(bool value) { m_ReverseOutput = value; };
+ bool StrictlySimple() { return m_StrictSimple; };
+ void StrictlySimple(bool value) { m_StrictSimple = value; };
+// set the callback function for z value filling on intersections (otherwise Z
+// is 0)
+#ifdef use_xyz
+ void ZFillFunction(ZFillCallback zFillFunc);
+#endif
+protected:
+ virtual bool ExecuteInternal();
+
+private:
+ JoinList m_Joins;
+ JoinList m_GhostJoins;
+ IntersectList m_IntersectList;
+ ClipType m_ClipType;
+ typedef std::list MaximaList;
+ MaximaList m_Maxima;
+ TEdge *m_SortedEdges;
+ bool m_ExecuteLocked;
+ PolyFillType m_ClipFillType;
+ PolyFillType m_SubjFillType;
+ bool m_ReverseOutput;
+ bool m_UsingPolyTree;
+ bool m_StrictSimple;
+#ifdef use_xyz
+ ZFillCallback m_ZFill; // custom callback
+#endif
+ void SetWindingCount(TEdge &edge);
+ bool IsEvenOddFillType(const TEdge &edge) const;
+ bool IsEvenOddAltFillType(const TEdge &edge) const;
+ void InsertLocalMinimaIntoAEL(const cInt botY);
+ void InsertEdgeIntoAEL(TEdge *edge, TEdge *startEdge);
+ void AddEdgeToSEL(TEdge *edge);
+ bool PopEdgeFromSEL(TEdge *&edge);
+ void CopyAELToSEL();
+ void DeleteFromSEL(TEdge *e);
+ void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2);
+ bool IsContributing(const TEdge &edge) const;
+ bool IsTopHorz(const cInt XPos);
+ void DoMaxima(TEdge *e);
+ void ProcessHorizontals();
+ void ProcessHorizontal(TEdge *horzEdge);
+ void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
+ OutPt *AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
+ OutRec *GetOutRec(int idx);
+ void AppendPolygon(TEdge *e1, TEdge *e2);
+ void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt);
+ OutPt *AddOutPt(TEdge *e, const IntPoint &pt);
+ OutPt *GetLastOutPt(TEdge *e);
+ bool ProcessIntersections(const cInt topY);
+ void BuildIntersectList(const cInt topY);
+ void ProcessIntersectList();
+ void ProcessEdgesAtTopOfScanbeam(const cInt topY);
+ void BuildResult(Paths &polys);
+ void BuildResult2(PolyTree &polytree);
+ void SetHoleState(TEdge *e, OutRec *outrec);
+ void DisposeIntersectNodes();
+ bool FixupIntersectionOrder();
+ void FixupOutPolygon(OutRec &outrec);
+ void FixupOutPolyline(OutRec &outrec);
+ bool IsHole(TEdge *e);
+ bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl);
+ void FixHoleLinkage(OutRec &outrec);
+ void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt);
+ void ClearJoins();
+ void ClearGhostJoins();
+ void AddGhostJoin(OutPt *op, const IntPoint offPt);
+ bool JoinPoints(Join *j, OutRec *outRec1, OutRec *outRec2);
+ void JoinCommonEdges();
+ void DoSimplePolygons();
+ void FixupFirstLefts1(OutRec *OldOutRec, OutRec *NewOutRec);
+ void FixupFirstLefts2(OutRec *InnerOutRec, OutRec *OuterOutRec);
+ void FixupFirstLefts3(OutRec *OldOutRec, OutRec *NewOutRec);
+#ifdef use_xyz
+ void SetZ(IntPoint &pt, TEdge &e1, TEdge &e2);
+#endif
+};
+//------------------------------------------------------------------------------
+
+class ClipperOffset {
+public:
+ ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25);
+ ~ClipperOffset();
+ void AddPath(const Path &path, JoinType joinType, EndType endType);
+ void AddPaths(const Paths &paths, JoinType joinType, EndType endType);
+ void Execute(Paths &solution, double delta);
+ void Execute(PolyTree &solution, double delta);
+ void Clear();
+ double MiterLimit;
+ double ArcTolerance;
+
+private:
+ Paths m_destPolys;
+ Path m_srcPoly;
+ Path m_destPoly;
+ std::vector m_normals;
+ double m_delta, m_sinA, m_sin, m_cos;
+ double m_miterLim, m_StepsPerRad;
+ IntPoint m_lowest;
+ PolyNode m_polyNodes;
+
+ void FixOrientations();
+ void DoOffset(double delta);
+ void OffsetPoint(int j, int &k, JoinType jointype);
+ void DoSquare(int j, int k);
+ void DoMiter(int j, int k, double r);
+ void DoRound(int j, int k);
+};
+//------------------------------------------------------------------------------
+
+class clipperException : public std::exception {
+public:
+ clipperException(const char *description) : m_descr(description) {}
+ virtual ~clipperException() throw() {}
+ virtual const char *what() const throw() { return m_descr.c_str(); }
+
+private:
+ std::string m_descr;
+};
+//------------------------------------------------------------------------------
+
+} // ClipperLib namespace
+
+#endif // clipper_hpp
diff --git a/deploy/cpp_infer/include/config.h b/deploy/cpp_infer/include/config.h
new file mode 100644
index 000000000..2adefb736
--- /dev/null
+++ b/deploy/cpp_infer/include/config.h
@@ -0,0 +1,99 @@
+// 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.
+
+#pragma once
+
+#include
+#include
+#include