Обнаружение и распознавание лиц на фотографиях с помощью OpenCV и Python

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

Каскады Хаара (Haar Cascade) – это метод обнаружения объектов, используемый для определения местоположения объектов на изображениях. Алгоритм обучается на большом количестве положительных и отрицательных образцов: положительные образцы – это изображения, которые содержат интересующий объект, а отрицательные образцы – это изображения, которые содержат что угодно, кроме искомого объекта. После обучения классификатор может найти интересующий объект на новых изображениях.

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

Требования

Для работы вам понадобится подготовленная среда Python 3, включая pip и venv. Настроить всё вам помогут эти мануалы:

1: Настройка локальной среды

Прежде чем приступить к написанию кода, сначала вам нужно подготовить свое рабочее пространство и установить пару зависимостей.

Создайте каталог для проекта:

mkdir face_scrapper

Перейдите в него:

cd face_scrapper

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

Создайте виртуальную среду по имени face_scrapper:

python3 -m venv face_scrapper

Активируйте изолированную среду:

source face_scrapper/bin/activate

Теперь в командной строке появится префикс – имя вашей виртуальной среды:

(face_scrapper) 8hosts-MPB:~ /face_scrapper $

Теперь, когда вы активировали свою виртуальную среду, вы можете использовать nano или другой текстовый редактор для создания файла require.txt. Этот файл указывает зависимости Python.

nano requirements.txt

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

  • numpy: это библиотека Python, которая обеспечивает поддержку больших многомерных массивов. Она также включает в себя большой набор математических функций для работы с массивами.
  • opencv-utils: расширенная библиотека для OpenCV, которая включает вспомогательные функции.
  • opencv-python: основной модуль OpenCV, который использует Python.

Добавьте в файл следующие зависимости:

numpy
opencv-utils
opencv-python

Сохраните и закройте файл.

Установите зависимости, передав файл requirements.txt пакетному менеджеру Python, pip. Флаг –r определяет расположение файла requirements.txt.

pip install -r requirements.txt

Итак, виртуальная среда проекта включена, необходимые библиотеки установлены. Теперь можно написать код для распознавания лиц на входных изображениях.

2: Написание и запуск детектора лиц

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

  • Количество лиц, найденных на входном изображении.
  • Новое изображение с прямоугольником вокруг каждого обнаруженного лица.

Создайте файл для хранения вашего кода:

nano app.py

В этом новом файле начните писать свой код, сначала импортировав необходимые библиотеки. Здесь нужно импортировать два модуля: cv2 и sys. Модуль cv2 импортирует библиотеку OpenCV в программу, а sys – обычные функции Python, которые будет использовать ваш код (такие как argv).

import cv2
import sys

Далее нужно указать, что входное изображение будет передано скрипту во время выполнения в качестве аргумента. Способ чтения первого аргумента в Python состоит в том, чтобы присвоить переменной значение, возвращаемое функцией sys.argv[1]:

...
imagePath = sys.argv[1]

Обычная практика обработки изображений – сначала преобразовать входное изображение в оттенки серого. Это связано с тем, что обнаружение яркости, в отличие от цвета, обычно дает лучшие результаты при обнаружении объектов. Добавьте следующий код, чтобы взять входное изображение в качестве аргумента и преобразовать его в оттенки серого:

...
image = cv2.imread(imagePath)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

Функция .imread() берет входное изображение, которое передается в качестве аргумента скрипту, и преобразовывает его в объект OpenCV. Затем функция OpenCV .cvtColor() преобразует объект входного изображения в объект в градациях серого.

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

...
faceCascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
faces = faceCascade.detectMultiScale(
gray,
scaleFactor=1.3,
minNeighbors=3,
minSize=(30, 30)
)
print("Found {0} Faces!".format(len(faces)))

Этот код создаст объект faceCascade, который загрузит файл каскада Хаара с помощью метода cv2.CascadeClassifier. Это позволяет Python и коду использовать Haar Cascade.

Затем код применяет метод .detectMultiScale() к объекту faceCascade. Это создает список прямоугольников для всех обнаруженных лиц на изображении. Список прямоугольников представляет собой набор расположений пикселей в форме Rect(x,y,w,h).

Вот список других параметров, использованных в этом коде:

  • gray: указывает на использование объекта изображения OpenCV в оттенках серого, который вы загрузили ранее.
  • scaleFactor: этот параметр указывает скорость уменьшения размера изображения при каждом масштабе. Модель использует фиксированный масштаб во время обучения, поэтому для лучшего обнаружения входные изображения можно уменьшить. Этот процесс останавливается после достижения порогового предела, определенного maxSize и minSize.
  • minNeighbors: этот параметр указывает, сколько соседей (или обнаружений) должен иметь каждый прямоугольник-кандидат для его сохранения. Более высокое значение может привести к меньшему количеству ложных срабатываний, но слишком высокое значение может исключить истинные положительные результаты.
  • minSize: позволяет определить минимально возможный размер объекта в пикселях. Объекты, меньшие, чем этот параметр, будут проигнорированы.

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

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

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

