介绍 此应用程序使用Tesseract 3的Tesseract OCR引擎,该引擎通过识别字符模式( https://github.com/tesseract-ocr/tesseract )来工作。 Tesseract具有unicode(UTF-8)支持,可以开箱即用识别100多种语言。 背景 我尝试了Google文本识别API- https://deve

介绍

此应用程序使用Tesseract 3的Tesseract OCR引擎,该引擎通过识别字符模式(https://github.com/tesseract-ocr/tesseract)来工作。Tesseract具有unicode(UTF-8)支持,可以“开箱即用”识别100多种语言。

背景

我尝试了Google文本识别API- https://developers.google.com/vision/android/text-overview,但它不适合我,所以我找到了这个惊人的引擎。

使用代码

开始吧!在Android studio中创建一个新项目(我使用的是3.2.1版),或者您可以下载源文件并选择:File-New-Import项目。

添加到build.gradle应用程序级别:

implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' implementation 'com.rmtheis:tess-two:9.0.0'

我使用Butterknife库,它非常有用,主库是 - ' tess-two:9.0.0'' - 它包含一个Android的Tesseract工具(tesseract-android-tools)的分支,它增加了一些额外的功能。此外,我们需要相机和写入权限,因此将其添加到AndroidManifest.xml

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.camera" />
<uses-permission android:name="android.permission.CAMERA" />

做一个简单的布局文件ButtonTextView并且ImageView

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:fillViewport="true" tools:context=".MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <Button android:id="@+id/scan_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="scan" />
</LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="4dp" android:orientation="horizontal"> <TextView android:id="@+id/ocr_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="fill" android:text=" text"> </TextView> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <ImageViewandroid:id="@+id/ocr_image"android:layout_width="match_parent"android:layout_height="wrap_content"/> </LinearLayout> </LinearLayout>
</ScrollView>

我们得到这样的东西:

编写一些代码来检查权限:

void checkPermissions() {
if (!hasPermissions(context, PERMISSIONS)) {
requestPermissions(PERMISSIONS,
PERMISSION_ALL);
flagPermissions = false;
}
flagPermissions = true;
} public static boolean hasPermissions(Context context, String... permissions) {
if (context != null && permissions != null) {
for (String permission : permissions) {
if (ActivityCompat.checkSelfPermission(context, permission)
!= PackageManager.PERMISSION_GRANTED) {
return false;
}
}
}
return true;
}

和代码来创建一个文件:

public File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("MMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = image.getAbsolutePath();
return image;
}

首先,我们需要写onClickScanButton函数,它:

@OnClick(R.id.scan_button)
void onClickScanButton() {
// check permissions
if (!flagPermissions) {
checkPermissions();
return;
}
//prepare intent
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (takePictureIntent.resolveActivity(context.getPackageManager()) != null) {
File photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException ex) {
Toast.makeText(context, errorFileCreate, Toast.LENGTH_SHORT).show();
Log.i("File error", ex.toString());
}
// Continue only if the File was successfully created
if (photoFile != null) {
oldPhotoURI = photoURI1;
photoURI1 = Uri.fromFile(photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI1);
startActivityForResult(takePictureIntent, REQUEST_IMAGE1_CAPTURE);
}
}
}

我们可以在这里查看结果:

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data); switch (requestCode) {
case REQUEST_IMAGE1_CAPTURE: {
if (resultCode == RESULT_OK) {
Bitmap bmp = null;
try {
InputStream is = context.getContentResolver().openInputStream(photoURI1);
BitmapFactory.Options options = new BitmapFactory.Options();
bmp = BitmapFactory.decodeStream(is, null, options); } catch (Exception ex) {
Log.i(getClass().getSimpleName(), ex.getMessage());
Toast.makeText(context, errorConvert, Toast.LENGTH_SHORT).show();
} firstImage.setImageBitmap(bmp);
doOCR(bmp); OutputStream os;
try {
os = new FileOutputStream(photoURI1.getPath());
if (bmp != null) {
bmp.compress(Bitmap.CompressFormat.JPEG, 100, os);
}
os.flush();
os.close();
} catch (Exception ex) {
Log.e(getClass().getSimpleName(), ex.getMessage());
Toast.makeText(context, errorFileCreate, Toast.LENGTH_SHORT).show();
} } else {
{
photoURI1 = oldPhotoURI;
firstImage.setImageURI(photoURI1);
}
}
}
}
}

