ラベル ocr の投稿を表示しています。 すべての投稿を表示
ラベル ocr の投稿を表示しています。 すべての投稿を表示

2014/04/29

Tesseract-OCRでMNISTのデータを用いて手書き数字認識をしようとしてみた

MNISTの手書き数字データベースのデータから
Tesseract-OCR用に学習データを生成し、手書き数字をオフライン認識してみます。

先に言い訳とお断りをさせていただきますと....
今回はとりあえずそのまま作るとどうなるかという実験のつもりでしたので
私の処理が「思いっきり手抜き」であり、数字しか認識できず認識率も低いですが、
とりあえず学習データができたというレベルの自分用メモとして記しておきます。
Tesseract-OCRもMNISTのデータベースも、正しく使えば、本来は相当な認識率となるはずです
 より正しい方法をご存知の方や、学習データの情報をご提供いただける方は是非教えていただければ幸いです
 ご指摘等をお待ちしております。 (記事のタイトルの歯切れが悪いのはそういうことです(汗;))


利用した環境は Linux (Arch Linux x64)であり、
Tesseract-OCR および engの学習データがインストール済みである事が前提です。
(Arch Linuxのpacmanでは tesseract, tesseract-data-eng でインストール可能。)

尚、Tesseract-OCRでの学習に関する手順は Tesseract-OCRの学習 - はだしの元さん を参照、引用させていただきました。感謝申し上げます。


1. MNISTのデータを取得

まずは 学習モデルの素となる MNISTのデータを取得...といきたいところですが、
今回はそのままでは使いづらい(もとい、使い方がよくわからない(^^;))ので
JPEG化されたデータを使わせていただくことにします。
http://www.cs.nyu.edu/~roweis/data.html

上記URLのページにある"MNIST Handwritten Digits"の
"training pictures"を0〜9までダウンロードします。
各々はJPEGファイル(mnist_train*.jpg)であり、その数字の手書き画像が大量に結合された巨大な画像ファイルとなっています。
training picturesの"0" (抜粋)
手書きされた数字の0が大量に結合されている。
さらに前処理として...これらのJPEGファイルを二値化してノイズを取り除き、PNGファイルとして保存しておきます。
この時、tesseractのboxファイルの命名規則に応じたファイル名にすると楽です:
(3文字の言語名).(フォント名(任意)).exp(インデックス番号)
という規則になっています (引用元)。  今回は数字だけですので...
  • 言語名: "eng"
  • フォント名:  "hand" (適当)
  • インデックス番号: PNGファイルの数字にあわせて0〜9
    (例: eng.hand.exp0.png 〜 eng.hand.exp9.png)
とします。
convertコマンドを使えば、二値化とPNGファイルとしての保存が一発ですね。
$ convert -threshold 30000 mnist_train*.jpg eng.hand.exp%d.png

2. boxファイルを生成

次にtesseract コマンドを用いて、PNGファイルからboxファイルを生成します。
$ tesseract eng.hand.exp0.png eng.hand.exp0 -l eng batch.nochop makebox &&\
tesseract eng.hand.exp1.png eng.hand.exp1 -l eng batch.nochop makebox &&\
tesseract eng.hand.exp2.png eng.hand.exp2 -l eng batch.nochop makebox &&\
tesseract eng.hand.exp3.png eng.hand.exp3 -l eng batch.nochop makebox &&\
tesseract eng.hand.exp4.png eng.hand.exp4 -l eng batch.nochop makebox &&\
tesseract eng.hand.exp5.png eng.hand.exp5 -l eng batch.nochop makebox &&\
tesseract eng.hand.exp6.png eng.hand.exp6 -l eng batch.nochop makebox &&\
tesseract eng.hand.exp7.png eng.hand.exp7 -l eng batch.nochop makebox &&\
tesseract eng.hand.exp8.png eng.hand.exp8 -l eng batch.nochop makebox &&\
tesseract eng.hand.exp9.png eng.hand.exp9 -l eng batch.nochop makebox
尚、処理には1時間程度かかりました。

3. boxファイルを修正

次にテキストエディタを用いて、boxファイルに対して修正を行います。
ここではまず、"0"の分のboxファイル(eng.hand.exp0.box)を見てみます:
o 2134 2132 2150 2152 0
o 2135 2104 2149 2124 0
o 2133 2075 2153 2095 0
a 2133 2048 2153 2066 0
a 2133 2021 2153 2037 0
o 2132 1992 2152 2012 0
o 2134 1964 2152 1984 0
o 2132 1936 2152 1956 0
o 2133 1907 2153 1927 0
o 2133 1880 2151 1900 0
o 2133 1853 2153 1869 0
o 2133 1825 2153 1842 0
o 2133 1797 2153 1815 0
n 2131 1770 2151 1784 0
o 2135 1739 2150 1759 0
....
先頭は認識された文字ですが、"0"ではなく"o"や"a"、"n"になってしまっています。
これをすべて"0"に置き換えます。(正規表現置換でs/^./0/すると良いですね。)
(※本当は行頭のあとの座標データも修正しなければならないようですが、今回は見逃しておきますw
本当はデータ数減らしてでもちゃんと修正すべきだと思います。)

