Java でニューラルネットOCR - Tess4Jの使い方 -


Tess4J とは

オープンソースのOCRエンジン Tesseract を Java から利用するラッパーライブラリです。

Tesseract は、1985年よりHPにより開発され、2005年にオープンソース化されたOCRエンジンです(2006年から2018年まではGoogleによって開発されていました)。

Tesseract 3 までは、文字パターン認識により動作するOCR エンジンでしたが、Tesseract 4 からは、行認識に特化したニューラルネット(LSTM)ベースのエンジンが利用可能となっています。


Tess4J の使い方

依存に以下を追加します(ここではGradle KotlinDSLを例にします)。

dependencies {  
    implementation("net.sourceforge.tess4j:tess4j:5.10.0")  
}

Windows の場合は、Microsoft Visual C++ 2022 Redistributable が必要とのことで、以下でインストールしておきます。

> winget add Microsoft.VisualStudio.2022.BuildTools

その他の環境では、あらかじめTesseract documentationに従って Tesseract を準備します(Macの場合は brew install tesseract で可)。


tesseract では、言語別のデータファイルが必要となります。

tesseract-ocr/tessdata から jpn.traineddata をダウンロード(jpn_vert.traineddata は縦書き用)して適当なディレクトリに配備します。

サンプルコードは以下のように書くことができます。

import net.sourceforge.tess4j.ITesseract;
import net.sourceforge.tess4j.Tesseract;
import java.io.File;

public class App {

    public static void main(String[] args) throws Exception {

        ITesseract tesseract = new Tesseract();
        tesseract.setDatapath("./tessdata");
        tesseract.setLanguage("jpn");

        String str = tesseract.doOCR(new File("./sample.png"));
        System.out.println(str);
        
    }
}

./tessdata には、データファイルを格納したディレクトリを指定します。

tesseract.doOCR() に画像ファイルを渡してやれば、OCRの結果を文字列として得ることができます(tesseract.doOCR() の初回呼び出し時に、一時ディレクトリjava.io.tmpdir に展開された Tesseract のネイティブライブラリがロードされます)。

ここでは、sample.png として以下のような画像を用意しました。

実行すると、以下のような結果が得られます。

認 識 精 度 ー
⑲⑨0 年 代 中 ご ろ 、 ア メ リ カ 合 衆 国 エ ネ ル ギ ー 省 (DOE) は 情 報 科 学 研 究 所 ①SRD に 印 刷 文 書 の 認
識 技 術 育 成 と い う 保 命 を 与 え た 。 そ れ に よ り ⑤ 年 間 に 渡 っ て Annual Test of OCR Accuracy が ま
と め ら れ た B0L

ラ テ ン 文 字 の 活 字 文 書 の 正 確 な 認 識 は ほ と ん ど 解 決 済 み の 問 題 だ が 、 識 字 率 ( 文 字 を 正 し く 認
識 す る 確 率 ) は ①00⑨⑥ で は な く 、 閾 違 い の 許 さ れ な い 状 況 で は 人 間 が 結 果 を 確 認 す る 必 要 が あ
る 。 ⑲ 世 紀 お よ び ⑳ 世 紀 初 頭 の 新 聞 を 使 っ た 研 究 に よ る と 、 単 純 に 文 字 単 位 で 認 識 す る 市 販 の
OCR ソ フ ト ウ ェ ア の 識 字 率 は ⑦①⑨% か ら ⑨⑧%% だ っ た B①J。 手 書 き 文 字 、 特 に 筆 記 体 の 手 書 き 文 字 認
識 や 文 字 数 の 多 い 言 語 の 文 字 認 識 で は ま だ 研 究 の 余 地 が あ る 。

文 字 認 識 の 精 度 は い く つ か の 測 定 法 で 表 さ れ 、 実 際 に 使 用 し た 測 定 法 に よ っ て 精 度 は 大 き く 左
右 さ れ る 。 例 え ば 、 文 脈 や 辞 書 を 使 わ ず に 純 粋 に 文 字 単 位 で 認 識 す る 場 合 、 識 字 率 が ⑨⑨%% で あ
っ て も 、 単 語 ベ ー ス の 誤 り 率 は ⑤ と な る か も し れ な い ②

