ラズパイによる顔認識器（CNN）

#はじめに
前回、[CNNを利用した画像の識別器](https://qiita.com/shuns0314/items/38ee72708238f8b6367b)を作成した。
ところで、私の所属している会社は機械関係ということもあって、ソフトだけでは納得してもらえなかった。
~~というか、ソフトは外注すりゃいいじゃんとか言われる。それじゃ何もできるようにならないと思うんだけどなぁ。~~
ということもあって、ラズパイを利用して簡単な顔認識器を作ってみた。

#顔認識器について
顔認識ということで、写真を撮影して、撮った写真に顔が写っていれば、ランプを点灯。写っていなければ、ランプを点滅させるものを目標にした。
####目標の動作

1. スタンバイ（最初はインポートとか多いため、あえて表示する。）
2. スイッチを押す。→写真を取る。
3. 撮った写真をフォルダに入れる。
4. 撮った写真に名前（番号）をつける。
5. 撮った写真を学習済みモデルに入れる。
6. 人が写っていたら点灯、写っていなければ点滅させる。
7. 写真の番号のカウントを増やす。
8. 復帰（スタンバイ状態に戻す）

#用意したもの
ラズパイの設定とか書ければよかったんだけど、相当難儀した上、結局よくわからないまま解決したので、今のバージョンを書いとくにとどめます。本当はラズパイもpython3にしたかった。

- raspberry Pi3B(python 2.7,tensorflow 1.1.0, keras 2.2.0,openCV ))
- raspberry PiカメラモジュールV2(http://akizukidenshi.com/catalog/g/gM-10518/)
- LED,スイッチ,抵抗etc
- しょぼいPC(python 3.6.5 ,tensorflow 1.8.0 ,Keras2.1.6 ,opencv-python3.4.1.15 etc)

etc etc

###作ったもの
ラズパイと各部品とのつなぎはこんな感じ。RaspberryPiで学ぶ電子工作（ブルーバックス）を読みながら作った（コードも）。

写真
![unnamed.jpg](https://qiita-image-store.s3.amazonaws.com/0/257926/28a419b5-4b80-c6f8-0604-6f4fea28be5f.jpeg)

#作成したコード
CNNのコード自体は前回書いた内容と多分一緒です。





###モデルの記録
モデルの記録は、学習後、以下で実装可能です（前回のコードの最後にいれる）。実行することで、現在のフォルダに同名のファイルが作成されます。

```python:PC（python3）
model.save("model_mnist_cnn.h5")
```

###モデルの読み込み
ラズパイでのモデルの読み込みは、上記で作成したファイルをラズパイ上の現在のディレクトリに移動させ、以下のコードを実行することで可能です。

```python:raspberryPi(python2)
import keras
from keras.models import load_model
model = keras.models.load_model("model_mnist_cnn.h5")
```

###コード全文
読みにくいと思う。どうしたら、いい感じになるのかしら。

```python:raspberryPi(python2)
#各パッケージをインポート
import time
import picamera
import RPi.GPIO as GPIO
import keras
import numpy as np
from keras.models import load_model
import glob
import cv2
import os
import datetime

#学習済みモデルをロード
model = keras.models.load_model("model_mnist_cnn.h5")

#GPIOポートのセットアップ
GPIO.setmode(GPIO.BCM)
GPIO.setup(25,GPIO.OUT)
GPIO.setup(24,GPIO.IN)

#写真を保存するフォルダの作成
path = "/home/pi/AI/"+str(datetime.datetime.now())
os.mkdir(path)

#カメラの写真の名前に用いる変数の初期化
camera_count=0

#importが多く、いつ始まったのかわからないので、準備ができたことを示す表示
print("stand by")

#実行
try:
    while True:
        #スイッチが押されたら、写真をとる。
        #起動を安定化するために、0.2s以上スイッチが押されられればとした。
        if GPIO.input(24) == GPIO.HIGH:#GPIO24:switchON
            start_time = time.time()
            while True:
                count_time=time.time()
                t = count_time - start_time
                if t > 0.2
                    #カメラの起動、写真撮影
                    with picamera.PiCamera() as camera:
                        camera.resolution = (1024,768)
                        camera.capture(path+"/film"+str(camera_count)+".jpg")
                        files_test = glob.glob(path+"/film"+str(camera_count)+'.jpg')
                        
                    #学習済みモデル（CNN）に入れる形に合わせる。
                    count_test=0
                    height,width=32,32
                    size = (height,width)
                    img_test = np.empty((len(files_test),height,width,1))
                    for file in files_test:
                        img3 = cv2.imread(file, cv2.IMREAD_GRAYSCALE)
                        img_resize3 = cv2.resize(img3,size)
                        img_reshape3 = img_resize3.reshape(height,width,1)
                        img_test[count_test,:,:] = img_reshape3
                    img_test_255 = img_test/255.
                    TEST = model.predict(img_test_255)
                    Len = TEST.shape
                    test_t = np.zeros(Len)
                    count = 0

                    #学習済みの0.5より小さければ1(正解)、それ以外なら0を出力
                    for t in TEST:
                        if t > 0.5:
                            test_t[count] = 1
                        else:
                             test_t[count] = 0
                    print(test_t)
                    camera_count=camera_count+1
                        
                    #顔を認識するとランプの点灯、認識しないと点滅
                    if test_t[0][0]==1:#LET(Red)_light
                        GPIO.output(25,GPIO.HIGH)
                        time.sleep(3)
                        GPIO.output(25,GPIO.LOW)
                    else:
                        for i in range(3):
                            GPIO.output(25,GPIO.HIGH)
                            time.sleep(0.25)
                            GPIO.output(25,GPIO.LOW)
                            time.sleep(0.25)
                    break
                

    time.sleep(0.1)            

#Ctrl+Cで終わる合図
except KeyboardInterrupt:
    pass

#GPIOの初期化
GPIO.cleanup()

```

#評価
正解率は良くないけれど、人の写真を取ると一応点灯するようだし、風景とかを取ると点滅するようだった。ただ、何もない壁とか机とかを取ると、正解率は低い。人の距離が近すぎると正解率が低い気がする。遠くても低い。
けどまぁ、あとは学習させる画像サイズを大きくしたり、実際の使用状態に合わせたり、学習させる写真の枚数を増やしたりすれば、正解率は良くなるんじゃないかなと思うし、LEDを光らせたけど、モータに繋げば、物の検査装置とかも作れそうだから、ほぼ枠組みとしてやりたかったことはできたと思う。

#感想
###苦労した点
- とにかくラズパイの環境構築が大変だった。python3を使っての環境構築は結局できなかった。python2が入っているので、それが邪魔しているのかなとも思うけど、違かった時に消したら相当めんどくさいしなぁ。ということで諦めた。
- 苦労というわけではないけど、思いの外、正解率が低い。試しに、色々学習させなおしたりとかしてみたけど、あまり変わらなかった。

###面白かった点

- 大したものではないけれど、とりあえずAIをハードに落とし込めたってのが一番うれしい。
- 電子工作は楽しい。光ると嬉しい。

#今後の予定と目標
- 判別性能の強化。学習させている画像サイズが小さい（32×32pix)ため、より大きい画像を学習させる。現在使用しているPCだと画像を大きくするとフリーズするため、google Colaboratoryを使い、GPUを利用することでの解決を図る。
- paizaでAランク（python）
- 言語Goの習得。流行りみたいだし。progate→A Tour of Go
- courseraの機械学習コースを終わらせる（現在9週目）。
- Kaggleのtitanicで80％以上。

#終わりに
4月から始めて、プログラミングもまともにできない状態（paizaのDランクもとけなかった、今はBランク）からここまで来たのは結構頑張ったと思う。courseraが終わったら、progateで他の色々な言語にも触れていきたい。

ここまで、読んでくれてありがとうございました。