この処理も0〜9のすべてのboxファイルに対して行います。

4. trファイルの生成

PNGファイルと修正したboxファイルから
tesseractコマンドを用いてtrファイルを生成します。
$ tesseract eng.hand.exp0.png eng.hand.exp0 nobatch box.train.stderr
これにより eng.hand.exp0.tr というファイルが生成されます。

この処理も0〜9のすべてのファイルに対して行います。

5. trファイルおよびboxファイルの結合

生成された 0〜9の分のtrファイル、boxファイルを各々結合します。
$ cat eng.hand.exp*.tr > eng.hand.exp.tr
$ cat eng.hand.exp*.box > eng.hand.exp.box
これ以降はほぼ参照させていただいたページのとおりですが、
丸投げもいけませんので、自分用メモとして掲載させていただきます m(_ _)m

6. 学習データの下準備

まず、boxファイルに対してunicharset_extractorコマンドを実行します。
$ unicharset_extractor eng.hand.exp.box
これにより、"unicharset"ファイルが生成されます。
※ この時、boxファイルの指定はこのように拡張子をつけます。

次に、テキストエディタで"font_properties"ファイルを作成します。
$ vim font_properties
hand 0 0 0 0 0
※ 先頭の文字列は最初に適当に決めたフォント名です。0 0 0 0 0はフォントの種類を表しますが、今回は特に何もないので0です。(詳しく参照ページをご覧ください。)

7. 学習データの生成

まず、先ほど作成したファイルをもとに、mftrainingコマンドを実行します。
$ mftraining -F font_properties -U unicharset eng.hand.exp.tr
これにより、"inttemp"、"shapetable"、"pffmtable"の3ファイルが生成されます。
※ この処理に約20時間要しました (Core i5 2520M @2.50Ghz, RAM8GB にて)。

さらに以下のコマンドを実行します。
$ mftraining -F font_properties -U unicharset -O eng.unicharset unicharset eng.hand.exp.tr
※ この処理にも約20時間要しました...(^^;)

続けて、cntrainingコマンドを実行します。
$ cntraining eng.hand.exp.tr
これにより、"normproto"ファイルが生成されます。
※ 以降の処理は数秒で行えます。

次に、本章で生成されたファイルのファイル名を変更し、
各々の先頭に言語名(今回は"eng")を付与します。
$ mv inttemp eng.inttemp
$ mv shapetable eng.shapetable
$ mv pffmtable eng.pffmtable
$ mv normproto eng.normproto

最後に、combine_tessdataコマンドを実行します。
$ combine_tessdata eng.
※ 最後のドットを忘れないようにしましょう。
これにより、"eng.traineddata"が生成されます。これが学習データです。

あとは...tesseractコマンドや、先日の記事のTesseract-OCRを用いたAndroidアプリケーションでこの学習データを利用できます。

8. 学習データを利用して認識してみる

生成した学習データを /usr/local/src/tesseract-ocr あたりにコピーしてもよいのですが、今回は環境変数で学習データのパスを指定してみることにします。
まず"tessdata"というディレクトリを作成し、学習データを入れておきます。
$ mkdir tessdata
$ mv eng.traineddata tessdata/
次に認識させたい画像をPNGファイルなどとして用意しておきます。(やはり2値化しておくと良いようです。)

あとは環境変数をつけて、tesseractコマンドを実行するだけです。
$ TESSDATA_PREFIX=. tesseract foo.png out.txt -l eng
これにより、out.txt に手書き数字の認識結果が得られます。

