Обнаружение лица и выделение характерных точек (Face Detection in Python)

Автор: | 30.08.2019

Tags:  OpenCV Face Detection keypoints landmarks Python MTCNN DLIB

Введение
Обнаружение лица через OpenCV
Обнаружение лица в реальном времени (с помощью веб-камеры)
Обнаружение лица через MTCNN
Извлечение изображений лиц из фото
Выделение 5 характерных точек лица через MTCNN
Обнаружение лица и выделение 5 характерных точек  в реальном времени через MTCNN
Обнаружение лица и выделение 68 характерных точек через DLIB
Полезные ссылки

Введение

Обнаружение лица — локализация и выделение области лица из фона. Это первый из этапов реализации системы распознавания лиц.

Два основных подхода к обнаружению лиц:

  • классический каскадный классификатор на основе признаков-фильтров (метод  Виолы-Джонса);
  • совместное обнаружение и выравнивание лиц с использованием многозадачных каскадных сверточных сетей.

Первый подход реализуется через библиотеку OpenCV, второй — через библиотеки DLIB. MTCNN и др.

Ниже приводятся примеры приложений для обоих подходов.

Для запуска приложений использовался Visual Studio редактор. Для начинающих рекомендуются статьи:

Обнаружение лица через OpenCV

Создайте Python приложение с программным кодом:

# plot photo with detected faces using opencv cascade classifier
from cv2 import imread
from cv2 import imshow
from cv2 import waitKey
from cv2 import destroyAllWindows
from cv2 import CascadeClassifier
from cv2 import rectangle
# load the photograph
pixels = imread('test1.jpg')
# load the pre-trained model
classifier = CascadeClassifier('haarcascade_frontalface_default.xml')
# perform face detection
bboxes = classifier.detectMultiScale(pixels)
# print bounding box for each detected face
for box in bboxes:
    # extract
    x, y, width, height = box
    x2, y2 = x + width, y + height
    # draw a rectangle over the pixels
    rectangle(pixels, (x, y), (x2, y2), (0,0,255), 1)
# show the image
imshow('face detection', pixels)
# keep the window open until we press a key
waitKey(0)
# close the window
destroyAllWindows()

Загрузите файл изображения test1.jpg (рисунок ниже) и предварительно обученную модель для фронтального распознавания лиц с именем файла  haarcascade_frontalface_default.xml.

Поместите файлы test1.jpg и haarcascade_frontalface_default.xml в рабочий каталог проекта приложения (где и файл с расширением .py).

При запуске программы система подскажет, какие пакеты необходимо инсталлировать, например — пакет opencv-python.

Результат запуска приложения:

Обнаружение лица в реальном времени

Программный код:

import cv2
from cv2 import CascadeClassifier
import sys

