ニューラルネットワークを使ったWindowsアプリを作成

#OpenCVを使って文字判読

日本の一般普通車・軽自動車のナンバープレートの一番大きい数字を判読するアプリを作成してみた。
ナンバープレートの数字を判読するアプリを開発するため、今回OpenCV3.1を使用した。現在はOpencv3.2がリリースされている。

アプリケーションのベースとして.NET Frameworkのフォームを利用し、OpenCVを含めてすべてC++でコードを完成させた。

全てのソースコードを公開できないため、一部を紹介し、プログラムの流れを見てみる。（OpenCVをある程度理解できる人向けに書いてあるため、OpenCVをまったく触ったことがない人には？？な部分も多々あると思いますのでご容赦ください。）

#ニューロンを作成する

人間の脳に似せたニューロンを使ったネットワークは、入力層と隠れ層、出力層に分かれる。入力層ニューロンには0または1で印加される。したがって、入力層となるべきデーターは0、1のバイナリデーターでなければならない。サンプルイメージを0と1のデーターに変換する作業から始める。

サンプルのイメージとなるべき数字のタイルをナンバープレートから採取する。今回はWindowsのペイントソフトを使って数字を切り取りして作成したが、OpenCvを使って、自動で切り取りもできるので利用したいところ。その場合、findcontoursというファンクションを使うが、ここでは説明は省略する。

![8.png](https://qiita-image-store.s3.amazonaws.com/0/139807/83dfed5e-f317-6d4a-c48d-fcee68744812.png)![9.png](https://qiita-image-store.s3.amazonaws.com/0/139807/107e00cf-3789-f185-564b-92b4ae3fd14b.png)![1.png](https://qiita-image-store.s3.amazonaws.com/0/139807/e13d701e-f508-c70f-3508-a1d2fb830b61.png)
![9.png](https://qiita-image-store.s3.amazonaws.com/0/139807/e9fef5d8-c8d7-ae20-fa77-5e408912f020.png)![10.png](https://qiita-image-store.s3.amazonaws.com/0/139807/23a21e94-06bc-7595-a2fb-c43ae0023f6c.png)

こんな感じでサイズは適当になった。これを0～9のフォルダーを作成し、それぞれの数字のフォルダーに投入する。今回、各数字につき30枚を作成した。合計300枚のサイズになる。

![tilefolder.png](https://qiita-image-store.s3.amazonaws.com/0/139807/9ddc3894-c36d-ef3b-65f0-c1a180728898.png)

これらのタイルをOpenCVで白黒イメージに変換する。さらに各ピクセルの数値を0と1に変換し、datファイルにいったん格納する。datファイル開くとこんな感じになる。![binary.png](https://qiita-image-store.s3.amazonaws.com/0/139807/cea8e9ba-0de3-264b-82f3-9c60f7924903.png)

ニューロンを訓練するにはこのデーターと教師データーが必要になる。
教師データーは、出力層が10個なので横（cols）が10、縦（rows）が300（サンプル数300なので）のこれまた0と1のデーターの集合だ。これをOpencvのMatに変換する。

作成するネットワークのプロパティは総てここ（下図）で変更するようにした。ほかのソースコードをいじる必要はないので分かりやすい。

```c++:ネットワークの設定関数

void neuronSetUp(){

	numberOfSmples = 300;//訓練用サンプル数

	neuron->create();
	const int inputNeuron = 200;//入力層のニューロン数、10*20の画像。
	const int hiddenNeuron = 25;//隠れ層のニューロンは10～25がいい結果となった。
	const int outputNeuron = 10;//出力層は数字10個の意味。

	vector<int> layerSize = { inputNeuron, hiddenNeuron, outputNeuron };

	neuron->setTrainMethod(ANN_MLP::BACKPROP,0.01,0.01);//訓練メソッドはバックプロパゲーションを使用。
	neuron->setLayerSizes(layerSize);
	neuron->setActivationFunction(ANN_MLP::SIGMOID_SYM, 0.7, 1);//シグモイド関数の設定。
	neuron->setTermCriteria(TermCriteria(TermCriteria::COUNT | TermCriteria::EPS, 10000, 0.0001));
}

```

trainメソッドでニューラルネットワークを作成する。

```c++:
neuron->train(src, ROW_SAMPLE, teacher);//srcはMatでピクセル数がCols、サンプル数がrowsになる。この場合、200*300のMatイメージ。Row_SAMPLEは訓練方向。
```

出来たらXMLファイルに変換し、プログラムフォルダーに保存する。
プログラムで使用する際は以下のようなラインで保存したネットワークをロードする。

```c++:
neuron = ANN_MLP::load<ANN_MLP>("./dat/neuron.xml", "nueron");
```

これで数字判読の準備は完了した。


#ナンバー数字判読プログラムの実行

今回はナンバープレートの大きな数字（1桁～4桁）に限って判読するプログラムを作成した。
まずフォームにボタン2つとピクチャーボックスを3つ作成。判読した数値を表示するデーターグリッドビューも設置した。

スタートボタンを押すとカメラ画像のリーディングが始まり、ナンバープレートの抽出、数字の切り抜き、数値判読がループするという流れでプログラムが進む。ストップボタンを押すと停止し、初期画面になるという簡単なUIだ。（ちなみにナンバープレートの抽出もOpenCVのライブラリーにあるHaar-Cascadeという機械学習のメソッドを使用しています。）

抽出された数値はデーターとして判読日時と一緒に記録され、データーグリッドビューに次々と表示されていくというオマケもつけてみた。

仮のナンバープレートの写真を印刷して、壁に貼り、スタートボタンを押してプログラムを実行してみると。。。

![test.png](https://qiita-image-store.s3.amazonaws.com/0/139807/c0ad7e44-a775-d5a1-ee7e-227daac0a44f.png)

#ニューラルネットワークの高い判読能力

ナンバープレートの数字は決まったフォントを使用しているため、ニューロンをトレーニングさせるのは手書き文字よりも少なくて済むのだが、それでも1文字たった30枚のサンプルで高い判読能力を発揮した。画像のテストではすべて正解となっている。もちろん100％の正解率ではないのも事実だが、他のナンバープレートでテストしても、ニューロンはほぼすべて正解のレスポンスを返してくる。手書き文字では数万枚というサンプルを使用するそうだが、今回は総数300枚しか使っていない。

驚くのはそれだけでなく、訓練スピードがとてつもなく高速だということ。300枚を訓練するのに10秒もかかっていない。これはマシンの影響も受けると思うが、実験ではCore i5の市販ノートパソコンを使用しているので、飛びぬけてハイスペックとはいえない。

作成したUIアプリは、中途半端なので継続して作成することにして今回の記事は終わりです。

アプリを作った感想：ニューラルネットワークを利用した画像認識能力は正直すごいの一言です。機械学習の将来性を実感できる良い体験になりました:cow: