#はじめに
本ページは主に下記URLの、
https://www.pyimagesearch.com/2017/04/10/detect-eyes-nose-lips-jaw-dlib-opencv-python/
の内容をフォークしたものです。主な内容は画像からの顔検出ですが、今回はOpenCVではなくDlibというツールを用いています。モジュールのインストールは、
pip install dlib
これで簡単にできました。実はこっちの方が顔検出の精度が良いです(既存のライブラリを用いた場合の話ですが)。そこらへんの議論は、
http://blog.dlib.net/2014/02/dlib-186-released-make-your-own-object.html
ここが大変参考になりました。また今回のコードの変更点は、
- 顔や輪郭検出をPC内蔵カメラからキャプチャした画像から行えるようにした
- スペースキーを押すことで、画像を連番の画像名で保存できるようにした
となります。
#顔検出
下の二行が主に顔検出を行う処理です。
detector = dlib.get_frontal_face_detector()
dets, scores, idx detector.run(img, 0)
検出された顔部分の情報はdetsに保存されます。顔検出のアルゴリズムとしてはSVMみたいなマージン最大化とHOG特徴量を用いているっぽいです。論文が公開されてます。
#輪郭などの検出
実際のところ輪郭とそれ以外にあご、目、鼻、眉、口を一気に検出してくれます。ここでは先ほどのアルゴリズムとはまた別の勾配ブースティング木というアルゴリズムを用いているそうです。また、このライブラリを用いるにはデータセットが必要ですが、iBUG 300-Wというところが公開している既存のものがございます。データセットのダウンロードは下記URLを踏んでください。
http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2.
輪郭などの検出には下記のような宣言が必要になります。
predictor_path = "./shape_predictor_68_face_landmarks.dat"
predictor = dlib.shape_predictor(predictor_path)
また、輪郭などの検出は顔検出より切り取った情報は用いるので、先ほどのdetsを用いて、
shape = predictor(img_rgb, rect)
shape = face_utils.shape_to_np(shape)
shapeに保存した後、OpenCVやnumpyで扱いやすい形に変換します。また、shapeには68個の輪郭情報が含まれていてshape[0:68]と指定するとあご、目、鼻、眉、口、全体となりますが各部位と数字は常に対応しており、
- 口 = 48:68
- 右眉 = 17:22
- 左眉 = 22:27
- 右目 = 36:42
- 左目 = 42:48
- 鼻 = 27:35
- あご = 0:17
各部位をしていすることも可能です。
#コード
実装してみたものが下記になります。ここでは口のみを指定して切り出しています。スペースボタンを押すことで切り出した画像を保存できます。また、ESCを押すとウィンドウを閉じることができます。
#! /usr/bin/python
# -*- coding: utf-8 -*-
import cv2
import dlib
import numpy as np
import imutils
from imutils import face_utils
def face_shape_detector_dlib(img):
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# frontal_face_detectorクラスは矩形, スコア, サブ検出器の結果を返す
dets, scores, idx = detector.run(img_rgb, 0)
if len(dets) > 0:
for i, rect in enumerate(dets):
shape = predictor(img_rgb, rect)
shape = face_utils.shape_to_np(shape)
clone = img.copy()
cv2.putText(clone, "mouth", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
# landmarkを画像に書き込む
for (x, y) in shape[48:68]:
cv2.circle(clone, (x, y), 1, (0, 0, 255), -1)
# shapeで指定した個所の切り取り画像(ROI)を取得
(x, y, w, h) = cv2.boundingRect(np.array([shape[48:68]])) #口の部位のみ切り出し
roi = img[y:y + h, x:x + w]
roi = cv2.resize(roi,(100,100))
return clone, roi
else :
return img, None
def main():
predictor_path = "./shape_predictor_68_face_landmarks.dat"
predictor = dlib.shape_predictor(predictor_path)
detector = dlib.get_frontal_face_detector()
cap = cv2.VideoCapture(0)
count = 0
while True:
ret, frame = cap.read()
frame = imutils.resize(frame, width=500)
frame, roi = face_shape_detector_dlib(frame)
cv2.imshow('img', frame)
if roi is not None :
cv2.imshow('roi', roi)
else :
cv2.destroyWindow('roi')
c = cv2.waitKey(1)
if c == 27:#ESCを押してウィンドウを閉じる
break
if c == 32:#spaceで保存
count += 1
cv2.imwrite('./filename%03.f'%(count)+'.jpg', roi) #001~連番で保存
print('save done')
cap.release()
cv2.destroyAllWindows()
if __name__ == '__main__':
main()