& 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 0000000000000000000000000000000000000000..49af0afea425561d65d435a8fe67e96e98912680
--- /dev/null
+++ b/deploy/android_demo/app/src/main/java/com/baidu/paddle/lite/demo/ocr/AppCompatPreferenceActivity.java
@@ -0,0 +1,128 @@
+/*
+ * 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.view.MenuInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.LayoutRes;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatDelegate;
+import androidx.appcompat.widget.Toolbar;
+
+/**
+ * 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 0000000000000000000000000000000000000000..afb261dcf2afb2a2eaebf58c8c1f30f89200c902
--- /dev/null
+++ b/deploy/android_demo/app/src/main/java/com/baidu/paddle/lite/demo/ocr/MainActivity.java
@@ -0,0 +1,473 @@
+package com.baidu.paddle.lite.demo.ocr;
+
+import android.Manifest;
+import android.app.ProgressDialog;
+import android.content.ContentResolver;
+import android.content.Context;
+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.media.ExifInterface;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.preference.PreferenceManager;
+import android.provider.MediaStore;
+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 androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+import androidx.core.content.FileProvider;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+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;
+ private String currentPhotoPath;
+
+ 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 takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
+ // Ensure that there's a camera activity to handle the intent
+ if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
+ // Create the File where the photo should go
+ File photoFile = null;
+ try {
+ photoFile = createImageFile();
+ } catch (IOException ex) {
+ Log.e("MainActitity", ex.getMessage(), ex);
+ Toast.makeText(MainActivity.this,
+ "Create Camera temp file failed: " + ex.getMessage(), Toast.LENGTH_SHORT).show();
+ }
+ // Continue only if the File was successfully created
+ if (photoFile != null) {
+ Log.i(TAG, "FILEPATH " + getExternalFilesDir("Pictures").getAbsolutePath());
+ Uri photoURI = FileProvider.getUriForFile(this,
+ "com.baidu.paddle.lite.demo.ocr.fileprovider",
+ photoFile);
+ currentPhotoPath = photoFile.getAbsolutePath();
+ takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
+ startActivityForResult(takePictureIntent, TAKE_PHOTO_REQUEST_CODE);
+ Log.i(TAG, "startActivityForResult finished");
+ }
+ }
+
+ }
+
+ private File createImageFile() throws IOException {
+ // Create an image file name
+ String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
+ String imageFileName = "JPEG_" + timeStamp + "_";
+ File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
+ File image = File.createTempFile(
+ imageFileName, /* prefix */
+ ".bmp", /* suffix */
+ storageDir /* directory */
+ );
+
+ return image;
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (resultCode == RESULT_OK) {
+ switch (requestCode) {
+ case OPEN_GALLERY_REQUEST_CODE:
+ if (data == null) {
+ break;
+ }
+ 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:
+ if (currentPhotoPath != null) {
+ ExifInterface exif = null;
+ try {
+ exif = new ExifInterface(currentPhotoPath);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
+ ExifInterface.ORIENTATION_UNDEFINED);
+ Log.i(TAG, "rotation " + orientation);
+ Bitmap image = BitmapFactory.decodeFile(currentPhotoPath);
+ image = Utils.rotateBitmap(image, orientation);
+ onImageChanged(image);
+ } else {
+ Log.e(TAG, "currentPhotoPath is null");
+ }
+ 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/MiniActivity.java b/deploy/android_demo/app/src/main/java/com/baidu/paddle/lite/demo/ocr/MiniActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..d5608911db2e043657eb01b9e8e92fe9b79c99b7
--- /dev/null
+++ b/deploy/android_demo/app/src/main/java/com/baidu/paddle/lite/demo/ocr/MiniActivity.java
@@ -0,0 +1,157 @@
+package com.baidu.paddle.lite.demo.ocr;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class MiniActivity extends AppCompatActivity {
+
+
+ public static final int REQUEST_LOAD_MODEL = 0;
+ public static final int REQUEST_RUN_MODEL = 1;
+ public static final int REQUEST_UNLOAD_MODEL = 2;
+ public static final int RESPONSE_LOAD_MODEL_SUCCESSED = 0;
+ public static final int RESPONSE_LOAD_MODEL_FAILED = 1;
+ public static final int RESPONSE_RUN_MODEL_SUCCESSED = 2;
+ public static final int RESPONSE_RUN_MODEL_FAILED = 3;
+
+ private static final String TAG = "MiniActivity";
+
+ protected Handler receiver = null; // Receive messages from worker thread
+ protected Handler sender = null; // Send command to worker thread
+ protected HandlerThread worker = null; // Worker thread to load&run model
+ protected volatile Predictor predictor = null;
+
+ private String assetModelDirPath = "models/ocr_v1_for_cpu";
+ private String assetlabelFilePath = "labels/ppocr_keys_v1.txt";
+
+ private Button button;
+ private ImageView imageView; // image result
+ private TextView textView; // text result
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_mini);
+
+ Log.i(TAG, "SHOW in Logcat");
+
+ // Prepare the worker thread for mode loading and inference
+ 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()) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(MiniActivity.this, "Load model failed!", Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+ break;
+ case REQUEST_RUN_MODEL:
+ // Run model if model is loaded
+ final boolean isSuccessed = onRunModel();
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if (isSuccessed){
+ onRunModelSuccessed();
+ }else{
+ Toast.makeText(MiniActivity.this, "Run model failed!", Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+ break;
+ }
+ }
+ };
+ sender.sendEmptyMessage(REQUEST_LOAD_MODEL); // corresponding to REQUEST_LOAD_MODEL, to call onLoadModel()
+
+ imageView = findViewById(R.id.imageView);
+ textView = findViewById(R.id.sample_text);
+ button = findViewById(R.id.button);
+ button.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ sender.sendEmptyMessage(REQUEST_RUN_MODEL);
+ }
+ });
+
+
+ }
+
+ @Override
+ protected void onDestroy() {
+ onUnloadModel();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ worker.quitSafely();
+ } else {
+ worker.quit();
+ }
+ super.onDestroy();
+ }
+
+ /**
+ * call in onCreate, model init
+ *
+ * @return
+ */
+ private boolean onLoadModel() {
+ if (predictor == null) {
+ predictor = new Predictor();
+ }
+ return predictor.init(this, assetModelDirPath, assetlabelFilePath);
+ }
+
+ /**
+ * init engine
+ * call in onCreate
+ *
+ * @return
+ */
+ private boolean onRunModel() {
+ try {
+ String assetImagePath = "images/5.jpg";
+ InputStream imageStream = getAssets().open(assetImagePath);
+ Bitmap image = BitmapFactory.decodeStream(imageStream);
+ // Input is Bitmap
+ predictor.setInputImage(image);
+ return predictor.isLoaded() && predictor.runModel();
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ private void onRunModelSuccessed() {
+ Log.i(TAG, "onRunModelSuccessed");
+ textView.setText(predictor.outputResult);
+ imageView.setImageBitmap(predictor.outputImage);
+ }
+
+ private void onUnloadModel() {
+ if (predictor != null) {
+ predictor.releaseModel();
+ }
+ }
+}
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 0000000000000000000000000000000000000000..2e78a3ece96bb5e37bebcdda7ebc77060686b710
--- /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 0000000000000000000000000000000000000000..9494574e0db2d0c5d11ce4ad1cb400710c71a623
--- /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 0000000000000000000000000000000000000000..078bba286cc9cd5f9904e0594b5608c755a2b131
--- /dev/null
+++ b/deploy/android_demo/app/src/main/java/com/baidu/paddle/lite/demo/ocr/Predictor.java
@@ -0,0 +1,355 @@
+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 volatile String outputResult = "";
+ protected float preprocessTime = 0;
+ protected float postprocessTime = 0;
+
+
+ public Predictor() {
+ }
+
+ public boolean init(Context appCtx, String modelPath, String labelPath) {
+ isLoaded = loadModel(appCtx, modelPath, cpuThreadNum, cpuPowerMode);
+ if (!isLoaded) {
+ return false;
+ }
+ isLoaded = loadLabel(appCtx, labelPath);
+ return isLoaded;
+ }
+
+
+ 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;
+ }
+ boolean isLoaded = init(appCtx, modelPath, 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 = 1;
+ 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; // do not need 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;
+ }
+ this.inputImage = image.copy(Bitmap.Config.ARGB_8888, true);
+ }
+
+ 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 < results.size(); i++) {
+ OcrResultModel result = results.get(i);
+ StringBuilder sb = new StringBuilder("");
+ sb.append(result.getLabel());
+ sb.append(" ").append(result.getConfidence());
+ sb.append("; Points: ");
+ for (Point p : result.getPoints()) {
+ sb.append("(").append(p.x).append(",").append(p.y).append(") ");
+ }
+ Log.i(TAG, sb.toString()); // show LOG in Logcat panel
+ outputResultSb.append(i + 1).append(": ").append(result.getLabel()).append("\n");
+ }
+ outputResult = outputResultSb.toString();
+ outputImage = inputImage;
+ Canvas canvas = new Canvas(outputImage);
+ Paint paintFillAlpha = new Paint();
+ paintFillAlpha.setStyle(Paint.Style.FILL);
+ paintFillAlpha.setColor(Color.parseColor("#3B85F5"));
+ paintFillAlpha.setAlpha(50);
+
+ Paint paint = new Paint();
+ paint.setColor(Color.parseColor("#3B85F5"));
+ paint.setStrokeWidth(5);
+ paint.setStyle(Paint.Style.STROKE);
+
+ for (OcrResultModel result : results) {
+ Path path = new Path();
+ List 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 0000000000000000000000000000000000000000..b3653ccc2ebaf2c4005a3b30c4c8162dad2f31fc
--- /dev/null
+++ b/deploy/android_demo/app/src/main/java/com/baidu/paddle/lite/demo/ocr/SettingsActivity.java
@@ -0,0 +1,201 @@
+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 androidx.appcompat.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 0000000000000000000000000000000000000000..ef4680576099972e4aa904b425025d08ec5fc87f
--- /dev/null
+++ b/deploy/android_demo/app/src/main/java/com/baidu/paddle/lite/demo/ocr/Utils.java
@@ -0,0 +1,159 @@
+package com.baidu.paddle.lite.demo.ocr;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Matrix;
+import android.media.ExifInterface;
+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);
+ }
+
+ public static Bitmap rotateBitmap(Bitmap bitmap, int orientation) {
+
+ Matrix matrix = new Matrix();
+ switch (orientation) {
+ case ExifInterface.ORIENTATION_NORMAL:
+ return bitmap;
+ case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
+ matrix.setScale(-1, 1);
+ break;
+ case ExifInterface.ORIENTATION_ROTATE_180:
+ matrix.setRotate(180);
+ break;
+ case ExifInterface.ORIENTATION_FLIP_VERTICAL:
+ matrix.setRotate(180);
+ matrix.postScale(-1, 1);
+ break;
+ case ExifInterface.ORIENTATION_TRANSPOSE:
+ matrix.setRotate(90);
+ matrix.postScale(-1, 1);
+ break;
+ case ExifInterface.ORIENTATION_ROTATE_90:
+ matrix.setRotate(90);
+ break;
+ case ExifInterface.ORIENTATION_TRANSVERSE:
+ matrix.setRotate(-90);
+ matrix.postScale(-1, 1);
+ break;
+ case ExifInterface.ORIENTATION_ROTATE_270:
+ matrix.setRotate(-90);
+ break;
+ default:
+ return bitmap;
+ }
+ try {
+ Bitmap bmRotated = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
+ bitmap.recycle();
+ return bmRotated;
+ }
+ catch (OutOfMemoryError e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+}
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 0000000000000000000000000000000000000000..1f6bb290603d7caa16c5fb6f61bbfdc750622f5c
--- /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 0000000000000000000000000000000000000000..0d025f9bf6b67c63044a36a9ff44fbc69e5c5822
--- /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 0000000000000000000000000000000000000000..adc70e200f737a5eaced2f3015ca675dc7a085db
--- /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/layout/activity_mini.xml b/deploy/android_demo/app/src/main/res/layout/activity_mini.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ec4622ae5c21334769a0ef7f084f73a3ac6a05ab
--- /dev/null
+++ b/deploy/android_demo/app/src/main/res/layout/activity_mini.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
\ 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 0000000000000000000000000000000000000000..fe74758ae568ebfab41537ff72affb0992d753de
--- /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 0000000000000000000000000000000000000000..eca70cfe52eac1ba66ba280a68ca7be8fcf88a16
--- /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 0000000000000000000000000000000000000000..eca70cfe52eac1ba66ba280a68ca7be8fcf88a16
--- /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 0000000000000000000000000000000000000000..898f3ed59ac9f3248734a00e5902736c9367d455
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 0000000000000000000000000000000000000000..dffca3601eba7bf5f409bdd520820e2eb5122c75
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 0000000000000000000000000000000000000000..64ba76f75e9ce021aa3d95c213491f73bcacb597
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 0000000000000000000000000000000000000000..dae5e082342fcdeee5db8a6e0b27028e2d2808f5
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 0000000000000000000000000000000000000000..e5ed46597ea8447d91ab1786a34e30f1c26b18bd
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 0000000000000000000000000000000000000000..14ed0af35023e4f1901cf03487b6c524257b8483
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 0000000000000000000000000000000000000000..b0907cac3bfd8fbfdc46e1108247f0a1055387ec
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 0000000000000000000000000000000000000000..d8ae03154975f397f8ed1b84f2d4bf9783ecfa26
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 0000000000000000000000000000000000000000..2c18de9e66108411737e910f5c1972476f03ddbf
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 0000000000000000000000000000000000000000..beed3cdd2c32af5114a7dc70b9ef5b698eb8797e
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 0000000000000000000000000000000000000000..8e08ad57ddaca4bb0cff1a3d61ec84b0442b1b0e
--- /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 0000000000000000000000000000000000000000..69b22338c6510250df3b43672635120dbce2fa49
--- /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 0000000000000000000000000000000000000000..f092ae4032321584b6dcd87de21fd250a1b4a035
--- /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 0000000000000000000000000000000000000000..853262016a2ffab26185bf9f3dcd59e10605630a
--- /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/file_paths.xml b/deploy/android_demo/app/src/main/res/xml/file_paths.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3d985443b3357dcf39c5631d3021f08000e047c7
--- /dev/null
+++ b/deploy/android_demo/app/src/main/res/xml/file_paths.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
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 0000000000000000000000000000000000000000..440f3e4785569167904e65885c23dac91ef48c94
--- /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 0000000000000000000000000000000000000000..d523a9a7087434ffe9ce539a1eab76f26ea46b49
--- /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 0000000000000000000000000000000000000000..fafc1b970be053f8a9ec61f55b94cb2e85b26a33
--- /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 0000000000000000000000000000000000000000..82618cecb4d1cf137df18eca8dbe88e1b3b2c2b8
--- /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 0000000000000000000000000000000000000000..f6b961fd5a86aa5fbfe90f707c3138408be7c718
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 0000000000000000000000000000000000000000..63dac4e990eb3ca985e9c1018c84f26fbab0ac78
--- /dev/null
+++ b/deploy/android_demo/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Jul 22 23:48:44 CST 2020
+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 0000000000000000000000000000000000000000..cccdd3d517fc5249beaefa600691cf150f2fa3e6
--- /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 0000000000000000000000000000000000000000..f9553162f122c71b34635112e717c3e733b5b212
--- /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 0000000000000000000000000000000000000000..e7b4def49cb53d9aa04228dd3edb14c9e635e003
--- /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 0000000000000000000000000000000000000000..466c2be8f79c11a9e6cf39631ef2dc5a2a213321
--- /dev/null
+++ b/deploy/cpp_infer/CMakeLists.txt
@@ -0,0 +1,201 @@
+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(WITH_TENSORRT "Compile demo with TensorRT." OFF)
+
+SET(PADDLE_LIB "" CACHE PATH "Location of libraries")
+SET(OPENCV_DIR "" CACHE PATH "Location of libraries")
+SET(CUDA_LIB "" CACHE PATH "Location of libraries")
+SET(CUDNN_LIB "" CACHE PATH "Location of libraries")
+SET(TENSORRT_DIR "" CACHE PATH "Compile demo with TensorRT")
+
+set(DEMO_NAME "ocr_system")
+
+
+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()
+
+if (WITH_MKL)
+ ADD_DEFINITIONS(-DUSE_MKL)
+endif()
+
+if(NOT DEFINED PADDLE_LIB)
+ message(FATAL_ERROR "please set PADDLE_LIB with -DPADDLE_LIB=/path/paddle/lib")
+endif()
+
+if(NOT DEFINED OPENCV_DIR)
+ message(FATAL_ERROR "please set OPENCV_DIR with -DOPENCV_DIR=/path/opencv")
+endif()
+
+
+if (WIN32)
+ include_directories("${PADDLE_LIB}/paddle/fluid/inference")
+ include_directories("${PADDLE_LIB}/paddle/include")
+ link_directories("${PADDLE_LIB}/paddle/fluid/inference")
+ find_package(OpenCV REQUIRED PATHS ${OPENCV_DIR}/build/ NO_DEFAULT_PATH)
+
+else ()
+ find_package(OpenCV REQUIRED PATHS ${OPENCV_DIR}/share/OpenCV NO_DEFAULT_PATH)
+ include_directories("${PADDLE_LIB}/paddle/include")
+ link_directories("${PADDLE_LIB}/paddle/lib")
+endif ()
+include_directories(${OpenCV_INCLUDE_DIRS})
+
+if (WIN32)
+ add_definitions("/DGOOGLE_GLOG_DLL_DECL=")
+ set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /bigobj /MTd")
+ set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /bigobj /MT")
+ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /bigobj /MTd")
+ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /bigobj /MT")
+ if (WITH_STATIC_LIB)
+ safe_set_static_flag()
+ add_definitions(-DSTATIC_LIB)
+ endif()
+else()
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -o3 -std=c++11")
+ set(CMAKE_STATIC_LIBRARY_PREFIX "")
+endif()
+message("flags" ${CMAKE_CXX_FLAGS})
+
+
+if (WITH_GPU)
+ if (NOT DEFINED CUDA_LIB OR ${CUDA_LIB} STREQUAL "")
+ message(FATAL_ERROR "please set CUDA_LIB with -DCUDA_LIB=/path/cuda-8.0/lib64")
+ endif()
+ if (NOT WIN32)
+ if (NOT DEFINED CUDNN_LIB)
+ message(FATAL_ERROR "please set CUDNN_LIB with -DCUDNN_LIB=/path/cudnn_v7.4/cuda/lib64")
+ endif()
+ endif(NOT WIN32)
+endif()
+
+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 (NOT WIN32)
+ if (WITH_TENSORRT AND WITH_GPU)
+ include_directories("${TENSORRT_DIR}/include")
+ link_directories("${TENSORRT_DIR}/lib")
+ endif()
+endif(NOT WIN32)
+
+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")
+
+
+if(WITH_MKL)
+ include_directories("${PADDLE_LIB}/third_party/install/mklml/include")
+ if (WIN32)
+ set(MATH_LIB ${PADDLE_LIB}/third_party/install/mklml/lib/mklml.lib
+ ${PADDLE_LIB}/third_party/install/mklml/lib/libiomp5md.lib)
+ else ()
+ 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})
+ execute_process(COMMAND cp -r ${PADDLE_LIB}/third_party/install/mklml/lib/libmklml_intel${CMAKE_SHARED_LIBRARY_SUFFIX} /usr/lib)
+ endif ()
+ set(MKLDNN_PATH "${PADDLE_LIB}/third_party/install/mkldnn")
+ if(EXISTS ${MKLDNN_PATH})
+ include_directories("${MKLDNN_PATH}/include")
+ if (WIN32)
+ set(MKLDNN_LIB ${MKLDNN_PATH}/lib/mkldnn.lib)
+ else ()
+ set(MKLDNN_LIB ${MKLDNN_PATH}/lib/libmkldnn.so.0)
+ endif ()
+ 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()
+
+if (NOT WIN32)
+ set(DEPS ${DEPS}
+ ${MATH_LIB} ${MKLDNN_LIB}
+ glog gflags protobuf z xxhash
+ )
+ if(EXISTS "${PADDLE_LIB}/third_party/install/snappystream/lib")
+ set(DEPS ${DEPS} snappystream)
+ endif()
+ if (EXISTS "${PADDLE_LIB}/third_party/install/snappy/lib")
+ set(DEPS ${DEPS} snappy)
+ endif()
+else()
+ set(DEPS ${DEPS}
+ ${MATH_LIB} ${MKLDNN_LIB}
+ glog gflags_static libprotobuf xxhash)
+ set(DEPS ${DEPS} libcmt shlwapi)
+ if (EXISTS "${PADDLE_LIB}/third_party/install/snappy/lib")
+ set(DEPS ${DEPS} snappy)
+ endif()
+ if(EXISTS "${PADDLE_LIB}/third_party/install/snappystream/lib")
+ set(DEPS ${DEPS} snappystream)
+ endif()
+endif(NOT WIN32)
+
+
+if(WITH_GPU)
+ if(NOT WIN32)
+ if (WITH_TENSORRT)
+ set(DEPS ${DEPS} ${TENSORRT_DIR}/lib/libnvinfer${CMAKE_SHARED_LIBRARY_SUFFIX})
+ set(DEPS ${DEPS} ${TENSORRT_DIR}/lib/libnvinfer_plugin${CMAKE_SHARED_LIBRARY_SUFFIX})
+ endif()
+ set(DEPS ${DEPS} ${CUDA_LIB}/libcudart${CMAKE_SHARED_LIBRARY_SUFFIX})
+ set(DEPS ${DEPS} ${CUDNN_LIB}/libcudnn${CMAKE_SHARED_LIBRARY_SUFFIX})
+ else()
+ set(DEPS ${DEPS} ${CUDA_LIB}/cudart${CMAKE_STATIC_LIBRARY_SUFFIX} )
+ set(DEPS ${DEPS} ${CUDA_LIB}/cublas${CMAKE_STATIC_LIBRARY_SUFFIX} )
+ set(DEPS ${DEPS} ${CUDNN_LIB}/cudnn${CMAKE_STATIC_LIBRARY_SUFFIX})
+ endif()
+endif()
+
+
+if (NOT WIN32)
+ set(EXTERNAL_LIB "-ldl -lrt -lgomp -lz -lm -lpthread")
+ set(DEPS ${DEPS} ${EXTERNAL_LIB})
+endif()
+
+set(DEPS ${DEPS} ${OpenCV_LIBS})
+
+AUX_SOURCE_DIRECTORY(./src SRCS)
+add_executable(${DEMO_NAME} ${SRCS})
+
+target_link_libraries(${DEMO_NAME} ${DEPS})
+
+if (WIN32 AND WITH_MKL)
+ add_custom_command(TARGET ${DEMO_NAME} POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_LIB}/third_party/install/mklml/lib/mklml.dll ./mklml.dll
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_LIB}/third_party/install/mklml/lib/libiomp5md.dll ./libiomp5md.dll
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_LIB}/third_party/install/mkldnn/lib/mkldnn.dll ./mkldnn.dll
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_LIB}/third_party/install/mklml/lib/mklml.dll ./release/mklml.dll
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_LIB}/third_party/install/mklml/lib/libiomp5md.dll ./release/libiomp5md.dll
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_LIB}/third_party/install/mkldnn/lib/mkldnn.dll ./release/mkldnn.dll
+ )
+endif()
\ No newline at end of file
diff --git a/deploy/cpp_infer/docs/windows_vs2019_build.md b/deploy/cpp_infer/docs/windows_vs2019_build.md
new file mode 100644
index 0000000000000000000000000000000000000000..21fbf4e0eb95ee82475164047d8051e90e9e224f
--- /dev/null
+++ b/deploy/cpp_infer/docs/windows_vs2019_build.md
@@ -0,0 +1,95 @@
+# Visual Studio 2019 Community CMake 编译指南
+
+PaddleOCR在Windows 平台下基于`Visual Studio 2019 Community` 进行了测试。微软从`Visual Studio 2017`开始即支持直接管理`CMake`跨平台编译项目,但是直到`2019`才提供了稳定和完全的支持,所以如果你想使用CMake管理项目编译构建,我们推荐你使用`Visual Studio 2019`环境下构建。
+
+
+## 前置条件
+* Visual Studio 2019
+* CUDA 9.0 / CUDA 10.0,cudnn 7+ (仅在使用GPU版本的预测库时需要)
+* CMake 3.0+
+
+请确保系统已经安装好上述基本软件,我们使用的是`VS2019`的社区版。
+
+**下面所有示例以工作目录为 `D:\projects`演示**。
+
+### Step1: 下载PaddlePaddle C++ 预测库 fluid_inference
+
+PaddlePaddle C++ 预测库针对不同的`CPU`和`CUDA`版本提供了不同的预编译版本,请根据实际情况下载: [C++预测库下载列表](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/advanced_guide/inference_deployment/inference/windows_cpp_inference.html)
+
+解压后`D:\projects\fluid_inference`目录包含内容为:
+```
+fluid_inference
+├── paddle # paddle核心库和头文件
+|
+├── third_party # 第三方依赖库和头文件
+|
+└── version.txt # 版本和编译信息
+```
+
+### Step2: 安装配置OpenCV
+
+1. 在OpenCV官网下载适用于Windows平台的3.4.6版本, [下载地址](https://sourceforge.net/projects/opencvlibrary/files/3.4.6/opencv-3.4.6-vc14_vc15.exe/download)
+2. 运行下载的可执行文件,将OpenCV解压至指定目录,如`D:\projects\opencv`
+3. 配置环境变量,如下流程所示
+ - 我的电脑->属性->高级系统设置->环境变量
+ - 在系统变量中找到Path(如没有,自行创建),并双击编辑
+ - 新建,将opencv路径填入并保存,如`D:\projects\opencv\build\x64\vc14\bin`
+
+### Step3: 使用Visual Studio 2019直接编译CMake
+
+1. 打开Visual Studio 2019 Community,点击`继续但无需代码`
+![step2](https://paddleseg.bj.bcebos.com/inference/vs2019_step1.png)
+2. 点击: `文件`->`打开`->`CMake`
+![step2.1](https://paddleseg.bj.bcebos.com/inference/vs2019_step2.png)
+
+选择项目代码所在路径,并打开`CMakeList.txt`:
+
+![step2.2](https://paddleseg.bj.bcebos.com/inference/vs2019_step3.png)
+
+3. 点击:`项目`->`cpp_inference_demo的CMake设置`
+
+![step3](https://paddleseg.bj.bcebos.com/inference/vs2019_step4.png)
+
+4. 点击`浏览`,分别设置编译选项指定`CUDA`、`CUDNN_LIB`、`OpenCV`、`Paddle预测库`的路径
+
+三个编译参数的含义说明如下(带`*`表示仅在使用**GPU版本**预测库时指定, 其中CUDA库版本尽量对齐,**使用9.0、10.0版本,不使用9.2、10.1等版本CUDA库**):
+
+| 参数名 | 含义 |
+| ---- | ---- |
+| *CUDA_LIB | CUDA的库路径 |
+| *CUDNN_LIB | CUDNN的库路径 |
+| OPENCV_DIR | OpenCV的安装路径 |
+| PADDLE_LIB | Paddle预测库的路径 |
+
+**注意:**
+ 1. 使用`CPU`版预测库,请把`WITH_GPU`的勾去掉
+ 2. 如果使用的是`openblas`版本,请把`WITH_MKL`勾去掉
+
+![step4](https://paddleseg.bj.bcebos.com/inference/vs2019_step5.png)
+
+**设置完成后**, 点击上图中`保存并生成CMake缓存以加载变量`。
+
+5. 点击`生成`->`全部生成`
+
+![step6](https://paddleseg.bj.bcebos.com/inference/vs2019_step6.png)
+
+
+### Step4: 预测及可视化
+
+上述`Visual Studio 2019`编译产出的可执行文件在`out\build\x64-Release`目录下,打开`cmd`,并切换到该目录:
+
+```
+cd D:\projects\PaddleOCR\deploy\cpp_infer\out\build\x64-Release
+```
+可执行文件`ocr_system.exe`即为样例的预测程序,其主要使用方法如下
+
+```shell
+#预测图片 `D:\projects\PaddleOCR\doc\imgs\10.jpg`
+.\ocr_system.exe D:\projects\PaddleOCR\deploy\cpp_infer\tools\config.txt D:\projects\PaddleOCR\doc\imgs\10.jpg
+```
+
+第一个参数为配置文件路径,第二个参数为需要预测的图片路径。
+
+
+### 注意
+* 在Windows下的终端中执行文件exe时,可能会发生乱码的现象,此时需要在终端中输入`CHCP 65001`,将终端的编码方式由GBK编码(默认)改为UTF-8编码,更加具体的解释可以参考这篇博客:[https://blog.csdn.net/qq_35038153/article/details/78430359](https://blog.csdn.net/qq_35038153/article/details/78430359)。
diff --git a/deploy/cpp_infer/include/clipper.h b/deploy/cpp_infer/include/clipper.h
new file mode 100644
index 0000000000000000000000000000000000000000..384a6cf44c191a369906373d40fb81ffb02bb7fa
--- /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 0000000000000000000000000000000000000000..8db693b121f1f91e30672de53e9b969babb49f8b
--- /dev/null
+++ b/deploy/cpp_infer/include/config.h
@@ -0,0 +1,103 @@
+// 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
+#include
+#include
+#include
+
+#include "include/utility.h"
+
+namespace PaddleOCR {
+
+class Config {
+public:
+ explicit Config(const std::string &config_file) {
+ config_map_ = LoadConfig(config_file);
+
+ this->use_gpu = bool(stoi(config_map_["use_gpu"]));
+
+ this->gpu_id = stoi(config_map_["gpu_id"]);
+
+ this->gpu_mem = stoi(config_map_["gpu_mem"]);
+
+ this->cpu_math_library_num_threads =
+ stoi(config_map_["cpu_math_library_num_threads"]);
+
+ this->use_mkldnn = bool(stoi(config_map_["use_mkldnn"]));
+
+ this->use_zero_copy_run = bool(stoi(config_map_["use_zero_copy_run"]));
+
+ this->max_side_len = stoi(config_map_["max_side_len"]);
+
+ this->det_db_thresh = stod(config_map_["det_db_thresh"]);
+
+ this->det_db_box_thresh = stod(config_map_["det_db_box_thresh"]);
+
+ this->det_db_unclip_ratio = stod(config_map_["det_db_unclip_ratio"]);
+
+ this->det_model_dir.assign(config_map_["det_model_dir"]);
+
+ this->rec_model_dir.assign(config_map_["rec_model_dir"]);
+
+ this->char_list_file.assign(config_map_["char_list_file"]);
+
+ this->visualize = bool(stoi(config_map_["visualize"]));
+ }
+
+ bool use_gpu = false;
+
+ int gpu_id = 0;
+
+ int gpu_mem = 4000;
+
+ int cpu_math_library_num_threads = 1;
+
+ bool use_mkldnn = false;
+
+ bool use_zero_copy_run = false;
+
+ int max_side_len = 960;
+
+ double det_db_thresh = 0.3;
+
+ double det_db_box_thresh = 0.5;
+
+ double det_db_unclip_ratio = 2.0;
+
+ std::string det_model_dir;
+
+ std::string rec_model_dir;
+
+ std::string char_list_file;
+
+ bool visualize = true;
+
+ void PrintConfigInfo();
+
+private:
+ // Load configuration
+ std::map LoadConfig(const std::string &config_file);
+
+ std::vector