MIRS2009 OpenCVによる数字認識 |
MIRS0901-SOFT-0010 |
版数 |
最終更新日 |
作成 |
承認 |
改訂記事 |
A01 |
2009/11/15 |
坂代 |
武藤 |
初版 |
A02 |
2009/11/21 |
坂代 |
武藤 |
DBの仕様に合わせてプログラムの改変&バグ・不具合修正 添付ソースの添付 |
A03 |
2009/12/26 |
坂代 |
武藤 |
仕様変更 2値化アルゴリズムの変更 バグ&不具合修正 添付ソースの更新 |
A04 |
2010/01/12 |
坂代 |
武藤 |
バグ&不具合修正 添付ソースの更新 |
A05 |
2010/02/06 |
坂代 |
武藤 |
添付画像・仕様を競技会本番のものに変更、添付ソースの最終更新 |
1.目的
2.仕様
3.概要
4.動作試験
5.まとめ
1 目的
本ドキュメントはOpenCVを用いた数字認識アルゴリズムについてまとめたものである。
2 仕様
前提条件: この数字認識プログラムは以下の前提条件の下に作成した。
・2つの数字が書かれているDBをほぼ正面からカメラで捉えている
・DBボードは 背景が白色、数字部が黒色で描かれているとする
・DBボードに描かれている数字は7セグメントLEDのような数字であるとする
達成目標: 最低でも以下の事項だけは達成できるようにする
・0〜9までのすべての数字の判別を(「2」と「5」の区別も)できるようにする
・2つの値を同時に(一回のキャプチャ行動で)判断できるようにする
・2つの値の相対位置(どちらが右でどちらが左か)を判断できるようにする
・多少の傾きや歪みを考慮できるようにする
仕様(競技会終了時点)
・
カメラで背景 白 数字部 黒のDBを全て捉えると
それを取り込み、グレースケールによる2値化(+大津の手法)を行い、ラベリング、トリミングを行った後に
独自(?)のアルゴリズムを用い数字認識を行う。
・ 2つの数字を同時に認識し、相対位置(右にあるか左にあるか)を判別する
・
角度補正、ハフ変換などのアルゴリズムは用いていない
・ 競技場の実態がはっきりして、「DB中央のしきりと競技場壁最上部にある黒いテープが繋がって、数字部よりも大きな黒色の領域を作っている」
ことがわかってしまったので、ラベリング後、面積の一番大きいものを除外することで数値部の抽出を行っている。
・ 現段階で、最小固有値法によるコーナー検出も可能(数字認識プログラムには直接かかわっていない)
3 概要
以下に、数字認識の一連の流れの概要を示す
fig.1 数字認識フローチャート
各チャートについて記述する
1. 画像の取り込み
uvccaptureを用いたキャプチャを行い、cvLoadImage関数を用いてその画像情報を構造体に取り込む
標準プログラムのimg_update関数をそのまま使わせてもらった。
2. グレースケールによる2値化
画像を、定めた閾値に従って、白(255)と黒(0)の二つの値に分類する。
標準プログラムではHSV表色系を用いた2値化を行っているが、ここでは「対象数字は”黒”で背景は”白”」という前提条件の下で行っているので、
グレースケール(1パラメータによる黒(0)~白(255)までの値を用いる表色系)による2値化でもほとんど支障がないと考え、これを用いた。
2値化のアルゴリズムで、「大津の手法」なるものがあり、これは2値化の閾値を固定するのではなく、画像の状況に合わせて自動で可変してくれるというものである。
通常の固定閾値による2値化では、同じ場所で同じ角度で撮影したにもかかわらず、2値化結果が大きく変わってしまい、そのせいでラベリングが正常に行われず、数字判別
が失敗するといったことがあった。そこで大津の手法を取り入れたところ、この手法を用いれば、角度や場所が同じであるならば、2値化画像結果はほとんど変動しないということがわかった。
以下に、通常の2値化画像と、大津の手法を用いた2値化画像を示す。
fig2. (a)通常の2値化画像 (b)大津の手法を用いた2値化画像
3.ラベリング
2値化した画像から数字部を検出する。連結している白の部分をひとつのグループと考え、また、面積を閾値にし、ある大きさ以下のグループは除外
するといったフィルタリングも行っている。
fig.3に、ラベリングの概念を図的に表したものを示す。
fig.3 (a)ラベリング前の対象画像 (b)ラベリング後の対象画像
fig4に、ラベリングのアルゴリズムのフローチャートを示す。
なお、fig.4の ラスタスキャン とは、画像の左上から右下まで走査を行うことをいう。
fig.4 ラベリングのフローチャート
3.5. DB中央部検出
今回のDBの仕様を見たところ数字部と数字部の間に、一本の長い黒い線があるため、これを利用し、DBの中心を検知し、画像認識失敗時の補正動作のための情報に加えようと考えたものである。
ただし、これは競技会開始直前に思いつきで作ったものであり、正直に言うと 信頼性は高くない
しかし、あってもマイナスにはならないと思い、現在も低信頼性のままでいる。 いつかはもう少し信頼性の高いな仕様にしようとは思っている。
4. トリミング
ラベリングした画像から、数字部(白の部分)を全て内包する最小の矩形領域を計算し、画像を生成する。
以下の図のように、4つのパターンで走査し、求めたい領域の始点・終点の座標を見つける。
図のように走査し、はじめて出会う数字部(白)を各点のx座標、y座標に設定する。4つの走査で2点のx,y座標が得られる。
fig.5 トリミングのアルゴリズム
(a)始点 (b)終点
5. 数字の位置判断
今回の競技会では、T字路に数字が2つ並んでいて、右の数字には右方向にある仕掛け、左の数字には左方向にある仕掛けが示されているので、
ただ数字の判別をするだけではなく、その2つの数字の相対位置を知らなければならない。
2つの画像において、4のトリミングで求まった始点(もしくは終点)のx座標の比較をすることで2つの画像が左右のどちらに位置しているかを知ることができる。
6. 数字判別アルゴリズム
今回考えたアルゴリズムは、競技会で用いるであろうDBの数字が7セグメント数字であることを踏まえ、考案したものである。
条件を7セグメント数字と限定しているため、汎用性は少ないが、この数字に関してだけは高い信頼性を持っていると自負している。
以下に、そのアルゴリズムを示す。
T,fig5のように、トリミングした画像を、7つのパターンにより走査する。
この際、走査は画像の端まで行わず、ある程度(fig6のように数字部の一辺を越すくらいまで)で終わるものとする。
U,各パターンにおいて、数字部を発見(2値化画像における「白」を検知)したかしなかったかのデータを格納しておく。
V,2で得られたデータをもとに、そのデータのパターンと一致する数字を探し、決定する。
以下の図は、0〜9までの数字におけるパターンを図的に示したものである。
fig6. 数字決定アルゴリズム
fig7.
各数字におけるアルゴリズムの適用
・上記のアルゴリズムであると、数字の「1」の判別をするときに問題が起こる。
「1」をトリミングすると、左側の空白部分も切り取られてしまうので、上記アルゴリズムを用いると「8」と誤認してしまう。
これに対しての解決策・打開策は
上記アルゴリズムを行った後、トリミング画像の縦横比を計算し、それが一定以上の値なら1と認識する。 という仕様にした。
利点
7つの走査をするだけで、0〜9までの数字を全てのパターンに分類することができる。
標準プログラムでは不可能だった「2」と「5」の判別が可能になった。
ある一点を見るのではなく、ある一帯を見るので、信頼性が高い。
多少の歪み(斜めだったり傾いていたり)などは無視できる。
複雑な演算を用いていないので、処理が比較的高速
演算を行う前に、領域を絞ってサイズを小さくし、さらにアルゴリズムでは画素単位でアクセスしているので、処理が比較的高速
欠点
今回用いるような数字のみにしか適応できないので、汎用性が低い
このアルゴリズムを用いる前のラベリングがうまくいっていないとほぼ失敗する
4 動作試験
今回作成したプログラムが正常な動作と正しい結果が得られるかを試す
実験環境(本競技場)
PC
:
OS:CentOS
5.4
MG3のCPU
カメラ :
Logicool Qcam E3500 (標準機と同様)
対象物体との距離 約50〜60cm
ピントはおおよそで調整
対象とする数字は、「4」と「2」である。
実行結果
1.画像の取り込み
画像キャプチャは320×240の解像度で行っている
fig.8 キャプチャ画像
2,グレースケールによる2値化
fig.9 2値化画像
グレースケール化を行い、大津の手法を用いた2値化を行った。
3, 面積を閾値としたラベリング
fig.10 (a)ラベリング画像1 (b)ラベリング画像2
プログラム内で、ラベリング結果から、面積が2番目に大きいものと、3番目に大きいものを抽出している。
4,トリミング
fig.11
(a)トリム画像1 (b)トリム画像2
fig.11のように、fig.9(a),(b)の数字部分を内包する最小画像が得られた。
5. 数字の位置判断
各数字部のx座標を取得する。(トリミングの時点で既に格納されている)
2つの画像のx座標の比較をし、右にある画像なのか左にある画像なのかを判断する。
6. 数字判別アルゴリズムの実行
競技会場ではterminalへの出力文字は見れなかったが、結論としてゴールできたので、正しく判別してくれたことが分かる。
以上により、この数字認識プログラムは正常に動作し、正しい値を得られることが分かった。
5 まとめ
画像処理は現在のMG3のCPUスペックでもかなり負担の大きい処理である。そのため、プログラムの高速化とリソースの節約は必要不可欠なことである。
OpenCV標準のキャプチャ関数を用いると、高い確率で処理落ち・処理ミスをしてしまうようである。現段階ではuvccaptureコマンドをsystem関数で呼び出すのが無難である。
また、OpenCVでのプログラミングではメモリの確保をしたらかならず開放しないとよろしくない。メモリリークを起こしてしまったりすると最悪PCが落ちてしまうかもしれない。
画像処理プログラミングの高速化の具体的手法としては
余分な領域はあらかじめ切り取って、本当に見たい部分だけを処理する
double型はなるべく使わず、float型で表現を試みる。(今回は、double型をあえて宣言するような細かい処理は必要ないため) マクロで定義されたPIなども見直してみるといいかもしれない。
可能であるならばビット演算を用いる(現在のコンパイラは優秀なのであまり意味はないかもしれないが)
関数のinline展開を用いる。もともとinline展開はC++特有のものであったが、C99ではinline展開をサポートしている。
cvGet2D
cvSet2D など画像にアクセスする関数は処理が遅いので、対象画像の画素が格納されているメモリ領域に直接アクセスしたほうが断然早いらしい(MIRS0902マネージャーもいってたよ)
もちろんその提案を取り入れて実装しましたとさ。
ただしどれも無闇に行うと精度が極端に落ちることになるかもしれない。どれを行うにしても十分な注意が必要である。
プログラムのソースコードを参考までに載せておく
linux上で作成したファイルをwindowsでアーカイブ化しただけなので、「メモ帳」で開くと改行コードが合わずに、非常に見づらくなると思われる。
その他多くのテキストエディタは改行コードの自動判別をしてくれるはずなので、もし見づらいようならその他のテキストエディタでどうぞ。
linuxでみれば間違いないはずです・・・
linuxのターミナルで解凍したい場合はunzipコマンドでも使ってくださいな.。
関連文書 |