faceCascade = CascadeClassifier('haarcascade_frontalface_default.xml')
video_capture = cv2.VideoCapture(0)
while True:
    # Capture frame-by-frame
    ret, frame = video_capture.read()
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    faces = faceCascade.detectMultiScale(
        gray,
        scaleFactor=1.1,
        minNeighbors=5,
        minSize=(30, 30),
        flags=cv2.CASCADE_SCALE_IMAGE)
    # Draw a rectangle around the faces
    for (x, y, w, h) in faces:
        cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
    # Display the resulting frame
    cv2.imshow('Video', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
# When everything is done, release the capture
video_capture.release()
cv2.destroyAllWindows()

Запускаем приложение:

Обнаружение лица через MTCNN

Программный код:

# face detection with mtcnn on a photograph
from matplotlib import pyplot
from matplotlib.patches import Rectangle
from mtcnn.mtcnn import MTCNN

# draw an image with detected objects
def draw_image_with_boxes(filename, result_list):
    # load the image
    data = pyplot.imread(filename)
    # plot the image
    pyplot.imshow(data)
    # get the context for drawing boxes
    ax = pyplot.gca()
    # plot each box
    for result in result_list:
        # get coordinates
        x, y, width, height = result['box']
        # create the shape
        rect = Rectangle((x, y), width, height, fill=False, color='red')
        # draw the box
        ax.add_patch(rect)
    # show the plot
    pyplot.show()

filename = 'test1.jpg'
# load image from file
pixels = pyplot.imread(filename)
# create the detector, using default weights
detector = MTCNN()
# detect faces in the image
faces = detector.detect_faces(pixels)
# display faces on the original image
draw_image_with_boxes(filename, faces)

Для запуска приложения потребуется инсталлировать в проекте пакет tensorflow. Обычно устанавливается последняя на сегодняшний день версия пакета (tensorflow 2.0.0). Однако, с этой версией код выдает ошибку: module ‘tensorflow’ has no attribute ‘ConfigProto’.

Инсталлировал пакет  facenet_recognition.

При инсталляции пакета facenet_recognition происходит инсталляция пакета tensorflow 1.2.0 взамен пакета tensorflow 2.0.0.

Результат запуска приложения:

Извлечение изображений лиц из фото

Следующий код извлекает каждое лицо и рисует его отдельно:

# extract and plot each detected face in a photograph
from matplotlib import pyplot
from matplotlib.patches import Rectangle
from matplotlib.patches import Circle
from mtcnn.mtcnn import MTCNN

# draw each face separately
def draw_faces(filename, result_list):
    # load the image
    data = pyplot.imread(filename)
    # plot each face as a subplot
    for i in range(len(result_list)):
        # get coordinates
        x1, y1, width, height = result_list[i]['box']
        x2, y2 = x1 + width, y1 + height
        # define subplot
        pyplot.subplot(1, len(result_list), i+1)
        pyplot.axis('off')
        # plot face
        pyplot.imshow(data[y1:y2, x1:x2])
    # show the plot
    pyplot.show()

filename = 'test1.jpg'
# load image from file
pixels = pyplot.imread(filename)
# create the detector, using default weights
detector = MTCNN()
# detect faces in the image
faces = detector.detect_faces(pixels)
# display faces on the original image
draw_faces(filename, faces)

Выделение характерных точек лица

Программный код:

# face detection with mtcnn on a photograph
from matplotlib import pyplot
from matplotlib.patches import Rectangle
from matplotlib.patches import Circle
from mtcnn.mtcnn import MTCNN

# draw an image with detected objects
def draw_image_with_boxes(filename, result_list):
    # load the image
    data = pyplot.imread(filename)
    # plot the image
    pyplot.imshow(data)
    # get the context for drawing boxes
    ax = pyplot.gca()
    # plot each box
    for result in result_list:
        # get coordinates
        x, y, width, height = result['box']
        # create the shape
        rect = Rectangle((x, y), width, height, fill=False, color='red')
        # draw the box
        ax.add_patch(rect)
        # draw the dots
        for key, value in result['keypoints'].items():
            # create and draw dot
            dot = Circle(value, radius=2, color='red')
            ax.add_patch(dot)
    # show the plot
    pyplot.show()

filename = 'test1.jpg'
# load image from file
pixels = pyplot.imread(filename)
# create the detector, using default weights
detector = MTCNN()
# detect faces in the image
faces = detector.detect_faces(pixels)
# display faces on the original image
draw_image_with_boxes(filename, faces)

Результат запуска приложения:

Глаза и нос хорошо обнаружены на каждом лице, однако рот на правом лице мог бы быть обнаружен и лучше — точки расположены чуть ниже углов рта.

Обнаружение лица и выделение характерных точек  в реальном времени

Программный код:

import numpy as np
import cv2
from mtcnn.mtcnn import MTCNN

detector = MTCNN()
video_capture = cv2.VideoCapture(0)
while True:
    # Capture frame-by-frame
    ret, frame = video_capture.read()
    image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    result = detector.detect_faces(image)

    # Result is an array with all the bounding boxes detected.
    bounding_box = result[0]['box']
    keypoints = result[0]['keypoints']
    cv2.rectangle(frame,
      (bounding_box[0], bounding_box[1]),
      (bounding_box[0]+bounding_box[2], bounding_box[1] + bounding_box[3]),
      (0,155,255),
      2)
    cv2.circle(frame,(keypoints['left_eye']), 3, (0,155,255), 2)
    cv2.circle(frame,(keypoints['right_eye']), 2, (0,155,255), 2)
    cv2.circle(frame,(keypoints['nose']), 3, (0,155,255), 2)
    cv2.circle(frame,(keypoints['mouth_left']), 3, (0,155,255), 2)
    cv2.circle(frame,(keypoints['mouth_right']), 3, (0,155,255), 2)

    cv2.line(frame,(keypoints['left_eye']),keypoints['right_eye'], (0,0,255), 1)
    cv2.line(frame,(keypoints['left_eye']),keypoints['nose'], (0,255,0), 2)
    cv2.line(frame,(keypoints['right_eye']),keypoints['nose'], (255,0,0), 2)
 
    dX = keypoints['right_eye'][0] - keypoints['left_eye'][0]
    dY = keypoints['right_eye'][1] - keypoints['left_eye'][1]
    dist_norm = np.sqrt((dX ** 2) + (dY ** 2))
 
    dX = keypoints['left_eye'][0] - keypoints['nose'][0]
    dY = keypoints['left_eye'][1] - keypoints['nose'][1]
    dist_left = np.sqrt((dX ** 2) + (dY ** 2))

    dX = keypoints['right_eye'][0] - keypoints['nose'][0]
    dY = keypoints['right_eye'][1] - keypoints['nose'][1]
    dist_right = np.sqrt((dX ** 2) + (dY ** 2))

    # Normalized Features-distances
    input_left = dist_left/dist_norm
    input_right = dist_right/dist_norm

    cv2.putText(frame, str(input_left), (50,50), 
                cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 255, 0), lineType=cv2.LINE_AA) 
    cv2.putText(frame, str(input_right), (50,100), 
                cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255, 0, 0), lineType=cv2.LINE_AA)
 
    print('FRAME')
    print(input_left)
    print(input_right)
 
    #print(result)

    # Display the resulting frame
    cv2.imshow('Video', frame)

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
# When everything is done, release the capture
video_capture.release()
cv2.destroyAllWindows()

