OCRエンジンをつくるのはそれなりに大変そうだし専門知識も必要そうなので、それがよほどのコア技術となるような会社/プロダクトでもない限りあまり自前実装をすることはなくて、どこかのライブラリを買ってくるか、オープンソースのものを使うか、ということがほとんどだと思います。で、iOSで無料で使えるOCRエンジン、となると実質 Tesseract 一択だったのではないでしょうか。
そんな状況の中、最近出てきたのが、Swiftで書かれたオープンソースのOCRライブラリ「SwiftOCR」です。
- Swift製
- 高精度
- 高速
- iOS と OS X で使用可能
をうたっていて、ライセンスはApache License, Version 2.0です。
画像認識にはニューラルネットワークを利用しているとのこと。
そしてREADMEに載っている以下の表がなんとも魅力的です。
|SwiftOCR | Tesseract
------------ |------------ | -------------
Speed | 0.08 sec. | 0.63 sec.
Accuracy | 97.7% | 45.2%
CPU | ~30% | ~90%
Memory | 45 MB | 73 MB
Tesseractよりも速く、高精度で、CPU負荷は小さく(つまりバッテリー消費も少なく)、メモリも食わない、と良いことづくめ。
READMEに書いてありますが、Connected-component labelingという手法を用いているそうです。
First, SwiftOCR binarizes the input image. Afterwards it extracts the characters of the image using a technique called Connected-component labeling. Finally the seperated characters get converted into numbers which then get feed into the neural network.
別の作業をしていてたまたま見つけたのですが、あまりに気になったので git clone してサンプルプロジェクトを試してみました。
##試行1
まずは同梱されているSwiftOCR Cameraというサンプルを「そのまま」試してみました。
真ん中の半透明の黒帯の範囲が認識対象で、Take Photoボタンを押すとOCR処理が走り、上部のラベルに認識結果が表示されます。
自分の名刺の中の自分の名前を認識させてみました。
("T50ISUTSH0C" 全然ダメ・・・)
("TSUISUMSHUICHI" 近い、が、間違えている)
あれ、領域を最初から絞ってあげてるという好条件なのに、間違ってるし、全然ロバストじゃない(ちょっと動くと認識結果がコロコロ変わる)・・・
ちなみにこの名刺のフォントはちょっと変わってますが、Core Image + Tesseractでかなりいい精度で認識できてました。
ここでハタと気付きました。
internal var recognizableCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
サンプルのデフォルトでは認識対象文字列に小文字アルファベットが入ってなかったということに。。
というわけで、と小文字を追加して再挑戦・・・
internal var recognizableCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
・・・と思いましたが、そのすぐ上にコメントでこう書いてありました。
///It **must** be in the **same order** as the network got trained
学習(トレーニング)時と同じ順序じゃないといけないよと。。1
##学習(モデル作成)
というわけで、リポジトリに同梱されている SwiftOCR Training というOS Xで動作する学習ツールでネットワークをつくりなおしました。
Build & Runで次のようなウィンドウが起動します。
小文字アルファベットを加えて2、フォントを全選択してStart Trainingするだけの簡単操作です。
ノイズというか歪みを加えるための背景画像が何パターンかリソースに入っていて、あとはフォントから文字列をつくって学習用のデータを生成するしくみのようです。
学習が完了すると、デフォルトでは ~/Desktop
に OCR-Network というファイルが生成されます。
##試行2
というわけで新しくできた OCR-Network を使い、かつ認識対象文字列として小文字アルファベットを追加して、
internal var recognizableCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
Cameraサンプルを再度試してみました。
("Tsutsunsbcb" あれ・・・)
("1sutsnksbulcbi" ダメだ・・・)
というわけで小文字は認識するようにはなったものの、精度は相変わらずイマイチでした。3
##なぜ精度が出ないのか?
Tesseractではもっとちゃんと認識できた的なことをちらっと上で書きましたが、そのときはTesseractの性能どうこうよりも、二値化の閾値を動的に計算する処理を入れたときに精度にグッと改善した記憶があります。
で、今回のCameraサンプルでは、ソースをさらっと見た限りだとGPUImageでグレースケール化してるだけっぽいので、まずその点でロバスト性を大きく欠いているのではないかという気がします。
あとちゃんと見てないですが文字領域の切り出しがうまくいってるのかも気になります。そこはCore ImageのText Detectorでも補完できるので。
それとネットワークをTrainingする際に、自分の環境に入っているフォントが一通り列挙されて、それをもとに学習データが自動生成されるっぽかったですが、そんなんでいいのかなと。所詮個人の環境なので古今東西のフォントを網羅してるわけじゃないですし。それよりはちゃんとした人(専門家)がちゃんとしたデータでちゃんとつくったであろうTesseractのモデルの方が良さそうだなと。
##おわりに
今回は残念ながら精度が出なかったわけですが、誤解しないでほしいのは、本記事ではあくまでサンプルをちょこっと試しただけであって、精度改善の余地はいくらでもある、ということです。本記事ではTesseractと同条件での比較も行っていませんし。
上で書いたように
- 学習をちゃんとする
- 前処理をちゃんとする
というだけで大いに改善するかもしれないですし、非常に可能性のあるライブラリだと思います。
仕事でOCR的なことが必要になったら改めてもっとちゃんと触ってみようと思います。
##こちらもどうぞ
- Swiftで書かれた人工知能・機械学習ライブラリ「Swift-AI」をiOSで動かしてみる - Qiita
- TensorFlowの学習済みモデルを拾ってきてiOSで利用する - Qiita
- 機械学習はじめの一歩に役立つ記事のまとめ - Qiita
- iOSと機械学習