はじめに
2020年3月頭に流れてきたツイートで
というものがあり、件のPaddlePaddleをのぞいてみるとデータセットを呼び出して利用する実装例がGithubに公開されており、Python初学者にもわかりやすいコードでまとまっていました。(コード内のコメントこそ簡体字が使われていますが基本的に名詞なのでPCの標準の翻訳機能で十分訳せそうです。)コロナウイルスの影響でマスクを着用した顔のデータセットや検出モデルの公開がここ最近急増している😷
— MARINA🦋|Edge AI Biz (@m__sb04) March 2, 2020
■ Baiduが公開したオープンソースのマスク着用顔検出モデルhttps://t.co/NurgI3KB30
■ 3500枚を超えるマスク検出するためのデータセットhttps://t.co/P3bVOaGawq pic.twitter.com/xtrVV86abF
@SatoshiGachiFujimotoさんのこちら(Baiduが公開しているマスク検出を試してみた)の記事も参考になりました。ありがとうございます。
本記事では、**カメラに映る対象がマスクをしているか否か判別するプログラム(mask_detection)**をMacbook Air (Python3.6.3)環境下で試してみます。
リポジトリを取ってくる
git clone git@github.com:PaddlePaddle/PaddleHub.git
や、リポジトリをZIPでDownLoadするなどしてローカルにリポジトリを落としてきます。
実際に走らせてみる
clone、もしくは解凍したディレクトリへと移動したらdemo/mask_detection/
へ移動します。
Readmeにもあるように
$ pip install paddlehub
したら
$ python mask_detection.py
でスクリプトが走ります。
うまくプログラムが動くと、PCに接続されたカメラの映像のうち、マスクをつけている人物が映ったコマのみをjpgで出力し、実際にDetectしている様子はプログラムの終了時に動画で出力されます。
詰まったところ
しかし、実際には私の環境に限っては、
- インストールしているコーデックの種類が少ない
- デバイスのカメラは適当なものを指定するべきである
という問題があり少しコードをいじる必要がありました。
31-36行目で出力する動画のアスペクト比などを指定している中でコーデックも指定しています。
fourcc = cv2.VideoWriter_fourcc(*'vp90')
私の環境にはvp90は入っていなかったので
cv2.VideoWriter_fourcc('m','p','4', 'v')
と、mp4vを指定しました。
42行目では、Detectに用いる映像を撮影するカメラデバイスを指定しています。
capture = cv2.VideoCapture(0)
当初プログラムを走らせた際、カメラの使用中の緑のランプは点滅するのになぜか動画が生成されないことが疑問だったのですが、
ffmpeg -list_devices true -f avfoundation -i dummy
で現在利用しているカメラのデバイス番号を確かめてみると、
といった感じで、リモート会議対応でインストールしたsnapcameraの仮想デバイスが[0]を占有しており、macのデフォルトのインカメラを利用するためには
capture = cv2.VideoCapture(1)
と書き換える必要がありました。(参考)
ちなみに、43行目にはコメントアウトした行がありますが、「打开视频文件」との文言の通り、42行目をコメントアウトして43行目で読み込みたい動画を指定すると、カメラでキャプチャした動画の分析をするのではなく、すでに撮影し保存している動画の分析を行うことができます。
capture = cv2.VideoCapture('./test_video.mp4')
以上二箇所を編集すると私の環境でも動かすことができました。
カメラとの距離が近い状態しか試せていないのですが、かなりの精度で検出できているようです。
以下、編集したコード全文です。(折りたたみかたを教えて欲しい...)
# -*- coding:utf-8 -*-
import paddlehub as hub
import cv2
from PIL import Image, ImageDraw, ImageFont
import numpy as np
import json
import os
module = hub.Module(name="pyramidbox_lite_server_mask", version='1.1.0')
# opencv输出中文
def paint_chinese(im, chinese, position, fontsize, color_bgr):
# 图像从OpenCV格式转换成PIL格式
img_PIL = Image.fromarray(cv2.cvtColor(im, cv2.COLOR_BGR2RGB))
font = ImageFont.truetype(
'SourceHanSansSC-Medium.otf', fontsize, encoding="utf-8")
#color = (255,0,0) # 字体颜色
#position = (100,100)# 文字输出位置
color = color_bgr[::-1]
draw = ImageDraw.Draw(img_PIL)
# PIL图片上打印汉字 # 参数1:打印坐标,参数2:文本,参数3:字体颜色,参数4:字体
draw.text(position, chinese, font=font, fill=color)
img = cv2.cvtColor(np.asarray(img_PIL), cv2.COLOR_RGB2BGR) # PIL图片转cv2 图片
return img
result_path = './result'
if not os.path.exists(result_path):
os.mkdir(result_path)
name = "./result/1-mask_detection.mp4"
width = 1280
height = 720
fps = 30
fourcc = cv2.VideoWriter_fourcc('m','p','4', 'v') # changed *'vp90' -> 'm','p','4', 'v'
writer = cv2.VideoWriter(name, fourcc, fps, (width, height))
maskIndex = 0
index = 0
data = []
capture = cv2.VideoCapture(1) # 打开摄像头 #0 -> 1
#capture = cv2.VideoCapture('./test_video.mp4') # 打开视频文件
while True:
frameData = {}
ret, frame = capture.read() # frame即视频的一帧数据
if ret == False:
break
frame_copy = frame.copy()
input_dict = {"data": [frame]}
results = module.face_detection(data=input_dict)
maskFrameDatas = []
for result in results:
label = result['data']['label']
confidence_origin = result['data']['confidence']
confidence = round(confidence_origin, 2)
confidence_desc = str(confidence)
top, right, bottom, left = int(result['data']['top']), int(
result['data']['right']), int(result['data']['bottom']), int(
result['data']['left'])
#将当前帧保存为图片
img_name = "avatar_%d.png" % (maskIndex)
path = "./result/" + img_name
image = frame[top - 10:bottom + 10, left - 10:right + 10]
cv2.imwrite(path, image, [int(cv2.IMWRITE_PNG_COMPRESSION), 9])
maskFrameData = {}
maskFrameData['top'] = top
maskFrameData['right'] = right
maskFrameData['bottom'] = bottom
maskFrameData['left'] = left
maskFrameData['confidence'] = float(confidence_origin)
maskFrameData['label'] = label
maskFrameData['img'] = img_name
maskFrameDatas.append(maskFrameData)
maskIndex += 1
color = (0, 255, 0)
label_cn = "有口罩"
if label == 'NO MASK':
color = (0, 0, 255)
label_cn = "无口罩"
cv2.rectangle(frame_copy, (left, top), (right, bottom), color, 3)
cv2.putText(frame_copy, label, (left, top-10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, color, 2)
#origin_point = (left, top - 36)
#frame_copy = paint_chinese(frame_copy, label_cn, origin_point, 24,
# color)
writer.write(frame_copy)
cv2.imshow('Mask Detection', frame_copy)
frameData['frame'] = index
# frameData['seconds'] = int(index/fps)
frameData['data'] = maskFrameDatas
data.append(frameData)
print(json.dumps(frameData))
index += 1
if cv2.waitKey(1) & 0xFF == ord('q'):
break
with open("./result/2-mask_detection.json", "w") as f:
json.dump(data, f)
writer.release()
cv2.destroyAllWindows()
終わりに
以上の情報は2020年4月8日時点でのソースコードを元にした情報です。今後更新に当たって行数はズレる可能性があります。
またあくまで"Demo"リポジトリの一部でしかないため今後も色々遊んで試してみようと思います。何か面白いものを見つけたらまた更新します。
また、余談ですが、推奨されてるconda activate paddlehub
をしようとしたところ、私の環境ではAnacondaとPyenvが衝突していることがわかりました...。
が、うまく動いてくれたようです。