【iOS】オープンソースのOCRライブラリ「SwiftyTesseract」を試してみる

## 背景

[【iOS】オープンソースのOCRライブラリ調査](https://qiita.com/shinjism/items/f29e95116bb1929a3261)の続きです。

前回の調査の過程で、[Tesseract OCR iOS](https://github.com/gali8/Tesseract-OCR-iOS)と[SwiftOCR](https://github.com/garnele007/SwiftOCR)の2つがGitHubのスター数から見ても人気だということが分かりました。しかしながら、日本語認識に関しては課題が多く、実用向きではない印象を受けました。そこで個人的に注目しているのが[SwiftyTesseract](https://github.com/SwiftyTesseract/SwiftyTesseract)です。

というわけで、SwiftyTesseractを実際に試してみることにしました。

## SwiftyTesseractについて

SwiftyTesseractの最大の特徴は、LSTMニューラルネットベースのOCRエンジンが追加された最新安定バージョンの[Tesseract](https://github.com/tesseract-ocr/tesseract) 4をラップしている点です。ディープラーニングは詳しくないので深掘りしませんが、これで何が嬉しいかというと、Tesseract OCR iOSやSwiftOCRと比べて、ある程度スピードと正確性を両立させた日本語OCRが手軽に実現できるということです。

## 前提

本記事で使用した環境は次のとおりです。

- macOS 10.14.4
- Xcode 10.2.1
- CocoaPods 1.4.0
- Swift 4.2

## Xcodeプロジェクトを作成する

まずはXcodeプロジェクトを新規作成し、Build SettingsでSwift Language Versionを変更します。
SwiftyTesseractのSwiftバージョンに合わせて、Swift 4.2を選択します。
<img width="911" alt="スクリーンショット_2019-05-04_0_37_53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2831/2395847a-099d-a7f9-fd4e-2ba8a4328850.png">

## SwiftyTesseractをXcodeプロジェクトへ導入する

CocoaPods、または、CarthageでSwiftyTesseractを導入します。
[SwiftyTesseractの解説には](https://github.com/SwiftyTesseract/SwiftyTesseract#installation)、CocoaPodsとCarthage双方の導入方法が紹介されていますので、好きな方法で導入してください。

ちなみに今回は、CocoaPodsを使ってSwiftyTesseractを導入してみます。
まずはPodfileを開いて次の一行を追加します。

```ruby:Podfile
pod 'SwiftyTesseract', '~> 2.0'
```

次にPodfileを保存して、下記のコマンドでインストールします。

```
$ pod install
```

以上でSwiftyTesseractの導入は完了です。
以降は「プロジェクト名.xcworkspace」というファイルを開いて開発を進めていきます。

## 学習データをXcodeプロジェクトへ追加する

SwiftyTesseractでは、[Tesseract](https://github.com/tesseract-ocr/tesseract) [^1]用にあらかじめ用意されている学習データをそのまま利用することができます。
Tesseract用の学習データには、次の3つのタイプが用意されています。

[^1]: TesseractはGoogleによって開発が進められているOCRエンジンです。

- [tessdata_best](https://github.com/tesseract-ocr/tessdata_best) (LSTM、正確性重視)
- [tessdata_fast](https://github.com/tesseract-ocr/tessdata_fast) (LSTM、速度重視)
- [tessdata](https://github.com/tesseract-ocr/tessdata) (パターン認識とLSTMの組み合わせ？、互換性重視？)

[SwiftyTesseractの解説によると](https://github.com/SwiftyTesseract/SwiftyTesseract#which-language-training-data-should-you-use)、たいていのケースで最良の選択肢はtessdata_fastになるだろうと書いてあるので、今回はtessdata_fastの日本語学習データを利用します。

1. パソコン上にtessdataという名称でフォルダを作成しておきます。
<img width="763" alt="スクリーンショット_2019-05-03_21_30_16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2831/2e04625f-810f-771a-adc7-4dc652d29876.png">
2. [tessdata_fast](https://github.com/tesseract-ocr/tessdata_fast)のリポジトリにアクセスし、[jpn.traineddata](https://github.com/tesseract-ocr/tessdata_fast/blob/master/jpn.traineddata)をクリックします。
<img width="1016" alt="スクリーンショット_2019-05-04_8_01_07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2831/1d4c5637-e67c-e0ff-db9f-e0ac20146500.png">
3. Downloadボタンをクリックします。
<img width="1002" alt="スクリーンショット_2019-05-04_8_01_14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2831/82e0a828-0637-9d60-1420-f36011c266ff.png">
4. あらかじめ作成しておいたtessdataフォルダへjpn.traineddataをダウンロードします。
<img width="782" alt="スクリーンショット_2019-05-03_21_32_53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2831/65ce0156-129d-e522-4fd0-74f423d45f94.png">
5. tessdataフォルダをXcodeプロジェクトへドラッグします。
<img width="954" alt="スクリーンショット_2019-05-03_21_54_12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2831/6af31794-8b34-42c2-cbbd-9f3883599891.png">
6. オプションは次のように選択します。
<img width="731" alt="スクリーンショット_2019-05-03_22_18_34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2831/996d7294-4aea-7d76-2e7f-85bc20c99860.png">
7. 水色のアイコンで表示されたtessdataフォルダの配下にjpn.traineddataがあればOKです。<img width="731" alt="スクリーンショット_2019-05-03_22_19_16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2831/cb8342d1-89be-ae71-ad4f-53fccf31edf6.png">

以上で日本語学習データを利用する準備が整いました。

## 画像をXcodeプロジェクトへ追加する

目的の画像ファイルを用意し、Xcodeプロジェクトへ取り込んでおきます。
今回は次の画像(sample.png)で試してみます。

![sample.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2831/8ca158fc-a0f1-39f4-dfec-b1d70fe6e2fe.png)

ロゴや下線があったり、「」＜＞：・〜※などの記号があったり、よく見ると紙が波打って文字のラインが若干ずれていたりといったトラップが仕込まれていますね。
果たして結果や如何に！？

## OCR機能を実装する

ここまで来たら、あとはOCR機能を実装するだけです。
ViewController.swiftを次のとおり書き換えます。

```Swift:ViewController.swift
import UIKit
import SwiftyTesseract

class ViewController: UIViewController {

    let swiftyTesseract = SwiftyTesseract(language: RecognitionLanguage.japanese)

    override func viewDidLoad() {
        super.viewDidLoad()

        let start = Date()

        let fileName = "sample.png"
        guard let image = UIImage(named: fileName) else { return }

        swiftyTesseract.performOCR(on: image) { recognizedString in
            guard let text = recognizedString else { return }
            print("\(text)")

            print("\(-start.timeIntervalSinceNow)")
        }
    }
}
```

### ポイント解説

#### SwiftyTesseractのインポート

何はともあれ、SwiftyTesseractをインポートします。
これによってSwiftyTesseractが提供するメソッド等が利用できるようになります。

```Swift
import SwiftyTesseract
```

#### SwiftyTesseractのインスタンスを生成

SwiftyTesseractのインスタンスを生成すると同時に、OCRで認識する言語の指定をここで行います。
下記は日本語を認識する場合の記述ですが、tessdataフォルダに含まれる学習データの言語を指定してあげる必要があります。 [^2]

[^2]: 今回は日本語のみ指定していますが、複数の言語を指定することもできます。

```Swift
    let swiftyTesseract = SwiftyTesseract(language: RecognitionLanguage.japanese)
```

#### OCRの実行

OCRの実行は、単純にsample.pngから生成したUIImageをperformOCR(on:completionHandler:)メソッドへ渡すだけです。
完了ハンドラでは認識された文字列を受け取って処理します。

```Swift
        let fileName = "sample.png"
        guard let image = UIImage(named: fileName) else { return }

        swiftyTesseract.performOCR(on: image) { recognizedString in
            guard let text = recognizedString else { return }
            print("\(text)")

            // ... 省略
        }
```

## 認識結果を確認する

気になる認識結果です。

1600ｘ900の画像を使ってシミュレータで実行した場合は、1.6秒程度を要しました。

```:1600ｘ900、シミュレータ実行時の認識結果（tessdata_fast）
「ねんきん定期便」「ねんきんネット」に関する
                           下             ら
お問い合わせは、 下記専用番号へ
マク               昭          2
sm 0570-058-555
※O5Oから始まる電話でおかけになる場合は(東京)03-6700-1144
<受付時間> 月 曜日 午前8:30征後7:00
火金曜日 。 午前8:30っ生後5:15
※祝日、 1 2 月 2 9日1月3日はご利用いただけません。
※月曜日が祝日の場合は、翌日以降の開所日初日に午後 7 : 00まで
相談をお受けします。
お問い合わせの際は、「ねんきん定期便」 (ヘー 1 ページ) の照会番号、 基礎年金番号または個人番号をお知らせください。
ご利用にあたっての留意事項
ら 0エルは、 一般の固定電話からおかけになる場合は、 全国どこからでも市内通話料金でご利用できます。
7当レ、
・「O57O」の最初の「O」 を省略したり、 市外局番を付けて間違い電話になるケースが完生しています.。
おかけ間違いのないよう、 ご注意ください。
・ 月曜日などの休日明けやお手元に通知書が届いた直後 (5日間程度) は、電話がつながりにくくなりのます。
週の後半月の後半はつながりやすくなっています。
・オンライ ン端末の移働時間によっては、  ご照会の回答が翌日以降になる場合があります。

```

800ｘ450の画像を使ってシミュレータで実行した場合は、1.3秒程度を要しました。

```:800ｘ450、シミュレータ実行時の認識結果（tessdata_fast）
「ねんきん定期便」「ねんきんネット」に関する
お問い合わせは、下記専用番号へ
トン                            回
記 0570-058-555
※O5Oから始まる電話でおかけになる場合は(東京) 03-6700-1144 g叶)
<受付時間> 月 果 日 。 生前Bi30一年径7:00
※祝日、1 2月29日1月3日はご利用いただけません。
※月曜日が祝日の場合は、避日以降の開所日補日午後7 : 0 0まで
相談をお受けします。
お問い合わせの隊は、「ねんきん定期便」 (ムー 1 ページ) の照会番号、 基礎年金番号または個人番号をお知らせください。
ご利用にあたっての留意事項
 本ルは  のたから33 国どこびらでも中衝料金でご利用できます。
ただし、
・_「O570」の最初の 「O」を省略したり、 市外局番を付けて間違い電話になるケースが発生しています。
おかけ商違いのないよう、 ご注意ください。
・ 月明日などの休日明けやお手元に運知書が届いた直後 (5日間程度) は、重話がつながりにくくなります。
 週の後半月の後半はつながりやすくなっています。
・オインライン端末の知午時聞によっては、 ご暑会の回答が翌日以降になる場合があります。

```

## まとめ

認識精度はお世辞にも実用レベルとは言い難いながらも、ハードル高めな画像を使ったわりには頑張ってくれてる気がします。（個人の感想です）

無料のOCRライブラリで、画像や学習データに手を加えずとも、ここまで認識できるのは凄いことではないでしょうか。

認識範囲を限定したり、前処理で画像を適切に加工したり、学習データをチューニングしたりなど、認識精度をさらに向上させる方法を試す余地も大いにありそうです。 [^3]

[^3]: [SwiftyTesseractの解説を見ても](https://github.com/SwiftyTesseract/SwiftyTesseract#recognition-results)、予測可能な結果を返すために画像加工などの最適化をほどこすのは、ライブラリ側ではなくアプリ側であるとも受け取れる記述が確認できます。

今後もSwiftyTesseractの動向を追っていきたいと思います。

## おまけ

参考までに、tessdata_bestとtessdataの学習データを使った認識結果も掲載しておきます。

### tessdata_bestでの認識結果

1600x900の画像を使ってシミュレータで実行した場合は、認識に5.3秒程度を要しました。

```:1600x900、シミュレータ実行時の認識結果（tessdata_best）
「 ね ん きん 定期 便 」「 ね ん きん ネッ ト 」 に 関す る
三 |
お 問い 合わ せ は 、 下 記 専 用 番号 へ
や -。
記 2 0570-058-555
※O050 か ら 始 まる 電話 で お か け に な る 場合 は (東京 ) 09-6 700-1144 mm
< 受付 時 間 > 月 曜 日
年 前 9 30 午後 4:00
※ 祝 日 、1 2 月 2.9 日 1 月 3 日 は ご 利用 いた だ け ま せん 。
※ 月 曜日 が 祝日 の 場合 は 、 翌 日 以降 の 開所 日 初日 に 午後 7 : 00 ま で
相談 を お 受け し ます 。
お 問い 合わ せ の 際 は 、「 ね ん きん 定期 便 」 (Aー 1 ペー ジ ) の 照会 番号 、 基礎 年 金 番号 また は 個人 番号 を お 知ら せく だ さい 。
ご 利用 に あたっ て の 留意 事項
* ヤル は 、 一 般 の 固定 電話 か ら お か け に な る 場合 は 、 全国 どこ か ら で も 市 内 通話 料金 で ご 利用 で きま す 。
に だ し 、
・「O570」 の 最初 の 「O」 を 省略 し た り 、 市 外 局 災 を 付け て 問 違 い 電 話 に な る ケ ー ス が 発生 し て いま す 。
お か け 間 違い の な いよ う 、 ご 注意 くだ さい 。
* 月 曜日 な どの 休日 明け や お 手元 に 通知 書 が 届い た 直後 (5 日 間 程度 ) は 、 電 話 が つなが り に くく な の ます 。
週 の 後半 や 月 の 後半 は つなが りや すぐ な っ て いま す 。
* オン ライ ン 端 末 の 稼働 時 間 に よ っ て は 、 ご 照会 の 回 答 が 翌日 以降 に な る 場合 が あり ます 。

```

### tessdataでの認識結果

1600x900の画像を使ってシミュレータで実行した場合は、認識に3.0秒程度を要しました。

```:1600x900、シミュレータ実行時の認識結果（tessdata）
` ね ん き ん 定 期 便 」` ね ん き ん ネ ッ ト 」 に 関 す る
ミ ロ
お 問 い 合 わ せ は 、 下 記 専 用 番 号 ヘ
ン シ 許
啓 0⑤⑦0-0⑤⑧-⑤⑤⑤
※O⑤O か ら 始 ま る 電 話 で お か け に な る 場 合 は ( 東 京 ) 0③-⑥⑦00- ⑪④④ c_-awms
< 受 付 時 間 > 月 _ 曜 _ 日
平 前 ⑨ : ③ 0 へ 平 後 ④ : 0 0
※ 祝 日 、① ② 月 ② ⑨ 日 ー① 月 ③ 日 は ご 利 用 い た だ け ま せ ん 。 *
※ 月 曜 日 が 祝 日 の 場 合 は 、 翌 日 以 降 の 開 所 日 初 日 に 午 後 ⑦ : 0 0 ま で
相 談 を お 受 け し ま す 。
お 問 い 合 わ せ の 際 は 、 ` ね ん き ん 定 期 便 」 (Aー ① ペ ー ジ ) の 照 会 番 号 、 基 礎 年 金 番 号 ま た は 個 人 番 号 を お 知 ら せ く だ さ い 。
ご 利 用 に あ た っ て の 留 意 事 項
* 戸昇乞イ ヤ ル は 、 一 般 の 固 定 電 話 か ら お か け に な る 場 合 は 、 全 国 ど こ か ら で も 市 内 通 話 料 金 で ご 利 用 で き ま す 。
に だ し 、
・ `0⑤⑦0」 の 最 初 の `O」 を 省 略 し た り 、 外局を付けて間這い電話になるケ ー ス が 発 生 し て い ま す 。
お か け 閾 違 い の な い よ う 、 ご 注 意 く だ さ い 。
・月鱧貝萱との休日明けやお手元仁通知害か届いた直後 (⑤ 日 間 程 度 ) は 、 電 話 が つ な が り に く く な り ま す 。
週の欝キゆ貝の後半はつなかりやすくなっています。
・ オ ン ラ イ ン 端 末 の 稼 働 時 間 に よ っ て は 、 ご 照 会 の 回 答 が 翌 日 以 降 に な る 場 合 が あ り ま す 。

```