Mastering OpenCV with Practical Computer Vision Projects / Опановуюємо OpenCV через практичні проекти комп'ютерного зору: Розділ 8. Розпізнавання обличчя за методами Eigenfaces та Fisherfaces

← →
Английский оригинал Перевод на украинский

Сожалеем, но текст оригинала доступен только зарегистрированным пользователям.

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

Atronax 22.03.21 в 15:03

0

Етап 3: Збір облич та тренування

Atronax 22.03.21 в 15:04

0

Збирати обличчя настільки ж просто, як і розміщувати кожне новооброблене обличчя в масив попередньо опрацьованих кадрів камери, а також зберігати відмітку в масиві (щоб мати можливість визначати, кому саме належить обличчя). Наприклад, можна використати 10 заздалегідь оброблених облич першої особи та 10 другої, щоб вхідні дані алгоритму розпізнавання обличчя включали масив 20 попередньо опрацьованих облич та масив 20 цілих чисел (серед яких перші десять - нулі, а наступні - одиниці).

Atronax 22.03.21 в 15:04

0

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

Atronax 22.03.21 в 15:04

0

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

Atronax 22.03.21 в 15:04

0

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

Atronax 22.03.21 в 15:04

0

Примітка

Atronax 22.03.21 в 15:04

0

Щоб отримати якісний тренувальний набір, що покриє різні реальні умови для кожної особи, можна скористатися знімками обличчя з поглядом, чітко спрямованим вліво, вверх, вправо та вниз. Потім людина нахиляє голову в боки, вгору та вниз, а також дещо змінює вираз обличчя, наприклад посміхається, виглядає розлюченою чи спокійною. Якщо кожна особа здійснить таку ж процедуру під час збору даних, вірогідність розпізнати усіх в реальних умовах значно підвищиться.

Atronax 22.03.21 в 15:05

0

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

Atronax 22.03.21 в 15:05

0

У загальному випадку, 100 тренувальних знімків облич для кожної людини дасть кращі результати, аніж 10, але якщо всі 100 облич виглядають практично однаковими, результат все ще буде поганим, адже більш важливим є те, щоб тренувальний набір мав достатню варіативність, щоб мати змогу покрити потрібні тестові умови, а не просто велику кількість різних облич. Щоб впевнитися, що не всі обличчя у тренувальному наборі схожі, маємо додати помітну затримку між кожним знімком. Наприклад, якщо камера діє зі швидкість 30 кадрів у секунду, вона здатна зібрати 100 облич за декілька секунд, а людина не матиме можливості повернутися за цей час, тому краще зберігати лише один знімок в секунду, даючи особі час для руху. Ще одним простим методом покращити варіативність тренувального набору - збирати обличчя, які не схожі на попередні.

Atronax 22.03.21 в 15:05

0

Збираємо попередньо оброблені обличчя для тренування

Atronax 22.03.21 в 15:05

0

Щоб впевнитися, що між знімками нових облич існує проміжок принаймні в одну секунду, потрібно вимірювати час. Це робиться таким чином:
// перевіримо, скільки часу пройшло з моменту додавання попереднього обличчя
double current_time = (double)getTickCount();
double timeDiff_seconds = (current_time –
old_time) / getTickFrequency();

Atronax 22.03.21 в 15:05

0

Щоб порівняти попіксельну подібність двох кадрів, можна обчислити відносну похибку L2, тобто відняти одне зображення від іншого, просумувати квадрати значень та знайти корінь квадратний від отриманого результату. Якщо людина не здійснила жодних рухів, результат віднімання поточного та попереднього облич має досить низькі значення в кожному пікселі, яле якщо відбувся рух у будь-якому напряму, результат віднімає сформує досить великі числа, тобто похибка L2 буде високою. Оскільки результат сумується по всім пікселям, значення сильно залежить від роздільної здатності зображення. Тому, щоб знайти середню похибку, поділимо це значення на загальну кількість пікселів зображення. Для цього скористаємось зручною функцією getSimilarity():

Atronax 22.03.21 в 15:06

0

double getSimilarity(const Mat A, const Mat B) {
// обчислимо відносну похибку L2 між двома зображеннями
double errorL2 = norm(A, B, CV_L2);
// масштабуємо значення, оскільки L2 сумується по всім пікселям
double similarity = errorL2 / (double)(A.rows * A.cols);
return similarity;
}
...
// перевіряємо, чи обличчя виглядає інакше, ніж попереднє
double imageDiff = MAX_DBL;
if (old_prepreprocessedFaceprepreprocessedFace.data) {
imageDiff = getSimilarity(preprocessedFace,
old_prepreprocessedFace);
}

Atronax 22.03.21 в 15:06

0

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

Atronax 22.03.21 в 15:06

0

