利用tess-two和cv4j實現簡單的ocr功能

Tony沈哲發表於2017-10-02

ocr

光學字元識別(英語:Optical Character Recognition, OCR)是指對文字資料的影象檔案進行分析識別處理,獲取文字及版面資訊的過程。

Tesseract

Tesseract是Ray Smith於1985到1995年間在惠普布里斯托實驗室開發的一個OCR引擎,曾經在1995 UNLV精確度測試中名列前茅。但1996年後基本停止了開發。2006年,Google邀請Smith加盟,重啟該專案。目前專案的許可證是Apache 2.0。該專案目前支援Windows、Linux和Mac OS等主流平臺。但作為一個引擎,它只提供命令列工具。 現階段的Tesseract由Google負責維護,是最好的開源OCR Engine之一,並且支援中文。

tess-two是Tesseract在Android平臺上的移植。

下載tess-two:

compile 'com.rmtheis:tess-two:8.0.0'複製程式碼

然後將訓練好的eng.traineddata放入android專案的assets資料夾中,就可以識別英文了。

1. 簡單地識別英文

初始化tess-two,載入訓練好的tessdata

    private void prepareTesseract() {
        try {
            prepareDirectory(DATA_PATH + TESSDATA);
        } catch (Exception e) {
            e.printStackTrace();
        }

        copyTessDataFiles(TESSDATA);
    }

    /**
     * Prepare directory on external storage
     *
     * @param path
     * @throws Exception
     */
    private void prepareDirectory(String path) {

        File dir = new File(path);
        if (!dir.exists()) {
            if (!dir.mkdirs()) {
                Log.e(TAG, "ERROR: Creation of directory " + path + " failed, check does Android Manifest have permission to write to external storage.");
            }
        } else {
            Log.i(TAG, "Created directory " + path);
        }
    }

    /**
     * Copy tessdata files (located on assets/tessdata) to destination directory
     *
     * @param path - name of directory with .traineddata files
     */
    private void copyTessDataFiles(String path) {
        try {
            String fileList[] = getAssets().list(path);

            for (String fileName : fileList) {

                // open file within the assets folder
                // if it is not already there copy it to the sdcard
                String pathToDataFile = DATA_PATH + path + "/" + fileName;
                if (!(new File(pathToDataFile)).exists()) {

                    InputStream in = getAssets().open(path + "/" + fileName);

                    OutputStream out = new FileOutputStream(pathToDataFile);

                    // Transfer bytes from in to out
                    byte[] buf = new byte[1024];
                    int len;

                    while ((len = in.read(buf)) > 0) {
                        out.write(buf, 0, len);
                    }
                    in.close();
                    out.close();

                    Log.d(TAG, "Copied " + fileName + "to tessdata");
                }
            }
        } catch (IOException e) {
            Log.e(TAG, "Unable to copy files to tessdata " + e.toString());
        }
    }複製程式碼

取景框.JPG
取景框.JPG

拍完照後,呼叫startOCR方法。

    private void startOCR(Uri imgUri) {
        try {
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inSampleSize = 4; // 1 - means max size. 4 - means maxsize/4 size. Don't use value <4, because you need more memory in the heap to store your data.
            Bitmap bitmap = BitmapFactory.decodeFile(imgUri.getPath(), options);

            String result = extractText(bitmap);
            resultView.setText(result);

        } catch (Exception e) {
            Log.e(TAG, e.getMessage());
        }
    }複製程式碼

extractText()會呼叫tess-two的api來實現ocr文字識別。

    private String extractText(Bitmap bitmap) {
        try {
            tessBaseApi = new TessBaseAPI();
        } catch (Exception e) {
            Log.e(TAG, e.getMessage());
            if (tessBaseApi == null) {
                Log.e(TAG, "TessBaseAPI is null. TessFactory not returning tess object.");
            }
        }

        tessBaseApi.init(DATA_PATH, lang);

        tessBaseApi.setImage(bitmap);
        String extractedText = "empty result";
        try {
            extractedText = tessBaseApi.getUTF8Text();
        } catch (Exception e) {
            Log.e(TAG, "Error in recognizing text.");
        }
        tessBaseApi.end();
        return extractedText;
    }複製程式碼