文字の間に無駄な空白が含まれていたり、数字の変換がおかしなことになってしまっていますが、それなりの識別はできています(後述で改善します)。

画像ファイル中の特定位置を読み込む場合は、以下の Rectangle を指定するメソッドを利用します。

String doOCR(File imageFile, List<Rectangle> rects) throws TesseractException;


OCR エンジンとデータファイル

OCR エンジンは以下のようにモードを指定できます。

tesseract.setOcrEngineMode(ITessAPI.TessOcrEngineMode.OEM_TESSERACT_ONLY);

TessOcrEngineMode は以下の4つがあります。

  • OEM_TESSERACT_ONLY :Tesseract のみ(従来の tesseract エンジンで最も高速)
  • OEM_LSTM_ONLY :LSTM line recognizer(ニューラル ネット ベースのエンジ)のみ
  • OEM_TESSERACT_LSTM_COMBINED :LSTM recognizer (Tesseract へのフォールバックあり)
  • OEM_DEFAULT :コマンドラインや言語固有の設定による自動推測(推測できない場合は OEM_TESSERACT_ONLY)

前述の例を OEM_TESSERACT_ONLY で実行した結果は以下のようになります。

認識精度 _
ー990年代中ごろ、 アメリカ合衆国工ネルギ一省①0E)は′情報科学研究所(ーsRー〕 に印刷文書の認
識技術育成という使命を与えた。 それにより5年間に渡ってA皿"址T鵠t。f0cRA皿m鱒がま
とめられた剛L

ラテン文字の活字文書の正確な認識はほとんど解決済みの問題だが、 識字率 〈文字を正しく認
識する確率) はー00%ではなく、 間違いの許されない状況では人間が結果を確認する必要があ
る。 ー9世紀および20世紀初頭の新聞を使つた研究によると、 単純に文字単位で認識する市販の
0cRソフ トウェアの識字率は刀%から98%だった鱒〝。 手書き文字、 特に筆記体の手書き文字認
識や文字数の多い言語の文字認識ではまだ研究の余地がある。
文字認識の精度はいくつかの測定法で表され、 実際に使用した測定法によって精度は大き〈左
右される。 例えば、 文脈や辞書を使わずに純粋に文字単位で認識する場合、 識字率が99%でぁ
っても、 単語ベ一スの誤り率は5%となるかもしれない鱒刀。

こちらはイマイチな結果です。


tesseract-ocr/tessdata のデータファイルには、Tesseract 用とLSTM モデル用の双方が含まれます。

データファイルには、この他に、tessdata_best と、tessdata_fast があります。 tessdata_best は精度が高いが低速で、tessdata_fast は精度は低いが高速のLSTM モデル となっています(ざっと試した感じだと、日本語の場合は、tessdata_fastが良好な結果を得ることができる)。 これら2種は、LSTM モデル となるため、OEM_TESSERACT_ONLYOEM_TESSERACT_LSTM_COMBINED は指定できません。

前述の例を tessdata_fast で実行した結果は以下のようになります。

認識精度

1990年代中ごろ、アメリカ合衆国エネルギー省 (DOE) は情報科学研究所 (SRT) に印刷文書の認
識技術育成という使命を与えた。それにより5年間に渡って Annual Test of OCR Accuracy がま
とめられたB0I。

ラテン文字の活字文書の正確な認識はほとんど解決済みの問題だが、識字率 (文字を正しく認
識する確率) は1000%ではなく、間違いの許されない状況では人間が結果を確認する必要があ
る。19世紀および20世紀初頭の新聞を使った研究によると、単純に文字単位で認識する市販の
OCRソフトウェアの識字率は71%%から98%だったBE手書き文字、特に筆記体の手書き文字認
識や文字数の多い言語の文字認識ではまだ研究の余地がある。

