ざこ文字認識の話
文字認識システムを作りました。開発速度重視で具体的な手法については全く調べていません。
実行してみて動いたらそれを使うくらいの勢いなので、かなり間違ってると思うので、生暖かく見てやってください。
1. 文字の写真を撮影
テスト時はカメラで取ってました。
この画像は、テストに使った写真です。
2. 写真からエッジ検出
OpenCVさんのちからを借りて、Canny法を使いました。
import cv2 imgpath = "/path/to/img" img = cv2.imread("/path/to/img", 0) # lowより下は、エッジにはならない、highより上はエッジになるというしきい値を指定 cv2.Canny(img, low, high) # => エッジ検出された画像データ
以下が、エッジを検出した結果の画像です。
3. 文字領域を抽出
文字領域とは、ある画像内で、文字が写っている一部分を抽出することです。
パッと見、隣接しているエッジ(白)ピクセルで、一文字と見ることができそうと感じたので、隣接する白ピクセルを走査し、 それで見つけたピクセルの最大のx,yと最少のx,yを抽出し、その座標を通る四角で囲われる部分を文字領域として認識しています。
実際には、見てもわかる通り、エッジは色が変わる境界線なので、Bや0のように、外側と内側が隣接しない場合があります。なので、少しだけ離れていても(間に黒ピクセルを含んでも)隣接していると判断する必要があり、調節が必要です。
以下は、その四角を画像に表示したものです。
4. 抽出した領域情報から写真内の文字領域を実際に抽出
........
四角に沿って、画像を切り分けて抽出します。
5. 抽出した画像をうまい具合にリサイズ
今のままだと文字の画像のサイズが、それぞれ違うため、そのまま学習機に突っ込むわけには行きません。 全ての画像を等倍(縦横のピクセルの数が全て同じ)にしなければ次の学習したり認識したりする時に支障がでるので、リサイズします。 全て同じサイズならどんなサイズでも問題ありません。とりあえず、今回使った32x32にするために行った手順を紹介したいと思います。
まず、縦横の長い方を32に合わせて比率を保持したままリサイズします。
→
元画像の比を保存しつつサイズを合わせたいので、短い方は両はじに黒ピクセルを追加します。ただし、元画像が中心に来るように追加する黒ピクセルの幅は両はじとも均等になるようにします。
→ わかりづらいかもしれませんが、左右に黒い空間ができていると思います。
6. リサイズした文字画像を機械学習を使って文字認識
trainデータはラボのメンバーに作成をお願いしました(一文字30文字づつで、全部で350文字ほど)。 そのtrainデータを上記の加工を施して学習機に学習させました。
modelはKNNとSVMで、グリッドサーチを行いパラメータ探索をして生成しました。
7. 結果
cross validationの結果を載せておきます。
- KNN (K=1)
- accuracy : 0.730
- SVM
- accuracy : 0.765
正直、このままでは使い物に成りませんが、ここから先は精度上げであって、実装ではないので、とりあえず良いかなと思います。
まとめ
2週間ほどの突貫作業でしたが、精度は置いておいて、意外とものに成りました。
実は、角度を変えた画像でtrainデータをかさ増ししたり、学習時に使うデータを加工してみたりした結果、accuracy 0.9くらいになったりしていたり、スマホで写真を取ると自動で認識を開始したりする機能も付けたりしてますが、それはそれでもう一記事くらいかけそうなのでここであえては書きません。
機械学習に入門してからかれこれ1年ほど立ちますが、こういった形でアウトプットできる機会がなかったので、非常に良い機会でした。また、自分のスキルアップを直に感じることができたのでモチベーションにもうまい具合に響いてくれると良いなと感じました。
以上です。ご読了ありがとうございます。