Tesseract用手書き文字学習データをOCR-Dで学習・生成する

OCRライブラリtesseractで利用できる`.traineddata`形式の学習ファイルを生成するOCR-Dを、環境構築と実行まで試してみました。  

## 概要

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341104/6701ebdb-4450-ca97-c573-e203a6668c9d.png)

## Tessetact OCRとは

OCRライブラリで、pythonで実行できるほか、Javascript版も開発されており、OCR処理を実装するのに役立つ。
標準で添付されている学習データや、配布されている各国語の学習データが存在し、これを用いることで印字文字であればなかなか精度よくOCR処理を行ってくれる。  
  
しかし、手書き文字の認識はこの標準の学習データでは認識することができないため、自前で学習データを作成する必要がある。

## OCR-Dとは

Tesseractでも利用できる`.traineddata`形式の学習データを生成できるPythonプロジェクト。  
[OCR-D](https://github.com/OCR-D)  
    
本記事はこのOCR-Dの環境構築と学習実行までを記載していきます。

## 動作環境

OCR-DはLinux上での動作を前提としているため、Linuxで環境構築行う。  
(OCR-Dは実行時にLinuxコマンドを使用していることと、生成中間ファイルの改行コードによりWindowsでは動作しません。)  
  
OSは今回`debian`を使用します。  
  
VM:VirtualBox-7.0.2-154219-Win  
OS:debian-11.5.0-amd64  
  
作業メモとして
- ubuntuはターミナルの起動に不具合があり、直せず使用を断念。
- AlmaLinuxはdnf,yum,rpmのパッケージでは必要なライブラリが足りず、また必要であるTesseractとleptonicaをmakeしてインストールすることも試したが実行時にこれらが呼び出せなかった。

## 必要ライブラリをインストール

```
$ su -

# パッケージ一覧を更新
$ apt update && apt upgrade

# 必要ライブラリのインストール
$ apt install git
$ apt install make

# Pythonに必要なライブラリをインストール
$ apt install build-essential libbz2-dev libdb-dev libreadline-dev libffi-dev libgdbm-dev liblzma-dev libncursesw5-dev libsqlite3-dev libssl-dev zlib1g-dev uuid-dev tk-dev

$ apt-get install libicu-dev libpango1.0-dev libcairo2-dev
# いらないかも
# apt install imagemagick libsm6

# pillowもインストール
$ apt install -y python3-pip
$ pip3 install pillow --upgrade pip

$ exit
```

## Tesseractをインストールする

```
$ su -
# Tesseractインストール
$ apt install -y tesseract-ocr
$ apt install libtesseract-dev
$ exit

# 言語情報の確認
$ tesseract --list-langs
```

## ocrdのソースコードをダウンロードする

```
# 作業フォルダ作成
$ mkdir ~/tess

# OCR-Dをダウンロード
$ cd ~/tess
$ git clone --depth 1 https://github.com/OCR-D/ocrd-train.git
```

## 学習データの用意

学習に必要なデータは2つの組である必要がある。
- 画像ファイル：`tif`か`png`（余白最小限？）
- 正解ファイル：画像ファイルと同じファイル名で、拡張子が`.gt.txt`。画像ファイルに書かれている内容をテキストで記述する

[https://github.com/tesseract-ocr/tesstrain](https://github.com/tesseract-ocr/tesstrain)
Provide ground truthの章に用意するデータについての記載がある  
  
いろいろ試した感じ画像は`tif`形式の方が安牌な感じでした。

### ocrd-trainディレクトリ配下に移動し、学習対象ファイルの配置先dataディレクトリを作成

```
$ cd ~/tess/ocrd-train
$ mkdir data
```
この中に  
`モデル名-ground-truth`ディレクトリを作成し、学習用画像データと正解データを配置する。  

`mono-number`というモデル名で作りたい場合ディレクトリ名は、`mono-number-ground-truth`となる。この直下に画像と正解データを格納する


Windows環境で画像・正解データを作って送ってくる場合は、画像のtif変換と文字コードに気を付ける必要がある。以下ソフトが便利でした。  
[converseen](https://converseen.fasterland.net/)  
画像データをocrdで入力可能なTIFF形式に変換する  
[CharCodeConverter](https://www.vector.co.jp/download/file/winnt/util/fh735527.html)  
正解データのテキストファイルの文字コードをlinuxで利用可能なものに変換する  
  
## 学習のベースとなる日本語学習データを準備する

今回は標準の日本語学習データをベースに、その続きから自前のデータで機械学習を開始し、学習データを生成するという方法を取る。

標準の日本語学習データはbest版の学習データがinteger (fast) modelの方式なので、best版の`jpn.traineddata`と`eng.traineddata`を用意する。ここから入手できる。  
[https://github.com/tesseract-ocr/tessdata_best](https://github.com/tesseract-ocr/tessdata_best)  
  
### ocrd-trainディレクトリ配下に移動し、学習のベースとなるファイルの配置先ディレクトリを作成

```
$ cd ~/tess/ocrd-train
$ mkdir usr/share/tessdata
```
この中に
`jpn.traineddata`と`eng.traineddata`を配置する

## 機械学習を実行する

`mono-number`という名前で学習データを作成する場合の例

```
# traindataを作成
make training MODEL_NAME=mono-number START_MODEL=jpn >> train.log 2>&1
```

(機械学習なので結構処理時間がかかります)  
問題なく終了後、`.traineddata`形式の学習データを圧縮して仕上げ

```
# traineddataの圧縮
# dataディレクトリに移動
$ cd ~/tess/ocrd-train/data
# 圧縮（【】をファイル名で置き換えること）
$ combine_tessdata -c 【作成した名称】.traineddata 
```


これで`.traineddata`形式の学習データが生成されます。お疲れさまでした。

## 上手く生成できない場合...

### ログ中に指摘の足りないデータを手作成する
機械学習がエラーで停止した場合、エラーログにファイルが足りないと表示される場合がある。以下はその場合の対応方法。  

以下`mono-number`という名前で学習データ作成実行を行った場合の例
  
作成したい言語名の生成ディレクトリ  
(例：`tess/ocrd-train/data/mono-number`)  
に以下ファイルを作成  
  
- `mono-number.punc`
内容：単語を囲む可能性のある記号を、間に半角スペースを入れた形式で列挙する
```
( )
「 」
```

- `mono-number.numbers`
内容：数字の前後に来る文字列を列挙するファイル。 数字が来る部分を半角スペースで表現する。例えば年号の場合は

```
令和  
  年
```
(令和の後、年の前にはそれぞれ半角スペースが2つあり)

- `mono-number.wordlist`
内容：単語を列挙するファイル

- `各種unicharset`
`data`ディレクトリ(例：`tess/ocrd-train/data/`)に以下ファイルをコピー  
[入手元：https://github.com/tesseract-ocr/langdata](https://github.com/tesseract-ocr/langdata)  
  - Han.unicharset  
  - Hiragana.unicharset
  - Katakana.unicharset
  - Latin.unicharset

配置が完了したら、再度機械学習を実行する。
と、出来るはず。

## traineddata最終結合がうまく行かない場合

traineddata最終結合がうまく行かない場合、中間ファイルとして生成された中で最新のcheckpointファイルを用いても問題ないと思われる。

以下`mono-number`という名前で学習データ作成実行を行った場合の例

```
# ocrd のディレクトリに移動
$ cd ~/tess/ocrd-train

# 手動でtraineddataを作成（【】をファイル名で置き換えること）
$ lstmtraining --stop_training --continue_from data/mono-number/checkpoints/【なるべく最新のcheckpointファイル】 --traineddata data/mono-number/mono-number.traineddata --model_output data/mono-number.traineddata
```

なるべく最新のcheckpointファイルでも大丈夫な理由
[https://tesseract-ocr.github.io/tessdoc/tess4/TrainingTesseract-4.00.html#iterations-and-checkpoints](https://tesseract-ocr.github.io/tessdoc/tess4/TrainingTesseract-4.00.html#iterations-and-checkpoints) から抜粋・翻訳

- lstmtraining プログラムは、次の 2 種類のチェックポイント ファイルを出力します。
<model_base>_checkpointトレーニングが発散した場合に使用されるバックアップ モデルを含む最新のモデル ファイルです。
<model_base>_<char_error>_<learning_iteration>_<training_iteration>.checkpointは、
トレーニングのその時点で最良のトレーニング エラーを持つモデルとして定期的に書き込まれます。
これは と同じトレーニング ダンプですが <model_base>_checkpoint、
トレーニングが発散した場合に使用するバックアップ モデルがないためサイズが小さくなります。

→バックアップモデルがないだけで最新の数字付きcheckpointは数字無しのものと学習深度は同等と思われるため  

このcheckpointデータを用いて、`.traineddata`形式の学習データを圧縮して仕上げ

```
# traineddataの圧縮
# dataディレクトリに移動
$ cd ~/tess/ocrd-train/data
# 圧縮（【】をファイル名で置き換えること）
$ combine_tessdata -c 【作成した名称】.traineddata 
```

## 入力学習データによる学習方法の傾向

入力画像データの1画像に文章が入っている場合、その文字列を一連の単語として読み取って学習しようとする傾向がある。
文章には強いが、例えば日付の数字読み取りにはこの学習方法は不適である。
数字の学習は手書き文字を1つ1つ切り出した画像にした方が良い結果が得られる。
MNISTの配布データとかはこの方式である。

## MNISTの画像データセット

学習データの自前作成が大変な場合、配布されている画像セットに頼る手もある。  
MNIST(エムニスト / Modified National Institute of Standards and Technology database)という非常に有名な画像データセットがあり、手書き数字(0~9)と、それが真に表している数字（正解ラベル）からなるデータセットが配布されている。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341104/cde0ed1d-5bd7-c1b7-b5ec-19dca606442e.png)

配布元では1つに画像がまとまってしまっており加工が適宜必要である。  
が、学習用に分解済の画像データを配布しているところをみつけたのでこれも使えるかも。  
[https://github.com/myleott/mnist_png](https://github.com/myleott/mnist_png)

## 精度向上のために

### .boxファイルの記法

学習を実行すると生成される.boxデータ  
入力画像のどの位置に対象の文字が記載されているかの座標情報である
自動生成される内容はかなりアバウト(というか無設定？)なので、これをしっかり手入力していけば精度が上がるかもしれない。大変だけど。

`<symbol> <left> <bottom> <right> <top> <page>
<symbol>`a または b などの文字です。
`<left> <bottom> <right> <top>`ページ上の文字に合う長方形の座標です。Tesseract で使用される座標系は、画像の左下隅に (0,0) があることに注意してください!
`<page>`複数ページの TIFF ファイルを使用している場合にのみ関連します。それ以外の場合はすべて、ここに 0 を入力してください。
[https://stackoverflow.com/questions/53384003/what-are-the-numbers-in-tesseract-box-file](https://stackoverflow.com/questions/53384003/what-are-the-numbers-in-tesseract-box-file)

### その他参考になるサイトや、公式トレーニングガイド

[精度向上のための手修正](http://masanoriprog.blogspot.com/2014/04/tesseract-ocrmnist.html)

[公式トレーニング手順ドキュメント](https://tesseract-ocr.github.io/tessdoc/tess4/TrainingTesseract-4.00.html)

[解説](https://laplace-daemon.com/training-tesseract/#toc_id_5)