最後,顯示識別的效果,此時的效果還算可以。

簡單地識別英文.JPG
簡單地識別英文.JPG

2. 識別程式碼

接下來,嘗試用上面的程式識別一段程式碼。

識別程式碼.JPG
識別程式碼.JPG

此時,效果一塌糊塗。我們重構一下startOCR(),增加區域性的二值化處理。

    private void startOCR(Uri imgUri) {
        try {
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inSampleSize = 4; // 1 - means max size. 4 - means maxsize/4 size. Don't use value <4, because you need more memory in the heap to store your data.
            Bitmap bitmap = BitmapFactory.decodeFile(imgUri.getPath(), options);

            CV4JImage cv4JImage = new CV4JImage(bitmap);
            Threshold threshold = new Threshold();
            threshold.adaptiveThresh((ByteProcessor)(cv4JImage.convert2Gray().getProcessor()), Threshold.ADAPTIVE_C_MEANS_THRESH, 12, 30, Threshold.METHOD_THRESH_BINARY);
            Bitmap newBitmap = cv4JImage.getProcessor().getImage().toBitmap(Bitmap.Config.ARGB_8888);

            ivImage2.setImageBitmap(newBitmap);

            String result = extractText(newBitmap);
            resultView.setText(result);

        } catch (Exception e) {
            Log.e(TAG, e.getMessage());
        }
    }複製程式碼

在這裡,使用cv4j來實現影象的二值化處理。

            CV4JImage cv4JImage = new CV4JImage(bitmap);
            Threshold threshold = new Threshold();
            threshold.adaptiveThresh((ByteProcessor)(cv4JImage.convert2Gray().getProcessor()), Threshold.ADAPTIVE_C_MEANS_THRESH, 12, 30, Threshold.METHOD_THRESH_BINARY);
            Bitmap newBitmap = cv4JImage.getProcessor().getImage().toBitmap(Bitmap.Config.ARGB_8888);複製程式碼

影象二值化就是將影象上的畫素點的灰度值設定為0或255,也就是將整個影象呈現出明顯的黑白效果。影象的二值化有利於影象的進一步處理,使影象變得簡單,而且資料量減小,能凸顯出感興趣的目標的輪廓。

cv4j的github地址:github.com/imageproces…

cv4jgloomyfish和我一起開發的影象處理庫,純java實現。

再來試試效果,圖片中間部分是二值化後的效果,此時基本能識別出程式碼的內容。

先做二值化再識別程式碼.JPG
先做二值化再識別程式碼.JPG

3. 識別中文

如果要識別中文字型,需要使用中文的資料包。可以去下面的網站上下載。

github.com/tesseract-o…

跟中文相關的資料包有chi_sim.traineddata、chi_tra.traineddata,它們分別表示是簡體中文和繁體中文。

tessBaseApi.init(DATA_PATH, lang);複製程式碼

前面的例子都是識別英文的,所以原先的lang值為"eng",現在要識別簡體中文的話需要將其值改為"chi_sim"。

識別中文.JPG
識別中文.JPG

最後

本專案只是demo級別的演示,離生產環境的使用還差的很遠。
本專案的github地址:github.com/fengzhizi71…

為何說只是demo級別呢?

  • 資料包很大,特別是中文的大概有50多M,放在移動端的肯定不合適。一般正確的做法,都是放在雲端。
  • 識別文字很慢,特別是中文,工程上還有很多優化的空間。
  • 做ocr之前需要做很多預處理的工作,在本例子中只用了二值化,其實還有很多預處理的步驟比如傾斜校正、字元切割等等。
  • 為了提高tess-two的識別率,可以自己訓練資料集。

相關文章