运行效果图
识别到的内容:
{ "words_result":[ { "words":"突然间有想看书的冲动" }, { "words":"eel" }, { "words":"你不是直正的快乐" }, { "words":"你说你有点难追" }, { "words":"我就打断你的腿CN" } ], "log_id":1329724251397160960, "words_result_num":5, "direction":0 }
百度SDK文字识别
一、创建平台应用
要使用百度文字识别,自然免不了要注册该平台的账号,否则凭什么让你使用,点击百度智能云进入,没有账号的可以先注册账号,注册应该就不用我讲解了吧?这里默认都有账号了,然后登录。
登录后 然后进入控制台,找到文字识别,然后点击。
可以看到这个文字识别SDK的使用场景,本文介绍的是通用场景,然后点击创建应用。
输入应用名称,指的是你在百度智能云上创建的文字识别应用。
向下滚动。填写相关资料之后,点击立即创建
创建完成之后,点击查看应用详情。
注意API Key、Secret Key这两个值,而这个包名就是你项目的包名。
点击上方下载SDK
下载到本地,然后解压。
解压之后文件如下图所示,里面的文件在项目中是要用到的。
二、创建及配置Android项目
打开Android Studio,创建一个名为TextOCRDemo的项目,包名和你刚才的要一致。
然后打开目录
先将libs目录中的ocr-sdk.jar文件拷贝到工程libs目录中,并加入工程依赖。
然后出现
点击OK,等待添加完成,然后打开你的app下的build.gradle中,你会发现多一句依赖。
然后在main下创建一个jniLibs文件夹。将libs目录下armeabi,arm64-v8a,armeabi-v7a,x86文件夹添加到工程src/main/jniLibs目录中,如下图所示
创建好之后打开AndroidManifest.xml,注册一些需要用到的权限。
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
三、初始化
然后进入到MainActivity中进行SDK的初始化。
/** * 对应百度平台上的应用apiKey */ private String apiKey = "gQm5vnWxGuz5khN4IZ16yriL"; /** * 对应百度平台上的应用secretKey */ private String secretKey = "c8t796hbq0DXdsngSsOou5FCK2fFckpn"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initTextSDK(); } /** * 用明文ak,sk初始化 */ private void initTextSDK() { OCR.getInstance(this).initAccessTokenWithAkSk(new OnResultListener<AccessToken>() { @Override public void onResult(AccessToken result) { String token = result.getAccessToken(); Log.d("result-->","成功!"+token); } @Override public void onError(OCRError error) { error.printStackTrace(); Log.d("result-->","失败!"+error.getMessage()); } }, getApplicationContext(), apiKey, secretKey); }
运行之后打印日志
四、UI模块导入
File → New → Import Module…
ocr_ui就是之前解压之后的一个文件,你可以放在一个好找的位置。
点击Finish,导入
然后发现报错了,模块中的gradle与app中的不一致。
点击Remove Build Tools version and sync project,删除构建项目并同步版本。
点击Do Refactor,发现还有报错。打开ocr_ui模块的build.gradle
修改如下,然后Sync
dependencies { androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) implementation 'com.android.support:appcompat-v7:25.1.1' testImplementation 'junit:junit:4.12' implementation files('libs/license.jar') }
现在就不报错了。为了确保导入的模块不会影响到之前的项目,现在称还没有用之前,先运行一下,运行到真机上,没有问题的话就说明这个模块导入正常,可以使用了。
五、UI模块使用
使用其他的模块也是需要添加依赖的,打开app的build.gradle,
implementation project(path: ':ocr_ui')
然后Sync,完成后。你就可以在app中使用ocr_ui中的方法了。
首先修改activity_main.xml,这里只放了一个按钮
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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" android:orientation="vertical" tools:context=".MainActivity"> <Button android:id="@+id/general_basic_button" android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="generalBasic" android:text="通用文字识别" /> </LinearLayout>
然后在MainActivity中创建一个generalBasic方法。如下:
/** * 通用文字识别 * @param view */ public void generalBasic(View view) { }
然后创建一个成员变量
/** * 通用文字识别请求码 */ private static final int REQUEST_CODE_GENERAL_BASIC = 100;
创建获取保存文件的方法和Toast提示的方法
/** * Toast提示 * @param msg */ private void showMsg(String msg) { Toast.makeText(this,msg,Toast.LENGTH_SHORT).show(); } /** * 获取保存文件 * @param context * @return */ public static File getSaveFile(Context context) { File file = new File(context.getFilesDir(), "pic.jpg"); return file; }
点击按钮时调用generalBasic
/** * 通用文字识别 * @param view */ public void generalBasic(View view) { Intent intent = new Intent(MainActivity.this, CameraActivity.class); //传入文件保存的路径 intent.putExtra(CameraActivity.KEY_OUTPUT_FILE_PATH, getSaveFile(getApplication()).getAbsolutePath()); //传入文件类型 intent.putExtra(CameraActivity.KEY_CONTENT_TYPE, CameraActivity.CONTENT_TYPE_GENERAL); //跳转页面时传递请求码,返回时根据请求码判断获取识别的数据。 startActivityForResult(intent, REQUEST_CODE_GENERAL_BASIC); }
在com.llw.textocr包下新建一个RecognizeService类
代码如下
/* * Copyright (C) 2017 Baidu, Inc. All Rights Reserved. */ package com.llw.textocr; import android.content.Context; import com.baidu.ocr.sdk.OCR; import com.baidu.ocr.sdk.OnResultListener; import com.baidu.ocr.sdk.exception.OCRError; import com.baidu.ocr.sdk.model.BankCardParams; import com.baidu.ocr.sdk.model.BankCardResult; import com.baidu.ocr.sdk.model.GeneralBasicParams; import com.baidu.ocr.sdk.model.GeneralParams; import com.baidu.ocr.sdk.model.GeneralResult; import com.baidu.ocr.sdk.model.OcrRequestParams; import com.baidu.ocr.sdk.model.OcrResponseResult; import com.baidu.ocr.sdk.model.Word; import com.baidu.ocr.sdk.model.WordSimple; import java.io.File; /** * Created by ruanshimin on 2017/4/20. */ public class RecognizeService { interface ServiceListener { public void onResult(String result); } public static void recGeneral(Context ctx, String filePath, final ServiceListener listener) { GeneralParams param = new GeneralParams(); param.setDetectDirection(true); param.setVertexesLocation(true); param.setRecognizeGranularity(GeneralParams.GRANULARITY_SMALL); param.setImageFile(new File(filePath)); OCR.getInstance(ctx).recognizeGeneral(param, new OnResultListener<GeneralResult>() { @Override public void onResult(GeneralResult result) { StringBuilder sb = new StringBuilder(); for (WordSimple wordSimple : result.getWordList()) { Word word = (Word) wordSimple; sb.append(word.getWords()); sb.append("\n"); } listener.onResult(result.getJsonRes()); } @Override public void onError(OCRError error) { listener.onResult(error.getMessage()); } }); } public static void recAccurate(Context ctx, String filePath, final ServiceListener listener) { GeneralParams param = new GeneralParams(); param.setDetectDirection(true); param.setVertexesLocation(true); param.setRecognizeGranularity(GeneralParams.GRANULARITY_SMALL); param.setImageFile(new File(filePath)); OCR.getInstance(ctx).recognizeAccurate(param, new OnResultListener<GeneralResult>() { @Override public void onResult(GeneralResult result) { StringBuilder sb = new StringBuilder(); for (WordSimple wordSimple : result.getWordList()) { Word word = (Word) wordSimple; sb.append(word.getWords()); sb.append("\n"); } listener.onResult(result.getJsonRes()); } @Override public void onError(OCRError error) { listener.onResult(error.getMessage()); } }); } public static void recAccurateBasic(Context ctx, String filePath, final ServiceListener listener) { GeneralParams param = new GeneralParams(); param.setDetectDirection(true); param.setVertexesLocation(true); param.setRecognizeGranularity(GeneralParams.GRANULARITY_SMALL); param.setImageFile(new File(filePath)); OCR.getInstance(ctx).recognizeAccurateBasic(param, new OnResultListener<GeneralResult>() { @Override public void onResult(GeneralResult result) { StringBuilder sb = new StringBuilder(); for (WordSimple wordSimple : result.getWordList()) { WordSimple word = wordSimple; sb.append(word.getWords()); sb.append("\n"); } listener.onResult(result.getJsonRes()); } @Override public void onError(OCRError error) { listener.onResult(error.getMessage()); } }); } public static void recGeneralBasic(Context ctx, String filePath, final ServiceListener listener) { GeneralBasicParams param = new GeneralBasicParams(); param.setDetectDirection(true); param.setImageFile(new File(filePath)); OCR.getInstance(ctx).recognizeGeneralBasic(param, new OnResultListener<GeneralResult>() { @Override public void onResult(GeneralResult result) { StringBuilder sb = new StringBuilder(); for (WordSimple wordSimple : result.getWordList()) { WordSimple word = wordSimple; sb.append(word.getWords()); sb.append("\n"); } listener.onResult(result.getJsonRes()); } @Override public void onError(OCRError error) { listener.onResult(error.getMessage()); } }); } public static void recGeneralEnhanced(Context ctx, String filePath, final ServiceListener listener) { GeneralBasicParams param = new GeneralBasicParams(); param.setDetectDirection(true); param.setImageFile(new File(filePath)); OCR.getInstance(ctx).recognizeGeneralEnhanced(param, new OnResultListener<GeneralResult>() { @Override public void onResult(GeneralResult result) { StringBuilder sb = new StringBuilder(); for (WordSimple wordSimple : result.getWordList()) { WordSimple word = wordSimple; sb.append(word.getWords()); sb.append("\n"); } listener.onResult(result.getJsonRes()); } @Override public void onError(OCRError error) { listener.onResult(error.getMessage()); } }); } public static void recWebimage(Context ctx, String filePath, final ServiceListener listener) { GeneralBasicParams param = new GeneralBasicParams(); param.setDetectDirection(true); param.setImageFile(new File(filePath)); OCR.getInstance(ctx).recognizeWebimage(param, new OnResultListener<GeneralResult>() { @Override public void onResult(GeneralResult result) { StringBuilder sb = new StringBuilder(); for (WordSimple wordSimple : result.getWordList()) { WordSimple word = wordSimple; sb.append(word.getWords()); sb.append("\n"); } listener.onResult(result.getJsonRes()); } @Override public void onError(OCRError error) { listener.onResult(error.getMessage()); } }); } public static void recBankCard(Context ctx, String filePath, final ServiceListener listener) { BankCardParams param = new BankCardParams(); param.setImageFile(new File(filePath)); OCR.getInstance(ctx).recognizeBankCard(param, new OnResultListener<BankCardResult>() { @Override public void onResult(BankCardResult result) { String res = String.format("卡号:%s\n类型:%s\n发卡行:%s", result.getBankCardNumber(), result.getBankCardType().name(), result.getBankName()); listener.onResult(res); } @Override public void onError(OCRError error) { listener.onResult(error.getMessage()); } }); } public static void recVehicleLicense(Context ctx, String filePath, final ServiceListener listener) { OcrRequestParams param = new OcrRequestParams(); param.setImageFile(new File(filePath)); OCR.getInstance(ctx).recognizeVehicleLicense(param, new OnResultListener<OcrResponseResult>() { @Override public void onResult(OcrResponseResult result) { listener.onResult(result.getJsonRes()); } @Override public void onError(OCRError error) { listener.onResult(error.getMessage()); } }); } public static void recDrivingLicense(Context ctx, String filePath, final ServiceListener listener) { OcrRequestParams param = new OcrRequestParams(); param.setImageFile(new File(filePath)); OCR.getInstance(ctx).recognizeDrivingLicense(param, new OnResultListener<OcrResponseResult>() { @Override public void onResult(OcrResponseResult result) { listener.onResult(result.getJsonRes()); } @Override public void onError(OCRError error) { listener.onResult(error.getMessage()); } }); } public static void recLicensePlate(Context ctx, String filePath, final ServiceListener listener) { OcrRequestParams param = new OcrRequestParams(); param.setImageFile(new File(filePath)); OCR.getInstance(ctx).recognizeLicensePlate(param, new OnResultListener<OcrResponseResult>() { @Override public void onResult(OcrResponseResult result) { listener.onResult(result.getJsonRes()); } @Override public void onError(OCRError error) { listener.onResult(error.getMessage()); } }); } public static void recBusinessLicense(Context ctx, String filePath, final ServiceListener listener) { OcrRequestParams param = new OcrRequestParams(); param.setImageFile(new File(filePath)); OCR.getInstance(ctx).recognizeBusinessLicense(param, new OnResultListener<OcrResponseResult>() { @Override public void onResult(OcrResponseResult result) { listener.onResult(result.getJsonRes()); } @Override public void onError(OCRError error) { listener.onResult(error.getMessage()); } }); } public static void recReceipt(Context ctx, String filePath, final ServiceListener listener) { OcrRequestParams param = new OcrRequestParams(); param.setImageFile(new File(filePath)); param.putParam("detect_direction", "true"); OCR.getInstance(ctx).recognizeReceipt(param, new OnResultListener<OcrResponseResult>() { @Override public void onResult(OcrResponseResult result) { listener.onResult(result.getJsonRes()); } @Override public void onError(OCRError error) { listener.onResult(error.getMessage()); } }); } public static void recPassport(Context ctx, String filePath, final ServiceListener listener) { OcrRequestParams param = new OcrRequestParams(); param.setImageFile(new File(filePath)); OCR.getInstance(ctx).recognizePassport(param, new OnResultListener<OcrResponseResult>() { @Override public void onResult(OcrResponseResult result) { listener.onResult(result.getJsonRes()); } @Override public void onError(OCRError error) { listener.onResult(error.getMessage()); } }); } public static void recVatInvoice(Context ctx, String filePath, final ServiceListener listener) { OcrRequestParams param = new OcrRequestParams(); param.setImageFile(new File(filePath)); OCR.getInstance(ctx).recognizeVatInvoice(param, new OnResultListener<OcrResponseResult>() { @Override public void onResult(OcrResponseResult result) { listener.onResult(result.getJsonRes()); } @Override public void onError(OCRError error) { listener.onResult(error.getMessage()); } }); } public static void recQrcode(Context ctx, String filePath, final ServiceListener listener) { OcrRequestParams param = new OcrRequestParams(); param.setImageFile(new File(filePath)); OCR.getInstance(ctx).recognizeQrcode(param, new OnResultListener<OcrResponseResult>() { @Override public void onResult(OcrResponseResult result) { listener.onResult(result.getJsonRes()); } @Override public void onError(OCRError error) { listener.onResult(error.getMessage()); } }); } public static void recNumbers(Context ctx, String filePath, final ServiceListener listener) { OcrRequestParams param = new OcrRequestParams(); param.setImageFile(new File(filePath)); OCR.getInstance(ctx).recognizeNumbers(param, new OnResultListener<OcrResponseResult>() { @Override public void onResult(OcrResponseResult result) { listener.onResult(result.getJsonRes()); } @Override public void onError(OCRError error) { listener.onResult(error.getMessage()); } }); } public static void recLottery(Context ctx, String filePath, final ServiceListener listener) { OcrRequestParams param = new OcrRequestParams(); param.setImageFile(new File(filePath)); OCR.getInstance(ctx).recognizeLottery(param, new OnResultListener<OcrResponseResult>() { @Override public void onResult(OcrResponseResult result) { listener.onResult(result.getJsonRes()); } @Override public void onError(OCRError error) { listener.onResult(error.getMessage()); } }); } public static void recBusinessCard(Context ctx, String filePath, final ServiceListener listener) { OcrRequestParams param = new OcrRequestParams(); param.setImageFile(new File(filePath)); OCR.getInstance(ctx).recognizeBusinessCard(param, new OnResultListener<OcrResponseResult>() { @Override public void onResult(OcrResponseResult result) { listener.onResult(result.getJsonRes()); } @Override public void onError(OCRError error) { listener.onResult(error.getMessage()); } }); } public static void recHandwriting(Context ctx, String filePath, final ServiceListener listener) { OcrRequestParams param = new OcrRequestParams(); param.setImageFile(new File(filePath)); OCR.getInstance(ctx).recognizeHandwriting(param, new OnResultListener<OcrResponseResult>() { @Override public void onResult(OcrResponseResult result) { listener.onResult(result.getJsonRes()); } @Override public void onError(OCRError error) { listener.onResult(error.getMessage()); } }); } public static void recCustom(Context ctx, String filePath, final ServiceListener listener) { OcrRequestParams param = new OcrRequestParams(); param.putParam("templateSign", ""); param.putParam("classifierId", 0); param.setImageFile(new File(filePath)); OCR.getInstance(ctx).recognizeCustom(param, new OnResultListener<OcrResponseResult>() { @Override public void onResult(OcrResponseResult result) { listener.onResult(result.getJsonRes()); } @Override public void onError(OCRError error) { listener.onResult(error.getMessage()); } }); } }
这是百度开发人员写的一个工具类,用来处理返回的数据的。
然后在MainActivity中重写onActivityResult方法。
/** * Activity回调 * @param requestCode * @param resultCode * @param data */ @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); // 识别成功回调,通用文字识别 if (requestCode == REQUEST_CODE_GENERAL_BASIC && resultCode == Activity.RESULT_OK) { RecognizeService.recGeneralBasic(this, getSaveFile(getApplicationContext()).getAbsolutePath(), new RecognizeService.ServiceListener() { @Override public void onResult(String result) { showMsg(result); Log.d("result-->",result); } }); } }
这是百度开发人员写的一个工具类,用来处理返回的数据的。
然后在MainActivity中重写onActivityResult方法。
/** * Activity回调 * @param requestCode * @param resultCode * @param data */ @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); // 识别成功回调,通用文字识别 if (requestCode == REQUEST_CODE_GENERAL_BASIC && resultCode == Activity.RESULT_OK) { RecognizeService.recGeneralBasic(this, getSaveFile(getApplicationContext()).getAbsolutePath(), new RecognizeService.ServiceListener() { @Override public void onResult(String result) { showMsg(result); Log.d("result-->",result); } }); } }
返回时根据传递文件路径进行解析,这里解析的是一个图片,因为跳转的是一个相机Actiivty,拍照之后进行内容区域选取,然后返回。现在运行,你就会发现又报错了,这是改变了ocr_ui模块的gradle版本造成的。
import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.annotation.IntDef;
这两个包没有了,需要改变为androidx下的。
import androidx.annotation.NonNull; import androidx.core.app.ActivityCompat; import androidx.annotation.IntDef;
这个要改的类比较多,慢慢改就好了,都改好之后再运行。
日志如下:
OK,就这么愉快而又简单的完成了。
六、高精度文字识别
通过之前的操作已经可以简单是识别拍照的文字,但是还不够,原因有两点,第一就是识别不准确,第二是返回的数据不够简洁。下面就来解决这两个点。
打开activity_main.xml,在里面增加一个按钮。
<Button android:id="@+id/high_precision_button" android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="highPrecision" android:text="通用文字识别(高精度)" />
然后进入到MainActivity中。
/** * 通用文字识别(高精度)请求码 */ private static final int REQUEST_CODE_ACCURATE_BASIC = 101; /** * 弹窗 */ private AlertDialog.Builder mDialog;
在onCreate中
然后创建一个highPrecision方法
/** * 通用文字识别 (高精度版) * * @param view */ public void highPrecision(View view) { Intent intent = new Intent(MainActivity.this, CameraActivity.class); intent.putExtra(CameraActivity.KEY_OUTPUT_FILE_PATH, getSaveFile(getApplication()).getAbsolutePath()); intent.putExtra(CameraActivity.KEY_CONTENT_TYPE, CameraActivity.CONTENT_TYPE_GENERAL); startActivityForResult(intent, REQUEST_CODE_ACCURATE_BASIC); }
高精度和普通的区别就在于不同的请求码,而关键就在于返回的处理上。
在onActivityResult中
// 识别成功回调,通用文字识别(高精度版) if (requestCode == REQUEST_CODE_ACCURATE_BASIC && resultCode == Activity.RESULT_OK) { RecognizeService.recAccurateBasic(this, getSaveFile(getApplicationContext()).getAbsolutePath(), new RecognizeService.ServiceListener() { @Override public void onResult(String result) { //弹窗显示识别内容 showDialog(result); Log.d("result-->", result); } }); }
现在高精度识别已经有了,下面就是对JSON字符串的处理了,打开app的build.gradle,添加如下依赖:
//GSON解析 implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
然后Sync同步一下,同步之后回到MainActivity中写showDialog方法。代码如下:
/** * 显示识别结果弹窗 * @param result */ private void showDialog(final String result) { this.runOnUiThread(new Runnable() { @Override public void run() { //数据解析 Gson将JSON字符串转为实体Bean TextResult textResult = new Gson().fromJson(result, TextResult.class); if (textResult.getWords_result() == null && textResult.getWords_result().size() <= 0) { return; } String text = ""; //数据不为空并且大于0 for(int i = 0;i<textResult.getWords_result().size();i++){ text += textResult.getWords_result().get(i).getWords()+"\n"; } mDialog.setMessage(text) .setPositiveButton("确定", null) .show(); } }); }
拿到高精度识别之后的JOSN字符串之后,进行转换,转换之后对数据进行判断,不为空则进行遍历,遍历完之后将数据显示在弹窗上。代码写完了,来运行一下吧。不过说真的,这是一个技术活,手抖一下就模糊了,就又有重新再拍,不过百度好像没有做防抖,不知道啥时候加上呢?
运行效果如下图: