読者です 読者をやめる 読者になる 読者になる

Tesseract-OCR-iOSと二値化処理の切り替え

久しぶりにOCR関連。

前置き

二値化処理 (image thresholding)というのは、各色ごとに(8ビットなど)の階調で表現された画像データを、あるしきい値閾値)を基準に0と1の二値で表現できるデータに変換すること。

このあるしきい値でという部分がポイントで、このしきい値を決定するためのアルゴリズムがいろいろと提案されている、らしい。

OCRライブラリであるtesseract-ocr(および、そのラッパーライブラリであるTesseract-OCR-iOS)も内部で画像を二値化(二値画像化)している。 通常、グレスケール化して二値化するが、ここで二値化処理がうまくいかないと当然のごとくOCR結果に影響する。

ノイズの多い画像であるとか、画像の種類によってアルゴリズムを切り替えるようにできると望ましい。

欲を言えば、画像のヒストグラムか何かの特徴量で自動判別させたい。

tesseract-ocr内部では Otsu's method を用いて二値化しているらしい。

これはこれで優秀なアルゴリズムらしい。グレースケールの仕方が悪いのか、特定の画像で二値化がうまくいかないケースがある。 アルゴリズムのせいなのか、ライブラリ側の問題なのか、あるいは前処理の問題かはなんとも言えない*1

やりたいこと

  • アプリ側のオプションで二値化アルゴリズムを切り替えたい
  • (できれば)tesseract-ocr の標準のアルゴリズムも残したい
  • OpenCVを組み込むのはバイナリサイズの都合で避けたい

Tesseract-OCR-iOS(tesseract-ocriOS向けラッパーライブラリ)では、デフォルトの二値化処理をバイパスするための手段が用意されている。

解決策(暫定)

公式のWikiを参考にすればいい。

要するに、 preprocessedImageForTesseractというメソッドを定義して、その内部で自分の使いたい方法で画像を二値化 して、UIImageオブジェクトで返せばいい。

これはこれでいいのだけれど、tesseract-ocr標準のアルゴリズムが使いたいケースに対応できない。本来なら実行時に(動的に)preprocessedImageForTesseractメソッドが定義されてない状態にするのか、オプションをしていできるようにライブラリ側を改造するのが最善だと思う。

残念ながら、Swiftで実行時に特定のメソッド呼び出しを無効化する方法がわからないのと、Objective-Cはわからないのでライブラリの拡張は諦める。

以上を踏まえて、少々お行儀の悪い方法で逃げることにする。

ライブラリ側のコードを見る限り、preprocessedImageForTesseractの戻り値がnilの場合は標準の二値化アルゴリズムが使用されるみたいなので、下記のようにしてみた。

画像データはグレースケール化されているものとする。

Example for "preprocessedImageForTesseract"

GPUImageの"Adaptive Threshold"と"Avarage Luminessence" フィルタ、tesseract-ocrのデフォルト(nilオブジェクトを返すことでTesseract-OCR-iOS側でフォールバック)の三種に切り替える例。

文字の線幅が太いと、"Adaptive Threshold"はエッジ抽出フィルタのような挙動をするのが難点。

以下のようにenumを定義しておく。

enum ThresholdMode : Int {
    case OtsuThreshold = 0
    case AvarageLuminessence
    case AdaptiveThreshold
}

ViewController側で、

var threshold_mode = ThresholdMode.OtsuThreshold

のようにして切り替える。OpenCVを使えばもっといろんなアルゴリズムを使えるだろうけど、理屈が分かっていないので保留。

とりあえずここまで。

ディジタル画像処理 [改訂新版]

ディジタル画像処理 [改訂新版]