Are you wearing a mask？プログラムの開発（Yolov5の利用）

## 今回の内容
マスクをしているかしてないかを出力するプログラムがありますが,今回をマスクをしているかしていないかを
出力した後にどう可視化するかを出力するかを考えるプログラムを書いていきたいと思います.
追記:
一応,いつも開発のゴールを書いているので書いておきたいと思います.
最終的には非接触AI温度計を目指して開発をしています.
まぁ世の中には類似製品は沢山ありますが,某製品は16万円とか安くても8万円とかですね.
この価格帯だと導入できない企業もあるかもしれない.
だから2万円くらいで自分で作れる非接触AI温度計を開発したいと思います.
その付随機能のマスク検知を今回開発していきたいと思います.
## 構成
⓵ Yolov5でマスク検知のモデルを作成
⓶ マスクをしているときはOK→音声でマスクをしていますねと音声で流す.
⓷ していないときはNG→マスクをしてください.→管理者のlineに通知
## モデルの作成
モデルについて
>Face Mask Lite Dataset
>https://www.kaggle.com/prasoonkottarathil/face-mask-lite-dataset

上記のモデルを利用してモデルを作成していきます.
##モデルの学習
いつも環境は同じなので,省略していますが,今回は久々なので記入していきます.
>環境
>GTX960 2GB
メモリ 8GB
>そろそろ環境を変えないといけないかなぁ...

## 学習工程
### ⓵事前練習
別のモデルを使って簡易的に学習をさせてみました.
>Mask Wearing Dataset
>https://public.roboflow.com/object-detection/mask-wearing

参考記事だとこの辺にも上がっていますのでURLを載せておきます.
https://qiita.com/YuyaOmori/items/ee3825d25afec8bf60ec

学習工程を載せておきます.
1000epochで学習させました.
![labels.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567360/78d6c129-f76b-c16d-a6bc-97c1e3253e28.png)
![results.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567360/d5e27082-2742-822d-01dd-cf834dfa439b.png)
### このモデルの学習結果
圧倒的にマスクをしている画像のが多くて,マスクをしている方に偏ってしまうような気がします.
したがって次作るモデルはマスクをしている画像としていない画像を半々で学習させる必要があるかなと思います.
### ⓶アノテーション作業
➢とりあえずこの中の画像1000枚くらい使って学習させていきたいと思います.
・maskあり500枚
・maskなし500枚

#### (1)アノテーション前の画像加工
画像の解像度は480*480に変更します.
#### (2)加工した画像をもとにアノテーションをしていきます.
今回着目したのは口元です.
口元にマスクがあればtrue
口元にマスクがなければfalseになります.
参考画像
マスクあり
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567360/646990bc-dcc9-a16c-1442-3e12460346e9.png)
マスクなし
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567360/d871a025-d525-8182-00f1-db7c3684c387.png)
### ⓷学習
#### 50epoch
![labels.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567360/d2cf8195-5c44-2937-ed7d-78448b9d0a0a.png)
![results.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567360/4d0f5c1c-7c49-22cc-d3b1-e093ef76f9e3.png)
リアルタイムwebカメラで実験してみた結果認識はできているけど精度がそこまで良くないです.
#### 75epoch
![results.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567360/83704df0-e7c4-7196-f613-b96098e17fe0.png)
#### 100epoch
![results.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567360/5d3e2241-b239-ac52-4bf7-a514d2058e5d.png)
###  200epoch
![results.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567360/ba9c45fa-7d31-d959-e56f-260256f379f7.png)
### テスト
それでは,最近総理や大統領が変わったりと大忙しなので政治家さんにテストを付き合ってもらいましょう.
![6.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567360/4effec5d-eaaa-d962-79c9-747066f442c3.jpeg)
![1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567360/87a2777c-322d-eda7-5499-26fec00efee5.jpeg)
![2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567360/f6da6aa1-7025-9592-8f3c-a3e7366bb9d1.jpeg)
![3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567360/1a7b5449-7863-e686-f2ac-205d8e97f851.jpeg)
![4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567360/d24d57d0-e249-3bfd-5c5f-d7ae82c2c579.jpeg)
![5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567360/6cf6a1ec-509e-2895-f91d-eb4bca64cf6c.jpeg)
結果
角度やマスクの種類によっては反応しない可能性がありますね.
リアルタイムでやっても白いマスクのみでやりましたが,角度がずれると
認識しない問題がありました.これは今後の課題です.

## 可視化部分
※可視化部分は後日載せたいと思います.
※前記事Yolo可視化部分開発と同時に進行中.
可視化部分の計画
・line通知機能の実装（実装済み）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567360/318fa401-2d30-e71a-c615-27a154b76861.png)
リアルタイムで検知しているときはこのように通知が来るように設定しています.
画像を一緒に送ることもできますが,時間当たりの使用量が限られているので実装していません.
基本的には前回書いたyolov5可視化プログラムをベースに追加しました.
##### yolov5可視化プログラム記事:
https://qiita.com/asmg07/items/1f8a1b8214cf32e614fc
⑤番の複数のモデルを検知している部分になります.
サンプルコード