// Оброблювати обличчя, якщо ті помітно відрізняються
// від попереднього кадру, і пройшов прийнятний проміжок часу.
if ((imageDiff > 0.3) && (timeDiff_seconds > 1.0)) {
// Додаємо віддзеркалене зображення до тренувального набору.
Mat mirroredFace;
flip(preprocessedFace, mirroredFace, 1);
// Додаємо звичайне і віддзеркалене обличчя до списку знайдених об'єктів.
preprocessedFaces.push_back(preprocessedFace);
preprocessedFaces.push_back(mirroredFace);
faceLabels.push_back(m_selectedPerson);
faceLabels.push_back(m_selectedPerson);
// Зберігаємо копію оброблених облич,
// щоб порівняти їх на наступній ітерації.
old_prepreprocessedFace = preprocessedFace;
old_time = current_time;
}

Atronax 22.03.21 в 15:07

0

Цей фрагмент коду наповнить вектори preprocessedFaces та faceLabels попередньо обробленими обличчями, а також відмітками чи номером паспорта цієї особи (припускаючи, що він зберігається у змінній m_selectedPerson).

Atronax 22.03.21 в 15:07

0

Щоб оповістити користувача про те, що його обличчя додано до колекції, достатньо вивести великий білий прямокутник на поверхні зображення або просто показати кадр на долі секунди. Завдяки С++ інтерфейсу бібліотеки OpenCV можна скористатися перевантаженим оператором + об'єкта cv::Mat, щоб додати значення кожного пікселя до зображення та обмежити його на рівні 255 (за допомогою функції saturate_cost, щоб білий колір не перетворився в чорний!). Припустимо, що displayedFrame є копією кольорового кадру камери та додамо такий фрагмент коду до процедури збору облич:

Atronax 22.03.21 в 15:07

0

// Отримаємо доступ до регіону інтересу обличчя.
Mat displayedFaceRegion = displayedFrame(faceRect);
// Зробимо пікселі регіону дещо яскравішими.
displayedFaceRegion += CV_RGB(90,90,90);

Atronax 22.03.21 в 15:08

0

Тренування системи розпізнавання на основі колекції облич

Atronax 22.03.21 в 15:08

0

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

Atronax 22.03.21 в 15:08

0

Якщо ви бажаєте й надалі вивчати питання розпізнавання обличчя, перегляньте теоретичні відомості, що стосуються таких тем:
Eigenfaces (аналіз головних компонентів, Principal Component Analysis, PCA)
Fisherfaces (аналіз лінійного дискримінанта, Linear Discriminant Analysis, LDA)
Інші класичні алгоритми розпізнавання (доступні за адресою http://www.face-
rec.org/algorithms/)

Atronax 22.03.21 в 15:08

0