認識率は...私の手抜きの分、うーん(´・ω・`)...という感じです。
認識結果は参考までに...

  • 元画像(MNISTのデータをPNG化したもの)から適当な行を抜き出して認識させてみると、そこそこ満足できる認識率が得られました。
    (これで一応、学習データがそれらしく生成できていることは確認できました。)
  • 数字4桁を紙に手書きして、スマートフォンのカメラで撮影したものは...
    そのままでは全く認識できませんでしたが、
    数字部分を切り抜き、2値化と膨張を施すと、後ろ2文字は認識できました。さらに1文字目の数字の間隔を広げたところ、4文字すべて認識できました。

恐らくデータをもっと丁寧に前処理して、boxファイルの修正も丁寧に行えば、精度は向上すると思います...。
素晴らしいデータベースオープンソースソフトウェアであるにも関わらず、私の手抜きでこんな記事になってしまい大変失礼いたしました m(_ _;)m

もう一回、データ数を減らしてまじめに作りなおしてみようかな...とも思ったり...。


参考にさせていただいたWebページ (感謝♪) :

2014/02/01

AndroidでTesseract-OCRを用いてカメラで光学文字認識をしてみた

AndroidでTesseract-OCRを使って、カメラで撮影した画像からOCR(光学文字認識)をしてみました。

"Tesseract-OCR"はOCRエンジンであり、元々HPによって開発され、OSS化されて今はGoogleがメインメンテナとなっています。
このTesseract-OCRをAndroidでNDKとして利用するためのライブラリが "tesseract-android-tools"です。

尚、本記事は Android SDK、GNU make、Awk、Gitがインストールされていることが前提です。また、OS環境としてLinux(Arch Linux x64)を使っています。

まずは、"Android NDK"をダウンロードします。
(すでにAndroid NDKをセットアップしていれば、この手順は不要です。)
今回は、Linux x64向けの最新版である "android-ndk-r9c-linux-x86_64.tar.bz2"を用いました。
さらにダウンロードしたアーカイブを展開し、これに含まれる"ndk-build"コマンドを利用できるようにPATHを通しておきます。
$ wget android-ndk-r9c-linux-x86_64.tar.bz2
$ tar jxf android-ndk-r9c-linux-x86_64.tar.bz2
$ sudo mv android-ndk-r9c/ /opt/
$ echo 'export PATH=$PATH:/opt/android-ndk-r9c' >> ~/.bashrc
$ echo 'export NDKROOT=$PATH:/opt/android-ndk-r9c' >> ~/.bashrc
$ source ~/.bashrc

次に、"tesseract-android-tools"を...
...のはずですが、執筆現在、Android NDK r9 でうまくビルドできませんでした。

代わりに、rmtheis 氏によってForkされたプロジェクトが公開されていますので、そちらを使わせていただくことにします。尚、このプロジェクトにはTesseract、Leptonica、その他の必要なファイルが同梱されています。
$ git clone  https://github.com/rmtheis/tess-two.git
$ cd tess-two/tess-two/

あとは READMEファイルのとおりにビルドを進めていきます。
但し、ここでAndroid SDKのAPI level 8がインストールされていないとエラーが発生しましたので、project.propertiesを書き換えることで対処しておきます。(自分のPCにインストールしておいたAPI levelに書き換えてください。)
--- project.properties
+++ project.properties
@@ -9,4 +9,4 @@
 android.library=true
 # Project target.
-target=android-8
+target=android-10
さらにコマンドを実行します。
$ ndk-build
$ android update project --path .
$ ant release

次に、Eclipseでプロジェクトをインポートします。
先ほどndk-build した "tess-two"プロジェクトを、Eclipse上で"Androidプロジェクト"としてインポートします。
インポートされたら、プロジェクトのプロパティで"ライブラリ"扱いになっているかを確認します。
Eclipse上でプロジェクト"tess-two"をインポートしたら、
プロジェクトのプロパティで"ライブラリー"にチェックが入っていることを確認する。

あとは、Androidアプリケーションを書きます。
今回は、最低限の簡単なサンプルアプリケーションを作ってみましたので、
一式をGitHubのリポジトリとして公開しておきました:
https://github.com/mugifly/android_tesseract-ocr_test
ご自由に git cloneして、動作を試したり改変したりしてご利用ください。

尚、このAndroidアプリケーションプロジェクトのほうでは、先ほどのライブラリプロジェクト"tess-two"をライブラリーとして追加しておきます。
Androidアプリケーションプロジェクトのプロパティで
使用するライブラリーとして先ほどの"tess-two"プロジェクトを追加する。
さらに、"tesseract-ocr"の学習データをダウンロードします。
日本語の学習データは http://tesseract-ocr.googlecode.com/files/tesseract-ocr-3.02.jpn.tar.gz です。
これをダウンロードして展開すると"jpn.traineddata"というファイルがあります。

このファイルを、先ほどのAndroidアプリケーションプロジェクト内の"assets"ディレクトリ内にコピーしておきます。
Androidアプリケーションプロジェクトに"assets"フォルダを作り、
そこに"jpn.traineddata "をコピーしておく。
※ assets内の学習データを直接Tesseract-OCRで読み込めるようにするには、
tess-two (tesseract-android-tools)を改変する必要がありそうでしたので、
代わりに私が作成したサンプルアプリケーションでは、起動時にassets内の学習データをSDカード上にコピーする処理を実装しておきました。

尚、学習データがないと当然ながらOCR処理でエラーが発生します。ご注意下さい。

あとはAndroidアプリケーションプロジェクトをビルドするだけです。
NDKを用いたアプリケーションなので、ビルドに少し時間がかかります。

さて、写真を撮って文字認識をしてみます...。
尚、今回は面倒くさかったので、実機ではなくAndroidの開発用エミュレータを用いました。カメラは適当にあったWebカメラ QCam S7500 で、解像度はVGAに落として撮影しています。


OCRをかける前に二値化処理を行い、さらに数字だけに絞ってOCRをかけています。
適当に写真を撮った割には、まあまあの認識精度だと思いますが、
やはり、前処理(傾き、歪みとか、二値化とか...)について改良する余地がありそうです。

また、手書きした文字についてはうまく読み取りできませんでしたが、手書きの文字ばかりを集めて学習データを生成すれば、不可能ではないようです。
(参照: tesseract for handwriting recognition)

それでは (^ ^)♪

参考にさせていただいたページ (感謝♪):