LINE Notify + ラズパイで自宅から研究室のモニタリングをした話。

## はじめに
【この記事で分かること】

- LINE Notifyのインストール方法と簡単な使い方
- ラズパイと専用カメラを用いた顔認識

## 背景 
新型コロナウイルスの拡大に伴い、
日本でもリモートワークを推奨するようになってきた今日この頃。
私の研究室でも緊急措置が取られました。
なんと...研究室に出勤日？が設けられたのです。

2021年5月現在は、無事解除されました。めでたし。
この経験を経て、リモートワークに感銘を受けたボクは、ふと思ったわけです。

- 『研究室に誰が来たか』を自宅から知りたい。
- Slackとか導入すればいいけど、みんなやりたがらないしなぁ…。
- 学生がたくさん来てる時間帯に研究室に行きたくない。

これらの希望を叶えるには、どうすればよいか…

__『そうだっ！研究室をモニタリングすればいいのでは！？』__

使ってないラズパイもあるし、なんとかなりそう...そう考えたボクは実行に移しました。

## 実行環境
今回実装にあたって、用いた実行環境・ガジェットは以下の通り。

- RaspberryPi 3 modelB+
- RaspberryPi専用カメラ
- Python ver3.6
- OpenCV ver3.4
- LINE Notify

![システム構想図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1538778/b30373ef-ec88-5ee8-11aa-ccc726e996d0.png)
↑こんな感じでできればいいなぁと考えてました。

## 実装
### 1. ラズパイとカメラで顔認識
まずは、ディレクトリの構成から。xmlファイルはOpenCVの該当フォルダから持ってきます。

```
./ 
 ├ imgs/
 ├ haarcascade_frontalface_alt.xml
 └ people_detected_with_camera.py    
```

プログラムのソースコードは以下の通り。

```python
from datetime import datetime
import time
import RPi.GPIO as GPIO
import requests

import os
import cv2
import picamera
import picamera.array

SLEEPTIME = 5
PEOPLE_NO = 1
DIR = os.getcwd()

with picamera.PiCamera() as camera:
    with picamera.array.PiRGBArray(camera) as stream:
        camera.resolution=(480, 360)
        camera.framerate=30
        
        while True:
            camera.capture(stream, 'bgr', use_video_port=True)
            grayimg = cv2.cvtColor(stream.array, cv2.COLOR_BGR2GRAY)
            face_cascade = cv2.CascadeClassifier(os.path.join(DIR, "haarcascade_frontalface_alt.xml"))
            facerect = face_cascade.detectMultiScale(grayimg, scaleFactor=1.2, minNeighbors=2, minSize=(100,100))
            
            if len(facerect)>0:
                img_name = os.path.join(DIR, ("imgs/people_{}.jpg".format(PEOPLE_NO)))
                cv2.imwrite(img_name, stream.array)
                print("No.{}  send!".format(PEOPLE_NO))
                time.sleep(SLEEPTIME)
                PEOPLE_NO += 1
                #break
            
            cv2.imshow('camera', stream.array)
            key = cv2.waitKey(1)
            if key & 0xFF == ord('q'):
                print("Finish!")
                break
            
            stream.seek(0)
            stream.truncate()
            
            
cv2.destoryAllWindows()
```

OpenCVライブラリに同梱のxmlファイルを元に顔認識を行いました。
本当はDeepLearningモデルなど用いた方が精度はいいのでしょうが…。
今回は簡単に実装することがコンセプトなので後回しです。

上のソースコードを実行すると、こんな感じで
ラズパイのディスプレイ上にウィンドウが出力されます。

![raspberrypi_caputure.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1538778/1837eb50-999b-f96e-12d0-5047dc10f4cb.png)


### 2. LINE Notifyの導入
つぎにLINE Notifyを導入して、ラズパイで撮影した画像を
自分のLINEアカウントに送信します。

