2018年03月04日

Program List OCRができるまで

Program List OCRを試された方、いかがだったでしょうか?
レトロPCクラスタの中でも雑誌掲載ソフトの打ち込みをされている方はごく一部で、非常にニッチなソフトであるので反応があるか心配でしたが、幸いにしていくつか使用報告を頂いています。

自分としては現状の認識精度にはまだまだ満足できませんが、それでも手打ちや市販OCRよりははるかに高い生産性を出せると自負しています。

このソフトは着想からリリースまで9ヶ月くらいかかった難産ソフトでした(基本的には学習データ作るだけなのに)。今回はリリースまでの試行錯誤のプロセスを綴って残しておこうと思います。

■Windows版Tesseract,gImageReaderのビルド
今回はOCRのエンジンとして、オープンソースOCRであるTesserctのバージョン4.0を使っています。
バージョン4.0は文字認識にLSTMというニューラルネットワークを使用しており、従来のバージョンに比べて飛躍的な認識精度向上を謳っているのが特徴です。
ただ、このバージョン4.0は開発中バージョンであり、バグも多くまた仕様変更も頻繁に行われています。
これに追従するため、githubの最新版のコードに追従できるようビルドスクリプトを書くところから始まりました。
Tessractの開発ペースによっては毎日ビルドを更新する必要があるため、ビルドの自動化にはかなり工数をかけています。
この辺の成果物は私のgithubで公開しています。

開発、ビルド環境はPC6001VXと同じくMSYS2とMingW-W64を使っています。
理由としては、Qtなど足回りになるライブラリが共通であるというのと、Tesseractで学習を行うためのスクリプトがbashを前提としており、VisualStudioとWindowsのシェル環境での開発は無理と判断したためです。

■学習データの生成
OCRが文字を認識、識別するためには文字の種類と字形を定義した言語データが必要です。
言語データは通常各自然言語(日本語、英語等)ごとに用意され、認識時に切り替えて使われるようになっています。

従来の日本語市販OCRを使ってプログラムリストを認識した場合、以下のような問題点があります。
・漢字など、プログラムリストに通常出現しない文字が誤検出されてしまう。
・検出した文字の分類候補が数千字あるため、分類精度が低い。
・8ビット時代のプログラムリストの印刷に使われるフォントを学習していないため、分類精度が低い。
市販OCRの中には、文字の分類候補を英数字や16進数に絞って文字認識できるものもありますが、上記の問題全てには対応できません。

Program List OCRのコンセプトは、
・実際のプログラムリストの印刷に使われるフォントを学習させる
・単語辞書、文字セットなどの言語設定を認識対象の言語(BASIC、マシン語)に特化して作成
・実際のプログラムリストのテキストを学習させる
ことでP6用BASIC、マシン語に特化した言語ファイルを作成し認識精度を向上するというものです。

実際のOCR学習には、最低限
・認識対象のフォントが含まれた画像データ
・どの文字が画像上のどの座標に位置しているかというラベルデータ
が必要になります。
これ以外に
・認識対象言語の文字セット
・認識対象言語の単語辞書
が必要になります。
後者2つはBASICの仕様書などを参考に手作業で作ることができます。
前者2つは実際の雑誌の紙面をスキャンしてどの文字がどこにあるかというラベリング作業を行うのが理想ですが、膨大な作業量が必要になり、現実的ではありません。

そこで、Tesseractでは画像データとラベルデータを自動生成する仕組みを提供しています。
この場合、画像データとラベルデータが不要になる代わりに以下のものが必要になります。
・プログラムリストの印刷に使われるフォントを模したTrueTypeフォント
・テキストファイル化されたプログラムリスト
上記の2つを用いて、プログラムリストのテキストを用意したTrueTypeフォントでレンダリングして大量の画像ファイルとラベルデータを生成します。

PC-6001のプログラムリストの場合、プリンタの内蔵フォントを使ったLLISTコマンドの出力ではひらがなやグラフィック文字が印刷できない場合が多く、初期のベーマガなどでは画面にLISTを表示させてLCOPYコマンドで画面のハードコピーを印刷するという方法が取られていました。
この方法では画面に表示されているフォントがそのまま印刷されるので、実機の内蔵フォントを模したTrueTypeフォントを使って学習させるのが理想的です。
幸いにしてPC-6001シリーズに関してはHashiさん作の「P6 TrueTypeフォント」がすでに存在しており、この問題をクリアすることができました。
これ以外にもネットで見つけることができたビットマップ風のTrueTypeフォントをいくつか学習に使わせていただいています。(クレジットはgithub上のドキュメントをご覧ください)
プログラムリストのテキストの方は、同じくHashiさんのサイト上に、雑誌掲載プログラムが作者様の許諾の元に公開されています。
プログラム数は100以上、ステップ数にして15000ステップを超える量が蓄積されています。
こちらのプログラムリストたちを学習データとして使わせていただいています。
Hashiさん、各フォントの作者様、プログラムリストの作者様にはこの場を借りてお礼申し上げます。