接下来将Tesseract集成到我们的项目中,进行额外的课程:TesseractOCR

我在Assets文件夹中为英语语言添加了训练有素的数据文件“ eng.traineddata ” ,因此我们需要将其从APK复制到内部存储器文件目录,然后启动Tesseract系统:。mTess.init(dstInitPathDir, language)

public class TesseractOCR {

    private final TessBaseAPI mTess;

    public TesseractOCR(Context context, String language) {
mTess = new TessBaseAPI();
boolean fileExistFlag = false; AssetManager assetManager = context.getAssets(); String dstPathDir = "/tesseract/tessdata/"; String srcFile = "eng.traineddata";
InputStream inFile = null; dstPathDir = context.getFilesDir() + dstPathDir;
String dstInitPathDir = context.getFilesDir() + "/tesseract";
String dstPathFile = dstPathDir + srcFile;
FileOutputStream outFile = null; try {
inFile = assetManager.open(srcFile); File f = new File(dstPathDir); if (!f.exists()) {
if (!f.mkdirs()) {
Toast.makeText(context, srcFile + " can't be created.", Toast.LENGTH_SHORT).show();
}
outFile = new FileOutputStream(new File(dstPathFile));
} else {
fileExistFlag = true;
} } catch (Exception ex) {
Log.e(TAG, ex.getMessage()); } finally { if (fileExistFlag) {
try {
if (inFile != null) inFile.close();
mTess.init(dstInitPathDir, language);
return; } catch (Exception ex) {
Log.e(TAG, ex.getMessage());
}
} if (inFile != null && outFile != null) {
try {
//copy file
byte[] buf = new byte[1024];
int len;
while ((len = inFile.read(buf)) != -1) {
outFile.write(buf, 0, len);
}
inFile.close();
outFile.close();
mTess.init(dstInitPathDir, language);
} catch (Exception ex) {
Log.e(TAG, ex.getMessage());
}
} else {
Toast.makeText(context, srcFile + " can't be read.", Toast.LENGTH_SHORT).show();
}
}
} public String getOCRResult(Bitmap bitmap) {
mTess.setImage(bitmap);
return mTess.getUTF8Text();
} public void onDestroy() {
if (mTess != null) mTess.end();
}
}

OCR代码很简单 - 我们需要将图像(位图BMP)传递给该对象并获得结果:

public String getOCRResult(Bitmap bitmap) {
mTess.setImage(bitmap);
return mTess.getUTF8Text(); }

OCR可能需要很长时间,因此我们需要在另一个中进行Thread

private void doOCR(final Bitmap bitmap) {
if (mProgressDialog == null) {
mProgressDialog = ProgressDialog.show(this, "Processing",
"Doing OCR...", true);
} else {
mProgressDialog.show();
}
new Thread(new Runnable() {
public void run() {
final String srcText = mTessOCR.getOCRResult(bitmap);
runOnUiThread(new Runnable() {
@Override
public void run() { if (srcText != null && !srcText.equals("")) {
ocrText.setText(srcText);
}
mProgressDialog.dismiss();
}
});
}
}).start();
}

源图像如下:

OCR的结果如下:

