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);
        }
    }
}
相关推荐
雨白5 小时前
Jetpack系列(二):Lifecycle与LiveData结合,打造响应式UI
android·android jetpack
kk爱闹7 小时前
【挑战14天学完python和pytorch】- day01
android·pytorch·python
每次的天空8 小时前
Android-自定义View的实战学习总结
android·学习·kotlin·音视频
恋猫de小郭9 小时前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
断剑重铸之日10 小时前
Android自定义相机开发(类似OCR扫描相机)
android
随心最为安10 小时前
Android Library Maven 发布完整流程指南
android
岁月玲珑10 小时前
【使用Android Studio调试手机app时候手机老掉线问题】
android·ide·android studio
还鮟14 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡15 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi0015 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体