Результат запуска приложения:

Обнаружение лица и выделение характерных точек через DLIB

Программный код:

# Импорт необходимых модулей
from imutils.video import VideoStream, FPS
import cv2
import numpy as np
import dlib

# Запуск видео потока
vs = VideoStream(src=0).start()

# Подключение детектора, настроенного на поиск человеческих лиц
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")

while True:
 
    # Получение изображения из видео потока
    frame = vs.read()

    # Конвертирование изображения в черно-белое
    grayFrame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # Обнаружение лиц и построение прямоугольного контура
    faces = detector(grayFrame)

    # Обход списка всех лиц попавших на изображение
    for face in faces:

        # Выводим количество лиц на изображении
        cv2.putText(frame, "{} face(s) found".format(len(faces)), (10, 40), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)

        # Получение координат вершин прямоугольника и его построение на изображении
        x1 = face.left()
        y1 = face.top()
        x2 = face.right()
        y2 = face.bottom()
        cv2.rectangle(frame, (x1, y1), (x2, y2), (255, 0, 0), 1)
 
        # Получение координат контрольных точек и их построение на изображении
        landmarks = predictor(grayFrame, face)
        for n in range(0, 68):
            x = landmarks.part(n).x
            y = landmarks.part(n).y
            cv2.circle(frame, (x, y), 3, (255, 0, 0), -1)

    cv2.putText(frame, "Press ESC to close frame", (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)

    # Вывод преобразованного изображения
    cv2.imshow("Frame", frame)

    # Для выхода из цикла нажать ESC
    key = cv2.waitKey(1)
    if key == 27:
        break

Могут быть проблемы с подключением библиотеки DLIB.  Как они решаются, см. Верификация лица с dlib

Результат запуска приложения:

Полезные ссылки:

 

Автор: Николай Свирневский