文字识别一般都用的tesseract-ocr。
GitHub: 而Android对应的比较推荐的有个tess-two。 GitHub:Demo的GitHub地址:
先看效果图
我主要是识别截图,所以图片比较规范,识别率应该很高。
简介什么都不说了,直接看简单的用法吧
首先肯定是引入依赖了
dependencies { compile 'com.rmtheis:tess-two:6.2.0'}
简单的用法其实就几行代码:
TessBaseAPI tessBaseAPI = new TessBaseAPI();tessBaseAPI.init(DATAPATH, DEFAULT_LANGUAGE);//参数后面有说明。tessBaseAPI.setImage(bitmap);String text = tessBaseAPI.getUTF8Text();
就这样简单的把一个bitmap设置进去,就能识别到里面的文字并输出了。
但是真正用的时候还是遇到了点麻烦,虽然只是简单的识别。 主要是tessBaseAPI.init(DATAPATH, DEFAULT_LANGUAGE)这个方法容易出错。 先看一下这个方法的源码吧:public boolean init(String datapath, String language) { return init(datapath, language, OEM_DEFAULT); }/** * Initializes the Tesseract engine with the specified language model(s). Returns *true
on success. * * @see #init(String, String) * * @param datapath the parent directory of tessdata ending in a forward * slash * @param language an ISO 639-3 string representing the language(s) * @param ocrEngineMode the OCR engine mode to be set * @returntrue
on success */ public boolean init(String datapath, String language, int ocrEngineMode) { if (datapath == null) throw new IllegalArgumentException("Data path must not be null!"); if (!datapath.endsWith(File.separator)) datapath += File.separator; File datapathFile = new File(datapath); 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); } } } boolean success = nativeInitOem(mNativeData, datapath, language, ocrEngineMode); if (success) { mRecycled = false; } return success; }
注意
从下面的方法中抛出的几个异常可以看出来,初始化的时候,第一个参数是个文件夹,而且这个文件夹中必须有一个tessdata的文件夹;而且这个文件夹中要有个文件叫做 第二个参数.traineddata 。具体的可以看下面代码里的注释。这些文件夹和文件没有的一定要创建好,不然会报错。
第二个参数.traineddata 是个什么文件呢?
这个是识别用到的语言库还是文字库什么的,按那个初始化方法的意思是哟啊放到SD卡中的。可以在下面的地址下载。我的demo里把这个文件放在了assets中,启动的时候复制到内存卡里。chi_sim.traineddata应该是健体中文吧,我用的是这个。中英文都能识别。
代码
下面是主要代码:
import android.Manifest;import android.content.pm.PackageManager;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.support.annotation.NonNull; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.TextView; 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 MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; private Button btn; private TextView tv; /** * TessBaseAPI初始化用到的第一个参数,是个目录。 */ private static final String DATAPATH = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator; /** * 在DATAPATH中新建这个目录,TessBaseAPI初始化要求必须有这个目录。 */ private static final String tessdata = DATAPATH + File.separator + "tessdata"; /** * TessBaseAPI初始化测第二个参数,就是识别库的名字不要后缀名。 */ private static final String DEFAULT_LANGUAGE = "chi_sim"; /** * assets中的文件名 */ private static final String DEFAULT_LANGUAGE_NAME = DEFAULT_LANGUAGE + ".traineddata"; /** * 保存到SD卡中的完整文件名 */ private static final String LANGUAGE_PATH = tessdata + File.separator + DEFAULT_LANGUAGE_NAME; /** * 权限请求值 */ private static final int PERMISSION_REQUEST_CODE=0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn = (Button) findViewById(R.id.btn); tv = (TextView) findViewById(R.id.tv); if (Build.VERSION.SDK_INT >= 23) { if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE); } } //Android6.0之前安装时就能复制,6.0之后要先请求权限,所以6.0以上的这个方法无用。 copyToSD(LANGUAGE_PATH, DEFAULT_LANGUAGE_NAME); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new Thread(new Runnable() { @Override public void run() { Log.i(TAG, "run: kaishi " + System.currentTimeMillis()); Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.quanbu); Log.i(TAG, "run: bitmap " + System.currentTimeMillis()); TessBaseAPI tessBaseAPI = new TessBaseAPI(); tessBaseAPI.init(DATAPATH, DEFAULT_LANGUAGE); tessBaseAPI.setImage(bitmap); final String text = tessBaseAPI.getUTF8Text(); Log.i(TAG, "run: text " + System.currentTimeMillis() + text); runOnUiThread(new Runnable() { @Override public void run() { tv.setText(text); } }); tessBaseAPI.end(); } }).start(); } }); } /** * 将assets中的识别库复制到SD卡中 * @param path 要存放在SD卡中的 完整的文件名。这里是"/storage/emulated/0//tessdata/chi_sim.traineddata" * @param name assets中的文件名 这里是 "chi_sim.traineddata" */ public void copyToSD(String path, String name) { Log.i(TAG, "copyToSD: "+path); Log.i(TAG, "copyToSD: "+name); //如果存在就删掉 File f = new File(path); if (f.exists()){ f.delete(); } if (!f.exists()){ File p = new File(f.getParent()); if (!p.exists()){ p.mkdirs(); } try { f.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } InputStream is=null; OutputStream os=null; try { is = this.getAssets().open(name); File file = new File(path); os = new FileOutputStream(file); byte[] bytes = new byte[2048]; int len = 0; while ((len = is.read(bytes)) != -1) { os.write(bytes, 0, len); } os.flush(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (is != null) is.close(); if (os != null) os.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 请求到权限后在这里复制识别库 * @param requestCode * @param permissions * @param grantResults */ @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { Log.i(TAG, "onRequestPermissionsResult: "+grantResults[0]); switch (requestCode){ case PERMISSION_REQUEST_CODE: if (grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED){ Log.i(TAG, "onRequestPermissionsResult: copy"); copyToSD(LANGUAGE_PATH, DEFAULT_LANGUAGE_NAME); } break; default: break; } } }