diff --git a/README.md b/README.md index 26ecef77dc7206e4ada4b89ac863a935222c8c07..4814fbb9e857c9bcbb1513c5622500454477b942 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,8 @@ English | [简体中文](README_cn.md) ## Introduction PaddleOCR aims to create rich, leading, and practical OCR tools that help users train better models and apply them into practice. -**Live stream on coming day**: July 21, 2020 at 8 pm BiliBili station live stream - **Recent updates** - +- 2020.7.23, Release the playback and PPT of live class on BiliBili station, PaddleOCR Introduction, [address](https://aistudio.baidu.com/aistudio/course/introduce/1519) - 2020.7.15, Add mobile App demo , support both iOS and Android ( based on easyedge and Paddle Lite) - 2020.7.15, Improve the deployment ability, add the C + + inference , serving deployment. In addtion, the benchmarks of the ultra-lightweight OCR model are provided. - 2020.7.15, Add several related datasets, data annotation and synthesis tools. @@ -214,3 +212,4 @@ We welcome all the contributions to PaddleOCR and appreciate for your feedback v - Many thanks to [lyl120117](https://github.com/lyl120117) for contributing the code for printing the network structure. - Thanks [xiangyubo](https://github.com/xiangyubo) for contributing the handwritten Chinese OCR datasets. - Thanks [authorfu](https://github.com/authorfu) for contributing Android demo and [xiadeye](https://github.com/xiadeye) contributing iOS demo, respectively. +- Thanks [BeyondYourself](https://github.com/BeyondYourself) for contributing many great suggestions and simplifying part of the code style. diff --git a/README_cn.md b/README_cn.md index 1594b6b25db9e8ccebf81ab4f164d9cbcaf97ca9..cc5cb00a38d97fd3dba46b30a76f2dc606e8d027 100644 --- a/README_cn.md +++ b/README_cn.md @@ -3,9 +3,8 @@ ## 简介 PaddleOCR旨在打造一套丰富、领先、且实用的OCR工具库,助力使用者训练出更好的模型,并应用落地。 -**直播预告:2020年7月21日晚8点B站直播,PaddleOCR开源大礼包全面解读,直播地址当天更新** - **近期更新** +- 2020.7.23 发布7月21日B站直播课回放和PPT,PaddleOCR开源大礼包全面解读,[获取地址](https://aistudio.baidu.com/aistudio/course/introduce/1519) - 2020.7.15 添加基于EasyEdge和Paddle-Lite的移动端DEMO,支持iOS和Android系统 - 2020.7.15 完善预测部署,添加基于C++预测引擎推理、服务化部署和端侧部署方案,以及超轻量级中文OCR模型预测耗时Benchmark - 2020.7.15 整理OCR相关数据集、常用数据标注以及合成工具 @@ -33,7 +32,7 @@ PaddleOCR旨在打造一套丰富、领先、且实用的OCR工具库,助力 上图是超轻量级中文OCR模型效果展示,更多效果图请见[效果展示页面](./doc/doc_ch/visualization.md)。 - 超轻量级中文OCR在线体验地址:https://www.paddlepaddle.org.cn/hub/scene/ocr -- 移动端DEMO体验(基于EasyEdge和Paddle-Lite, 支持iOS和Android系统):[安装包二维码获取地址](https://ai.baidu.com/easyedge/app/openSource?from=paddlelite) +- 移动端DEMO体验(基于EasyEdge和Paddle-Lite, 支持iOS和Android系统):[安装包二维码获取地址](https://ai.baidu.com/easyedge/app/openSource?from=paddlelite) Android手机也可以扫描下面二维码安装体验。 @@ -206,8 +205,9 @@ PaddleOCR文本识别算法的训练和使用请参考文档教程中[模型训 ## 贡献代码 我们非常欢迎你为PaddleOCR贡献代码,也十分感谢你的反馈。 -- 非常感谢 [Khanh Tran](https://github.com/xxxpsyduck) 贡献了英文文档。 +- 非常感谢 [Khanh Tran](https://github.com/xxxpsyduck) 贡献了英文文档 - 非常感谢 [zhangxin](https://github.com/ZhangXinNan)([Blog](https://blog.csdn.net/sdlypyzq)) 贡献新的可视化方式、添加.gitgnore、处理手动设置PYTHONPATH环境变量的问题 - 非常感谢 [lyl120117](https://github.com/lyl120117) 贡献打印网络结构的代码 - 非常感谢 [xiangyubo](https://github.com/xiangyubo) 贡献手写中文OCR数据集 - 非常感谢 [authorfu](https://github.com/authorfu) 贡献Android和[xiadeye](https://github.com/xiadeye) 贡献IOS的demo代码 +- 非常感谢 [BeyondYourself](https://github.com/BeyondYourself) 给PaddleOCR提了很多非常棒的建议,并简化了PaddleOCR的部分代码风格。 diff --git a/deploy/android_demo/README.md b/deploy/android_demo/README.md index 4d85dee99ab3616594b4ff3a17acb97a6267b12d..e35e757914aa355c97293662652b1e02676e32eb 100644 --- a/deploy/android_demo/README.md +++ b/deploy/android_demo/README.md @@ -1,6 +1,6 @@ # 如何快速测试 ### 1. 安装最新版本的Android Studio -可以从https://developer.android.com/studio下载。本Demo使用是4.0版本Android Studio编写。 +可以从https://developer.android.com/studio 下载。本Demo使用是4.0版本Android Studio编写。 ### 2. 按照NDK 20 以上版本 Demo测试的时候使用的是NDK 20b版本,20版本以上均可以支持编译成功。 diff --git a/deploy/android_demo/app/build.gradle b/deploy/android_demo/app/build.gradle index adf3968b40960b50bc62a7ba669ce28346afa362..5ecb11692c2a66f941dc41425761519607bad39e 100644 --- a/deploy/android_demo/app/build.gradle +++ b/deploy/android_demo/app/build.gradle @@ -3,11 +3,11 @@ import java.security.MessageDigest apply plugin: 'com.android.application' android { - compileSdkVersion 28 + compileSdkVersion 29 defaultConfig { applicationId "com.baidu.paddle.lite.demo.ocr" - minSdkVersion 15 - targetSdkVersion 28 + minSdkVersion 23 + targetSdkVersion 29 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" @@ -39,9 +39,8 @@ android { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'com.android.support:appcompat-v7:28.0.0' - implementation 'com.android.support.constraint:constraint-layout:1.1.3' - implementation 'com.android.support:design:28.0.0' + implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' diff --git a/deploy/android_demo/app/src/main/AndroidManifest.xml b/deploy/android_demo/app/src/main/AndroidManifest.xml index ff1900d637a827998c4da52b9a2dda51b8ae89c8..54482b1dcc9de66021d0109e5683302c8445ba6a 100644 --- a/deploy/android_demo/app/src/main/AndroidManifest.xml +++ b/deploy/android_demo/app/src/main/AndroidManifest.xml @@ -14,10 +14,10 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> + - @@ -25,6 +25,15 @@ android:name="com.baidu.paddle.lite.demo.ocr.SettingsActivity" android:label="Settings"> + + + \ No newline at end of file diff --git a/deploy/android_demo/app/src/main/cpp/native.cpp b/deploy/android_demo/app/src/main/cpp/native.cpp index 33233e5372e307a892786c6bea779691e1f6781a..390c594deb02a8f82693f2c83741a4750fe7cb25 100644 --- a/deploy/android_demo/app/src/main/cpp/native.cpp +++ b/deploy/android_demo/app/src/main/cpp/native.cpp @@ -30,7 +30,7 @@ Java_com_baidu_paddle_lite_demo_ocr_OCRPredictorNative_init(JNIEnv *env, jobject } /** - * "LITE_POWER_HIGH" 转为 paddle::lite_api::LITE_POWER_HIGH + * "LITE_POWER_HIGH" convert to paddle::lite_api::LITE_POWER_HIGH * @param cpu_mode * @return */ diff --git a/deploy/android_demo/app/src/main/cpp/ocr_ppredictor.cpp b/deploy/android_demo/app/src/main/cpp/ocr_ppredictor.cpp index 6548157b7ecac09ca3802ee6e226d555bfcd9099..3d0147715519c195fd48f7f84b7a28a5a82f5363 100644 --- a/deploy/android_demo/app/src/main/cpp/ocr_ppredictor.cpp +++ b/deploy/android_demo/app/src/main/cpp/ocr_ppredictor.cpp @@ -37,7 +37,7 @@ int OCR_PPredictor::init_from_file(const std::string &det_model_path, const std: return RETURN_OK; } /** - * 调试用,保存第一步的框选结果 + * for debug use, show result of First Step * @param filter_boxes * @param boxes * @param srcimg diff --git a/deploy/android_demo/app/src/main/cpp/ocr_ppredictor.h b/deploy/android_demo/app/src/main/cpp/ocr_ppredictor.h index 9adbf1e35214d4c83230ecf89b650aa1c1125a8f..eb2bc3bc989c5dd9a2c5a8aae3508ca733602bd7 100644 --- a/deploy/android_demo/app/src/main/cpp/ocr_ppredictor.h +++ b/deploy/android_demo/app/src/main/cpp/ocr_ppredictor.h @@ -12,26 +12,26 @@ namespace ppredictor { /** - * 配置 + * Config */ struct OCR_Config { - int thread_num = 4; // 线程数 + int thread_num = 4; // Thread num paddle::lite_api::PowerMode mode = paddle::lite_api::LITE_POWER_HIGH; // PaddleLite Mode }; /** - * 一个四边形内图片的推理结果, + * PolyGone Result */ struct OCRPredictResult { - std::vector word_index; // + std::vector word_index; std::vector> points; float score; }; /** - * OCR 一共有2个模型进行推理, - * 1. 使用第一个模型(det),框选出多个四边形 - * 2. 从原图从抠出这些多边形,使用第二个模型(rec),获取文本 + * OCR there are 2 models + * 1. First model(det),select polygones to show where are the texts + * 2. crop from the origin images, use these polygones to infer */ class OCR_PPredictor : public PPredictor_Interface { public: @@ -50,7 +50,7 @@ public: int init(const std::string &det_model_content, const std::string &rec_model_content); int init_from_file(const std::string &det_model_path, const std::string &rec_model_path); /** - * 返回OCR结果 + * Return OCR result * @param dims * @param input_data * @param input_len @@ -69,7 +69,7 @@ public: private: /** - * 从第一个模型的结果中计算有文字的四边形 + * calcul Polygone from the result image of first model * @param pred * @param output_height * @param output_width @@ -81,7 +81,7 @@ private: const cv::Mat &origin); /** - * 第二个模型的推理 + * infer for second model * * @param boxes * @param origin @@ -91,14 +91,14 @@ private: infer_rec(const std::vector>> &boxes, const cv::Mat &origin); /** - * 第二个模型提取文字的后处理 + * Postprocess or sencod model to extract text * @param res * @return */ std::vector postprocess_rec_word_index(const PredictorOutput &res); /** - * 计算第二个模型的文字的置信度 + * calculate confidence of second model text result * @param res * @return */ diff --git a/deploy/android_demo/app/src/main/cpp/ppredictor.h b/deploy/android_demo/app/src/main/cpp/ppredictor.h index 9cdf3a88170ca2fbae9a2b1d8353fc99ebdfb971..1391109f9197b5e53796c940857c9d01b30a1125 100644 --- a/deploy/android_demo/app/src/main/cpp/ppredictor.h +++ b/deploy/android_demo/app/src/main/cpp/ppredictor.h @@ -7,7 +7,7 @@ namespace ppredictor { /** - * PaddleLite Preditor 通用接口 + * PaddleLite Preditor Common Interface */ class PPredictor_Interface { public: @@ -21,7 +21,7 @@ public: }; /** - * 通用推理 + * Common Predictor */ class PPredictor : public PPredictor_Interface { public: @@ -33,9 +33,9 @@ public: } /** - * 初始化paddlitelite的opt模型,nb格式,与init_paddle二选一 + * init paddlitelite opt model,nb format ,or use ini_paddle * @param model_content - * @return 0 目前是固定值0, 之后其他值表示失败 + * @return 0 */ virtual int init_nb(const std::string &model_content); diff --git a/deploy/android_demo/app/src/main/cpp/predictor_output.h b/deploy/android_demo/app/src/main/cpp/predictor_output.h index c56e2d9a4e9890faae89d6d183b81773a9c9a228..ec7086c62f0d5ca555ec17b38b27b6eea824fdb5 100644 --- a/deploy/android_demo/app/src/main/cpp/predictor_output.h +++ b/deploy/android_demo/app/src/main/cpp/predictor_output.h @@ -21,10 +21,10 @@ public: const std::vector> get_lod() const; const std::vector get_shape() const; - std::vector data; // 通常是float返回,与下面的data_int二选一 - std::vector data_int; // 少数层是int返回,与 data二选一 - std::vector shape; // PaddleLite输出层的shape - std::vector> lod; // PaddleLite输出层的lod + std::vector data; // return float, or use data_int + std::vector data_int; // several layers return int ,or use data + std::vector shape; // PaddleLite output shape + std::vector> lod; // PaddleLite output lod private: std::unique_ptr _tensor; 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 index 397e4e39fe35a541fd534634ac509b94dd4b2b86..49af0afea425561d65d435a8fe67e96e98912680 100644 --- 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 @@ -19,15 +19,16 @@ package com.baidu.paddle.lite.demo.ocr; import android.content.res.Configuration; import android.os.Bundle; import android.preference.PreferenceActivity; -import android.support.annotation.LayoutRes; -import android.support.annotation.Nullable; -import android.support.v7.app.ActionBar; -import android.support.v7.app.AppCompatDelegate; -import android.support.v7.widget.Toolbar; import android.view.MenuInflater; import android.view.View; import android.view.ViewGroup; +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. 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 index b72d72df47a3c6d769559230185c50823276fe85..afb261dcf2afb2a2eaebf58c8c1f30f89200c902 100644 --- 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 @@ -3,23 +3,22 @@ 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.support.annotation.NonNull; -import android.support.v4.app.ActivityCompat; -import android.support.v4.content.ContextCompat; -import android.support.v7.app.AppCompatActivity; import android.text.method.ScrollingMovementMethod; import android.util.Log; import android.view.Menu; @@ -29,9 +28,17 @@ 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(); @@ -69,6 +76,7 @@ public class MainActivity extends AppCompatActivity { protected float[] inputMean = new float[]{}; protected float[] inputStd = new float[]{}; protected float scoreThreshold = 0.1f; + private String currentPhotoPath; protected Predictor predictor = new Predictor(); @@ -368,18 +376,56 @@ public class MainActivity extends AppCompatActivity { } private void takePhoto() { - Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); - if (takePhotoIntent.resolveActivity(getPackageManager()) != null) { - startActivityForResult(takePhotoIntent, TAKE_PHOTO_REQUEST_CODE); + 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 && data != null) { + if (resultCode == RESULT_OK) { switch (requestCode) { case OPEN_GALLERY_REQUEST_CODE: + if (data == null) { + break; + } try { ContentResolver resolver = getContentResolver(); Uri uri = data.getData(); @@ -393,9 +439,22 @@ public class MainActivity extends AppCompatActivity { } break; case TAKE_PHOTO_REQUEST_CODE: - Bundle extras = data.getExtras(); - Bitmap image = (Bitmap) extras.get("data"); - onImageChanged(image); + 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; 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 index 103d5d37aec3ddc026d48a202df17b140e3e4533..2e78a3ece96bb5e37bebcdda7ebc77060686b710 100644 --- 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 @@ -35,8 +35,8 @@ public class OCRPredictorNative { } - public void release(){ - if (nativePointer != 0){ + public void release() { + if (nativePointer != 0) { nativePointer = 0; destory(nativePointer); } 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 index d491481e7b61bca8043c34a65ecb3bbf6a72487d..078bba286cc9cd5f9904e0594b5608c755a2b131 100644 --- 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 @@ -38,7 +38,7 @@ public class Predictor { protected float scoreThreshold = 0.1f; protected Bitmap inputImage = null; protected Bitmap outputImage = null; - protected String outputResult = ""; + protected volatile String outputResult = ""; protected float preprocessTime = 0; protected float postprocessTime = 0; @@ -46,6 +46,16 @@ public class Predictor { 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, @@ -76,11 +86,7 @@ public class Predictor { Log.e(TAG, "Only BGR color format is supported."); return false; } - isLoaded = loadModel(appCtx, modelPath, cpuThreadNum, cpuPowerMode); - if (!isLoaded) { - return false; - } - isLoaded = loadLabel(appCtx, labelPath); + boolean isLoaded = init(appCtx, modelPath, labelPath); if (!isLoaded) { return false; } @@ -127,12 +133,12 @@ public class Predictor { } public void releaseModel() { - if (paddlePredictor != null){ + if (paddlePredictor != null) { paddlePredictor.release(); paddlePredictor = null; } isLoaded = false; - cpuThreadNum = 4; + cpuThreadNum = 1; cpuPowerMode = "LITE_POWER_HIGH"; modelPath = ""; modelName = ""; @@ -222,7 +228,7 @@ public class Predictor { for (int i = 0; i < warmupIterNum; i++) { paddlePredictor.runImage(inputData, width, height, channels, inputImage); } - warmupIterNum = 0; // 之后不要再warm了 + warmupIterNum = 0; // do not need warm // Run inference start = new Date(); ArrayList results = paddlePredictor.runImage(inputData, width, height, channels, inputImage); @@ -287,9 +293,7 @@ public class Predictor { if (image == null) { return; } - // Scale image to the size of input tensor - Bitmap rgbaImage = image.copy(Bitmap.Config.ARGB_8888, true); - this.inputImage = rgbaImage; + this.inputImage = image.copy(Bitmap.Config.ARGB_8888, true); } private ArrayList postprocess(ArrayList results) { @@ -310,7 +314,7 @@ public class Predictor { private void drawResults(ArrayList results) { StringBuffer outputResultSb = new StringBuffer(""); - for (int i=0;i - - \ No newline at end of file + \ 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 @@ + + + + + + + + +