その前に、[LINE Notify](https://notify-bot.line.me/ja/)に登録しなければなりません。(参考サイトは[コチラ](https://www.smilevision.co.jp/blog/tsukatte01/))

上記の参考サイトを参照し、順当に進めていただきますと、アクセストークンが発行されます。
これをコピーしておきます。（念のため、メモもしておきましょう。）

ラズパイに戻って顔認識のソースコードを一部追記します。
下記のソースコードの__ACCESS_TOKEN__のところに
発行されたアクセストークンを入力します。

```python
def line_notify(img_name):
    payload = {'message' : '人いるよ'}
    headers = {'Authorization' : "Bearer ACCESS_TOKEN"}
    files = {"imageFile":open(img_name,'rb')}
    requests.post('https://notify-api.line.me/api/notify', headers = headers, params = payload, files = files)

line_notify(img_name)
```

全体のコードがコチラ。

```python
from datetime import datetime
import time
import RPi.GPIO as GPIO
import requests

import os
import cv2
import picamera
import picamera.array

SLEEPTIME = 5
PEOPLE_NO = 1
DIR = os.getcwd()

def line_notify(img_name):
    payload = {'message' : '人いるよ'}
    headers = {'Authorization' : "Bearer ACCESS_TOKEN"}
    files = {"imageFile":open(img_name,'rb')}
    requests.post('https://notify-api.line.me/api/notify', headers = headers, params = payload, files = files)

with picamera.PiCamera() as camera:
    with picamera.array.PiRGBArray(camera) as stream:
        camera.resolution=(480, 360)
        camera.framerate=30
        
        while True:
            camera.capture(stream, 'bgr', use_video_port=True)
            grayimg = cv2.cvtColor(stream.array, cv2.COLOR_BGR2GRAY)
            face_cascade = cv2.CascadeClassifier(os.path.join(DIR, 'haarcascade_frontalface_alt.xml'))
            facerect = face_cascade.detectMultiScale(grayimg, scaleFactor=1.2, minNeighbors=2, minSize=(100,100))
            
            if len(facerect)>0:
                img_name = os.path.join(DIR, ("imgs/people_{}.jpg".format(PEOPLE_NO)))
                cv2.imwrite(img_name, stream.array)
                line_notify(img_name)
                print("No.{}  send!".format(PEOPLE_NO))
                time.sleep(SLEEPTIME)
                PEOPLE_NO += 1
                #break
            
            #cv2.imshow('camera', stream.array)
            key = cv2.waitKey(1)
            if key & 0xFF == ord('q'):
                print("Finish!")
                break
            
            stream.seek(0)
            stream.truncate()
            
            
cv2.destoryAllWindows()
```

### 3. 実際に検証！
それでは、ラズパイの前に立ち
LINEに送信されるかの検証をします。

__"ピコンッ！"__

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1538778/85c06401-5539-0475-6701-db2d5d1651e7.png" width=50%>

うん、ちゃんと送信されています。

まずまずといったところでしょうか。
システムは完成したので、一日仕掛けてみて
他の学生もかかるか試してみます。


結果はこんな感じ。

![結果2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1538778/ac5ff573-8eda-ef8b-4bdc-eedef3c15cad.jpeg)

![結果3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1538778/520048c1-74ab-908e-f048-b67ee8847f7d.jpeg)

カメラ目線をくれているものは、バッチリ撮れていますね。
また、見切れている画像も多々ありました。

![結果5.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1538778/b36bdaaa-c518-849d-ca87-624011115f13.jpeg)


フレームレートや画質に対して調整が必要なようです。


加えて、このように__誰もいないのに送信されるケース__もありました。

![結果4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1538778/0ea9598b-fab5-074e-f812-ba7cb583f8c4.jpeg)


後ろの坂本龍馬さんに反応してしまったようです。
ある意味、ホラーでした。

## さいごに
いかがでしたでしょうか？今回は、LINE Notifyとラズパイで自宅からの研究室モニタリングシステムを実装しました。

当初はサーバーを立てて、そちらにデータ蓄積・送信する予定でしたが、よくよく考えたら、外出時にリアルタイムで見れないし、自宅に居たとしても、わざわざPCの前に行かないと確認できないのでは？と思い、急遽LINEに送るシステムにシフトしました。

現状、人が来たことは分かりますが、__誰が来たか__はボクが判断して初めて分かります。この性質上、__自動で出勤簿をつけたり__、__個人に対するの出勤パターンの解析__をするにはまだまだ改良が必要なことが分かりますね。

あと、当システムは双方向受信ではなく、LINE Notify側から一方的に取得画像が送られてくるだけなので、こちら側からアクセスできないのが難点。例えば、『今誰いる？』みたいにメッセージを送ったら、__当日登校している学生のリストを送信してくれる__とか、こういうのができないわけです。

また、認識精度の問題にも難点が見られたので、これは早急に改良する必要がありそう…。

というわけで、今回はこのあたりでお開きとしたいです。
それでは、よいAIライフを～(^^)/

# 追伸
まずは坂本さんのような誤検出を減らしたかったので、(夜にずっと送られてくる…( ﾟДﾟ)ﾋｴｯ
取り合えず人感センサーを取り付けて、誤検出を少なくしました。