Этот код использует цикл for для итерации по списку расположений пикселей, возвращаемых методом faceCascade.detectMultiScale для каждого обнаруженного объекта. Метод rectangle будет принимать четыре аргумента:

  • image рисует прямоугольники на оригинальном изображении.
  • (x,y), (x+w, y+h) – это четыре расположения пикселей обнаруженного объекта. Метод rectangle с их помощью нарисует прямоугольник вокруг найденного лица на входном изображении.
  • (0, 255, 0) задает цвет формы. Этот аргумент передается как кортеж BGR. Например, чтобы прямоугольник был синим, нужно использовать (255, 0, 0). Мы используем зеленый.
  • 2 — толщина линии в пикселях.

Теперь, когда вы добавили код для рисования прямоугольников, используйте метод .imwrite(), чтобы записать новое изображение в локальную файловую систему как face_detected.jpg. Этот метод вернет true, если запись была успешной, и false, если записать новое изображение не удалось.

...
status = cv2.imwrite('faces_detected.jpg', image)

Теперь добавьте этот код, чтобы вывести в консоль статус true или false функции .imwrite(). Это даст вам знать, была ли запись изображения успешной после запуска скрипта.

...
print ("Image faces_detected.jpg written to filesystem: ",status)

В результате получился такой скрипт:

import cv2
import sys
imagePath = sys.argv[1]
image = cv2.imread(imagePath)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
faceCascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
faces = faceCascade.detectMultiScale(
gray,
scaleFactor=1.3,
minNeighbors=3,
minSize=(30, 30)
)
print("[INFO] Found {0} Faces!".format(len(faces)))
for (x, y, w, h) in faces:
cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)
status = cv2.imwrite('faces_detected.jpg', image)
print("[INFO] Image faces_detected.jpg written to filesystem: ", status)

Убедившись, что все введено правильно, сохраните и закройте файл.

Примечание: Этот код взят из публичной документации OpenCV.

Ваш код готов, пора запустить скрипт.

3: Запуск скрипта

Теперь вам нужно какое-нибудь тестовое изображение для проверки вашего скрипта. Когда вы найдете изображение, которое хотите использовать для тестирования, сохраните его в одном каталоге со скриптом app.py.

curl -O https://path/to/input_image

Когда у вас будет фотография, на которой нужно обнаружить лица, запустите скрипт и укажите путь к изображению:

python app.py path/to/input_image

Вы должны получить такой результат:

[INFO] Found 4 Faces!
[INFO] Image faces_detected.jpg written to filesystem:  True

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

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

4: Извлечение лиц и сохранение их на локальной машине (опционально)

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

Откройте app.py в текстовом редакторе:

nano app.py

Затем добавьте выделенные строки после строки cv2.rectangle:

...
for (x, y, w, h) in faces:
cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)
roi_color = image[y:y + h, x:x + w]

print("[INFO] Object found. Saving locally.")


cv2.imwrite(str(w) + str(h) + '_faces.jpg', roi_color)

...

Объект roi_color отображает расположения пикселей в списке лиц на исходном входном изображении. Переменные x, y, h и w являются расположениями в пикселях для каждого объекта, обнаруженного методом faceCascade.detectMultiScale. Затем код возвращает вывод о том, что объект был найден и будет сохранен локально.

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

Обновленный скрипт app.py будет выглядеть так:

import cv2
import sys
imagePath = sys.argv[1]
image = cv2.imread(imagePath)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
faceCascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
faces = faceCascade.detectMultiScale(
gray,
scaleFactor=1.3,
minNeighbors=3,
minSize=(30, 30)
)
print("[INFO] Found {0} Faces.".format(len(faces)))
for (x, y, w, h) in faces:
cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)
roi_color = image[y:y + h, x:x + w]
print("[INFO] Object found. Saving locally.")
cv2.imwrite(str(w) + str(h) + '_faces.jpg', roi_color)
status = cv2.imwrite('faces_detected.jpg', image)
print("[INFO] Image faces_detected.jpg written to filesystem: ", status)

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

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

python app.py path/to/image

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

[INFO] Found 4 Faces.
[INFO] Object found. Saving locally.
[INFO] Object found. Saving locally.
[INFO] Object found. Saving locally.
[INFO] Object found. Saving locally.
[INFO] Image faces_detected.jpg written to file-system: True

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

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

Теперь ваш скрипт может извлекать обнаруженные объекты из входного изображения и сохранять их локально.

Заключение

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

Tags: , , , ,