Поиск лиц на изображении с использованием OpenCV в Python

Обнаружение объектов — это компьютерная технология, связанная с компьютерным зрением и обработкой изображений, которая занимается обнаружением экземпляров семантических объектов определенного класса, например, человеческие лица, автомобили, фрукты и т. д., в цифровых изображениях и видео.

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

После этого мы погрузимся в использование детекторов Single Shot Multibox (или коротко SSD), которые представляют собой метод обнаружения объектов на изображениях с использованием одной глубокой нейронной сети.

Примечание: стоит упомянуть, что вам нужно различать обнаружение объекта и классификацию объекта, обнаружение объекта — это обнаружение объекта и его расположение на изображении, а классификация объектов — это распознавание того, к какому классу принадлежит объект. Если вас интересует классификация изображений, перейдите к этому уроку.

Распознавание лиц с помощью каскадов Хаара

Каскадные классификаторы Хаара на основе функций — это подход, основанный на машинном обучении, при котором каскадная функция обучается на основе большого количества положительных и отрицательных изображений. Затем он используется для обнаружения объектов на других изображениях. Преимущество каскадных классификаторов Хаара в том, что вы можете создать классификатор любого объекта, который захотите, OpenCV уже предоставил вам некоторые параметры классификатора, поэтому вам не нужно собирать какие-либо данные для обучения.

Для начала установите нужные нам пакеты:

pip3 install opencv-python numpy

Хорошо, создайте новый файл Python и продолжайте, для начала, импортируя OpenCV:

import cv2

Вам понадобится образец изображения для тестирования, убедитесь, что на нем есть четкие лицевые стороны. Я буду использовать стоковое изображение, которое содержит двух очень милых детишек:

# Загрузка изображения
image = cv2.imread("kids.jpg")

Функция imread() загружает изображение из указанного файла и возвращает его как N-мерный массив numpy.

Прежде чем мы обнаружим лица на изображении, нужно преобразовать изображение в оттенки серого, потому что функция, которую мы собираемся использовать для обнаружения лиц, ожидает изображение в оттенках серого:

# преобразуем изображение к оттенкам серого
image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

Функция cvtColor() преобразует входное изображение из одного цветового пространства в другое, мы указали код cv2.COLOR_BGR2GRAY, что означает преобразование из BGR (Blue Green Red) в оттенки серого.

Поскольку это руководство посвящено обнаружению человеческих лиц, загрузите каскад Хаара для обнаружения этих лиц из списка. Точнее файл haarcascade_frontalface_default.xml. Поместим его в папку с названием cascades, а затем загрузим:

# инициализировать распознаватель лиц (каскад Хаара по умолчанию)
face_cascade = cv2.CascadeClassifier("cascades/haarcascade_fontalface_default.xml")

Давайте теперь обнаружим все лица на изображении:

# обнаружение всех лиц на изображении
faces = face_cascade.detectMultiScale(image_gray)
# печатать количество найденных лиц
print(f"{len(faces)} лиц обнаружено на изображении.")

Функция detectMultiScale() принимает изображение в качестве параметра и обнаруживает объекты разных размеров в виде списка прямоугольников, давайте нарисуем эти прямоугольники на изображении:

# для всех обнаруженных лиц рисуем синий квадрат
for x, y, width, height in faces:
    cv2.rectangle(image, (x, y), (x + width, y + height), color=(255, 0, 0), thickness=2)

Наконец, сохраним новое изображение:

# сохраним изображение с обнаруженными лицами
cv2.imwrite("kids_detected.jpg", image)

Вот что у меня получилось:

Довольно круто, не правда ли? Не стесняйтесь использовать другие классификаторы объектов, другие изображения и, что еще интереснее, используйте свою веб-камеру! Вот код для этого:

import cv2

# создать новый объект камеру
cap = cv2.VideoCapture(0)
# инициализировать поиск лица (по умолчанию каскад Хаара)
face_cascade = cv2.CascadeClassifier("cascades/haarcascade_fontalface_default.xml")

while True:
    # чтение изображения с камеры
    _, image = cap.read()
    # преобразование к оттенкам серого
    image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    # обнаружение лиц на фотографии
    faces = face_cascade.detectMultiScale(image_gray, 1.3, 5)
    # для каждого обнаруженного лица нарисовать синий квадрат
    for x, y, width, height in faces:
        cv2.rectangle(image, (x, y), (x + width, y + height), color=(255, 0, 0), thickness=2)
    cv2.imshow("image", image)
    if cv2.waitKey(1) == ord("q"):
        break

cap.release()
cv2.destroyAllWindows()