文字認識の精度はいくつかの測定法で表され、実際に使用した測定法によって精度は大きく左
右される。例えば、文脈や辞書を使わずに純粋に文字単位で認識する場合、識字率が99%であ
っても、単語ベースの誤り率は5%となるかもしれないB2!。

ほぼ満足できるレベルと言って良いのではないでしょうか。


パラグラフ単位の結果取得

先の例では、文字列として解析結果を得ていましたが、以下のようにすることで、特定の単位で結果を取得することができます。

BufferedImage img = ImageIO.read(new File("./sample.png"));  
List<Word> words = tesseract.getWords(img, ITessAPI.TessPageIteratorLevel.RIL_PARA);

getWords() の第二引数には以下を指定することができます。

public static interface TessPageIteratorLevel {
    /** Block of text/image/separator line. */
    public static final int RIL_BLOCK = 0;
    /** Paragraph within a block. */
    public static final int RIL_PARA = 1;
    /** Line within a paragraph. */
    public static final int RIL_TEXTLINE = 2;
    /** Word within a textline. */
    public static final int RIL_WORD = 3;
    /** Symbol/character within a word. */
    public static final int RIL_SYMBOL = 4;
};

TessPageIteratorLevel.RIL_PARA を指定した場合、以下のようにパラグラフ単位で位置と精度と共にテキストを得ることができます。

{ Text: 認識精度,
  Confidence: 96.446579, Bounding box: 63 24 144 35 }
  
{ Text: 1990年代中ごろ、アメリカ合衆国エネルギー省 (DOE) は情報科学研究所 (SRT) に印刷文書の認
識技術育成という使命を与えた。それにより5年間に渡って Annual Test of OCR Accuracy がま
とめられたB0I。,
  Confidence: 91.630501, Bounding box: 63 97 1017 84 }
  
{ Text: ラテン文字の活字文書の正確な認識はほとんど解決済みの問題だが、識字率 (文字を正しく認
識する確率) は1000%ではなく、間違いの許されない状況では人間が結果を確認する必要があ
る。19世紀および20世紀初頭の新聞を使った研究によると、単純に文字単位で認識する市販の
OCRソフトウェアの識字率は71%%から98%だったBE手書き文字、特に筆記体の手書き文字認
識や文字数の多い言語の文字認識ではまだ研究の余地がある。,
  Confidence: 92.127251, Bounding box: 63 211 1017 140 }
  
{ Text: 文字認識の精度はいくつかの測定法で表され、実際に使用した測定法によって精度は大きく左
右される。例えば、文脈や辞書を使わずに純粋に文字単位で認識する場合、識字率が99%であ
っても、単語ベースの誤り率は5%となるかもしれないB2!。,
  Confidence: 92.901482, Bounding box: 63 382 1016 84 }


PDFファイルからのOCR読み取り

Tess4J では、PDFファイルを直接扱うことができるため、単に以下のようにすれば済みます。

String str = tesseract.doOCR(new File("sample.pdf"));

内部では拡張子に応じて、PDFBox により PDF → TIFF に変換してOCR処理します。

Word を得る場合には、明示的に以下のようにTIFFに変換して処理することができます。

File file = PdfUtilities.convertPdf2Tiff(new File("sample.pdf"));
  
List<Word> words = tesseract.getWords(  
        ImageIO.read(file),  
        ITessAPI.TessPageIteratorLevel.RIL_BLOCK);
        
words.forEach(System.out::println);

と言っても、テキストが埋め込まれたPDFの場合は、以下のようにすれば済みます。

PDDocument doc = PDDocument.load(pdfFile);
String text = PDFTextStripper().getText(doc);

テキストが画像として埋め込まれたPDFをどうにかしたい場合は、先のようにOCRでテキストを抜き出すことになります。


もし、実行環境が Java 9 よりも古い場合、標準では ImageIO でTIFF を扱うことができません。 幸い ImageIO はプラグインで対応フォーマットを追加できるので、jai-imageio-core などを導入する必要があります。

または、自身で以下のようにPNGに変換することで、(TIFFを使わず)対応することができます。

File[] files = PdfUtilities.convertPdf2Png(file);
String str = tesseract.doOCR(files[0]);