Більш нові алгоритми з нещодавно викладених у публічний доступ дослідницьних праць (CVPR чи ICCV в http://www.cvpapers.com/), оскільки кожного року публікуються сотні подібних статей.

Atronax 22.03.21 в 15:08

0

Але вам не обов'язково знати теоретичні викладки алгоритмів, щоб користуватися ними для поставлених у книзі завдань. Дякуючи команді OpenCV та вкладу Філіпа Вагнера в libfacerec, версія 2.4.1 постачає простий та загальний метод cv::Algorithm для здійснення розпізнавання обличчя за допомогою одного з декількох різних алгоритмів (які навіть можна обирати в режимі реального часу) без розуміння деталей їх реалізації. Щоб переглянути доступні алгоритми для вашої версії бібліотеки OpenCV, досить викликати функцію Algorithm::getList()

Atronax 22.03.21 в 15:08

0

vector<string> algorithms;
Algorithm::getList(algorithms);
cout << "Доступні алгоритми: " << algorithms.size() << endl;
for (int i=0; i<algorithms.size(); i++) {
cout << algorithms[i] << endl;

Atronax 22.03.21 в 15:09

0

У бібліотеці OpenCV v2.4.1 доступні три алгоритми:
FaceRecognizer.Eigenfaces. Також відомі як PCA, вперше застосовані Тюрком та Пентлендом в 1991 році.
FaceRecognizer.Fisherfaces. Також відомі як LDA, винайдені Белхьюмером, Хеспаном та Крігменом у 1997 році.
FaceRecognizer.LBPH. Гістограми локальних бінарних шаблонів, розроблені Еоненом, Хедідом та Пайтікейненом у 2004 році.

Atronax 22.03.21 в 15:09

0

Примітка
Більше даних про ці реалізації алгоритму розпізнавання обличчя можна знайти в документації, прикладах та фрагментах коду Python на вебсайті Філіпа Вагнера за адресами http://bytefish.de/blog та http://bytefish.de/dev/libfacerec/.

Atronax 22.03.21 в 15:09

0

Ці алгоритми розпізнавання обличчя доступні в класі FaceRecognizer модуля contrib бібліотеки OpenCV. Зважаючи на динамічне лінкування, можлива ситуація, за якої програму поєднано з модулем, але він не завантажений (якщо відповідний файл не знайдено). Тому рекомендовано викликати функцію cv::initModule_contrib() до спроб отримати доступ до алгоритмів FaceRecognizer. Така функція доступна в бібліотеці OpenCV, починаючи з версії 2.4.1, вона дозволяє впевнитися, що алгоритми розпізнавання доступні на етапі компіляції.

Atronax 22.03.21 в 15:09

0

// Завантажимо модуль contrib динамічно
bool haveContribModule = initModule_contrib();
if (!haveContribModule) {
cerr << "ПОМИЛКА: модуль contrib, необхідний для ";
cerr << "FaceRecognizer, не вдалося завантажити!";
cerr << endl;
exit(1);
}

Atronax 22.03.21 в 15:09

0

Щоб скористатися одним з алгоритмів розпізнавання обличчя, потрібно створити об'єкт FaceRecognizer за допомогою функції cv::Algorithm::create<FaceRecognizer>(). В якості параметра передаємо назву бажаного алгоритму у вигляді стрічки. Це дасть змогу застосовувати його, якщо він доступний у нашій версії. Також можна використати цей факт для перевірки, має користувач бібліотеку OpenCV v2.4.1 і вище чи ні. Наприклад:

Atronax 22.03.21 в 15:10

0

string facerecAlgorithm = "FaceRecognizer.Fisherfaces";
Ptr<FaceRecognizer> model;
// Скористаємось новим класом FaceRecognizer з модуля contrib.
model = Algorithm::create<FaceRecognizer>(facerecAlgorithm);
if (model.empty()) {
cerr << "ПОМИЛКА: Алгоритм [[" << facerecAlgorithm;
cerr << "] недоступний для вашої версії OpenCV.";
cerr << "Будь-ласка, оновіть OpenCV до версії 2.4.1 чи старшої." << endl;
exit(1);
}

Atronax 22.03.21 в 15:10

0

Завантаживши алгоритм FaceRecognizer, можна просто викликати функцію FaceRecognizer::train() із зібраними даними таким чином:
// Здійснюємо попереднє тренування на основі колекції облич
model->train(preprocessedFaces, faceLabels);

Atronax 22.03.21 в 15:11

0

Ця стрічка коду розпочинає процес тренування обраного алгоритму (наприклад, Eigenfaces, Fisherfaces або будь-який інший). Якщо є лише декілька людей і менше за 20 облич, тренування займе незначний проміжок часу, але для багатьох людей з великою кількістю знімків, для обробки даних може знадобитися декілька секунд чи навіть хвилин.

Atronax 22.03.21 в 15:11

0

Огляд отриманих знань

Atronax 22.03.21 в 15:11

0

Хоч це й не обов'язково, згенеровані алгоритмом розпізнавання обличчя внутрішні структури даних корисно переглядати, особливо якщо ви розумієте теорію та бажаєте перевірити коректність результати чи знайти причини похибок. Внутрішні структури даних можуть відрізнятися від алгоритму до алгоритму, але, на щастя, вони однакові для Eigenfaces та Fisherfaces, тому розглянемо саме їх. Обидві представлені у вигляду одновимірних матриць власних векторів, схожих на обличчя, якщо переглядати їх як двовимірні зображення, тому для алгоритму Eigenfaces їх ще називають власними обличчями, а для Fisherfaces - обличчями Фішера.

Atronax 22.03.21 в 15:11

0

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

Atronax 22.03.21 в 15:12

0

Якщо набір сформовано для 5 осіб з 20-ма фрагментами облич для кожної, для розпізнавання 100 облич знадобляться 100 власних облич та 100 власних значень, причому вони відсортовані таким чином, щоб декілька перших елементів вказували на найбільш значимі відмінності, а останні - всього лише випадкові шуми, які ніяк не допомагають розрізнити дані. Прийнято відкидати останні власні обличчя і залишати приблизно 50 елементів.

Atronax 22.03.21 в 15:14

0

На противагу цьому, головний принцип методу Fisherfaces замість обчислення особливого власного вектора та власного значення для кожного зображення тренувального набору вимагає пошуку одного вектора та одного значення для особи. У попередньому прикладі 5 людей та 20 знімків облич для кожної, алгоритм Eigenfaces скористається сотнею власних облич та власних значень, у той час як метод Fisherfaces - лише п'ятьма.

Atronax 22.03.21 в 15:17

0

Щоб отримати доступ до внутрішніх структур даних алгоритмів Eigenfaces та Fisherfaces, ми повинні скористатися функцією cv::Algorithm::get() у процесі роботи програми, оскільки вона не діє на етапі компіляції. Ці структури використовуються для математичних обчислень, а не обробки зображень, тому зберігаються у вигляді плаваючих чисел з типовим діапазоном від 0.0 до 1.0, а не 8-бітних пікселів uchar від 0 до 255, подібно до звичайних зображень.

Atronax 22.03.21 в 15:17

0

Крім цього, це або одновимірний вектор чи один з багатьох рядів чи колонок більшої матриці. Перед тим як показати внутрішні структури даних, потрібно надати їм коректної прямокутної форми та конвертувати у 8-бітні пікселів uchar, що мають значення від 0 до 255. Оскільки дані матриці можуть належати діапазону від 0.0 до 1.0 чи від -1.0 до 1.0, можна скористатися функцією cv::normalize() з опцією cv::NORM_MINMAX, щоб упевнитися, що вихідні дані знаходяться у межах від 0 до 255, неважливо, яким є вхідний діапазон. Створимо функцію для виконання перетворення у прямокутну функцію та сконвертуємо у 8-бітні пікселі таким чином:

Atronax 22.03.21 в 15:17

0

// Конвертуємо ряд чи колонку матриці плаваючих чисел у
// прямокутне 8-бітне зображення, яке можна показати чи зберегти.
// Масштабуємо значення в діапазон від 0 до 255.
Mat getImageFrom1DFloatMat(const Mat matrixRow, int height)
{
// Робимо прямокутну форму замість одного ряду.
Mat rectangularMat = matrixRow.reshape(1, height);
// Масштабуємо значення між 0 та 255 та зберігаємо їх
// у вигляді звичайного зображення з 8-бітними пікселями
Mat dst;
normalize(rectangularMat, dst, 0, 255, NORM_MINMAX,
CV_8UC1);
return dst;
}

Atronax 22.03.21 в 15:18

0

Щоб легше налагоджувати код OpenCV та перевіряти структуру даних cv::Algorithm, можна скористатися файлами ImageUtils.cpp та ImageUtils.h для виводу інформації про сv::Mat:
Mat img = ...;
printMatInfo(img, "Моє зображення");

Atronax 22.03.21 в 15:18

0

Ви побачите дещо подібне у консолі:
Моє зображення: 640w480h 3ch 8bpp, range[79,253][20,58][18,87]
Ця стрічка вказує на те, що зображення має ширину 640 елементів та висоту 480 (тобто кадр з розмірами 640х480 чи матриця розмірності 480х640, залежно від погляду) з трьома 8-бітними каналами на піксель (тобто, звичайне BGR зображення), і для кожного каналу зазначені мінімальне та максимальне значення.

Atronax 22.03.21 в 15:18

0

Примітка
Можна також надрукувати власне зміст зображення чи матриці за допомогою функції printMat() замість printMatInfo(). Вона досить корисна для огляду звичайних та багатоканальних матриць плаваючих чисел, оскільки їх доволі важко відслідковувати початківцям.

Atronax 22.03.21 в 15:19

0

Код класу ImageUtils практично повністю заснований на С інтерфейсі бібліотеки OpenCV, але з часом включатиме все більше методів С++. Остання верcія доступна за адресою http://shervinemami.info/openCV.html.

Atronax 22.03.21 в 15:19

0

Середнє обличчя

Atronax 22.03.21 в 15:19

0

Алгоритми Eigenfaces та Fisherfaces спочатку обраховують середнє обличчя математично для усіх тренувальних зображень, тому від кожного можна відняти його та отримати кращі результати розпізнавання. Розглянемо, яким саме воно є. Іншою назвою цієї характеристики в реалізаціях Eigenfaces та Fisherfaces є "mean", описаний таким чином:

Atronax 22.03.21 в 15:19

0

Mat averageFace = model->get<Mat>("mean");
printMatInfo(averageFace, "averageFace (ряд)");
// конвертуємо ряд матриці плаваючих чисел у звичайне 8-бітне зображення
averageFace = getImageFrom1DFloatMat(averageFace, faceHeight);
printMatInfo(averageFace, "averageFace");
imshow("середнє обличчя", averageFace);

Atronax 22.03.21 в 15:20

0

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

Atronax 22.03.21 в 15:20

0

averageFace (ряд): 4900w1h 1ch 64bpp, range[5.21,251.47]
averageFace: 70w70h 1ch 8bpp, range[0,255]
Малюнок матиме такий вигляд:
averageFace (ряд) - це проста матриця 64-бітних плаваючих значень, а просто averageFace - це прямокутне зображення з 8-бітних пікселів, які покривають межі від 0 до 255.
Власні значення, власні обличчя та обличчя Фішера
Виведемо на екран власні значення у вигляді тексту:
Mat eigenvalues = model->get<Mat>("eigenvalues");
printMat(eigenvalues, "eigenvalues");

Atronax 22.03.21 в 15:21

0
Минутку...

← Предыдущая страница

Следующая страница →