Android开发 OCR:通过Tesseract实现图片文字识别

下面是整个详解步骤过程

效果图

  • 流程:获取assets中的图片显示到页面,提取照片内的文字

一、OCR的含义

ocr是Optical Character Recognition(光学字符识别)是指电子设备(例如扫描仪或数码相机)检查纸上打印的字符,通过检测暗、亮的模式确定其形状,然后用字符识别方法将形状翻译成计算机文字的过程

二、什么是Tesseract

官网奉上

简单地说,Tesseract 就是OCR所说的"识别软件"的具体实现

  • 其实看官网已经是5、6年前就开始不维护了
  • 这里也指明了,不在维护,可前往 Tesseract Tools 的一个分支Tesseract4Android官网,这里还是写一下Tesseract 的demo吧,做参考

当然你也可以直接去Tesseract4Android的参考文章Tesseract4Android参考文章

三、前提准备

1、添加依赖

注意:

1、Android 2.3 或更高版本

2、数据文件必须是 复制到 Android 设备的子目录中tessdata(上一级文件夹的名称必须是tessdata,后缀必须是.traineddata)

javascript 复制代码
dependencies {
    implementation 'com.rmtheis:tess-two:9.1.0'
}

2、数据文件下载路径

  • 数据包下载下来放到assets文件夹下

四、实际代码案例Demo如下:

Main.xml

javascript 复制代码
<?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">
    <ImageView
        android:id="@+id/main_iv_image"
        android:layout_width="match_parent"
        android:layout_height="500dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginLeft="5dp"
        android:layout_marginRight="5dp"/>

    <Button
        android:id="@+id/main_bt_recognize"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="5dp"
        android:layout_marginRight="5dp"
        android:layout_gravity="center_horizontal"
        android:text="读取一张图片并识别" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="5dp"
        android:layout_marginRight="5dp"
        android:layout_gravity="center_horizontal"
        android:text="识别结果:" />

    <TextView
        android:id="@+id/main_tv_result"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="5dp"
        android:layout_marginRight="5dp"
        android:layout_gravity="center_horizontal" />
</LinearLayout>

Main.java