```
    line_notify_token = ''
    line_notify_api = 'https://notify-api.line.me/api/notify'
    message = ''
    payload = {'message': message}
    headers = {'Authorization': 'Bearer ' + line_notify_token} 
line_notify = requests.post(line_notify_api, data=payload, headers=headers)
```
##### ストームを用いたsocket通信
前記事で音声で可視化するプログラムを書きましたが,従来のsocket通信だと遅延が多くて使い物になりませんでした.
なので方法を少し考えてストームを使ったsocket通信を試みました.
その方法を記入します.
公式のリファレンス通りに構築しているのでそちらをもとに記入します.
client側


```
import asyncio

async def tcp_echo_client(message):
    reader, writer = await asyncio.open_connection(
        '127.0.0.1', 8888)

    print(f'Send: {message!r}')
    writer.write(message.encode())

    data = await reader.read(100)
    print(f'Received: {data.decode()!r}')

    print('Close the connection')
    writer.close()
#マスクをしている時
asyncio.run(tcp_echo_client('1'))
#マスクをしていない時
asyncio.run(tcp_echo_client('2'))
```

server側

```

import asyncio
from playsound import playsound
async def handle_echo(reader, writer):
    data = await reader.read(100)
    message = data.decode()
    addr = writer.get_extra_info('peername')

    print(f"Received {message!r} from {addr!r}")

    print(f"Send: {message!r}")
    print(message)
    meg=str(message)
    writer.write(data)
    await writer.drain()
    if meg=="1":
        playsound('ok.wav')
        print("OK")
    if meg=="2":
        playsound('nomask.wav')
        print("NG")

    print("Close the connection")
    writer.close()

async def main():
    server = await asyncio.start_server(
        handle_echo, '127.0.0.1', 8888)

    addr = server.sockets[0].getsockname()
    print(f'Serving on {addr}')

    async with server:
        await server.serve_forever()

asyncio.run(main())
```
上記のように実装することで音声をなるべく遅延なく実行することができます.
後のものは後々実装することにしたいと思います.
##### データベース化する前にcsvファイルに書き込みを行う.
```
    if meg=="1":
        playsound('ok.wav')
        print("OK")
        with open("mask.csv","a")as csv_file:
            writer1 = csv.writer(csv_file)
            writer1.writerow([str(day),  str(time1.hour)+":"+str(time1.minute)+":"+str(time1.second), 'OK'])
            print(str(day))
            print(str(time1.hour)+":"+str(time1.minute)+":"+str(time1.second))
            print("CSVファイルを書き込みました.")

    if meg=="2":
        playsound('nomask.wav')
        print("NG")
        with open("mask.csv","a")as csv_file:
            writer1 = csv.writer(csv_file)
            writer1.writerow([str(day), str(time1.hour)+":"+str(time1.minute)+":"+str(time1.second), 'NG'])
            print(str(day))
            print(str(time1.hour)+":"+str(time1.minute)+":"+str(time1.second))
            print("CSVファイルを書き込みました.")
```
##### SQLを使ったデータベース化
okの場合

```
def sqlok():
 day = datetime.date.today()
 time1 = datetime.datetime.now()
 conn = mydb.connect(
        host='127.0.0.1',
        port='3306',
        user='',
        password='',
        database='mask'
    )
 # コネクションが切れた時に再接続してくれるよう設定
 conn.ping(reconnect=True)
 # 接続できているかどうか確認
 print(conn.is_connected())
 cur = conn.cursor()
 time2=str(time1.hour)+":"+str(time1.minute)+":"+str(time1.second)
 print(day)
 print(time2)
 cur.execute("INSERT INTO `mask-ok-or-ng1` (`day`, `time`, `ok-or-ng`)"+ "VALUES"+ "("+"'"+str(day)+"'"+","+"'"+str(time1)+"'"+","+"'OK')")
 conn.commit()
 cur.close()
 conn.close()
```

ngの場合

```
def sqlng():
 day = datetime.date.today()
 time1 = datetime.datetime.now()
 conn = mydb.connect(
        host='127.0.0.1',
        port='3306',
        user='',
        password='',
        database='mask'
    )
 # コネクションが切れた時に再接続してくれるよう設定
 conn.ping(reconnect=True)
 # 接続できているかどうか確認
 print(conn.is_connected())
 cur = conn.cursor()
 time2=str(time1.hour)+":"+str(time1.minute)+":"+str(time1.second)
 print(day)
 print(time2)
 cur.execute("INSERT INTO `mask-ok-or-ng1` (`day`, `time`, `ok-or-ng`)"+ "VALUES"+ "("+"'"+str(day)+"'"+","+"'"+str(time1)+"'"+","+"'NG')")
 conn.commit()
 cur.close()
 conn.close()
```

以下のやり方でコードを書くと実装できます.
・webでの管理画面の実装
①APIサーバの構築
②ログイン機能の実装
※ログイン機能というのは一つのプラットフォームでの提供を見越す.
## 現時点の改善点
・顔を検知して,そのうえでマスクを検出できた場合のみOKorNGを出せるようにした方がより良いプログラムになるかもしれません.※後日の改善点
## 結論
予備実験で使ったモデルよりは今回使ったモデルのが精度は伸びたように感じます.
※ただ比較の結果を載せていないので後日のせようと思います.
今回はwebカメラの実験映像を載せていませんが,リアルタイムのwebカメラではそれなりの精度がありました.
なので,角度やマスクの種類,画像の解像度などもいろんな制約がありそうな気がします.
面白いので皆さんもぜひやってみてください!
それでは今日はこんな感じで終わります.