Raspberry Pi のカメラで顔を認識しファイルに保存する

## 概要
Raspberry Pi でSIM経由で通信する顔認証システムを作成し、認証結果を kintone で管理するシステムを構築します。
今回は、Raspberry Pi のカメラで顔を認識し、顔が含まれる画像をファイルに保管するまでを行います。
![IMG_1985.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75892/0f51de5b-e166-6289-1b87-e176c58b9b76.jpeg)

### 顔認証システム構築シリーズ
Raspberry Pi で顔を認識しファイルに保存する
https://qiita.com/yukataoka/items/7510217f4b6efdefff06

Amazon Rekognition で顔が同じか比較判定し、感情などの情報を取得する
https://qiita.com/yukataoka/items/3fde5d6b22255ff1aed9

Raspberry Pi でCO2センサを使う
https://qiita.com/yukataoka/items/a3b4065e8210b8f372ff

Raspberry Pi で温湿度気圧センサを使う
https://qiita.com/yukataoka/items/8f9046587c978e91f689

## 環境
![IMG_2001.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75892/628c5c77-7d3b-fac3-5b58-d24d2e2ff26d.jpeg)
- Raspberry Pi 3 Model B
- OS Raspbian Stretch Lite ( Release date:2018-11-13, Kernel version:4.14 )
- Raspberry Pi Camera Module V2 (element14)
- [カメラ穴付きケース Raspberry Pi 3 B & Raspberry Pi 2 B 専用キット](https://www.amazon.co.jp/gp/product/B01FPCNHLG/ "カメラ穴付きケース")
- SanDisk Ultra micro SD カード 16GB
- [LTE 対応データ通信端末 Huawei MS2372h-607](https://soracom.jp/products/module/ms2372h_607/ "LTE 対応データ通信端末")
- [日本向け Air SIM (plan-D)](https://soracom.jp/products/sim/plan-d/ "SIM")

## Raspberry Pi OS の設定
- SSHの有効化設定

OSのインストールからSSHまでの設定は、こちらでは説明を省略します。
不明な方は以下の情報を参考に作業をすると良いでしょう。
[Raspberry Pi セットアップ(書き込み～SSH設定まで)](https://qiita.com/Halhira/items/1da2ae543217be26988a "書き込み～SSH設定まで")
[Raspbianをヘッドレスインストールする (SSHの有効化)](https://www.agilegroup.co.jp/technote/raspbian-jessie-headless-install.html "SSHの有効化")
[Raspberry PiのSSH有効化方法](https://qiita.com/hikobotch/items/a33fc77114a4963cc68b "SSH有効化方法")

- 固定IPアドレス設定

デフォルトの設定はDHCPですが、これはIPアドレスが固定されないので面倒。
固定IPアドレスを設定します。

- Wi-Fiとbluetoothの無効化設定

Wi-Fi と bluetooth は利用しないので、無効化します。

```
$ sudo nano /boot/config.txt
(最下部に以下を追加)
#turn wifi and bluetooth off
dtoverlay=pi3-disable-wifi
dtoverlay=pi3-disable-bt
```

- 最新の状態に更新

以下のコマンドを実行して、最新に更新します。

```
$ sudo apt-get update
$ sudo apt-get upgrade
```

- Raspberry Pi Camera の有効化

以下の情報を参考に、取り付けと設定を行うと良いでしょう。
[専用カメラモジュールを使う](https://raspi-wannabe.com/digital-camera/ "専用カメラモジュールを使う")

- FTPサーバの設定

お好みで設定ください。（私の場合作業は全てTeraTerm、FFFTPで行うので。）

- SORACOMのSIM通信の設定

[各種デバイスで SORACOM Air を使用する Raspberry Pi ＋ USB モデム](https://dev.soracom.io/jp/start/device_setting/#raspi_usb "SIM通信の設定")を参考に設定します。
Huawei MS2372h-607 も記述通りで設定できます。

## 画像処理環境の構築
画像処理には Python3 と、OpenCV を利用します。
Python3 と、OpenCV でできることについては「[Pythonで画像処理をするならOpenCVがオススメ！](https://www.sejuku.net/blog/54272 "Pythonで画像処理をするならOpenCV")」を参考に作業をすると良いでしょう。
Python2.7 でなく Python3 を使うのは、後の顔認証部分の実装で利用するライブラリィの1つが Python3 にしか対応していないためです。

- ビデオデバイスとして認識させる
- 
Raspberry Pi Camera Module V2 は、そのままでは OS のビデオデバイス（ /dev/video0 ）として認識しません。
OpenCVで処理させるためには、以下の手順を用いてビデオデバイスとして認識させておくと便利です。（今回は利用しませんが。）

```
＜手動で都度行う＞
$ sudo modprobe bcm2835-v4l2

$ ls /dev/video*
/dev/video0

＜起動時の設定に追加＞
$ sudo nano /etc/modules
(最下部に以下を追加)
bcm2835-v4l2

$ sudo reboot
```

- Python3 と OpenCV のインストール

Pyton3 の OpenCV インストールには、依存パッケージが多く注意が必要です。
以下の情報を参考に作業をすると良いでしょう。
[Raspbian Liteでビルド済みのOpenCVパッケージを使うための依存パッケージいろいろ](https://qiita.com/atuyosi/items/5f73baa08c3408f248e8 "OpenCVパッケージ")
Python3 のライブラリィ picamera もインストールしておきます。
picamera については [PYTHON PICAMRA](http://igarashi-systems.com/sample/translation/raspberry-pi/usage/python-camera.html "PYTHON PICAMRA") や [picamera](https://picamera.readthedocs.io/en/release-1.10/index.html "picamera") の情報を参考にすると良いでしょう。

```
$ sudo apt-get install libopencv-dev libatlas-base-dev libcblas3 libjasper1 libqt4-test libgstreamer1.0-0 libqtgui4
$ sudo apt-get install python3-pip
$ sudo pip3 install opencv-python picamera
```

__2020/01/02 追記：__
__最新の Pyton3 で opencv-python を利用する場合は上記では設定できません。__
__@MX03 さんの以下の情報を参考に設定してください。__

ラズパイでpython3にopencvを入れたらエラーが出た【対処法】
https://qiita.com/XM03/items/48463fd910470b226f22

- カスケードファイルの入手

OpenCVには顔を検出するカスケード型分類器があらかじめ用意されていますので、今回はこちらを利用します。
OpenCVがどのように顔検出しているかについては「[【入門者向け解説】openCV顔検出の仕組と実践(detectMultiScale)](https://qiita.com/FukuharaYohei/items/ec6dce7cc5ea21a51a82 "openCV顔検出の仕組と実践") 」や、「[OpenCVを使った顔認識（Haar-like特徴分類器）](https://qiita.com/hitomatagi/items/04b1b26c1bc2e8081427 "OpenCVを使った顔認識")」
などの情報を参考にすると良いでしょう。

今回利用するカスケードファイルは、顔（正面）を認識する [haarcascade_frontalface_default.xml](https://github.com/opencv/opencv/blob/master/data/haarcascades/haarcascade_frontalface_default.xml) を用います。
github の [opencv/opencv](https://github.com/opencv/opencv) より入手できます。

## Python3 のコード
最初に Raspberry Pi Camera をVGAの画像解像度で、画像を180度回転して表示します。
途中に2秒間sleepさせて、Raspberry Pi Camera の初期化を待ちます。
RGB画像をキャプチャーし、グレースケールに変換し、カスケードファイルを利用して顔の位置を見つけます。
顔が検知できたら、その画像の顔部分を赤い線で四角囲みし画像ファイルとして保管します。（顔部分の加増も保管します。）

``` FasePicture.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import picamera
import picamera.array
import time
import cv2

CascadeFile = "./haarcascades/haarcascade_frontalface_default.xml"

with picamera.PiCamera() as camera:

    # カメラのセッティング
    camera.resolution = (640, 480)
    camera.rotation = 180
    camera.start_preview()
    time.sleep(2)
        
    with picamera.array.PiRGBArray(camera) as stream:
        
        count = 0
        while True:
            
            # RGB映像データを格納
            camera.capture(stream, 'bgr', use_video_port=True)
            
            # グレースケールに変換
            gray = cv2.cvtColor(stream.array, cv2.COLOR_BGR2GRAY)
            
            # カスケードファイルを利用して顔の位置を見つける
            cascade = cv2.CascadeClassifier(CascadeFile)
            face_list = cascade.detectMultiScale(gray, minSize=(100, 100))
            
            timeString = time.strftime('%Y%m%d_%H%M%S')
            
            if len(face_list) > 0:
                for (x, y, w, h) in face_list:
                    print(timeString,"face_position:",count,x, y, w, h)
                    
                    # 顔の周りに赤い線を引く
                    color = (0, 0, 255)
                    pen_w = 5
                    cv2.rectangle(stream.array, (x, y), (x+w, y+h), color, thickness = pen_w)
                    
                    path = "./" + timeString + "_" + str(count) + ".jpg"
                    cv2.imwrite(path, stream.array)
                    path = "./" + timeString + "_" + str(count) + "_face.jpg"
                    dst = stream.array[y:y+h, x:x+w]
                    cv2.imwrite(path, dst)
            else:
                print(timeString,"face_position:",count,"none")
            
            # streamをリセット
            stream.seek(0)
            stream.truncate()
            
            count+=1
            
    camera.stop_preview()

```

## 結果
実施した結果、秒4コマ程度の画像を処理し、夜間の室内照明で正面の顔を無事認知しました。
多少のブレなどがあっても問題なく検知できています。
Raspberry Pi Camera で 1m ～ 2m の範囲で正面に設置した場合は、十分実用になりそうです。

```
$ python3 FasePicture.py
20190430_135416 face_position: 0 none
20190430_135416 face_position: 1 none
20190430_135416 face_position: 2 none
20190430_135416 face_position: 3 none
20190430_135417 face_position: 4 none
20190430_135417 face_position: 5 none
20190430_135417 face_position: 6 none
20190430_135417 face_position: 7 none
20190430_135418 face_position: 8 none
20190430_135418 face_position: 9 none
20190430_135418 face_position: 10 296 82 168 168
20190430_135419 face_position: 11 358 86 169 169
20190430_135419 face_position: 12 336 76 177 177
20190430_135419 face_position: 13 312 84 183 183
20190430_135419 face_position: 14 276 92 177 177
20190430_135420 face_position: 15 241 91 174 174
20190430_135420 face_position: 16 227 80 182 182
20190430_135420 face_position: 17 220 87 168 168
20190430_135420 face_position: 18 212 87 169 169
20190430_135421 face_position: 19 none
20190430_135421 face_position: 20 none
20190430_135421 face_position: 21 none
20190430_135421 face_position: 22 none
20190430_135422 face_position: 23 none
20190430_135422 face_position: 24 none
20190430_135422 face_position: 25 none
20190430_135422 face_position: 26 none
20190430_135423 face_position: 27 none
20190430_135423 face_position: 28 none
20190430_135423 face_position: 29 none
20190430_135423 face_position: 30 184 64 162 162
20190430_135424 face_position: 31 290 64 170 170
20190430_135424 face_position: 32 297 68 169 169
20190430_135424 face_position: 33 286 68 168 168
20190430_135424 face_position: 34 285 66 169 169
20190430_135425 face_position: 35 276 71 173 173
20190430_135425 face_position: 36 255 69 175 175
20190430_135425 face_position: 37 186 85 149 149
20190430_135425 face_position: 38 48 128 130 130
20190430_135426 face_position: 39 none
20190430_135426 face_position: 40 none
20190430_135426 face_position: 41 none
20190430_135426 face_position: 42 none
20190430_135427 face_position: 43 none
20190430_135427 face_position: 44 none
20190430_135427 face_position: 45 none
20190430_135427 face_position: 46 none
^CTraceback (most recent call last):
  File "FasePicture.py", line 30, in <module>
KeyboardInterrupt
```

- 普通に正面を向いて顔検出した結果
![20190429_123419.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75892/e5833fe5-f2a7-e838-e7e7-54b190dcce56.jpeg)
![20190429_123419-s.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75892/3e4a7109-4890-e880-05d3-dde187072ecb.jpeg)

- わざとカメラで手振れを起こして顔検出した結果
![20190429_123408.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75892/ffcedd33-5eba-6983-cfa3-cff8898cfe07.jpeg)
![20190429_123408-s.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75892/9352fe80-b10d-2381-7ae7-731859099160.jpeg)

- 少し顔を横にして顔検出した結果
![20190429_123415.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75892/8de689f0-cec2-0eee-f0bb-f5625de24a98.jpeg)
![20190429_123415-s.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75892/afd8f08c-ec3f-b9e1-cb80-1bc388e59b26.jpeg)

## 参考サイト
専用カメラモジュールを使う
https://raspi-wannabe.com/digital-camera

Raspbian Liteでビルド済みのOpenCVパッケージを使うための依存パッケージいろいろ
https://qiita.com/atuyosi/items/5f73baa08c3408f248e8

picameraで顔認識してみた
https://qiita.com/K_M95/items/41a24d7639cffc629227

Raspberry Piにカメラモジュールを接続して写真を撮る。
https://www.taneyats.com/entry/raspi-smart-aquarium-1

PYTHON PICAMRA
http://igarashi-systems.com/sample/translation/raspberry-pi/usage/python-camera.html
https://picamera.readthedocs.io/en/release-1.10/index.html

opencv/opencv
https://github.com/opencv/opencv

OpenCV-Pythonチュートリアル
http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_tutorials.html

OpenCVを使った顔認識（Haar-like特徴分類器）
https://qiita.com/hitomatagi/items/04b1b26c1bc2e8081427

RaspberryPi＋GoogleHome+Webカメラで顔認識でお出迎えしてくれるGoogleHomeを構築する
https://qiita.com/sey323/items/65c41264bd1ca9b765a0

PythonでOpenCVを呼び出して、Raspiカメラに写る顔を認識してみた
http://asukiaaa.blogspot.com/2018/07/pythonopencvraspi.html