OpenCV
機械学習
python3
dlib

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

はじめに

本ページは主に下記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()