Pythonで日本語OCRを行うときのメモ

フリーのtesseractというOCRを使って、pythonで日本語のOCRを行うまでに行った手順をまとめます。

## 環境
* Ubuntu 14.04
* Python 2.7

## インストール
tesseractをインストールします。

### インストール方針
インストールの方法には、
1. apt-getでインストール
2. ソースからビルド、インストール

の二つがあります。
1のapt-getでインストールできるバージョンは3.0.3です。
tesseractで日本語を扱うには、日本語で訓練されたデータ(jpn.traindata)が必要です。
これは自分でダウンロードしてくる必要があるのですが、ネット上で見つけられたものはver3.0.4のものだけです。3.03でこのデータを使おうとしても、うまくいかず、こんなエラーが出ます。

```
read_params_file: parameter not found: allow_blob_division
```

[こちらのかた](http://a244.hateblo.jp/entry/2015/08/25/051222)のように、traindataを編集して3.0.3で使うこともできますが、そのために必要な```combine_tessdata```というコマンドはapt-getインストールでは使えません。そのため、現状で日本語でやりたかったら、ソースでインストールする他ないかと思われます。

基本的に、公式のコンパイルインストールのページを参考に、tesseract3.0.4をインストールします。

https://github.com/tesseract-ocr/tesseract/wiki/Compiling

### 依存関係インストール

```
$ sudo apt-get install autoconf automake libtool
$ sudo apt-get install libpng12-dev
$ sudo apt-get install libjpeg62-dev
$ sudo apt-get install libtiff4-dev
$ sudo apt-get install zlib1g-dev
$ sudo apt-get install libicu-dev      # (if you plan to make the training tools)
$ sudo apt-get install libpango1.0-dev # (if you plan to make the training tools)
$ sudo apt-get install libcairo2-dev   # (if you plan to make the training tools)
```

### Laptonicaインストール
[Laptonica](http://www.leptonica.org/)という画像ライブラリが必要のようです。
ソースを[ダウンロードページ](http://www.leptonica.org/download.html)からダウンロード、解凍します。tesseract3.0.4のインストールには、最低でもLaptonica1.71が必要なので、最新の1.7.3をインストールします。

```
# 解凍
gzip -dc leptonica-1.73.tar.gz |tar xvf -
cd leptonica-1.73

# makeとか
$ ./configure
$ make
$ sudo make install
```

## tesseractインストール
基本的に[これ](https://github.com/tesseract-ocr/tesseract/wiki/Compiling)どおりやります。

3.0.4のソースを[こちら](https://github.com/tesseract-ocr/tesseract/tree/3.04)から取得

```
# 解凍、移動
$ unzip 3.04.zip 
$ cd tesseract-3.04

# ライブラリパスを通しておく
$ export -p LD_LIBRARY=$LD_LIBRARY:/usr/local/lib

# インストール
$ ./autogen.sh
$ ./configure
$ sudo make          # ここだけsudoにした。laptonicaが見当たらなかったので。
$ sudo make install
$ sudo ldconfig
```

### 日本語ファイルの取得、設定
[こちら](https://github.com/tesseract-ocr/tessdata)の言語データセットから、jpn.traindataという日本語のものをダウンロードし、こちらに置きます。

```
/usr/local/share/tessdata/
```

そして、このフォルダのパスを設定しておきます。

```
export TESSDATA_PREFIX="/usr/local/share/tessdata/tessdata/
```

### 動作確認
インストールがうまくいっていれば、コマンドライン上でOCRの実行が可能になっているはずです。
試しにこんな画像を日本語OCRにかけてみます。
![ocr_test.png](https://qiita-image-store.s3.amazonaws.com/0/50474/1ec73bae-036b-c6c8-62b7-10173d3c80b3.png)

```
tesseract ocr_test.png out -l jpn
```

と実行すると、out.txtというファイルに結果が書き込まれます。

```text:out.txt
笑顔が一番 ! れにちや ん
```

小さい「ゃ」が大きい「や」になってますが、概ね認識できています。
小文字の概念は他の英語とかにないので、そこは難しいのでしょうか。

### pyocrの導入
pythonで使えるように、pyocrというラッパーライブラリを使います。

インストールは、

```
$ pip install pyocr
```

で終わりです。

しかしながら、ソースからインストールしたtesseractに対応しておらず、テスト用に以下のerror.pyを実行させると、うまくいきません。

```py:error.py
import pyocr
tools = pyocr.get_available_tools()
```

```
Traceback (most recent call last):
  File "error.py", line 12, in <module>
    tools = pyocr.get_available_tools()
  File "/usr/local/lib/python2.7/site-packages/pyocr/pyocr.py", line 74, in get_available_tools
    if tool.is_available():
  File "/usr/local/lib/python2.7/site-packages/pyocr/libtesseract/__init__.py", line 152, in is_available
    version = get_version()
  File "/usr/local/lib/python2.7/site-packages/pyocr/libtesseract/__init__.py", line 179, in get_version
    upd = int(version[2])
ValueError: invalid literal for int() with base 10: '02dev'
```

エラーを読むと、"02dev"という文字列をintに変換しようとして怒られています。ソースからインストールしたバージョンが、tesseract 3.04.02devで、devパッケージを想定していないようです。
なので、このソースを変更してあげます。

変更するソースは、virtualenv使用の場合は、適当に置き換えてください。

```py:/usr/local/lib/python2.7/site-packages/pyocr/libtesseract/__init__.py
    if len(version) >= 3:
        upd = int(version[2].replace('dev', ''))
        # upd = int(version[2])

```

これでうまくいくはずです。

## OCRを試してみる

色々なOCRの仕組みがあるので、試してみます。
この画像で試してみます。

![mlct-14-638.jpg](https://qiita-image-store.s3.amazonaws.com/0/50474/18d7180e-8e6d-b5d1-327f-941ee38451d8.jpeg)

### Text
もっともシンプルなOCRです。画像から文字を読み取り、文字列として返します。

```py
import pyocr
import pyocr.builders
import argparse
from PIL import Image

parser = argparse.ArgumentParser(description='tesseract ocr test')
parser.add_argument('image', help='image path')
args = parser.parse_args()

tools = pyocr.get_available_tools()

if len(tools) == 0:
    print("No OCR tool found")
    sys.exit(1)
# The tools are returned in the recommended order of usage
tool = tools[0]

res = tool.image_to_string(Image.open(args.image),
                           lang="jpn",
                           builder=pyocr.builders.TextBuilder(tesseract_layout=6))

print res

```

結果

```
機鵬習の卿
テス 卜ヲータ
扉 叫隣 史習給果
縄
隻デーヮ ^~璽属~ 慣賭史習 バ『メーヮ
```
難しい単語のせいか、結果はひどいです。

### WordBox

単語がどこにあるのか、boxを返してくれます。openCVで結果を可視化してみます。（openCVは[こちら](http://qiita.com/bohemian916/items/23abea5ef9fadcc8bce0)でインストール）

```py
import pyocr
import pyocr.builders
import argparse
import cv2
from PIL import Image

parser = argparse.ArgumentParser(description='tesseract ocr test')
parser.add_argument('image', help='image path')
args = parser.parse_args()


tools = pyocr.get_available_tools()

if len(tools) == 0:
    print("No OCR tool found")
    sys.exit(1)
# The tools are returned in the recommended order of usage
tool = tools[0]


res = tool.image_to_string(Image.open(args.image),
                           lang="jpn",
                           builder=pyocr.builders.WordBoxBuilder(tesseract_layout=6))

# draw result 
out = cv2.imread(args.image)
for d in res:
    print d.content
    print d.position
    cv2.rectangle(out, d.position[0], d.position[1], (0, 0, 255), 2)

cv2.imshow('image',out)
cv2.waitKey(0)
cv2.destroyAllWindows()
```


![Screenshot from 2016-07-20 15:12:12.png](https://qiita-image-store.s3.amazonaws.com/0/50474/996b1d5d-e792-6ae1-5929-fbfe7dddaddd.png)

```
機鵬習の卿
((226, 12), (412, 37))
テス
((255, 138), (278, 148))
卜ヲータ
((283, 137), (326, 148))
扉
((397, 149), (406, 159))
叫隣
((411, 149), (430, 159))
史習給果
((477, 148), (523, 159))
縄
((165, 170), (199, 181))
隻デーヮ
((115, 202), (156, 212))
^~璽属~
((210, 196), (247, 220))
慣賭史習
((297, 202), (343, 213))
バ『メーヮ
((390, 203), (438, 212))
```

領域はそこそことれていますが、認識した単語は相変わらずひどいです。

### LineBox
WordBoxによるものは、単語ごとでしたが、LineBoxでは、同じ行にある単語をまとめてくれるようです。

先ほどのWordBoxのソースを一部だけ変えます
WordBoxBuilder→LineBoxBuilder　にするだけです。

```py
res = tool.image_to_string(Image.open(args.image),
                           lang="jpn",
                           builder=pyocr.builders.LineBoxBuilder(tesseract_layout=6))


```

結果
![Screenshot from 2016-07-20 15:34:57.png](https://qiita-image-store.s3.amazonaws.com/0/50474/3e40f771-06e3-005b-d1d0-06f4234eff24.png)

```
機鵬習の卿
((226, 12), (412, 37))
テス 卜ヲータ
((255, 137), (326, 148))
扉 叫隣 史習給果
((397, 148), (523, 159))
縄
((165, 170), (199, 181))
隻デーヮ ^~璽属~ 慣賭史習 バ『メーヮ
((115, 196), (438, 220))

```

この画像は同じ行にまとめてくれなくてもよいですが、複数行の文章のときに使えそうです。

### tesseract_layoutについて
それぞれのbuilderで、```tesseract_layout=6```
と設定していました。この数字は、どのような方針で画像に対してOCRを行うか設定するものらしく、

こちらのかたがまとめてくださっています。
http://tanaken-log.blogspot.jp/2012/08/imagemagick-tesseract.html

```
pagesegmode values are:
0 = Orientation and script detection (OSD) only.
1 = Automatic page segmentation with OSD.
2 = Automatic page segmentation, but no OSD, or OCR
3 = Fully automatic page segmentation, but no OSD. (Default)
4 = Assume a single column of text of variable sizes.
5 = Assume a single uniform block of vertically aligned text.
6 = Assume a single uniform block of text.
7 = Treat the image as a single text line.
8 = Treat the image as a single word.
9 = Treat the image as a single word in a circle.
10 = Treat the image as a single character.
```

## 学習データについて
見ての通り、既存の日本語データを使うと、精度がよろしくないようです。
自分で学習データを作成すれば、もっとまともになるとのことです。

http://hadashi-gensan.hatenablog.com/entry/2014/01/15/135316

## おまけ　Google Cloud Vision
Google Cloud Vision API のTEXT_DETECTを利用すると、こんな感じになります。

![Screenshot from 2016-07-21 11:28:43.png](https://qiita-image-store.s3.amazonaws.com/0/50474/d0165bd5-f12a-e4b4-ce11-0090ff143a28.png)

```
機械
学習
の
流れ
テスト
データ
予
測
器
学習
結果
前
処理
教師
データ
生
データ
機械
学習
パラメータ
L
理
A

```

やっぱり精度いいですね。そんなにたくさんリクエストを投げず、手軽に処理したい場合は、Vision API使った方がいいですね。