Unverified Commit fee2c17b authored by MissPenguin's avatar MissPenguin Committed by GitHub
Browse files

Merge branch 'develop' into develop

parents da75ef8b bad9f6cd
...@@ -3,10 +3,8 @@ English | [简体中文](README_cn.md) ...@@ -3,10 +3,8 @@ English | [简体中文](README_cn.md)
## Introduction ## Introduction
PaddleOCR aims to create rich, leading, and practical OCR tools that help users train better models and apply them into practice. 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** **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, 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, 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. - 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 ...@@ -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. - 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 [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 [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.
...@@ -3,9 +3,8 @@ ...@@ -3,9 +3,8 @@
## 简介 ## 简介
PaddleOCR旨在打造一套丰富、领先、且实用的OCR工具库,助力使用者训练出更好的模型,并应用落地。 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 添加基于EasyEdge和Paddle-Lite的移动端DEMO,支持iOS和Android系统
- 2020.7.15 完善预测部署,添加基于C++预测引擎推理、服务化部署和端侧部署方案,以及超轻量级中文OCR模型预测耗时Benchmark - 2020.7.15 完善预测部署,添加基于C++预测引擎推理、服务化部署和端侧部署方案,以及超轻量级中文OCR模型预测耗时Benchmark
- 2020.7.15 整理OCR相关数据集、常用数据标注以及合成工具 - 2020.7.15 整理OCR相关数据集、常用数据标注以及合成工具
...@@ -206,8 +205,9 @@ PaddleOCR文本识别算法的训练和使用请参考文档教程中[模型训 ...@@ -206,8 +205,9 @@ PaddleOCR文本识别算法的训练和使用请参考文档教程中[模型训
## 贡献代码 ## 贡献代码
我们非常欢迎你为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环境变量的问题 - 非常感谢 [zhangxin](https://github.com/ZhangXinNan)([Blog](https://blog.csdn.net/sdlypyzq)) 贡献新的可视化方式、添加.gitgnore、处理手动设置PYTHONPATH环境变量的问题
- 非常感谢 [lyl120117](https://github.com/lyl120117) 贡献打印网络结构的代码 - 非常感谢 [lyl120117](https://github.com/lyl120117) 贡献打印网络结构的代码
- 非常感谢 [xiangyubo](https://github.com/xiangyubo) 贡献手写中文OCR数据集 - 非常感谢 [xiangyubo](https://github.com/xiangyubo) 贡献手写中文OCR数据集
- 非常感谢 [authorfu](https://github.com/authorfu) 贡献Android和[xiadeye](https://github.com/xiadeye) 贡献IOS的demo代码 - 非常感谢 [authorfu](https://github.com/authorfu) 贡献Android和[xiadeye](https://github.com/xiadeye) 贡献IOS的demo代码
- 非常感谢 [BeyondYourself](https://github.com/BeyondYourself) 给PaddleOCR提了很多非常棒的建议,并简化了PaddleOCR的部分代码风格。
# 如何快速测试 # 如何快速测试
### 1. 安装最新版本的Android Studio ### 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 以上版本 ### 2. 按照NDK 20 以上版本
Demo测试的时候使用的是NDK 20b版本,20版本以上均可以支持编译成功。 Demo测试的时候使用的是NDK 20b版本,20版本以上均可以支持编译成功。
......
...@@ -3,11 +3,11 @@ import java.security.MessageDigest ...@@ -3,11 +3,11 @@ import java.security.MessageDigest
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
android { android {
compileSdkVersion 28 compileSdkVersion 29
defaultConfig { defaultConfig {
applicationId "com.baidu.paddle.lite.demo.ocr" applicationId "com.baidu.paddle.lite.demo.ocr"
minSdkVersion 15 minSdkVersion 23
targetSdkVersion 28 targetSdkVersion 29
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
...@@ -39,9 +39,8 @@ android { ...@@ -39,9 +39,8 @@ android {
dependencies { dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3' implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.android.support:design:28.0.0'
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
......
...@@ -14,10 +14,10 @@ ...@@ -14,10 +14,10 @@
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme"> android:theme="@style/AppTheme">
<!-- to test MiniActivity, change this to com.baidu.paddle.lite.demo.ocr.MiniActivity -->
<activity android:name="com.baidu.paddle.lite.demo.ocr.MainActivity"> <activity android:name="com.baidu.paddle.lite.demo.ocr.MainActivity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter> </intent-filter>
</activity> </activity>
...@@ -25,6 +25,15 @@ ...@@ -25,6 +25,15 @@
android:name="com.baidu.paddle.lite.demo.ocr.SettingsActivity" android:name="com.baidu.paddle.lite.demo.ocr.SettingsActivity"
android:label="Settings"> android:label="Settings">
</activity> </activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.baidu.paddle.lite.demo.ocr.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"></meta-data>
</provider>
</application> </application>
</manifest> </manifest>
\ No newline at end of file
...@@ -30,7 +30,7 @@ Java_com_baidu_paddle_lite_demo_ocr_OCRPredictorNative_init(JNIEnv *env, jobject ...@@ -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 * @param cpu_mode
* @return * @return
*/ */
......
...@@ -37,7 +37,7 @@ int OCR_PPredictor::init_from_file(const std::string &det_model_path, const std: ...@@ -37,7 +37,7 @@ int OCR_PPredictor::init_from_file(const std::string &det_model_path, const std:
return RETURN_OK; return RETURN_OK;
} }
/** /**
* 调试用,保存第一步的框选结果 * for debug use, show result of First Step
* @param filter_boxes * @param filter_boxes
* @param boxes * @param boxes
* @param srcimg * @param srcimg
......
...@@ -12,26 +12,26 @@ ...@@ -12,26 +12,26 @@
namespace ppredictor { namespace ppredictor {
/** /**
* 配置 * Config
*/ */
struct OCR_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 paddle::lite_api::PowerMode mode = paddle::lite_api::LITE_POWER_HIGH; // PaddleLite Mode
}; };
/** /**
* 一个四边形内图片的推理结果, * PolyGone Result
*/ */
struct OCRPredictResult { struct OCRPredictResult {
std::vector<int> word_index; // std::vector<int> word_index;
std::vector<std::vector<int>> points; std::vector<std::vector<int>> points;
float score; float score;
}; };
/** /**
* OCR 一共有2个模型进行推理, * OCR there are 2 models
* 1. 使用第一个模型(det),框选出多个四边形 * 1. First model(det),select polygones to show where are the texts
* 2. 从原图从抠出这些多边形,使用第二个模型(rec),获取文本 * 2. crop from the origin images, use these polygones to infer
*/ */
class OCR_PPredictor : public PPredictor_Interface { class OCR_PPredictor : public PPredictor_Interface {
public: public:
...@@ -50,7 +50,7 @@ public: ...@@ -50,7 +50,7 @@ public:
int init(const std::string &det_model_content, const std::string &rec_model_content); 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); int init_from_file(const std::string &det_model_path, const std::string &rec_model_path);
/** /**
* 返回OCR结果 * Return OCR result
* @param dims * @param dims
* @param input_data * @param input_data
* @param input_len * @param input_len
...@@ -69,7 +69,7 @@ public: ...@@ -69,7 +69,7 @@ public:
private: private:
/** /**
* 从第一个模型的结果中计算有文字的四边形 * calcul Polygone from the result image of first model
* @param pred * @param pred
* @param output_height * @param output_height
* @param output_width * @param output_width
...@@ -81,7 +81,7 @@ private: ...@@ -81,7 +81,7 @@ private:
const cv::Mat &origin); const cv::Mat &origin);
/** /**
* 第二个模型的推理 * infer for second model
* *
* @param boxes * @param boxes
* @param origin * @param origin
...@@ -91,14 +91,14 @@ private: ...@@ -91,14 +91,14 @@ private:
infer_rec(const std::vector<std::vector<std::vector<int>>> &boxes, const cv::Mat &origin); infer_rec(const std::vector<std::vector<std::vector<int>>> &boxes, const cv::Mat &origin);
/** /**
* 第二个模型提取文字的后处理 * Postprocess or sencod model to extract text
* @param res * @param res
* @return * @return
*/ */
std::vector<int> postprocess_rec_word_index(const PredictorOutput &res); std::vector<int> postprocess_rec_word_index(const PredictorOutput &res);
/** /**
* 计算第二个模型的文字的置信度 * calculate confidence of second model text result
* @param res * @param res
* @return * @return
*/ */
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
namespace ppredictor { namespace ppredictor {
/** /**
* PaddleLite Preditor 通用接口 * PaddleLite Preditor Common Interface
*/ */
class PPredictor_Interface { class PPredictor_Interface {
public: public:
...@@ -21,7 +21,7 @@ public: ...@@ -21,7 +21,7 @@ public:
}; };
/** /**
* 通用推理 * Common Predictor
*/ */
class PPredictor : public PPredictor_Interface { class PPredictor : public PPredictor_Interface {
public: public:
...@@ -33,9 +33,9 @@ public: ...@@ -33,9 +33,9 @@ public:
} }
/** /**
* 初始化paddliteliteopt模型,nb格式,与init_paddle二选一 * init paddlitelite opt model,nb format ,or use ini_paddle
* @param model_content * @param model_content
* @return 0 目前是固定值0, 之后其他值表示失败 * @return 0
*/ */
virtual int init_nb(const std::string &model_content); virtual int init_nb(const std::string &model_content);
......
...@@ -21,10 +21,10 @@ public: ...@@ -21,10 +21,10 @@ public:
const std::vector<std::vector<uint64_t>> get_lod() const; const std::vector<std::vector<uint64_t>> get_lod() const;
const std::vector<int64_t> get_shape() const; const std::vector<int64_t> get_shape() const;
std::vector<float> data; // 通常是float返回,与下面的data_int二选一 std::vector<float> data; // return float, or use data_int
std::vector<int> data_int; // 少数层是int返回,与 data二选一 std::vector<int> data_int; // several layers return int ,or use data
std::vector<int64_t> shape; // PaddleLite输出层的shape std::vector<int64_t> shape; // PaddleLite output shape
std::vector<std::vector<uint64_t>> lod; // PaddleLite输出层的lod std::vector<std::vector<uint64_t>> lod; // PaddleLite output lod
private: private:
std::unique_ptr<const paddle::lite_api::Tensor> _tensor; std::unique_ptr<const paddle::lite_api::Tensor> _tensor;
......
...@@ -19,15 +19,16 @@ package com.baidu.paddle.lite.demo.ocr; ...@@ -19,15 +19,16 @@ package com.baidu.paddle.lite.demo.ocr;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceActivity; 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.MenuInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; 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 * A {@link PreferenceActivity} which implements and proxies the necessary calls
* to be used with AppCompat. * to be used with AppCompat.
......
...@@ -3,23 +3,22 @@ package com.baidu.paddle.lite.demo.ocr; ...@@ -3,23 +3,22 @@ package com.baidu.paddle.lite.demo.ocr;
import android.Manifest; import android.Manifest;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.database.Cursor; import android.database.Cursor;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.media.ExifInterface;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.os.Message; import android.os.Message;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.provider.MediaStore; 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.text.method.ScrollingMovementMethod;
import android.util.Log; import android.util.Log;
import android.view.Menu; import android.view.Menu;
...@@ -29,9 +28,17 @@ import android.widget.ImageView; ...@@ -29,9 +28,17 @@ import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; 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.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MainActivity extends AppCompatActivity { public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName(); private static final String TAG = MainActivity.class.getSimpleName();
...@@ -69,6 +76,7 @@ public class MainActivity extends AppCompatActivity { ...@@ -69,6 +76,7 @@ public class MainActivity extends AppCompatActivity {
protected float[] inputMean = new float[]{}; protected float[] inputMean = new float[]{};
protected float[] inputStd = new float[]{}; protected float[] inputStd = new float[]{};
protected float scoreThreshold = 0.1f; protected float scoreThreshold = 0.1f;
private String currentPhotoPath;
protected Predictor predictor = new Predictor(); protected Predictor predictor = new Predictor();
...@@ -368,18 +376,56 @@ public class MainActivity extends AppCompatActivity { ...@@ -368,18 +376,56 @@ public class MainActivity extends AppCompatActivity {
} }
private void takePhoto() { private void takePhoto() {
Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePhotoIntent.resolveActivity(getPackageManager()) != null) { // Ensure that there's a camera activity to handle the intent
startActivityForResult(takePhotoIntent, TAKE_PHOTO_REQUEST_CODE); 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 @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK && data != null) { if (resultCode == RESULT_OK) {
switch (requestCode) { switch (requestCode) {
case OPEN_GALLERY_REQUEST_CODE: case OPEN_GALLERY_REQUEST_CODE:
if (data == null) {
break;
}
try { try {
ContentResolver resolver = getContentResolver(); ContentResolver resolver = getContentResolver();
Uri uri = data.getData(); Uri uri = data.getData();
...@@ -393,9 +439,22 @@ public class MainActivity extends AppCompatActivity { ...@@ -393,9 +439,22 @@ public class MainActivity extends AppCompatActivity {
} }
break; break;
case TAKE_PHOTO_REQUEST_CODE: case TAKE_PHOTO_REQUEST_CODE:
Bundle extras = data.getExtras(); if (currentPhotoPath != null) {
Bitmap image = (Bitmap) extras.get("data"); 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); onImageChanged(image);
} else {
Log.e(TAG, "currentPhotoPath is null");
}
break; break;
default: default:
break; break;
......
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();
}
}
}
...@@ -35,8 +35,8 @@ public class OCRPredictorNative { ...@@ -35,8 +35,8 @@ public class OCRPredictorNative {
} }
public void release(){ public void release() {
if (nativePointer != 0){ if (nativePointer != 0) {
nativePointer = 0; nativePointer = 0;
destory(nativePointer); destory(nativePointer);
} }
......
...@@ -38,7 +38,7 @@ public class Predictor { ...@@ -38,7 +38,7 @@ public class Predictor {
protected float scoreThreshold = 0.1f; protected float scoreThreshold = 0.1f;
protected Bitmap inputImage = null; protected Bitmap inputImage = null;
protected Bitmap outputImage = null; protected Bitmap outputImage = null;
protected String outputResult = ""; protected volatile String outputResult = "";
protected float preprocessTime = 0; protected float preprocessTime = 0;
protected float postprocessTime = 0; protected float postprocessTime = 0;
...@@ -46,6 +46,16 @@ public class Predictor { ...@@ -46,6 +46,16 @@ public class Predictor {
public 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, public boolean init(Context appCtx, String modelPath, String labelPath, int cpuThreadNum, String cpuPowerMode,
String inputColorFormat, String inputColorFormat,
long[] inputShape, float[] inputMean, long[] inputShape, float[] inputMean,
...@@ -76,11 +86,7 @@ public class Predictor { ...@@ -76,11 +86,7 @@ public class Predictor {
Log.e(TAG, "Only BGR color format is supported."); Log.e(TAG, "Only BGR color format is supported.");
return false; return false;
} }
isLoaded = loadModel(appCtx, modelPath, cpuThreadNum, cpuPowerMode); boolean isLoaded = init(appCtx, modelPath, labelPath);
if (!isLoaded) {
return false;
}
isLoaded = loadLabel(appCtx, labelPath);
if (!isLoaded) { if (!isLoaded) {
return false; return false;
} }
...@@ -127,12 +133,12 @@ public class Predictor { ...@@ -127,12 +133,12 @@ public class Predictor {
} }
public void releaseModel() { public void releaseModel() {
if (paddlePredictor != null){ if (paddlePredictor != null) {
paddlePredictor.release(); paddlePredictor.release();
paddlePredictor = null; paddlePredictor = null;
} }
isLoaded = false; isLoaded = false;
cpuThreadNum = 4; cpuThreadNum = 1;
cpuPowerMode = "LITE_POWER_HIGH"; cpuPowerMode = "LITE_POWER_HIGH";
modelPath = ""; modelPath = "";
modelName = ""; modelName = "";
...@@ -222,7 +228,7 @@ public class Predictor { ...@@ -222,7 +228,7 @@ public class Predictor {
for (int i = 0; i < warmupIterNum; i++) { for (int i = 0; i < warmupIterNum; i++) {
paddlePredictor.runImage(inputData, width, height, channels, inputImage); paddlePredictor.runImage(inputData, width, height, channels, inputImage);
} }
warmupIterNum = 0; // 之后不要再warm warmupIterNum = 0; // do not need warm
// Run inference // Run inference
start = new Date(); start = new Date();
ArrayList<OcrResultModel> results = paddlePredictor.runImage(inputData, width, height, channels, inputImage); ArrayList<OcrResultModel> results = paddlePredictor.runImage(inputData, width, height, channels, inputImage);
...@@ -287,9 +293,7 @@ public class Predictor { ...@@ -287,9 +293,7 @@ public class Predictor {
if (image == null) { if (image == null) {
return; return;
} }
// Scale image to the size of input tensor this.inputImage = image.copy(Bitmap.Config.ARGB_8888, true);
Bitmap rgbaImage = image.copy(Bitmap.Config.ARGB_8888, true);
this.inputImage = rgbaImage;
} }
private ArrayList<OcrResultModel> postprocess(ArrayList<OcrResultModel> results) { private ArrayList<OcrResultModel> postprocess(ArrayList<OcrResultModel> results) {
...@@ -310,7 +314,7 @@ public class Predictor { ...@@ -310,7 +314,7 @@ public class Predictor {
private void drawResults(ArrayList<OcrResultModel> results) { private void drawResults(ArrayList<OcrResultModel> results) {
StringBuffer outputResultSb = new StringBuffer(""); StringBuffer outputResultSb = new StringBuffer("");
for (int i=0;i<results.size();i++) { for (int i = 0; i < results.size(); i++) {
OcrResultModel result = results.get(i); OcrResultModel result = results.get(i);
StringBuilder sb = new StringBuilder(""); StringBuilder sb = new StringBuilder("");
sb.append(result.getLabel()); sb.append(result.getLabel());
...@@ -319,8 +323,8 @@ public class Predictor { ...@@ -319,8 +323,8 @@ public class Predictor {
for (Point p : result.getPoints()) { for (Point p : result.getPoints()) {
sb.append("(").append(p.x).append(",").append(p.y).append(") "); sb.append("(").append(p.x).append(",").append(p.y).append(") ");
} }
Log.i(TAG, sb.toString()); Log.i(TAG, sb.toString()); // show LOG in Logcat panel
outputResultSb.append(i+1).append(": ").append(result.getLabel()).append("\n"); outputResultSb.append(i + 1).append(": ").append(result.getLabel()).append("\n");
} }
outputResult = outputResultSb.toString(); outputResult = outputResultSb.toString();
outputImage = inputImage; outputImage = inputImage;
......
...@@ -5,7 +5,8 @@ import android.os.Bundle; ...@@ -5,7 +5,8 @@ import android.os.Bundle;
import android.preference.CheckBoxPreference; import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference; import android.preference.EditTextPreference;
import android.preference.ListPreference; import android.preference.ListPreference;
import android.support.v7.app.ActionBar;
import androidx.appcompat.app.ActionBar;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
......
...@@ -2,6 +2,8 @@ package com.baidu.paddle.lite.demo.ocr; ...@@ -2,6 +2,8 @@ package com.baidu.paddle.lite.demo.ocr;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.os.Environment; import android.os.Environment;
import java.io.*; import java.io.*;
...@@ -110,4 +112,48 @@ public class Utils { ...@@ -110,4 +112,48 @@ public class Utils {
} }
return Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true); 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;
}
}
} }
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
...@@ -96,4 +96,4 @@ ...@@ -96,4 +96,4 @@
</RelativeLayout> </RelativeLayout>
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file \ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!-- for MiniActivity Use Only -->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintLeft_toRightOf="parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/sample_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/imageView"
android:scrollbars="vertical"
/>
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="20dp"
android:paddingBottom="20dp"
app:layout_constraintBottom_toTopOf="@id/imageView"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="@tools:sample/avatars" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:text="Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
tools:layout_editor_absoluteX="161dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="my_images" path="Pictures" />
</paths>
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment