Распознавание лиц — одна из задач, которую люди пытались решить с самого начала вычислительной эры. В настоящее время его можно найти где угодно, и наши лица, вероятно, сканируют несколько сотен раз в день.

В этой статье мы будем использовать каскадный классификатор на основе признаков Хаара. Сам алгоритм довольно старый, изобретен в 2001 году. Несмотря на свой возраст, алгоритм до сих пор используется благодаря своей удивительной скорости — его легко использовать в маломощных устройствах.

Подготовка

Прежде чем мы начнем, нам нужна хотя бы одна картинка для классификации. Вы можете выбрать его из своей личной коллекции или найти что-то в Интернете. Пожалуйста, имейте в виду, что классификатор, который мы используем, очень прост, поэтому у него могут быть проблемы с обнаружением всех типов лиц. Если ваша программа не определяет лицо, попробуйте изменить тестовое изображение. В моем случае у классификатора были трудности с поиском людей в очках. Это также не работает для повернутых лиц. Это можно исправить, обучив собственный классификатор, но этот процесс выходит далеко за рамки темы следующей статьи.

Для классификации нам также нужен предварительно обученный классификатор в файле XML. Я использую классификаторы в комплекте с самим OpenCV, я нашел haarcascade_frontalface_alt2 наиболее надежным. Вы можете найти это здесь". В будущем мы будем использовать другие предварительно обученные классификаторы из этого репозитория.

Распознавание лиц

Для начала нам нужно загрузить изображение. Изображения, которые я использовал, были довольно большими, поэтому я дополнительно изменил их размер. Вы можете пропустить это, если ваши изображения меньше. Преобразование изображения в оттенки серого необходимо, поскольку классификаторы работают только в оттенках серого.

orgimg = cv2.imread('img/test1.jpg')
img = cv2.resize(orgimg, (0,0), fx=0.2, fy=0.2)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

Теперь мы можем загрузить и использовать наш детектор:

face_cascade = cv2.CascadeClassifier('classifiers/haarcascade_frontalface_alt2.xml')
faces = face_cascade.detectMultiScale(gray, 1.3, 5)

После запуска этого кода мы получим все координаты лиц в массиве faces.

Вам может быть интересно, что это за магические числа, которые передаются методу detectMultiScale. Это в основном параметры для тонкой настройки алгоритма:

  • scaleFactor — параметр, указывающий, насколько уменьшается размер изображения при каждом масштабе изображения.
  • minNeighbors — параметр, указывающий, сколько соседей должен иметь каждый прямоугольник-кандидат, чтобы сохранить его.

Теперь мы можем пройтись по списку лиц:

for (x,y,w,h) in faces:
        cv2.rectangle(img, (x,y), (x+w, y+h), (255,0,0), 2)

Для каждого обнаруженного нами лица мы рисуем прямоугольник в том месте, где оно было найдено. Обратите внимание, что мы рисуем поверх исходного (цветного) изображения, а не поверх серого, который мы передали классификатору. Делая это, мы можем классифицировать уменьшенное изображение, но по-прежнему показывать результат на полной цветной версии.

Последнее, что нам нужно сделать, это показать результат. Мы можем сделать это, используя самый распространенный и простой способ отображения изображений в OpenCV:

cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Полный код на данный момент

import cv2
orgimg = cv2.imread('img/test1.jpg')
img = cv2.resize(orgimg, (0,0), fx=0.2, fy=0.2)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
face_cascade = cv2.CascadeClassifier('classifiers/haarcascade_frontalface_alt2.xml')
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
for (x,y,w,h) in faces:
    cv2.rectangle(img, (x,y), (x+w, y+h), (255,0,0), 2)
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Результат

Рисование кругов

Результат вполне удовлетворительный, но большинство граней круглые. Почему бы нам не нарисовать круг вместо прямоугольника, чтобы отметить их:

center = (x+w//2, y+h//2)
radius = (w+h)//4
cv2.circle(img, center, radius, (255, 0, 0), 2)

Вместо использования cv2.rectangle мы используем метод cv2.circle. Радиус - это среднее значение ширины и высоты, деленное на два. Поскольку наш классификатор всегда возвращает квадрат, мы могли бы просто использовать w//2 или h//2 напрямую, но это решение позволит нам использовать разные классификаторы, не меняя часть рисунка (мы могли бы нарисовать многоточие, но в случае граней разница между двумя радиусами должна быть меньше). быть таким большим).

Результат

Обнаружение глаз

Следующее, что мы можем сделать, это обнаружить глаза. В данных OpenCV для этого есть файл haarcascade_eye.xml с предварительно обученным классификатором. Вместо того, чтобы вызывать его на нашем полном изображении, мы можем искать только глаза на лицах. Кроме того, при этом у нас будет ссылка на то, сколько глаз мы нашли на одном лице, поэтому мы можем, например, отбросить все лица, на которых люди закрыли глаза (или сделать постапокалиптический детектор лиц, который не даст доступ для любых мутантов).

face_cascade = cv2.CascadeClassifier('classifiers/haarcascade_frontalface_alt2.xml')
eye_cascade = cv2.CascadeClassifier('classifiers/haarcascade_eye.xml')
faces = face_cascade.detectMultiScale(gray, 1.2, 3)
for (x,y,w,h) in faces:
        center = (x+w//2, y+h//2)
        radius = (w+h)//4
face_color = img[y:y+h, x:x+w]
        face_gray = img[y:y+h, x:x+w]
        eyes = eye_cascade.detectMultiScale(face_gray, 1.2, 3)
        if (len(eyes) != 2):
            break
        cv2.circle(img, center, radius, (255, 0, 0), 2)
        for (ex, ey, ew, eh) in eyes:
            eye_center = (ex+ew//2, ey+eh//2)
            eye_radius = (ew+eh)//4
            cv2.circle(face_color, eye_center, eye_radius, (128, 128, 0), 2)

Код по-прежнему довольно прост. Для каждого лица мы извлекаем его область из изображения (обратите внимание, что мы берем y в качестве первого аргумента, x в качестве второго — img — это пустой ndarray). Срезы не копируются, они по-прежнему ссылаются на исходную память, поэтому мы можем легко рисовать на них, а также будет обновляться основная картинка. Мы отбрасываем все лица, на которых нет 2 глаз.

Полный код

import cv2
orgimg = cv2.imread('img/test1.jpg')
img = cv2.resize(orgimg, (0,0), fx=0.2, fy=0.2)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
face_cascade = cv2.CascadeClassifier('classifiers/haarcascade_frontalface_alt2.xml')
eye_cascade = cv2.CascadeClassifier('classifiers/haarcascade_eye.xml')
faces = face_cascade.detectMultiScale(gray, 1.2, 3)
for (x,y,w,h) in faces:
        center = (x+w//2, y+h//2)
        radius = (w+h)//4
face_color = img[y:y+h, x:x+w]
        face_gray = img[y:y+h, x:x+w]
        eyes = eye_cascade.detectMultiScale(face_gray, 1.2, 3)
        if (len(eyes) != 2):
            break
        cv2.circle(img, center, radius, (255, 0, 0), 2)
        for (ex, ey, ew, eh) in eyes:
            eye_center = (ex+ew//2, ey+eh//2)
            eye_radius = (ew+eh)//4
            cv2.circle(face_color, eye_center, eye_radius, (128, 128, 0), 2)
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Результат

использованная литература

Статья в стиле Харра в Википедии

Изображение, использованное для тестов с Unsplash

Документальная статья из OpenCV

Фото на обложке Даниил Авилов на Unsplash

Первоначально опубликовано на странице http://heapster.net/blog/detecting-faces-in-opencv-python/ 3 декабря 2017 г.