Tesseract OCRの単語リストをポケモン用にカスタマイズする (Windows)

# 0.はじめに
Tesseractには、言語ごとに認識される単語や文字のセットを定義するファイルがあります。この中には「単語リスト」なるものがあり、ここに存在する単語は認識率が上がるらしいです。
ここにポケモンの名前やら技名などの固有名詞を入れ込んでしまおうという話。

# 1.用意するもの
* Windows版Tesseract をインストールすると含まれている以下のexeファイル
```
combine_tessdata.exe
dawg2wordlist.exe
wordlist2dawg.exe
```
* 学習済み日本語モデル
BEST版を使用します。適当な場所にダウンロード。
https://github.com/tesseract-ocr/tessdata_best/blob/main/jpn.traineddata

# 2. 作業手順
#### 2-1. 学習済み日本語モデルを分解する
```powershell
# combine_tessdata.exe -u `ダウンロードしたtraineddataの場所` `任意の出力先＋名前`
# （実行例）
combine_tessdata.exe -u C:\download\jpn.traineddata C:\temp\pokemon
```
上記例では`C:\temp\pokemon`としていますが、この場合は下記の様なファイルが出力されます。
```console
C:\temp\pokemon.config
C:\temp\pokemon.lstm
C:\temp\pokemon.lstm-unicharset
C:\temp\pokemon.lstm-word-dawg
…
```
今回は`lstm-unicharset`と`lstm-word-dawg`が必要になります。
#### 2-2. 分解したlstm-word-dawgファイルをテキストに変換する
```powershell
# combine_tessdata.exe -u `lstm-unicharsetの場所` `lstm-word-dawgの場所` `任意の出力先ファイル名`
# （実行例）
dawg2wordlist.exe C:\temp\pokemon.lstm-unicharset C:\temp\pokemon.lstm-word-dawg C:\temp\word.text
```
2-1で出力された`lstm-word-dawg`が単語リストです。これを編集可能なテキスト形式に変換します。
#### 2-3. 変換したテキストファイルを編集する
1行につき1単語でポケモン用語を羅列します。
> フシギダネ
> フシギソウ
> フシギバナ
> …
#### 2-4. 編集したテキストファイルをlstm-word-dawgファイルに変換する
```powershell
# combine_tessdata.exe -r 1 `編集したテキストファイル` `lstm-unicharsetを出力する場所` `lstm-unicharsetの場所`
# （実行例）
wordlist2dawg.exe -r 1 C:\temp\word.text C:\temp\pokemon.lstm-word-dawg C:\temp\pokemon.lstm-unicharset
```
上記例の場合、`lstm-word-dawg`が編集したテキストファイルを変換した物で上書きされます。
#### 2-5. 変換したlstm-word-dawgを結合して学習済み日本語モデルに戻す
```powershell
# combine_tessdata.exe `結合したいファイルがある場所`
# （実行例）
combine_tessdata.exe C:\temp\pokemon
```
`C:\temp\pokemon.*`ファイルが結合され、`C:\temp\pokemon.traineddata`が出力されています。このファイルをTesseractのtessdataフォルダにコピーし、言語指定する事でこのファイルでOCRが実行されます。

# 3. 結果
試してみました。
「ウーラオス」と「インファイト」を単語リストに含めています。
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3749141/93bcbca0-880e-423e-5bf0-2bb6841d057c.png)
```powershell
tesseract.exe -l pokemon tsv
```
`-l`オプションで言語を指定、`tsv`を付けると単語区切りで結果を出力してくれます。

単語リスト差し替え前
```console
5	1	1	1	1	1	20	20	60	19	91.014992	相手
5	1	1	1	1	2	82	16	18	32	0.000000	の
5	1	1	1	1	3	114	16	40	32	0.000000	ウー
5	1	1	1	1	4	154	21	31	18	93.265129	ラオ
5	1	1	1	1	5	190	22	14	16	92.753342	ス
5	1	1	1	1	6	208	22	17	16	93.231453	の
5	1	1	1	1	7	250	20	36	18	88.840370	イン
5	1	1	1	1	8	289	21	33	17	96.691963	ファ
5	1	1	1	1	9	324	20	32	18	92.663544	イト
```
単語リスト差し替え後
```console
5	1	1	1	1	1	20	20	60	19	93.129326	相手
5	1	1	1	1	2	85	16	14	32	90.676468	の
5	1	1	1	1	3	105	20	120	19	93.156639	ウーラオス
5	1	1	1	1	4	210	16	20	32	84.360298	の
5	1	1	1	1	5	250	20	121	18	0.000000	インファイト
```
差し替えた単語リストがちゃんと機能しています。多分。きっと。

# 4. OCRする画像について
* 画像内の文字は一行にまとめ、`--psm 7` オプションで実行すると認識率が上がる傾向があります。
* 文字の周りには余白を付けた方が良いそうです。公式曰く10ピクセル程度。
* 文字サイズが大きすぎると単語のつながりとして認識してくれなくなります。Switchの画面1920x1080をそのまま認識しても上手くいかない事が多いです。テスト画像は50%に縮小しています。
### BEST版とFAST版の違い
今回、学習モデルにはBEST版を使用しましたが、認識率より速度を重視したFAST版もあります。FAST版でも同じ方法で単語リストを差し替える事が可能です。
FAST版は認識可能な文字の数や学習データ量が抑えられており、configファイル（2-1で分解した時に出来る***.configファイル）の内容がBEST版とFAST版では大きく異なります。
configの中には、特定の文字を認識対象外にする`tessedit_char_blacklist`、単語リストの優先度を上げるらしい`segment_penalty_dict_nonword`等が存在しており、これらを上手く調整すると認識率の向上が見込めると思います。
が、如何せん資料がなさすぎて断念。
chatGPTに「segment_penalty_dict_nonwordって何？」とか聞くとそれっぽい答えが返ってきます。

### 余談
本来、単語リストはLSTM学習用のテキストから学習中に生成されるもので、この様な差し替え方はイレギュラーだと思われます。なんか動いちゃった的な奴。
とはいえTesseractの学習はやたら敷居が高いので、お手軽に専門用語を認識しやすくする方法として残しておく事にしました。