Рубрики
Без рубрики

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

В этой статье мы будем выполнять распознавание лиц на изображениях и веб-камере в реальном времени с помощью Python и OpenCV.

Автор оригинала: Guest Contributor.

Вступление

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

В этой статье мы будем выполнять распознавание лиц в 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)

Когда модели обнаружения объектов обучаются, они обучаются обнаруживать лица определенного размера и могут пропустить лица, которые больше или меньше, чем они ожидают. Имея это в виду, изображение изменяется несколько раз в надежде, что лицо в конечном итоге будет “обнаруживаемым” размером. Масштабный коэффициент позволяет OpenCV знать, насколько масштабировать изображения. В нашем случае 1.3 это означает, что он может масштабироваться на 30% вниз, чтобы попытаться лучше соответствовать граням.

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

Оба параметра масштабный коэффициент и 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}')

На этом снимке мы использовали две разные модели. Модель по умолчанию для обнаружения лиц, обращенных вперед, и модель, построенная для лучшего обнаружения лиц, смотрящих в сторону.

Лица, обнаруженные с помощью модели frontal face , обведены зеленым цветом, а лица, обнаруженные с помощью модели profile face , обведены красным. Большинство лиц, найденных первой моделью, также были бы найдены второй, поэтому мы рисовали только красные прямоугольники, где модель profileface обнаружила лицо, но frontalface этого не сделала:

profiles_not_faces = [x for x in detected_profiles if x not in detected_faces]

Метод imshow() просто показывает переданное изображение в окне с предоставленным заголовком. С изображением, которое мы выбрали, это дало бы следующий результат:

фронтальное и профильное распознавание лиц

Использование различных значений для масштабного коэффициента и minNeighbors даст нам разные результаты. Например, используя масштабный коэффициент.1 и minNeighbors дает нам больше ложных срабатываний и истинных срабатываний с обеими моделями:

распознавание лиц более низкий масштабный коэффициент

Мы видим, что алгоритм не идеален, но он очень эффективен. Это особенно заметно при работе с данными реального времени, такими как видеопоток с веб-камеры.

Распознавание лиц в реальном времени С помощью веб-камеры

Видеопотоки-это просто потоки изображений. С эффективностью алгоритма Виола-Джонс мы можем делать распознавание лиц в режиме реального времени.

Шаги, которые нам нужно предпринять, очень похожи на предыдущий пример только с одним изображением – мы будем выполнять это на каждом изображении в потоке.

Чтобы получить видеопоток, мы будем использовать класс cv2.VideoCapture . Конструктор для этого класса принимает целочисленный параметр, представляющий видеопоток. На большинстве машин доступ к веб-камере можно получить, пройдя мимо 0 , но на машинах с несколькими видеопотоками вам может потребоваться попробовать разные значения.

Далее нам нужно прочитать отдельные изображения из входного потока. Это делается с помощью функции read () , которая возвращает retval и 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 очень просто для базовых программ обнаружения объектов. Экспериментальная настройка масштабного коэффициента и minNeighbors параметров для типов изображений, которые вы хотите обработать, может дать довольно точные результаты очень эффективно.