javascript 复制代码
package com.example.ocrapplication.ui;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
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 com.example.ocrapplication.R;
import com.googlecode.tesseract.android.TessBaseAPI;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class TesseractActivity extends AppCompatActivity {

    public static final String TESS_DATA = "/tessdata";
    private static final String TARGET_FILENAME = "cs.png";
    //    private static final String DATA_FILENAME = "eng.traineddata";
    private static final String DATA_FILENAME = "chi_sim.traineddata";
    private static final String TAG = TesseractActivity.class.getSimpleName();

    private Button main_bt_recognize;
    private TextView main_tv_result;
    private ImageView main_iv_image;
    private Bitmap bitmap;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tesseract);
        // 检查并请求应用所需权限
        checkPermission();
        // 获取控件对象
        initView();
        // 设置控件的监听器
        setListener();
    }
    @SuppressLint("HandlerLeak")
    private Handler handler=new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case 1:
                    Bundle data = msg.getData();
                    main_tv_result.setText(data.getString("data"));
                    break;
            }
        }
    };
    private void setListener() {
        // 设置识别按钮的监听器
        main_bt_recognize.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 点击后的主程序
                mainProgram();
            }
        });
    }

    // 获得界面需要交互的控件
    private void initView() {
        main_bt_recognize = findViewById(R.id.main_bt_recognize);
        main_tv_result = findViewById(R.id.main_tv_result);
        main_iv_image = findViewById(R.id.main_iv_image);
        // 从assets中获取一张Bitmap图片
        bitmap = getBitmapFromAssets(TesseractActivity.this, TARGET_FILENAME);
        // 同时显示在界面
        main_iv_image.setImageBitmap(bitmap);
    }

    // OCR识别的主程序
    private void mainProgram() {
        if (bitmap != null) {
            // 准备工作:创建路径和Tesserect的数据
            prepareTess();
            // 初始化Tesserect
            TessBaseAPI tessBaseAPI = new TessBaseAPI();
            String dataPath = getExternalFilesDir("/").getPath() + "/";
            tessBaseAPI.init(dataPath, "chi_sim");
            //因为识别比较耗时,建议开启开启子线程识别
            new Thread(new Runnable() {
                @Override
                public void run() {
                    // 识别并显示结果
                    String result = getOCRResult(tessBaseAPI, bitmap);
                    //把数据返回到主线程上面显示
                    Message message=new Message();
                    message.what=1;
                    Bundle bundle = new Bundle();
                    bundle.putString("data",result);
                    message.setData(bundle);
                    handler.sendMessage(message);
                }
            }).start();
        }
    }
    // 进行OCR并返回识别结果
    private String getOCRResult(TessBaseAPI tessBaseAPI, Bitmap bitmap) {
        tessBaseAPI.setImage(bitmap);
        String result = "-";
        try{
            result = tessBaseAPI.getUTF8Text();
        }catch (Exception e){
        }
        tessBaseAPI.end();
        return result;
    }

    // 为Tesserect复制(从assets中复制过去)所需的数据
    private void prepareTess() {
        try{
            // 先创建必须的目录
            File dir = getExternalFilesDir(TESS_DATA);
            if(!dir.exists()){
                if (!dir.mkdir()) {
                    Toast.makeText(getApplicationContext(), "目录" + dir.getPath() + "没有创建成功", Toast.LENGTH_SHORT).show();
                }
            }
            // 从assets中复制必须的数据
            String pathToDataFile = dir + "/" + DATA_FILENAME;
            if (!(new File(pathToDataFile)).exists()) {
                InputStream in = getAssets().open(DATA_FILENAME);
                OutputStream out = new FileOutputStream(pathToDataFile);
                byte[] buff = new byte[1024];
                int len;
                while ((len = in.read(buff)) > 0) {
                    out.write(buff, 0, len);
                }
                in.close();
                out.close();
            }
        } catch (Exception e) {
            Log.e(TAG, e.getMessage());
        }
    }

    // 从assets中读取一张Bitmap类型的图片
    private Bitmap getBitmapFromAssets(Context context, String filename) {
        Bitmap bitmap = null;
        AssetManager assetManager = context.getAssets();
        try {
            InputStream is = assetManager.open(filename);
            bitmap = BitmapFactory.decodeStream(is);
            is.close();
            Log.i("TAG", "图片读取成功。");
//            Toast.makeText(getApplicationContext(), "图片读取成功。", Toast.LENGTH_SHORT).show();
        } catch (IOException e) {
            Log.i("TAG", "图片读取失败。");
//            Toast.makeText(getApplicationContext(), "图片读取失败。", Toast.LENGTH_SHORT).show();
            e.printStackTrace();
        }
        return bitmap;
    }

    // 检查应用所需的权限,如不满足则发出权限请求
    private void checkPermission() {
        if (ContextCompat.checkSelfPermission(getApplicationContext(),
                Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(TesseractActivity.this,
                    new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 120);
        }
        if (ContextCompat.checkSelfPermission(getApplicationContext(),
                Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(TesseractActivity.this,
                    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 121);
        }
    }
}
相关推荐
服装学院的IT男34 分钟前
【Android 13源码分析】WindowContainer窗口层级-4-Layer树
android
CCTV果冻爽2 小时前
Android 源码集成可卸载 APP
android
码农明明2 小时前
Android源码分析:从源头分析View事件的传递
android·操作系统·源码阅读
秋月霜风3 小时前
mariadb主从配置步骤
android·adb·mariadb
Python私教4 小时前
Python ORM 框架 SQLModel 快速入门教程
android·java·python
编程乐学5 小时前
基于Android Studio 蜜雪冰城(奶茶饮品点餐)—原创
android·gitee·android studio·大作业·安卓课设·奶茶点餐
翔云API5 小时前
人证合一接口:智能化身份认证的最佳选择
大数据·开发语言·node.js·ocr·php
problc6 小时前
Android中的引用类型:Weak Reference, Soft Reference, Phantom Reference 和 WeakHashMap
android
IH_LZH6 小时前
Broadcast:Android中实现组件及进程间通信
android·java·android studio·broadcast
去看全世界的云6 小时前
【Android】Handler用法及原理解析
android·java