1. 程式人生 > >dlib 面部表情跟蹤

dlib 面部表情跟蹤

原理

面部表情跟蹤就是根據檢測的人臉特徵點對應到特定的器官,比如眼睛、鼻子、嘴巴、耳朵等等,以此來跟蹤各個面部器官的動作。

本文首先開啟攝像頭,然後利用dlib中dlib.get_frontal_face_detector()識別人臉,並利用dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")預測人臉的68點,之後根據特徵點對應關係,在新建的影象中,繪製面部各個器官的輪廓,最終顯示的影象統一為500*500.

程式碼實現:

# -*- coding: utf-8 -*-
import cv2
import dlib
#import datetime
import numpy as np

def face_boundary(img, shape):
    # enumerate方法同時返回資料物件的索引和資料
    for i, d in enumerate(shape.parts()):
        if i == 0:
            x_min = d.x
            x_max = d.x
            y_min = d.y
            y_max = d.y
        else:
            if d.x < x_min:
                x_min = d.x
                
            if d.x > x_max:
                x_max = d.x
                
            if d.y < y_min:
                y_min = d.y
                
            if d.y > y_max:
                y_max = d.y
            
    #如果出現負值,即人臉位於影象框之外的情況,應忽檢視像外的部分,將負值置為0
    if x_min < 0:
        x_min = 0

    if y_min < 0:
        y_min = 0
    
    if x_min == x_max or y_min == y_max:
        return None
    else:
        return img[y_min:y_max, x_min:x_max]
    
def draw_left_eyebrow(img, shape):
    #17 - 21
    pt_pos = []
    for index, pt in enumerate(shape.parts()[17:22]):
        pt_pos.append((pt.x, pt.y))

    for num in range(len(pt_pos)-1):
        cv2.line(img, pt_pos[num], pt_pos[num+1], 255, 2)
        
def draw_right_eyebrow(img, shape):
    # 22 - 26
    pt_pos = []
    for index, pt in enumerate(shape.parts()[22:27]):
        pt_pos.append((pt.x, pt.y))

    for num in range(len(pt_pos) - 1):
        cv2.line(img, pt_pos[num], pt_pos[num + 1], 255, 2)
        
def draw_left_eye(img, shape):
    # 36 - 41
    pt_pos = []
    for index, pt in enumerate(shape.parts()[36:42]):
        pt_pos.append((pt.x, pt.y))

    for num in range(len(pt_pos) - 1):
        cv2.line(img, pt_pos[num], pt_pos[num + 1], 255, 2)

    cv2.line(img, pt_pos[0], pt_pos[-1], 255, 2)

def draw_right_eye(img, shape):
    # 42 - 47
    pt_pos = []
    for index, pt in enumerate(shape.parts()[42:48]):
        pt_pos.append((pt.x, pt.y))

    for num in range(len(pt_pos) - 1):
        cv2.line(img, pt_pos[num], pt_pos[num + 1], 255, 2)

    cv2.line(img, pt_pos[0], pt_pos[-1], 255, 2)
    
def draw_nose(img, shape):
    # 27 - 35
    pt_pos = []
    for index, pt in enumerate(shape.parts()[27:36]):
        pt_pos.append((pt.x, pt.y))

    for num in range(len(pt_pos) - 1):
        cv2.line(img, pt_pos[num], pt_pos[num + 1], 255, 2)

    cv2.line(img, pt_pos[0], pt_pos[4], 255, 2)
    cv2.line(img, pt_pos[0], pt_pos[-1], 255, 2)
    cv2.line(img, pt_pos[3], pt_pos[-1], 255, 2)

def draw_mouth(img, shape):
    # 48 - 59
    pt_pos = []
    for index, pt in enumerate(shape.parts()[48:60]):
        pt_pos.append((pt.x, pt.y))

    for num in range(len(pt_pos) - 1):
        cv2.line(img, pt_pos[num], pt_pos[num + 1], 255, 2)

    cv2.line(img, pt_pos[0], pt_pos[-1], 255, 2)
    
    # 60 - 67
    pt_pos = []
    for index, pt in enumerate(shape.parts()[60:]):
        pt_pos.append((pt.x, pt.y))

    for num in range(len(pt_pos) - 1):
        cv2.line(img, pt_pos[num], pt_pos[num + 1], 255, 2)

    cv2.line(img, pt_pos[0], pt_pos[-1], 255, 2)
    
def draw_jaw(img, shape):
    # 0 - 16
    pt_pos = []
    for index, pt in enumerate(shape.parts()[0:17]):
        pt_pos.append((pt.x, pt.y))

    for num in range(len(pt_pos) - 1):
        cv2.line(img, pt_pos[num], pt_pos[num + 1], 255, 2)

# 使用特徵提取器get_frontal_face_detector
detector = dlib.get_frontal_face_detector()
# dlib的68點模型,使用訓練好的特徵預測器
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
#使用電腦自帶攝像頭
cap = cv2.VideoCapture(0)
font = cv2.FONT_HERSHEY_SIMPLEX


while(1):
    # cap.read()
    # 返回兩個值:
    #    一個布林值true/false,用來判斷讀取視訊是否成功/是否到視訊末尾
    #    影象物件,影象的三維矩陣
    ret, frame = cap.read()
    
    img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    dets = detector(img, 1)
    
    for index, face in enumerate(dets):
        # 使用預測器得到68點資料的座標
        shape = predictor(img, face)
        #image.shape獲取影象的形狀,返回值是一個包含行數、列數、通道數的元組
        features = np.zeros(img.shape[0:-1], dtype=np.uint8)#黑色影象
        for i, d in enumerate(shape.parts()):
            d_pos = (d.x, d.y)
            cv2.circle(features, d_pos, 2, 255, 1)
            
        draw_left_eyebrow(features, shape)
        draw_right_eyebrow(features, shape)
        draw_left_eye(features, shape)
        draw_right_eye(features, shape)
        draw_nose(features, shape)
        draw_mouth(features, shape)
        draw_jaw(features, shape)
        
        faceROI = face_boundary(features, shape)
        faceROI = cv2.resize(faceROI, (500, 500), interpolation=cv2.INTER_LINEAR)
        cv2.imshow('face {}'.format(index), faceROI)
     
    if cv2.waitKey(10) == 27:   #按ESC
                Break

執行結果: