投稿日:
2018/08/29 ハラショー!!今日ハ肉ノ日デスネ!!
というわけで、以下の記事等を参考にWEBカメラの映像をPythonとOpenCVで顔認識して遊んでみました。
これらを試したいきさつは最後に書くとして、とりあえず何を作ったのかを書いていきます。
何を作ったのか
WEBカメラの映像から顔と目を検出して目にはモザイクをかけるやつ
目をつぶったら close your eyes
と表示する
目をつぶったら画面キャプチャを終了するやつ
目が開いている時間をカウントダウン表示
ちょっとしたゲーム感覚
を作りました。
環境
Mac, Windows ともに下記環境で試しました。
Python
OpenCV(OpenCVPython)
PythonからOpenCVを使うにはOpenCVPython
なるものが必要なようなのでインストールします。
1
$ pip
install
opencv-python
確認
1
2
$ pip freeze |
grep
opencv
opencv-python==3.4.2.17
カスケード型分類器
顔認識させるにはカスケード型分類器なるものが必要なようなので
GitHub – opencv/opencv: Open Source Computer Vision Library
をcloneするなり落としてきて、 https://github.com/opencv/opencv/tree/master/data/haarcascades の各種.xmlを用意しておきます。
後述するサンプルでは、
haarcascades/haarcascade_frontalface_alt2.xml
haarcascades/haarcascade_eye_tree_eyeglasses.xml
を利用しました。
(カスケードっていうバンド昔いたよね)
WEBカメラの映像から顔と目を検出して目にはモザイクをかけるやつ
こんな感じです
検出した顔の周りに四角を描画、目の部分にモザイク処理を行っています。
少し分かりづらいかもしれませんが、目がつぶられたら画面にclose your eyes
と表示しています。
目をつぶっている状態は、顔の検出が出来ているのに目の検出が出来ていない状態
の時をそれとしました。
ソース
face_and_eye_recognition.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys
import cv2 # OpenCV のインポート
'''
参考
@link http://ensekitt.hatenablog.com/entry/2017/12/19/200000
@link https://note.nkmk.me/python-opencv-face-detection-haar-cascade/
@link https://note.nkmk.me/python-opencv-mosaic/
@link http://workpiles.com/2015/04/opencv-detectmultiscale-scalefactor/
'''
# VideoCaptureのインスタンスを作成する。
# 引数でカメラを選べれる。
cap = cv2.VideoCapture(0)
if cap.isOpened() is False:
print("can not open camera")
sys.exit()
# 評価器を読み込み
# https://github.com/opencv/opencv/tree/master/data/haarcascades
cascade = cv2.CascadeClassifier('haarcascades/haarcascade_frontalface_alt2.xml')
eye_cascade = cv2.CascadeClassifier('haarcascades/haarcascade_eye_tree_eyeglasses.xml')
def mosaic(src, ratio=0.1):
small = cv2.resize(src, None, fx=ratio, fy=ratio, interpolation=cv2.INTER_NEAREST)
return cv2.resize(small, src.shape[:2][::-1], interpolation=cv2.INTER_NEAREST)
def mosaic_area(src, x, y, width, height, ratio=0.1):
dst = src.copy()
dst[y:y + height, x:x + width] = mosaic(dst[y:y + height, x:x + width], ratio)
return dst
while True:
# VideoCaptureから1フレーム読み込む
ret, frame = cap.read()
# そのままの大きさだと処理速度がきついのでリサイズ
frame = cv2.resize(frame, (int(frame.shape[1]*0.7), int(frame.shape[0]*0.7)))
# 処理速度を高めるために画像をグレースケールに変換したものを用意
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 顔検出
facerect = cascade.detectMultiScale(
gray,
scaleFactor=1.11,
minNeighbors=3,
minSize=(100, 100)
)
if len(facerect) != 0:
for x, y, w, h in facerect:
# 顔の部分(この顔の部分に対して目の検出をかける)
face_gray = gray[y: y + h, x: x + w]
# くり抜いた顔の部分を表示(処理には必要ない。ただ見たいだけ。)
show_face_gray = cv2.resize(face_gray, (int(gray.shape[1]), int(gray.shape[0])))
cv2.imshow('face', show_face_gray)
# 顔の部分から目の検出
eyes = eye_cascade.detectMultiScale(
face_gray,
scaleFactor=1.11, # ここの値はPCのスペックに依存するので適宜修正してください
minNeighbors=3,
minSize=(15, 15)
)
if len(eyes) == 0:
# 目が閉じられたとみなす
cv2.putText(
frame,
'close your eyes',
(x, y - 10), # 位置を少し調整
cv2.FONT_HERSHEY_PLAIN,
2,
(0, 255,0),
2,
cv2.LINE_AA
)
else:
for (ex, ey, ew, eh) in eyes:
# 目の部分にモザイク処理
frame = mosaic_area(
frame,
int((x + ex) - ew / 2),
int(y + ey),
int(ew * 2.5),
eh
)
# 顔検出した部分に枠を描画
cv2.rectangle(
frame,
(x, y),
(x + w, y + h),
(255, 255, 255),
thickness=2
)
cv2.imshow('frame', frame)
# キー入力を1ms待って、k が27(ESC)だったらBreakする
k = cv2.waitKey(1)
if k == 27:
break
# キャプチャをリリースして、ウィンドウをすべて閉じる
cap.release()
cv2.destroyAllWindows()
View the code on Gist .
目をつぶったらキャプチャを終了するやつ
こんな感じです
遊び方
起動したら、画面frame
にフォーカスを合わせておきます
顔をカメラに向け目をクワッ
と開けます
s
を押します
顔認識中かつまだスタートしていない場合、少しカクつく(キー入力を待っている)のでキー入力待ちが分かるかと思います
目が開いている状態の場合、1秒ずつカウントダウン表示します
目が閉じられたら画面キャプチャを終了します
目が閉じられたとみなされたタイミングが最後の画面キャプチャとなります
esc
を押して終了です
ソース
blink_game.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys
import cv2 # OpenCV のインポート
import time
from datetime import datetime
'''
参考
@link http://ensekitt.hatenablog.com/entry/2017/12/19/200000
@link https://note.nkmk.me/python-opencv-face-detection-haar-cascade/
@link https://note.nkmk.me/python-opencv-mosaic/
@link http://workpiles.com/2015/04/opencv-detectmultiscale-scalefactor/
'''
# VideoCaptureのインスタンスを作成する。
# 引数でカメラを選べれる。
cap = cv2.VideoCapture(0)
if cap.isOpened() is False:
print("can not open camera")
sys.exit()
# 経過時間を描画
def draw_elapsed_time(frame, start_time, closed_time=0):
now = closed_time if closed_time > 0 else int(datetime.now().timestamp())
cv2.putText(
frame,
str(now - start_time),
(10, 50),
cv2.FONT_HERSHEY_PLAIN,
3,
(0, 255,0),
3,
cv2.LINE_AA
)
# 評価器を読み込み
# https://github.com/opencv/opencv/tree/master/data/haarcascades
cascade = cv2.CascadeClassifier('haarcascades/haarcascade_frontalface_alt2.xml')
eye_cascade = cv2.CascadeClassifier('haarcascades/haarcascade_eye_tree_eyeglasses.xml')
closed_eyes = False
is_started = False
start_time = 0
closed_time = 0
while True:
# VideoCaptureから1フレーム読み込む
ret, frame = cap.read()
# そのままの大きさだときついのでリサイズ
frame = cv2.resize(frame, (int(frame.shape[1]*0.7), int(frame.shape[0]*0.7)))
if closed_eyes == True:
draw_elapsed_time(frame=frame, start_time=start_time, closed_time=closed_time)
cv2.imshow('frame', frame) # 目が閉じられたであろう瞬間を残す
break
if is_started == True:
# 経過時間を描画
draw_elapsed_time(frame=frame, start_time=start_time)
# 処理速度を高めるために画像をグレースケールに変換したものを用意
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 顔検出
facerect = cascade.detectMultiScale(
gray,
scaleFactor=1.11,
minNeighbors=3,
minSize=(100, 100)
)
if len(facerect) != 0:
if is_started == False: # まだスタートしてなくて
key = cv2.waitKey(100)
if key == 115: # s が押されたら
is_started = True
start_time = int(datetime.now().timestamp())
for x, y, w, h in facerect:
# 顔の部分
face_gray = gray[y: y + h, x: x + w]
# 顔の部分から目の検出
eyes = eye_cascade.detectMultiScale(
face_gray,
scaleFactor=1.11, # ここの値はPCのスペックに依存するので適宜修正してください
minNeighbors=3,
minSize=(15, 15)
)
if len(eyes) == 0 and is_started == True:
# 目が閉じられたとみなす
closed_time = int(datetime.now().timestamp())
closed_eyes = True
break
elif is_started == True:
# 顔検出できなければ目が閉じられたとみなす
closed_time = int(datetime.now().timestamp())
closed_eyes = True
continue
cv2.imshow('frame', frame)
# キー入力を1ms待って、k が27(ESC)だったらBreakする
k = cv2.waitKey(1)
if k == 27:
break
# 後処理
if closed_eyes == True:
while True:
k = cv2.waitKey(100)
if k == 27: # ESC が押されたらclose
break
cap.release()
cv2.destroyAllWindows()
View the code on Gist .
実行方法
こういうディレクトリ構成だとします。
1
2
3
4
5
6
sample/
haarcascades/
haarcascade_frontalface_alt2.xml
haarcascade_eye_tree_eyeglasses.xml
face_and_eye_recognition.py
blink_game.py
WEBカメラの映像から顔と目を検出して目にはモザイクをかけるやつ
1
$ python face_and_eye_recognition.py
目をつぶったらキャプチャを終了するやつ
注意点
正直、OpenCVをはじめてさわったのでなんとも言えないですが処理性能はマシンのスペックに依存しそうです。
※ 処理の最適化をしていないとかそういうのは抜きで
あっちのPCではサクサク認識していたのに、こっちのPCだとなんかちょっとモッサリ。みたいな。
特に今回のサンプルでは、目をつぶったのに判定してくれないとか目の開閉判定がマシンのスペックによってチマチマでした。
ですので、サンプルを試して目の開閉判定にストレスがある場合、目の検出に用いているdetectMultiScale()
のscaleFactor
の値を少し大きくしたりするとまぁまぁいい感じになったりします。
※ これでもだめならminSize
も少し大きくしてみたり、顔の位置を変えてみたり(近づく/離れる)、場所を変えてみたり(背景が白いとことか)とかしてみてください
1
2
3
4
5
6
7
eyes
=
eye_cascade.detectMultiScale(
face_gray,
scaleFactor
=
1.31
,
minNeighbors
=
3
,
minSize
=
(
15
,
15
)
)
特に、子供と遊ぼうとなった時に上記には注意が必要です。
(自分がそうだった!!)
追記
上記問題を少し改善したバージョンを書いてみました。
PythonとOpenCVを使ったまばたき検知ゲームの(プチ)改善 – Shimabox Blog
いきさつ
ここ最近、画像認識・画像解析とはどんな感じなのだろうかという名目のもと、Amazon Rekognition を使って遊んでいます(一応業務)。
Webカメで撮っている映像をcanvasに描画 → サーバーにPOST → 解析 → サーバーから解析結果を返す → 解析結果をcanvasに重ねる
みたいなところまでは簡単にできました。
が、Webカメで撮っている映像に対してほぼリアルタイムで解析(検出)結果を重ねてみたいなぁとなるとちょっとめんどくさそうです。
(定期的にPOSTするん?とか、Amazon Kinesis Video Streams とか、AWS DeepLens とかを使うん?とかサクッと試せる感が無いし、そもそもリアルタイムが無理じゃね?みたいな)
少し途方に暮れてなんか似た感じのやつないかなぁとpython webカメ 顔認識
で調べてみると、ローカルでやる分には山程サンプルがあるじゃないですか!!みんなすごい!!
※AWS SDK for Python (Boto3)
を使っていたこともあり出来ればPythonで探していた
そもそも、画像認識・画像解析とはどんな感じなのだろうかというふわっとした目的なので、手段は問わず試してみたというのが今回のいきさつです。
少し調べてみると、jsでもリアルタイムで顔認識できるようなので近い内に試してみます。
試してみました
clmtrackr.jsやpico.jsで顔認識して遊ぶ | Shimabox Blog
おわりに
はじめてPython(OpenCVも)をさわったので正直戸惑いましたが、慣れてくるとワクワクするというかなんというか忘れていた何かを思い出したような気がしました。
今回のサンプルは最初にあげた記事を参考(ほぼ丸パクリ)にして書いただけなので自身でイケてるものを作れるように勉強していきたいです。と意識高い系の言葉で締めます。
Python楽しい!!