Вступление
Обнаружение лиц - мощный и распространенный вариант использования машинного обучения. Его можно использовать для автоматизации ручных задач, таких как посещение школы и правоохранительные органы. С другой стороны, его можно использовать для биометрической авторизации.
В этой статье мы выполним обнаружение лиц в Python, используя OpenCV.
OpenCV
OpenCV - одна из самых популярных библиотек компьютерного зрения. Он был написан на C и C ++, а также обеспечивает поддержку Python, помимо Java и MATLAB. Хотя это не самая быстрая библиотека, с ней легко работать, и она предоставляет высокоуровневый интерфейс, позволяющий разработчикам писать стабильный код.
Давайте установим OpenCV, чтобы мы могли использовать его в нашем коде Python:
$ pip install opencv-contrib-python
В качестве альтернативы вы можете установить opencv-python
только для
основных модулей OpenCV. opencv-contrib-python
содержит основные
модули, а также модули contrib, которые обеспечивают расширенную
функциональность.
Обнаружение лиц на изображении с помощью OpenCV
Установив OpenCV, мы можем импортировать его как cv2
в наш код.
Чтобы прочитать изображение, мы будем использовать imread()
вместе с
путем к изображению, которое мы хотим обработать. Функция imread()
просто загружает изображение из указанного файла в ndarray
. Если
изображение не может быть прочитано, например, в случае отсутствия файла
или неподдерживаемого формата, функция вернет None
.
Мы будем использовать изображение из набора данных Kaggle :
import cv2
path_to_image = 'Parade_12.jpg'
original_image = cv2.imread(path_to_image)
Полная информация RGB не требуется для распознавания лиц. Цвет содержит много нерелевантной информации об изображении, поэтому более эффективно просто удалить его и работать с изображением в градациях серого. Кроме того, алгоритм Виолы-Джонса, который работает под капотом OpenCV, проверяет разницу в интенсивности области изображения. Изображения в градациях серого подчеркивают эту разницу более резко.
Примечание: в случае цветных изображений декодированные изображения
будут иметь каналы, сохраненные в порядке BGR, поэтому при изменении их
на оттенки серого нам нужно использовать флаг cv2.COLOR_BGR2GRAY
image = cv2.cvtColor(original_image, cv2.COLOR_BGR2GRAY)
Это можно было сделать напрямую при использовании imread()
, установив
флаг cv2.IMREAD_GRAYSCALE
original_image = cv2.imread(path_to_image, cv2.IMREAD_GRAYSCALE)
Библиотека OpenCV поставляется с несколькими предварительно обученными классификаторами, которые обучены находить разные вещи, такие как лица, глаза, улыбки, верхняя часть тела и т. Д.
Функции Haar для
обнаружения этих объектов хранятся в виде XML и, в зависимости от того,
как вы установили OpenCV, чаще всего могут быть найдены в
Lib\site-packages\cv2\data
. Их также можно найти в репозитории
OpenCVÂ
GitHub
.
Чтобы получить к ним доступ из кода, вы можете использовать
cv2.data.haarcascades
и добавить имя XML-файла, который вы хотите
использовать.
Мы можем выбрать, какие функции Haar мы хотим использовать для
обнаружения объектов, добавив путь к CascadeClassifier()
, который
использует предварительно обученные модели для обнаружения объектов:
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
Теперь мы можем использовать этот face_cascade
для обнаружения лиц на
изображении:
detected_faces = face_cascade.detectMultiScale(image=image, scaleFactor=1.3, minNeighbors=4)
Когда модели обнаружения объектов обучаются, они обучаются обнаруживать
лица определенного размера и могут пропускать лица, которые больше или
меньше, чем они ожидают. Имея это в виду, размер изображения несколько
раз изменяется в надежде, что лицо в конечном итоге станет
"обнаруживаемым" размером. scaleFactor
позволяет OpenCV знать,
насколько масштабировать изображения. В нашем случае 1.3
означает, что
его можно уменьшить на 30%
, чтобы попытаться лучше сопоставить лица.
Что касается minNeighbors
, он используется для контроля количества
ложных срабатываний и ложных отрицаний. Он определяет минимальное
количество положительных прямоугольников (определение черт лица),
которые должны быть рядом с положительным прямоугольником, чтобы он
считался действительно положительным. Если minNeighbors
установлено в
0
, малейший намек на лицо будет считаться окончательным, даже если
рядом с ним не обнаружены другие черты лица.
Оба параметра scaleFactor
и minNeighbors
в некоторой степени
произвольны и устанавливаются экспериментально. Мы выбрали значения,
которые хорошо работали для нас и не давали ложных срабатываний, за счет
большего количества ложноотрицательных результатов (необнаруженных лиц).
Метод detectMultiScale()
возвращает список прямоугольников всех
обнаруженных объектов (в нашем первом случае лица). Каждый элемент в
списке представляет собой уникальное лицо. Этот список содержит кортежи
(x, y, w, h)
, где значения x, y
представляют верхние левые
координаты прямоугольника, а значения w, h
представляют ширину и
высоту прямоугольника соответственно.
Мы можем использовать возвращенный список прямоугольников и использовать
cv2.rectangle()
чтобы легко рисовать прямоугольники, в которых было
обнаружено лицо. Имейте в виду, что предоставленный цвет должен быть
кортежем в порядке RGB:
for (x, y, width, height) in detected_faces:
cv2.rectangle(
image,
(x, y),
(x + width, y + height),
color,
thickness=2
)
А теперь давайте соберем все это вместе:
import cv2
def draw_found_faces(detected, image, color: tuple):
for (x, y, width, height) in detected:
cv2.rectangle(
image,
(x, y),
(x + width, y + height),
color,
thickness=2
)
path_to_image = 'Parade_12.jpg'
original_image = cv2.imread(path_to_image)
if original_image is not None:
# Convert image to grayscale
image = cv2.cvtColor(original_image, cv2.COLOR_BGR2GRAY)
# Create Cascade Classifiers
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
profile_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_profileface.xml")
# Detect faces using the classifiers
detected_faces = face_cascade.detectMultiScale(image=image, scaleFactor=1.3, minNeighbors=4)
detected_profiles = profile_cascade.detectMultiScale(image=image, scaleFactor=1.3, minNeighbors=4)
# Filter out profiles
profiles_not_faces = [x for x in detected_profiles if x not in detected_faces]
# Draw rectangles around faces on the original, colored image
draw_found_faces(detected_faces, original_image, (0, 255, 0)) # RGB - green
draw_found_faces(detected_profiles, original_image, (0, 0, 255)) # RGB - red
# Open a window to display the results
cv2.imshow(f'Detected Faces in {path_to_image}', original_image)
# The window will close as soon as any key is pressed (not a mouse click)
cv2.waitKey(0)
cv2.destroyAllWindows()
else:
print(f'En error occurred while trying to load {path_to_image}')
На этой картинке мы использовали две разные модели. Модель по умолчанию для обнаружения лиц, обращенных вперед, и модель, созданная для лучшего обнаружения лиц, смотрящих в сторону.
Лица, обнаруженные с frontalface
зеленым, а лица, обнаруженные с
profileface
красным. Большинство лиц, найденных первой моделью, также
были бы найдены второй, поэтому мы нарисовали только красные
прямоугольники там, где profileface
лица обнаружила лицо, а
frontalface
нет:
profiles_not_faces = [x for x in detected_profiles if x not in detected_faces]
Метод imshow()
просто показывает переданное изображение в окне с
заданным заголовком. С выбранным нами изображением это даст следующий
результат:
{.ezlazyload}
Использование разных значений для scaleFactor
и minNeighbors
даст
разные результаты. Например, использование scaleFactor = 1.1
и
minNeighbors = 4
дает нам больше ложных срабатываний и истинных
срабатываний с обеими моделями:
{.ezlazyload}
Мы видим, что алгоритм не идеален, но очень эффективен. Это особенно заметно при работе с данными в реальном времени, такими как видеопоток с веб-камеры.
Обнаружение лица в реальном времени с помощью веб-камеры
Видеопотоки - это просто потоки изображений. Благодаря эффективности алгоритма Виолы-Джонса мы можем обнаруживать лица в режиме реального времени.
Шаги, которые нам нужно предпринять, очень похожи на предыдущий пример только с одним изображением - мы будем выполнять это для каждого изображения в потоке.
Для получения видеопотока воспользуемся классом cv2.VideoCapture
Конструктор этого класса принимает целочисленный параметр,
представляющий видеопоток. На большинстве машин к веб-камере можно
получить доступ, передав 0
, но на машинах с несколькими видеопотоками
вам может потребоваться попробовать другие значения.
Далее нам нужно прочитать отдельные изображения из входного потока. Это
делается с помощью функции read()
, которая возвращает retval
и
image
. image
- это просто полученный кадр. retval
используется
для определения того, был ли получен кадр, и будет иметь значение
False
если это не так.
Однако, как правило, это несовместимо с потоками видеовхода (например, не определяет, что веб-камера была отключена), поэтому мы будем игнорировать это значение.
Давайте продолжим и изменим предыдущий код для обработки видеопотока:
import cv2
def draw_found_faces(detected, image, color: tuple):
for (x, y, width, height) in detected:
cv2.rectangle(
image,
(x, y),
(x + width, y + height),
color,
thickness=2
)
# Capturing the Video Stream
video_capture = cv2.VideoCapture(0)
# Creating the cascade objects
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
eye_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_eye_tree_eyeglasses.xml")
while True:
# Get individual frame
_, frame = video_capture.read()
# Covert the frame to grayscale
grayscale_image = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# Detect all the faces in that frame
detected_faces = face_cascade.detectMultiScale(image=grayscale_image, scaleFactor=1.3, minNeighbors=4)
detected_eyes = eye_cascade.detectMultiScale(image=grayscale_image, scaleFactor=1.3, minNeighbors=4)
draw_found_faces(detected_faces, frame, (0, 0, 255))
draw_found_faces(detected_eyes, frame, (0, 255, 0))
# Display the updated frame as a video stream
cv2.imshow('Webcam Face Detection', frame)
# Press the ESC key to exit the loop
# 27 is the code for the ESC key
if cv2.waitKey(1) == 27:
break
# Releasing the webcam resource
video_capture.release()
# Destroy the window that was showing the video stream
cv2.destroyAllWindows()
Заключение
В этой статье мы создали приложение для распознавания лиц с использованием Python и OpenCV.
Библиотека OpenCV очень проста в использовании для базовых программ
обнаружения объектов. Экспериментальная настройка параметров
scaleFactor
и minNeighbors
для типов изображений, которые вы хотите
обработать, может дать довольно точные результаты и очень эффективно.