Android OCR之tesseract

超猩猩小隊長發表於2017-11-26

實現小猿搜題、作業幫類似效果。
基於Google Tesseract-OCR實現,由於這是基於C++開發,Android中不能直接使用,所以本專案使用tess-two是對於Android的分支。

準備工作

1)Android Studio匯入gradle依賴(懶人做法)

//編譯好的SO庫 和 jar包
compile 'com.rmtheis:tess-two:6.1.1'
//影象裁剪
compile 'com.edmodo:cropper:1.0.1'複製程式碼

其實也可以自己下載github.com/rmtheis/tes…原始碼在Linux環境中進行編譯。

2)第二種方法 自己編譯原始碼(!!!)

  • 安裝虛擬機器VMware
  • 安裝linux系統Ubuntu
  • 安裝必要工具
sudo apt-get update
sudo apt-get install git複製程式碼
  • 配置JDK、NDK、SDK環境(踩了一萬個坑)
  • 下載tess-two程式碼
git clone https://github.com/rmtheis/tess-two.git tess複製程式碼
  • 開始編譯
cd /tess/tess-two/jni

ndk-build複製程式碼
  • 使用
    將編譯好的tess-two目錄複製到自己專案的libraries下,將專案下的app關聯 libraries。

tess-two使用

  • 資料字典下載,在app/main目錄下新建assets目錄,從github.com/tesseract-o…下載對應語言的字典庫。

  • MainActivity.java
    主要就是將assets中的字典檔案寫到手機檔案目錄

/**
 * 將assets中的檔案複製出
 *
 * @param path
 */
public void deepFile(String path) {
    String newPath = getExternalFilesDir(null) + "/";
    try {
        String str[] = getAssets().list(path);
        if (str.length > 0) {//如果是目錄
            File file = new File(newPath + path);
            file.mkdirs();
            for (String string : str) {
                path = path + "/" + string;
                deepFile(path);
                path = path.substring(0, path.lastIndexOf('/'));//回到原來的path
            }
        } else {//如果是檔案
            InputStream is = getAssets().open(path);
            FileOutputStream fos = new FileOutputStream(new File(newPath + path));
            byte[] buffer = new byte[1024];
            int count = 0;
            while (true) {
                count++;
                int len = is.read(buffer);
                if (len == -1) {
                    break;
                }
                fos.write(buffer, 0, len);
            }
            is.close();
            fos.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}複製程式碼

點選跳轉到拍照介面

public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_camera:
                Intent intent = new Intent(this, TakePhoteActivity.class);
                startActivity(intent);
                break;
        }
    }複製程式碼

  • TakePhoteActivity.java
    主要執行拍照、裁剪操作。

以下是拍照成功後回掉介面,拍照成功後顯示剪裁介面

 /**
     * 拍照成功後回撥
     * 儲存圖片並顯示截圖介面
     *
     * @param data
     */
    @Override
    public void onCameraStopped(byte[] data) {
        Log.i("TAG", "==onCameraStopped==");
        // 建立影象
        Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
        // 系統時間
        long dateTaken = System.currentTimeMillis();
        // 影象名稱
        String filename = DateFormat.format("yyyy-MM-dd kk.mm.ss", dateTaken).toString() + ".jpg";
        // 儲存影象(PATH目錄)
        Uri source = insertImage(getContentResolver(), filename, dateTaken, PATH, filename, bitmap, data);
        //準備截圖
        try {
            mCropImageView.setImageBitmap(MediaStore.Images.Media.getBitmap(this.getContentResolver(), source));
        } catch (IOException e) {
            Log.e(TAG, e.getMessage());
        }
        showCropperLayout();
    }複製程式碼

剪裁操作,完成後跳轉到識別介面

private View.OnClickListener cropcper = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            switch (view.getId()) {
                case R.id.btn_closecropper:
                    showTakePhotoLayout();
                    break;
                case R.id.btn_startcropper:
                    //獲取截圖並旋轉90度
                    Bitmap cropperBitmap = mCropImageView.getCroppedImage();
                    Bitmap bitmap = Utils.rotate(cropperBitmap, -90);

                    // 系統時間
                    long dateTaken = System.currentTimeMillis();
                    // 影象名稱
                    String filename = DateFormat.format("yyyy-MM-dd kk.mm.ss", dateTaken).toString() + ".jpg";
                    Uri uri = insertImage(getContentResolver(), filename, dateTaken, PATH, filename, bitmap, null);

                    Intent intent = new Intent(TakePhoteActivity.this, ShowCropperedActivity.class);
                    intent.setData(uri);
                    intent.putExtra("path", PATH + filename);
                    intent.putExtra("width", bitmap.getWidth());
                    intent.putExtra("height", bitmap.getHeight());
//                  intent.putExtra("cropperImage", bitmap);
                    startActivity(intent);
                    bitmap.recycle();
                    finish();
                    break;
            }
        }
    };複製程式碼
  • ShowCropperedActivity.java
    主要是識別操作

初始化識別器

//sd卡路徑
private static String LANGUAGE_PATH = "";
//識別語言
private static final String LANGUAGE = "chi_sim";//chi_sim | eng
private TessBaseAPI baseApi = new TessBaseAPI();

@Override
protected void onCreate(Bundle savedInstanceState) {
    LANGUAGE_PATH = getExternalFilesDir("") + "/";

    baseApi.init(LANGUAGE_PATH, LANGUAGE);
    //設定設別模式
    baseApi.setPageSegMode(TessBaseAPI.PageSegMode.PSM_AUTO);
}複製程式碼

將圖片灰度化處理,去除雜色可以提高準確度

/**
 * 灰度化處理
 *
 * @param bitmap3
 * @return
 */
public Bitmap convertGray(Bitmap bitmap3) {
    colorMatrix = new ColorMatrix();
    colorMatrix.setSaturation(0);
    ColorMatrixColorFilter filter = new ColorMatrixColorFilter(colorMatrix);

    Paint paint = new Paint();
    paint.setColorFilter(filter);
    Bitmap result = Bitmap.createBitmap(bitmap3.getWidth(), bitmap3.getHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(result);

    canvas.drawBitmap(bitmap3, 0, 0, paint);
    return result;
}複製程式碼

開始識別

//傳入圖片
baseApi.setImage(bitmap);
//獲取識別後的結果
String result = baseApi.getUTF8Text();
//結束識別
baseApi.end();複製程式碼

結果

Demo已上傳Github如需要可下載github.com/wangtaoT/An…

相關文章