Android开发如何轻松实现基于Tesseract的Android OCR应用程序的更多相关文章

  1. Android开发学习之路--基于vitamio的视频播放器(二)

      终于把该忙的事情都忙得差不多了,接下来又可以开始good good study,day day up了.在Android开发学习之路–基于vitamio的视频播放器(一)中,主要讲了播放器的界面的 ...

  2. Android开发快速入门(环境配置、Android Studio安装)

    Android是一种激动人心的开源移动平台,它像手机一样无处不在,得到了Google以及其他一些开放手机联盟成员(如三星.HTC.中国移动.Verizon和AT&T等)的支持,因而不能不加以学 ...

  3. 基于Tesseract组件的OCR识别

    基于Tesseract组件的OCR识别 背景以及介绍 欲研究C#端如何进行图像的基本OCR识别,找到一款开源的OCR识别组件.该组件当前已经已经升级到了4.0版本.和传统的版本(3.x)比,4.0时代 ...

  4. Android开发学习之路-该怎么学Android(Service和Activity通信为例)

    在大部分地方,比如书本或者学校和培训机构,教学Android的方式都基本类似,就是告诉先上原理方法,然后对着代码讲一下. 但是,这往往不是一个很好的方法,为什么? ① 学生要掌握这个方法的用途,只能通 ...

  5. Android开发学习总结——搭建最新版本的Android开发环境

    原文出自:https://www.cnblogs.com/xdp-gacl/p/4322165.html#undefined 最近由于工作中要负责开发一款Android的App,之前都是做JavaWe ...

  6. android开发学习笔记系列(1)-android起航

    前言 在学习安卓的过程中,我觉得非常有必要将自己所学的东西进行整理,因为每每当我知道我应该是如何去实现功能的时候,有许多细节问题我总是会遗漏,因此我也萌生了写一系列博客来描述自己学习的路线,让我的an ...

  7. Android开发——1轻松战胜开发环境

    写在前头的话:鄙人乃2016年本科毕业的程序yuan一枚,大学阶段从未学过安卓,java也是一知半解,回想这一年半的开发生涯真的是相当悲壮.你要是问我喜欢开发吗,当然确定一定以及肯定地告诉你不喜欢啊! ...

  8. 【Android开发资料分享】自己整理的Android开发资料,非常全面

    学习Android以来,不知不觉中收集了大量非常优秀的Android开发资料,一直没有系统的整理,最近抽时间把收藏夹中的资料做了一下整理,现在分享给大家,希望能够帮助到需要的人.这份资料我还会不断的更 ...

  9. 【Android开发】创建你的第一个Android项目

    原文:http://android.eoe.cn/topic/summary 本文中你将了解到: 1. 使用Eclipse创建项目 2. 使用命令行创建项目 你还应该阅读: 1. 安装SDK(官网页面 ...

随机推荐

  1. [Swift]LeetCode31. 下一个排列 | Next Permutation

    Implement next permutation, which rearranges numbers into the lexicographically next greater permuta ...

  2. [Swift]LeetCode712. 两个字符串的最小ASCII删除和 | Minimum ASCII Delete Sum for Two Strings

    Given two strings s1, s2, find the lowest ASCII sum of deleted characters to make two strings equal. ...

  3. 开启SSH

    开启 ssh 远程连接 1.修改 sshd_config 输入 sudo vim /etc/ssh/sshd_config 做如下修改 PermitRootLogin yes [需要把注释 #号去掉, ...

  4. vue-textarea 自适应高度

    需求简介 一个搜索页面,上面输入框,下面列表展示搜索到的结果. 重点是:产品要求搜索框默认显示一行,当输入的文字超过一行时,输入框的高度会随着改变,直到输入完毕. 解决思路设想 本想利用textare ...

  5. 记录一次安装OpenGL的漫长过程

    尝试codeblock和Dev-C++ 这学期新开了一门计算机图形图像的课,里面涉及到openGL,中午跑到图书馆开始倒腾OpenGL. 因为电脑里本来有codeblock,于是就想不用教材里面所说的 ...

  6. C# 当中 LINQ 的常规用法(Lambda 方式)

    仅以本篇博文记录 LINQ 相关操作的基本知识,原型参考自 MSDN 相关知识,中间加以自己的理解与 DEMO. 1. IEnuemrable<T>.Select() Select 方法比 ...

  7. Vim 复制粘帖格式错乱问题的解决办法

    有时候,复制文本(尤其是代码)到 Vim,会出现格式错乱的问题.看样子,应该是自动缩进惹得祸.本文不去深究原因,直接给出解决方法. 1. paste 模式 运行如下命令,进入 paste 模式: :s ...

  8. 『Power Hungry Cows A*启发式搜索』

    Power Hungry Cows(POJ 1945) Description FJ的奶牛想要快速计算整数P的幂 (1 <= P <=20,000),它们需要你的帮助.因为计算极大数的幂, ...

  9. 《深入java虚拟机》读书笔记之Java内存区域

    前言 该读书笔记用于记录在学习<深入理解Java虚拟机--JVM高级特性与最佳实践>一书中的一些重要知识点,对其中的部分内容进行归纳,主要是方便之后进行复习. 运行时数据区域 Java虚拟 ...

  10. IDEA搭建Spring Boot项目

    所需工具 新建项目 创建一个login控制器 写入两个注释 import导入项会自动添加@RestController@RequestMapping(value = "/login" ...