LoginSignup
35
37

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-11-03

はじめに

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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
35
37