Edited at

OpenCVでキャプチャした画像から、Dlibで顔と輪郭などを検出する

More than 1 year has passed since last update.


はじめに

本ページは主に下記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を押すとウィンドウを閉じることができます。


capture_face_landmarks.py

#! /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()