メガネな人を見つめてくれるWebカメラ

メガネな人を見つめてくれるWebカメラを作った。
メガネが無い人は見つめてくれません。
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">メガネな人を見つめてくれるWebカメラ作った。<br>動画の人物は同僚のK島氏。許可をとってご登場頂いております。<a href="https://t.co/nf5TNJpcjI">https://t.co/nf5TNJpcjI</a></p>&mdash; foka (@foka22ok) <a href="https://twitter.com/foka22ok/status/893101764743909378">2017年8月3日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

最近ぼんやりとやってみたいと思ってた中から、以下の4つをまとめたらこれができました。

- 機械学習の学習データを自分で集めて精度を検討
- Kerasを試す
- 機械学習をリアルタイム処理で使う
- モーター制御したい

ざっくりとはこんな感じの仕組みです。

![outline.png](https://qiita-image-store.s3.amazonaws.com/0/16457/b0675276-2ab7-9f4f-52b2-246986569120.png)




# 機械学習の学習データを自分で集めて精度を検討
お仕事で画像分類系の機械学習をやらせてもらう機会が何度かありましたが、学習データを用意してもらってその中でどこまでできるかということをやってました。

元々は機械学習出身のエンジニアでは無いこともあり、自分で素材を集めるところからはやってなかったので、そこからやっておきたいと思ったのが発端です。

## 学習データ

画像を顔認識して目の辺りだけを抽出して学習データとしました。
具体的にはこんな画像を使ってます。

例）メガネ有
![megane.jpg](https://qiita-image-store.s3.amazonaws.com/0/16457/c21b34d4-740f-7c6a-8c45-b2e2431e53d0.jpeg)

例）メガネ無
![nomegane.jpg](https://qiita-image-store.s3.amazonaws.com/0/16457/b56cb9e8-9dbb-51f0-db62-8db10efe80b9.jpeg)


## 学習データの集め方
手持ちの写真データ、画像検索、フリー素材から、メガネ顔とメガネ無い顔を手動で収集。
　↓
めんどくさくなったので、スクレイピングを検討する。
　↓
スクレイピング作ろうとするのもめんどくさくなる。
　↓
Webカメラから顔認識させて直接学習データ用の顔画像を収集。

## 精度
### メガネ顔　→　メガネ有り判定
それなりに良い精度が出ました。
学習データに含まない人の顔でも安定した判定精度。

### メガネ無し顔　→　メガネ無し判定
少々誤判定あり。
学習データに含む人の顔はまずまずの判定精度。
学習データに含まない人の顔は安定しない判定精度でした。

大量の学習データは集めてないわりにはそこそこの精度が出たと思います。
でも汎用性を高めるならやはり学習データ数は重要です。


# Kerasを試す
TensorFlowを使っていたけど、Kerasが使いやすいと聞いたので乗り換え。

## Kerasとは
Keras: Pythonの深層学習ライブラリ
https://keras.io/ja/

機械学習ライブラリであるTensorFlowやCNTK、Theanoをラップしてくれるライブラリです。
それぞれを直接使うよりもコードの記述量がグッと減ります。

TensorFlowとの統合も進んでいるという話もあります。
http://qiita.com/TomokIshii/items/178938b6db1edc16b94e

## 学習モデル
画像系の機械学習でよく使う、畳み込みニューラルネットワークをKerasで試してみました。
畳み込み → プーリング → 畳み込み → プーリング → 全結合 → 全結合
のシンプルなモデルだとこんな感じです

```python
#x_trainには学習データが入っている

model=Sequential()

model.add(Conv2D(32,(5,5),input_shape=(x_train.shape[1:]))) #畳み込み　フィルターの数,フィルターカーネルサイズw,h,inputデータのshape
model.add(Activation('relu')) #活性化
model.add(MaxPool2D(pool_size=(2, 2))) #Pooling

model.add(Conv2D(64,(5,5))) #畳み込み
model.add(Activation('relu')) #活性化
model.add(MaxPool2D(pool_size=(2, 2))) #Pooling

model.add(Flatten()) #入力を平滑化する．
model.add(Dense(1024)) #全結合
model.add(Activation('relu')) #活性化

model.add(Dropout(0.5)) #ドロップアウト
model.add(Dense(classNum)) #全結合　ここで最終的にclassNum分類にクラス分け
model.add(Activation('softmax'))#出力を確率に変換

#学習の前に、学習のためのモデルを設定をする
# optimizer:最適化アルゴリズム
# loss:損失関数
# metrics:評価関数のリスト
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
```

ちなみにTensorFlowだと、↓の「def deepnn(x)」と130〜132行目のところがほぼ同じ内容に該当します。
https://github.com/tensorflow/tensorflow/blob/r1.2/tensorflow/examples/tutorials/mnist/mnist_deep.py

Kerasの方が学習モデルの流れが追いやすく、理解がしやすいです。

## 学習

Kerasには学習データを水増しする機能があり、これを使うかどうかで学習のコードがちょっと違います。

学習データ水増ししない場合

```python
#学習（データ水増ししない場合）
#batch_size：学習時のバッチ数
#epochs：学習のステップ数
model.fit(x_train,y_train,batch_size=batch_size,epochs=epochs)
```

学習データ水増しする場合

```python
#学習（データ水増しする場合。datagenについては後述参照）
#batch_size：学習時のバッチ数
#epochs：学習のステップ数
#datagen：データ水増し
#x_train.shape[0]のところは学習ステップ毎にgeneratorから使用する総ステップ数が入る。通常は元々のサンプル数と同じになる
model.fit_generator(
	datagen.flow(x_train,y_train,batch_size=batch_size),
	x_train.shape[0],
	epochs=epochs)

```

## 学習データの水増し
学習データの水増しは学習結果の汎用性を高めるのに重要です。
KerasにはImageDataGeneratorという画像データ前処理がまとまっている機能があり、とても便利なのでこれを活用。
以前はOpenCVでデータ水増しを自作してたけど、この辺もKerasの便利機能に乗っかりました。

```python
#学習データ水増し内容
datagen = ImageDataGenerator(
	rotation_range=30,#画像のランダム回転範囲
	width_shift_range=0.1,#画像のランダム水平移動割合範囲
	height_shift_range=0.1,#画像のランダム垂直移動割合範囲
	zoom_range=0.2,#画像のランダム拡縮割合範囲
	horizontal_flip=True)#画像の水平反転の有無

#ImageDataGeneratorを使う場合、学習は model.fit ではなく model.fit_generator を使います。
```

今回使ったのはこれだけですが、他にも色々あります。
・Kerasの画像データの前処理
https://keras.io/ja/preprocessing/image/

具体的な水増しの中身がどうなってるかは↓の記事が参考になります。
・Kerasによるデータ拡張
http://aidiary.hatenablog.com/entry/20161212/1481549365

ちなみに、TensorFlowにも似たような機能はあります。
http://qiita.com/Hironsan/items/e20d0c01c95cb2e08b94
Kerasだと学習時にまとめて水増ししてくれますが、TensorFlowの場合だと、学習前に一つ一つ水増しデータを作ってる感じのようです。

## Kerasを使ってみて
とても良いです。
機械学習のスペシャリストではない自分のようなエンジニアにもわかりやすく、便利機能も多くておすすめです。
しかも、複雑な学習モデルを作る手段もあるようで、エキスパートでも扱える応用力もあります。

欲を言えば、TensorFlowとかで作られている学習モデルをKeras用に変換してくれるKerasコンバーターみたいなのがあると嬉しいけどあるのかな...

# 機械学習をリアルタイム処理で使う
今までは、Webサーバー立ててそこに送られてきた画像を処理して結果を返す、ということをしていたので、今回はリアルタイム処理に挑戦。

といっても特別なことは無く、Webカメラの映像を順次処理しているだけです。
通信を減らして処理をなるべくまとめたかったので、OpenCV・dlib・KerasをPythonからまとめて制御しました。

主な役割は以下の通り。
OpenCV：Webカメラの映像取得・映像データのリサイズ、反転、顔位置に四角を描画
dlib：顔検出
Keras：メガネ判定

# モーター制御したい
モーター制御をやってみたいと前から思っていたので今回挑戦。

![device_s.jpg](https://qiita-image-store.s3.amazonaws.com/0/16457/7fa93fdf-dfe2-b04f-f63c-21a5e8ae0c28.jpeg)


Webカメラを↓にくっつけて、arduinoからコントロールしてます。
http://amzn.to/2gS18aQ

サーボモーターとマウントを別々に購入するともっとお安いですが、サクッと組み立てできないらしく、組み立て済みを購入しました。
（サーボモーターのプロペラみたいなのをガリガリ削らないと組み立てできないらしい）

Webカメラはこれ使ってます。
http://www.ebay.com/itm/Mini-USB-Webcam-Works-with-Windows-Linux-Mac-/152632476427

ただ、使ってみたら画角がかなり狭く、扱いにくかったのでスマホ用広角レンズをテープでくっつけて無理やり広角にしました。


## カメラコントロール
Webカメラに映る顔の位置と映像の中心を使って、顔の位置が映像の中心に来るように三角関数でモーター回転角度を算出する方法でコントロールしてます。
ちなみに、顔がメガネじゃなかったら、カメラ映像に中心には入らないようにしてメガネスキーな感じを出してみました。

サーボモータの回転角度計算まではPythonで行っていて、回転情報をシリアル通信でarduinoに送る仕組みです。
ついでに、LEDが余ってたので、メガネだったら緑LED、メガネじゃなかったら赤LEDを光らせました。


# まとめ
そこそこうまく動いた。
Kerasおすすめ。
モーター制御楽しい。