OCR 图片识别 Tesseract基于Android Studio的示例演示搭建

前言:之前在外包网站看到身份证识别和车牌号的识别的需求,立马就想到了OCR技术。国内三巨头BAT的云计算都提供了OCR技术服务,但他们的API大都收费;如何自己实现OCR呢?google开源的Tesseract就是今天的主题,tess-two是Tesseract在Android上的应用。

所需环境:
- Android Studio 2.2.2
- JDK1.8
- tesseract中文简体字库chi_sim.traineddata download

一、新建项目TesserAct

打开Android Studio,New Project 项目名暂定为TesserActOCR,Next后选择Empty Activity,默认名为MainActivity,然后Finish;
在app的build.gradle中添加依赖:

    compile 'com.rmtheis:tess-two:8.0.0'

重新build成功后就可以在External Libraries中找到tess-two-8.0.0

二、项目设置和代码编写

1、添加权限:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />

若字库路径的创建和复制是非程序完成的,可以去掉MOUNT_UNMOUNT_FILESYSTEMS

2、编写布局文件activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context="com.example.administrator.tesseractocr.MainActivity">

<ImageView
    android:id="@+id/img_ocr_source"
    android:src="@drawable/time"
    android:layout_gravity="center_horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

<TextView
    android:layout_marginTop="20dp"
    android:layout_width="match_parent"
    android:layout_height="30dp"
    android:text="识别结果:" />

<TextView
    android:id="@+id/ocr_result"
    android:layout_marginTop="20dp"
    android:layout_width="match_parent"
    android:layout_height="40dp" />

<Button
    android:text="识别时间"
    android:layout_marginTop="20dp"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:onClick="recognition" />
</LinearLayout>

4、复制目标文件到res/drawable目录

这里写图片描述

5、MainActivity.java

public class MainActivity extends AppCompatActivity {

    private TextView tvOCRResult;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        tvOCRResult = (TextView) findViewById(R.id.ocr_result);
        getStorageAccessPermission();
    }
    /**
     * 识别按钮
     * @param v
     */
    public void recognition(View v) {
        TessBaseAPI lvBaseAPI = null;
        try {
            Bitmap lvBitmap = BitmapFactory.decodeResource(this.getResources(), R.drawable.time);

            // 核心预设置代码
            lvBaseAPI = new TessBaseAPI();
            String path = Environment.getExternalStorageDirectory().getPath();
            lvBaseAPI.init(path + path + "/tesseract/", "chi_sim");

            lvBaseAPI.setPageSegMode(TessBaseAPI.PageSegMode.PSM_AUTO);
            lvBaseAPI.setImage(lvBitmap);

            // 获取并显示识别结果
            String result = lvBaseAPI.getUTF8Text();
            tvOCRResult.setText(result);
        } catch (Exception e) {
            Log.e("OCR", e.getMessage());
        } finally {
            lvBaseAPI.clear();
            lvBaseAPI.end();
        }
    }

    /**
     * 申请授权
     */
    private void getStorageAccessPermission() {
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            // 没有获得授权,申请授权
            if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                Toast.makeText(MainActivity.this, "请先授权!", Toast.LENGTH_LONG).show();
            } else {
                // 不需要解释为何需要该权限,直接请求授权
                ActivityCompat.requestPermissions(MainActivity.this,
                        new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
            }
            return;
        }
}

6、将下载好的字库放在手机的/storage/emulated/0/tesseract/tessdata/目录下;

三、程序真机运行

运行授权后,点击识别时间按钮,识别后的文字显示在TextView中;

这里写图片描述

可以看到图片的部分文字识别是错误的,我们可以对字体库进行训练提高识别准确度,以后有时间再训练吧。

四、Data path does not exist!路径报错解决

核心程序有下面两句代码,path的输出为:/storage/emulated/0,那我为什么重复加了两次path呢?

String path = Environment.getExternalStorageDirectory().getPath();
lvBaseAPI.init(path + path + "/tesseract/", "chi_sim");

在电脑上看字库的路径确实是:
/storage/emulated/0/tesseract/tessdata/chi_sim.traineddata

但是使用手机查看确是:
/storage/emulated/0/storage/emulated/0/tesseract/tessdata/chi_sim.traineddata
不是忽悠你,有图有真相

这里写图片描述

至于为什么会发生这种情况,我暂时也没有深入研究。所以先检查一下自己字库的真实路径保持和程序中的路径一致就好。

五、源码解读

进入init中找到源码init()方法

    if (!datapathFile.exists())
        throw new IllegalArgumentException("Data path does not exist!");

    File tessdata = new File(datapath + "tessdata");
    if (!tessdata.exists() || !tessdata.isDirectory())
        throw new IllegalArgumentException("Data path must contain subfolder tessdata!");

    //noinspection deprecation
    if (ocrEngineMode != OEM_CUBE_ONLY) {
        for (String languageCode : language.split("\\+")) {
            if (!languageCode.startsWith("~")) {
                File datafile = new File(tessdata + File.separator + 
                        languageCode + ".traineddata");
                if (!datafile.exists())
                    throw new IllegalArgumentException("Data file not found at " + datafile);
            }
        }
    }

可以看到Data path does not exist!是在这里触发的,同时还可以看到程序会在datapath的后面追加”tessdata”目录和languageCode + “.traineddata”,这样就能找到字库,这也是为什么字库一定要放在tessdata文件夹中的原因。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值