python dlib学习(十二):面部表情跟踪

前言

面部表情跟踪的原理就是检测人脸特征点,根据特定的特征点可以对应到特定的器官,比如眼睛、鼻子、嘴巴、耳朵等等,以此来跟踪各个面部器官的动作。

程序实现

原理很简单,下面直接上程序了:

# *_*coding:utf-8 *_*
# author: 许鸿斌

import sys
import cv2
import dlib
import os
import logging
import datetime
import numpy as np

def cal_face_boundary(img, shape):
    for index_, pt in enumerate(shape.parts()):
        if index_ == 0:
            x_min = pt.x
            x_max = pt.x
            y_min = pt.y
            y_max = pt.y
        else:
            if pt.x < x_min:
                x_min = pt.x

            if pt.x > x_max:
                x_max = pt.x

            if pt.y < y_min:
                y_min = pt.y

            if pt.y > y_max:
                y_max = pt.y

    # print('x_min:{}'.format(x_min))
    # print('x_max:{}'.format(x_max))
    # print('y_min:{}'.format(y_min))
    # print('y_max:{}'.format(y_max))

    # 如果出现负值,即人脸位于图像框之外的情况,应当忽视图像外的部分,将负值置为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:21 + 1]):
        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:26 + 1]):
        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:41 + 1]):
        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:47 + 1]):
        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:35 + 1]):
        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:59 + 1]):
        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:16 + 1]):
        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)

# 获取logger实例,如果参数为空则返回root logger
logger = logging.getLogger("PedestranDetect")
# 指定logger输出格式
formatter = logging.Formatter('%(asctime)s %(levelname)-8s: %(message)s')
# 文件日志
# file_handler = logging.FileHandler("test.log")
# file_handler.setFormatter(formatter)  # 可以通过setFormatter指定输出格式
# 控制台日志
console_handler = logging.StreamHandler(sys.stdout)
console_handler.formatter = formatter  # 也可以直接给formatter赋值
# 为logger添加的日志处理器
# logger.addHandler(file_handler)
logger.addHandler(console_handler)
# 指定日志的最低输出级别,默认为WARN级别
logger.setLevel(logging.INFO)


pwd = os.getcwd()
predictor_path = os.path.join(pwd, 'shape_predictor_68_face_landmarks.dat')

logger.info(u'导入人脸检测器')
detector = dlib.get_frontal_face_detector()
logger.info(u'导入人脸特征点检测器')
predictor = dlib.shape_predictor(predictor_path)

cap = cv2.VideoCapture(0)
cnt = 0
total_time = 0
start_time = 0
while(1):

    ret, frame = cap.read()
    # cv2.imshow("window", frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

    img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    dets = detector(img, 1)
    if dets:
        logger.info('Face detected')
    else:
        logger.info('No face detected')
    for index, face in enumerate(dets):
        # print('face {}; left {}; top {}; right {}; bottom {}'.format(index, face.left(), face.top(), face.right(),
        #                                                              face.bottom()))
        shape = predictor(img, face)

        # for index_, pt in enumerate(shape.parts()):
        #     pt_pos = (pt.x, pt.y)
        #     cv2.circle(frame, pt_pos, 2, (255, 0, 0), 1)

        features = np.zeros(img.shape[0:-1], dtype=np.uint8)
        for index_, pt in enumerate(shape.parts()):
            pt_pos = (pt.x, pt.y)
            cv2.circle(features, pt_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)

        logger.info('face shape: {} {}'.format(face.right()-face.left(), face.bottom()-face.top()))
        faceROI = cal_face_boundary(features, shape)
        logger.info('ROI shape: {}'.format(faceROI.shape))
        # faceROI = features[face.top():face.bottom(), face.left():face.right()]
        faceROI = cv2.resize(faceROI, (500, 500), interpolation=cv2.INTER_LINEAR)
        # logger.info('face {}'.format(index))
        cv2.imshow('face {}'.format(index), faceROI)

    if cnt == 0:
        start_time = datetime.datetime.now()
        cnt += 1
    elif cnt == 100:
        end_time = datetime.datetime.now()
        frame_rate = float(100) / (end_time-start_time).seconds
        # logger.info(start_time)
        # logger.info(end_time)
        logger.info(u'帧率:{:.2f}fps'.format(frame_rate))
        cnt = 0
    else:
        cnt += 1

    # logger.info(cnt)

程序不算复杂,所以就只解释要点。

  1. 首先开启摄像头,可以自行修改程序选择打开默认摄像头还是外置摄像头,默认选择电脑自带的默认摄像头;
  2. 检测人脸,并检测人脸68个特征点;
  3. 根据特征点对应关系,在新建的图像中,绘制面部各个器官的轮廓,图像大小统一缩放为 500 × 500

运行结果

按q键退出;如果摄像头中检测到人脸会自动提取信息,并在如下窗口中重新绘制面部器官轮廓。
1

2

猜你喜欢

转载自blog.csdn.net/hongbin_xu/article/details/79926839