Розпізнаємо особи на фото з допомогою Python і OpenCV


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

Що нам знадобиться:
• Встановлений Python 2.7 з бібліотеками NumPy PIL
• OpenCV 2-ї версії

Здесь посилання на матеріал по встановленню всіх необхідних компонентів. Установка всього необхідного не складе праці.

Для початку давайте розберемося, як розпізнати обличчя на фотографії. По-перше, потрібно знайти, де на фото розташоване обличчя людини і не сплутати його з годинником на стіні і кактусом на підвіконні. Здавалося б, просте завдання для людини, що виявляється не такою простою для комп'ютера. Для того, щоб знайти особа ми повинні виділити його основні компоненти, такі як ніс, лоб, очі, губи і т. д. Для цього будемо використовувати шаблони (вони ж примітиви Хаара) на кшталт таких:



Якщо шаблони відповідають конкретним областям на зображенні, будемо вважати, що на зображенні є людське обличчя. Насправді подібних шаблонів набагато більше. Для кожного з них вважається різниця між яскравістю білої і чорної областей. Це значення порівнюється з еталоном і приймається рішення про те, чи є тут частина людського обличчя чи ні.
Цей метод називається методом Віоли-Джонса (так само відомий як каскади Хаара). Давайте уявимо, що у нас на фотографії не одне велике обличчя, а багато дрібних. Якщо застосувати шаблони до всієї картинці ми не знайдемо там осіб, т. к. вони будуть менше шаблонів. Для того, щоб шукати на всьому фото особи різних розмірів використовується метод ковзного вікна. Саме всередині цього вікна і вираховуються примітиви. Вікно як би ковзає по всьому зображенню. Після кожного проходження зображення вікно збільшується, щоб знайти особи більшого масштабу.

Наочно демонстрацію алгоритму можна подивитися на цьому відео:


І так ми знайшли обличчя на фотографії, але як визначити, що це обличчя саме того, кого ми шукаємо? Для вирішення цієї задачі будемо використовувати алгоритм Local Binary Patterns. Суть його полягає в тому, що ми розбиваємо зображення на частини і в кожній такій частині кожен піксель порівнюється з сусідніми 8 пікселями. Якщо значення центрального пікселя більше сусіднього, то пишемо 0, в іншому випадку 1. І так для кожного пікселя у нас виходить деяке число. Далі на основі цих чисел для всіх частин, на які ми розбивали фотографію, вважається гістограма. Всі гістограми з усіх частин об'єднуються в один вектор характеризує зображення в цілому. Якщо ми хочемо дізнатися, наскільки схожі дві особи, нам доведеться обчислити для кожного з них такий вектор і порівняти їх. Малюнки нижче допоможуть краще зрозуміти суть алгоритму:




Ну добре, давайте, нарешті напишемо трохи коду.

# Імпортуємо необхідні модулі
import cv2, os
import numpy as np
from PIL Image import

# Для детектування осіб використовуємо каскади Хаара
cascadePath = "haarcascade_frontalface_default.xml"
faceCascade = cv2.CascadeClassifier(cascadePath)

# Для розпізнавання використовуємо локальні бінарні шаблони
recognizer = cv2.createLBPHFaceRecognizer(1,8,8,8,123)

Параметр cascadePath містить ім'я файлу з вже готовими значеннями для розпізнавання осіб. Цей файл можна взяти з директорії з OpenCV (opencv\build\etc\haarcascades\).
Далі створюємо об'єкт CascadeClassifier і об'єкт розпізнавання осіб LBPHFaceRecognizer. На останньому зупинимося детальніше, точніше, на його параметрах. Перші два значення 1 і 8 характеризують околі пікселя. Наочно, що це таке можна продемонструвати цією картинкою:


Тобто перше число це радіус в якому ми вибираємо пікселі, а другий число цих пікселів. Чим більше пікселів в околі точки ми візьмемо, тим точніше буде наше розпізнавання.
Наступні параметри (8,8) характеризують розміри областей на які ми розбиваємо вихідне зображення з обличчям. Чим воно менше, тим більше буде таких областей і тим якісніше розпізнавання.
І нарешті, останнє значення це параметр confidence threshold, визначає граничне значення для розпізнавання особи. Чим менше confidence тим більше алгоритм впевнений в тому, що на фотографії зображено відома йому особа. Поріг означає, що коли впевненості мало алгоритм просто вважає це особа незнайомим. В даному випадку поріг дорівнює 123.

Йдемо далі. Напишемо функцію, яка знаходить по визначеному шляху у всіх фотографіях обличчя людей і зберігає їх.


def get_images(path):
# Шукаємо всі фотографії і записуємо їх в image_paths
image_paths = [os.path.join(path, f) for in f os.listdir(path) if not f.endswith('.happy')]

images = []
labels = []

for image_path in image_paths:
# Переводимо зображення в чорно-білий формат і наводимо його до формату масиву
gray = Image.open(image_path).convert('L')
image = np.array(gray, 'uint8')
# З кожного імені файлу витягаємо номер людини, зображеного на фото
subject_number = int(os.path.split(image_path)[1].split(".")[0].replace("subject", ""))

