OpenCVとRaspberry Piでマスクの装着状況を検知する

## 概要
* 以前、アート作品の鑑賞状況をOpenCVを使って可視化することを行った
https://qiita.com/cami_oshimo/items/ce33491c07f9625ed121
  
* これを応用して、目ではなく口の検知に変更すれば、マスクを装着の判断に使えるのではと考えた。
* まずは単純に口が露出しているかを判定して、マスクをしているかの判断を行ってみる。

![IMG_0076.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/701445/7703012d-e444-d302-f19d-c93a64169ca1.jpeg)

![IMG_0077.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/701445/68876317-9573-6db2-e76e-e60723547932.jpeg)

## 用意するもの
* Raspberry Pi3 Model B (4でもおそらく大丈夫)
* Raspberry Pi用のケース
* Raspberry Pi用のヒートシンク
* Raspberry Pi3 Model B B+ 対応 電源セット(5V 3.0A)
* aspberry Pi用のカメラ
* HDMI入力が付いたモニター
* 100円ショップの三脚  
※VNCを使ってPCからRaspberry Piをリモート操作すると開発が楽になる

## 準備

* RaspberryPiのセットアップを行う。
* RaspberryPiのカメラが使用できる状態にしておく。  
  https://www.raspberrypi.org/downloads/
* OpenCVのライブラリ、カスケードファイルが使用できるようにしておく。
haarcascade_frontalface_default.xml (顔検出)  
haarcascade_mcs_mouth.xml (口検出)

## プログラミング

* 顔をフレームインするよう促すメッセージ、顔・口検出のフレーム、マスク非装着時の警告をカメラ映像にオーバーレイ表示する。
* 顔と検出され、かつ口が検出された場合にマスク非装着と判定する。

```Python
import sys

sys.path.append('/home/pi/.local/lib/python2.7/site-packages')

import numpy as np

import cv2

import time

import datetime

import ambient

args = sys.argv

face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')

mouth_cascade = cv2.CascadeClassifier('haarcascade_mcs_mouth.xml')

cap = cv2.VideoCapture(0)

while(True):


    ret, frame = cap.read()

    height = frame.shape[0]

    width = frame.shape[1]

    import sys

    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    faces = face_cascade.detectMultiScale(gray, 1.3, 5)

    cv2.putText(frame, 'Please face in frame.', (350, 20), cv2.FONT_HERSHEY_PLAIN, 1.5, (0,255,0), thickness=2)

    for (x,y,w,h) in faces:

        roi_gray = gray[y:y+h, x:x+w]

        roi_color = frame[y:y+h, x:x+w]

        cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,0),2)

        mouth = mouse_cascade.detectMultiScale(roi_gray)

        for (ex,ey,ew,eh) in mouth:

            cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,255,255),2)

            cv2.putText(frame, 'Not masked!!', (10, 460), cv2.FONT_HERSHEY_DUPLEX, 1.0, (0,0,255), thickness=2)

    resized_img = cv2.resize(frame,(width*2, height*2))

    cv2.imshow('maskcheck', resized_img)

    if cv2.waitKey(1) & 0xFF == ord('q'):

        break

cap.release()

cv2.destroyAllWindows()
```

* RaspberryPiのデスクトップにシェルスクリプトを設置し、Pythonのプログラムが起動するようにしておく。
* アイコンのダブルクリックで起動するようにしておく。

```sh:maskcheck.sh
#! /bin/bash
cd /home/pi/opencv_programs/
sudo python3 detect_face_camera.py
```

![スクリーンショット 2020-11-08 10.30.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/701445/478a3c6b-1ec3-b1cf-1676-bc3de1e9e3d2.png)

![スクリーンショット 2020-11-08 10.32.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/701445/a4dd11dc-d9a4-1755-075f-2edc56cf3f1e.png)

## 課題
* 外的要因で検出が難しい場合がある。（顔との距離、角度、光量など）逆光でノイズが入り検出不能となったためカーテンを締め切っている
* マスクの種類・装着の仕方によっては、顔と検出されない場合がある。
* 口検出のカスケードファイルが原因なのか、口以外の場所が口と認識される場合がある。カスケードファイルは修正できるようなので、時間をかけてチューニングすれば解決できる可能性あり。
* 顔と認識→口の有無を認識するロジックとなっているため、そもそも顔を認識させるようにさせないといけない。口の認識のみのロジックを試したが、うまく動作しなかった。（OpenCVの問題？）
* 描画の遅延が目立つ、RaspberryPi 4でどの程度パフォーマンスが上がるのか興味がある。