Как только вы загрузите этот скрипт (если, конечно, у вас есть веб-камера), он подключит вашу веб-камеру и начнет рисовать синие прямоугольники вокруг всех лицевых сторон изображения. Код не такой уж сложный, все, что я изменил: вместо чтения изображения из файла я создал объект VideoCapture, который каждый раз в цикле while читает камеру, но как только вы нажмете кнопку q, основной цикл завершится.

Распознавание лиц с помощью SSD

Как видите, предыдущий метод не так уж и сложен. К сожалению, он устарел и сегодня, в реальном мире, редко когда используется. Однако, нейронные сети всегда приходят на помощь, и, к счастью для нас, OpenCV содержит замечательный для нас модуль dnn в пакете cv2, который позволяет находить лица, используя предварительно обученные модели глубокого обучения.

Прежде, чем начать искать лица с использованием SSD в OpenCV, необходимо загрузить архитектуру модели обнаружения лиц ResNet вместе с ее предварительно найденными весами обученной DNN, а затем сохранить их в папку weights в текущем рабочем каталоге:

import cv2
import numpy as np

# https://raw.githubusercontent.com/opencv/opencv/master/samples/dnn/face_detector/deploy.prototxt
prototxt_path = "weights/deploy.prototxt.txt"
# https://raw.githubusercontent.com/opencv/opencv_3rdparty/dnn_samples_face_detector_20180205_fp16/res10_300x300_ssd_iter_140000_fp16.caffemodel 
model_path = "weights/res10_300x300_ssd_iter_140000_fp16.caffemodel"

Теперь, чтобы загрузить реальную модель, нам нужно использовать метод readNetFromCaffe(), который принимает в качестве аргументов архитектуру модели и веса:

# загрузим модель Caffe
model = cv2.dnn.readNetFromCaffe(prototxt_path, model_path)

Мы будем использовать то же изображение:

# читаем изображение
image = cv2.imread("kids.jpg")
# получаем ширину и высоту изображения
h, w = image.shape[:2]

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

# предварительная обработка: изменение размера и вычитание среднего
blob = cv2.dnn.blobFromImage(image, 1.0, (300, 300), (104.0, 177.0, 123.0))

Будем использовать объект blob в качестве входа в сеть и выполнить прямую связь, чтобы получить обнаруженные лица:

# устанавливаем на вход нейронной сети изображение
model.setInput(blob)
# выполняем логический вывод и получаем результат
output = np.squeeze(model.forward())

Теперь выходной объект содержит все обнаруженные объекты (в данном случае лица), давайте переберем этот массив и нарисуем все лица на изображении с достоверностью более 50%:

font_scale = 1.0
for i in range(0, output.shape[0]):
    # получить уверенность
    confidence = output[i, 2]
    # если достоверность выше 50%, то нарисуйте окружающий прямоугольник
    if confidence > 0.5:
        # получить координаты окружающего блока и масштабировать их до исходного изображения
        box = output[i, 3:7] * np.array([w, h, w, h])
        # преобразовать в целые числа
        start_x, start_y, end_x, end_y = box.astype(np.int)
        # рисуем прямоугольник вокруг лица
        cv2.rectangle(image, (start_x, start_y), (end_x, end_y), color=(255, 0, 0), thickness=2)
        # также нарисуем текст
        cv2.putText(image, f"{confidence*100:.2f}%", (start_x, start_y-5), cv2.FONT_HERSHEY_SIMPLEX, font_scale, (255, 0, 0), 2)

После того, как мы убедились в достоверности модели обнаруженного объекта, получаем охватывающий прямоугольник и умножаем его на ширину и высоту исходного изображения для вычисления правильных координат прямоугольника, потому что, как вы помните, ранее изменялся размер изображения до (300, 300), поэтому и на выходе также должно быть значение от 0 до 300.

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

# show the image
cv2.imshow("image", image)
cv2.waitKey(0)
# save the image with rectangles
cv2.imwrite("kids_detected_dnn.jpg", image)

Вот получившееся изображение:

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

Существует множество реальных приложений для обнаружения лиц, например, мы использовали обнаружение лиц для их размытия на изображениях и в видео в реальном времени, используя OpenCV!

Хорошо! Всё для этого урока, все учебные материалы (включая тестовое изображение, параметры каскада хаара, веса моделей SSD и полный код) можно посмотреть и скачать здесь.

По мотивам Face Detection using OpenCV in Python

Print Friendly, PDF & Email

CC BY-NC 4.0 Поиск лиц на изображении с использованием OpenCV в Python, опубликовано К ВВ, лицензия — Creative Commons Attribution-NonCommercial 4.0 International.


Респект и уважуха

Добавить комментарий