# Визначаємо області де є особи
faces = faceCascade.detectMultiScale(image, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))
# Якщо особа знайшлося додаємо його в список images, а відповідний йому номер у списку labels
for (x, y, w, h) faces in:
images.append(image[y: y + h, x: x + w])
labels.append(subject_number)
# У вікні показуємо зображення
cv2.imshow("", image[y: y + h, x: x + w])
cv2.waitKey(50)
return images, labels

Для прикладу я використовував БД осіб під назвою Yale Faces. У ній є 15 осіб з різними виразами осіб на кожній фотографії.


Ім'я кожного файлу в БД виглядає наступним чином: subject01.sad. Спочатку йде слово subject, далі порядковий номер людини, а після характеристика фото. Наприклад, характеристика sad означає сумне обличчя, happy веселе і т. п.
Функція get_images зчитує кожну фотографію, крім тих, що з закінченням .happy і виділяє ту область, де перебуває особа. Фотографії з веселим виразом обличчя будемо використовувати на наступному кроці для розпізнавання, це буде контрольна вибірка, тобто ті фото на яких ми будемо перевіряти якість розпізнавання.
Так само з кожної назви файлу витягується номер людину на фотографії і зберігається список labels. Кожної фотографії в підсумку буде прив'язаний цей номер.
Функція faceCascade.detectMultiScale() визначає області на фотографії, де є людські
особи. Вона повертає список з параметрами [x,y,w,h] для кожного знайденого особи. Ці
параметри описують прямокутну область в тому місці, де знайшлося особа.

Тепер давайте розберемося з параметрами функції:
image – вихідне зображення
scaleFactor – визначає те, на скільки буде збільшуватися ковзне вікно пошуку на кожній ітерації. 1.1 означає на 10%, 1.05 на 5% і т. д. Чим більше це значення, тим швидше працює алгоритм.
minNeighbors — Чим більше це значення, тим більш параноїдальним буде пошук і тим частіше він буде пропускати реальні особи, вважаючи, що це помилкове спрацьовування. Оптимальне значення 3-6.
minSize – мінімальний розмір обличчя на фото. 30 на 30 зазвичай цілком достатньо.

Ну що ж, тепер ми можемо створити набір осіб і відповідних їм міток. Давайте навчимо програму розпізнавати ці особи.

# Шлях до фотографій
path = './yalefaces'
# Отримуємо особи і відповідні їм номери
images, labels = get_images(path)
cv2.destroyAllWindows()

# Навчаємо програму розпізнавати обличчя
recognizer.train(images, np.array(labels))


Вказуємо шлях до наших фото, отримуємо список з фотографіями та підписами. А далі запускаємо нашу функцію тренування з допомогою алгоритму LBP. Нічого надприродного в ньому немає, просто передаємо їй значення, отримані після запуску функції get_images(). Все інше програма зробить сама.
І так у нас є навчений «розпізнавач» і є набір щасливих осіб. Тепер нам необхідно попросити алгоритм розпізнати ці щасливі обличчя.

# Створюємо список фотографій для розпізнавання
image_paths = [os.path.join(path, f) for in f os.listdir(path) if f.endswith('.happy')]

for image_path in image_paths:
# Шукаємо обличчя на фотографіях
gray = Image.open(image_path).convert('L')
image = np.array(gray, 'uint8')
faces = faceCascade.detectMultiScale(image, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))


for (x, y, w, h) faces in:
# Якщо особи знайдені, намагаємося розпізнати їх
# Функція recognizer.predict у разі успішного розпізнавання повертає номер і параметр confidence,
# цей параметр вказує на впевненість алгоритму, що це саме та людина, чим він менше, тим більше впевненість
number_predicted, conf = recognizer.predict(image[y: y + h, x: x + w])

# Витягаємо справжній номер людини на фото і порівнюємо з тим, що видав алгоритм
number_actual = int(os.path.split(image_path)[1].split(".")[0].replace("subject", ""))

if number_actual == number_predicted:
print "{} is Correctly Recognized with confidence {}".format(number_actual, conf)
else:
print "{} is Incorrect Recognized as {}".format(number_actual, number_predicted)
cv2.imshow("визнаючи проектів житлового Face", image[y: y + h, x: x + w])
cv2.waitKey(1000)


У циклі знову визначаємо розташування особи на кожному фото з закінченням .happy. Всі параметри і процедури такі ж, як і на попередньому етапі.
Для кожного знайденого особи запускаємо функцію recognizer.predict(), яка повертає номер-ідентифікатор суб'єкта, який імовірно перебуває на фото, а так же параметр confidence. Далі порівнюємо значення, яке нам повернула функція з реальним номером суб'єкта, якщо вони рівні, розпізнавання пройшло успішно.
Ну, ось і все, далі в консоль виводяться результати розпізнавання для кожної фотографії з контрольної вибірки.


Вихідний код програми можна знайти натут.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.