学習データの生成工程はTesseractのソースコードに含まれているtesstrain.shというシェルスクリプトで自動化されています。
この内部ではtext2imageというツールを呼び出して学習用画像を生成しています。
単にフォントを画像化するだけではノイズのない綺麗な画像になってしまい、ノイズや歪みを含んだ現実のスキャン画像に対応できないので、画像生成の際わざと傾けたりカスレさせたりにじませたりというパターンを複数作成しています。
PLO_fontimage.png
(github等にアップする際、プログラム自体の再配布にならないよう、テキストデータにはシャッフルをかけています)

画像が生成できたら次はそのデータをOCRのニューラルネットワークに学習させていきます。
学習は基本的に眺めていれば進んでいきますが、現状のTesseractはGPUを使わずCPUのみで動作しているため(しかも作者のデスクトップは9年前の年代物のためSIMDが十分に効かず)1週間程度は終夜運転が必要になります。
PLO_learning2.pngPLO_learning.png

■フォントの作成
こうして作成した学習データを用いてベーマガのプログラムリストを読み込ませてみましたが、1987年以降の後期のプログラムリストの認識率が芳しくありません。
初期のプログラムは前述の通り実機の内蔵フォント相当で印刷されており、それらはすでに学習しているのでかなりの精度が出るのですが、1987年9月号からは「P6プリンタ・ルーチン」という専用のプログラムで出力されています。
このプログラムは独自にフォントビットマップを持っており、そのビットマップをプリンタに送ることで、LLISTコマンドでP6のひらがなやグラフィック文字が印刷できなかったプリンタでも印刷することができるようになります。

ところがこの独自フォントの字形が独特であるため(大文字のDやOにクロスバーが入っている)、十分な認識精度ば得られません。
そこでこのP6プリンタ・ルーチンのフォントを自分でTrueType化することにしました。
後期のベーマガ掲載プログラムリストはすべてこのプログラムで出力されており、これらが読み取れないとOCRの(自分的な)価値が下がってしまいます。
逆に言えばプログラム数が多いため、フォントを作る手間に対して費用対効果が高いということでもあります。

フォントの作成工程としては、まず「PC-6001・6601プログラム大全集」掲載のP6プリンタ・ルーチン本体を打ち込み(もちろんOCRを使います)、バイナリファイルとしてPC上に保存します。
そのバイナリファイルを画像ファイルに変換するPythonスクリプトを作成し、1文字ずつPNGファイルとして保存します。
PLO_printerfont.png
次にpotraceというLinux用のコマンドラインツールを使い、各文字のビットマップをSVGファイルに変換します。
最後にsvgs2ttfというツールを使い、TrueTypeフォントに変換します。
面倒ですが、フォントのビットマップさえあれば後は半自動でTTFファイルにできるので、GUIのツールで手作業でベジェ曲線を書くよりは遥かに生産性が高いです。

このフォントを加えて学習させることにより、ベーマガ掲載リストの認識率はだいぶ向上しました。

■インストーラーの作成
PC6001VXのWindows版を作っていたときには、インストーラーを作るのが面倒であったため、Qtはじめすべての利用ライブラリをスタティックリンクして、EXE1つあればどこに配置しても起動するようにしていました。
ただし、今回使用しているTessractおよびgImageReaderは非常に多くのライブラリに依存しており、またその全てがスタティックリンクに対応しているわけではないので、本体のEXEとともに大量のDLLを同梱しなければなりませんでした。
また、EXEだけあれば起動するわけではなく、言語データなども適切なフォルダ構成で配置しなければならないため、観念してインストーラーを作成することにしました。

今回インストーラーフレームワークにはQt Installer Frameworkを採用しました。
理由としてはMSYS2にすでにパッケージが用意されており導入が簡単であったこと、そのうちLinuxに対応させようと思ったときに資産を流用できるという期待があったためです。
インストーラーを作るにはどんなフレームワークを使うにしてもそれに合わせたメタデータファイルが必要になるもので、その書き方を習得する必要があります。

Qt Installer Frameworkには「インストーラーを作ろう!」という解説書が同人誌という形で発行されており、現在でも電子書籍として購入できます。

しばらく前に購入してずっと積んだままになっていましたが、今回こちらの書籍を参考にインストーラーを作成しました。

■おわりに
今回PC-6001のプログラムリスト用に特化した形でリリースしましたが、本当は他機種ユーザーの皆様にも使っていただきたいという気持ちはあります。
ただP6以外の機種についてはエミュレータ用のプログラム打ち込みフローに関する知識がないため、どうすれば使ってもらえるかということはこれから検討したいと思います。
また、Apple||など海外にプログラム打ち込み文化があれば、英語を頑張ってリーチしたいと考えています。
posted by eighttails at 23:09| Comment(0) | Program List OCR | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

※ブログオーナーが承認したコメントのみ表示されます。