Готовый перевод Mastering OpenCV with Practical Computer Vision Projects / Опановуюємо OpenCV через практичні проекти комп'ютерного зору: Розділ 7. Оцінка 3D пози голови за допомогою AAM та POSIT
Якісний алгоритм комп'ютерного зору не може обійтися без корисних та надійних можливостей, узагальненості та твердої математичної основи. Всі ці особливості описані в праці Тіма Кута під назвою "Моделі активного вигляду" (Active Appearance Models, AAM). Цей розділ навчить вас створювати власну модель за допомогою бібліотеки OpenCV, а також використовувати її для пошуку найближчого положення на заданому кадрі. Крім цього, ви зрозумієте, як застосувати алгоритм POSIT та як розмістити тривимірну модель на зображенні зі встановленою позою. За допомогою цих інструментів ви зможете відслідкувати 3D модель на відео в реальному часу; хіба це не чудово? Хоча приклади фокусуються на позі голови, практично будь-яка модель з деформаціями може скористатися подібним підходом.
Читаючи подальший матеріал, ви познайомитесь з такими темами:
Огляд моделей активного вигляду
Огляд моделей активних форм
Інстанціювання моделі - граємо з моделями активного вигляду
Пошук AAM і розміщення на зображенні
Алгоритм POSIT
Наступний список пояснює терміни, які ми використаємо у розділі:
Моделі активного вигляду (AAM) - це модель об'єкта, яка містить статистичні дані про його форму та текстуру. Це потужний спосіб отримати відомості про варіативність форм та текстур об'єкта.
Модель активної форми (ASM) - це статистична модель форми об'єкта, дуже корисна для вивчення варіантів форм.
Аналіз головних компонент (Principal Component Analysis, PCA) - ортогональне лінійне перетворення, що трансформує дані в нову координатну систему, для якої найбільша дисперсія будь-якої проєкції даних лежить на першій координаті (що зветься першим головним компонентом), друга найбільша дисперсія - на другій координаті, і так далі. Цю процедуру часто називають зменшенням розмірності. Знижуючи це значення, ми зможемо створити більш швидкий алгоритм.
Триангуляція Делоне - це така триангуляція набору точок P на площині, для якої жодна точка P не лежить всередині кола, описаного навколо будь-якого трикутника триангуляції. Алгоритм не опрацьовує малі трикутники. Триангуляція необхідна для накладання текстури.
Афінні перетворення - будь-яке перетворення, що може бути виражене у формі добутку матриць та додавання вектора. Застосовується для накладання текстури.
Поза з орфографічної проєкції та ітеративне масштабування (Pose from Orthography and Scaling with Iterations, POSIT): алгоритм комп'ютерного зору, що здійснює обчислення 3D пози.
Огляд моделей активного вигляду
Коротко кажучи, моделі активного вигляду - це якісна модель, параметризована комбінацією текстури та форми, що пов'язана з ефективним алгоритмом пошуку, який може знайти точне положення та позу моделі на зображенні кадру. Для цього ми почнемо з огляду і побачимо, що вони тісно пов'язані з позиціями орієнтирів. Аналіз головних компонентів та досвід практики детальніше опишемо у наступних частинах. Тоді ж скористаємось допомогою функцій Делоне та дізнаємось дещо про триангуляцію. Далі, здобувши дані з текстури об'єкта, застосуємо пофрагментні афінні викривлення у частині про перетворення текстурних трикутників.
Здобувши достатню кількість даних для побудови якісної моделі, можна погратися з техніками в частині інстанціювання моделі. Потім ми зможемо знайти рішення зворотньої проблеми за рахунок пошуку AAM та розміщення 3D моделі.
Це вже досить корисні алгоритми для 2D або навіть 3D співпадінь зображень. Але коли вдасться змусити їх працювати, чому б не удосконалити їх за рахунок POSIT, ще одного потужного алгоритму для розміщення моделі? Занурившись в матеріали частини POSIT, дізнаємось про особливості роботи з ним в OpenCV, а потім розглянемо, як приєднати до неї модель голови. Таким чином ми зможемо розмістити 3D модель на знайденому 2D кадрі. А якщо зацікавлений читач хоче дізнатися, куди це приведе нас, об'єднавши AAM та POSIT для послідовних кадрів, ми зможемо отримати 3D відслідковування моделей з деформаціями в режимі реального часу! Деталі пояснимо у частині про процедуру обробки даних з камери чи файлу.
Кажуть, що одне зображення варте тисячі слів; уявіть, що ми маємо N кадрів. Пояснений раніше процес можна побачити на наступній ілюстрації:
Огляд алгоритмів розділу. Маючи зображення (у верхній лівій частині попереднього малюнка), можемо застосувати алгоритм пошуку активного вигляду для розпізнавання двовимірної пози людської голови. Ілюстрація у верхній правій частині скріншоту зображає заздалегідь натреновану модель активного вигляду, що використовується алгоритмом пошуку. Коли буде знайдено 2D позу, POSIT можна пристосувати для розширення результатів на 3D. Якщо процедуру виконати для послідовності відеокадрів, в результаті отримаємо 3D розпізнавання.
Моделі активних форм
Як зазначено раніше, AAM потребує модель активної форми (Active Shape Model, ASM). У наступних частинах ми створимо ASM, що є статистичною моделлю варіативності форм. Модель генерується на основі комбінації різних форм. Для цього потрібен тренувальний набір анотованих зображень, описаний у статті "Active Shape Models - Their Training and Application" Тімоті Кута. Щоб побудувати модель форми обличчя, декілька зображень з точками, визначеними на ключових позиціях, мають вказувати на головні характеристики. Наступний скріншот показує такий приклад:
Існує 76 орієнтирів обличчя, завантажених з набору даних MUCT. Вони, звичайно, відмічені вручну та описують декілька характеристик, наприклад контур рота, ніс, очі, брови та форму обличчя, оскільки їх легше відслідкувати.
Примітка
Аналіз Прокруста - це вид статистичного аналізу форми, що використовується для вивчення розподілу набору форм. У результаті отримаємо оптимальні значення перенесення, повороту та масштабування об'єктів.
Маючи попередньо вказаний набір зображень, можемо згенерувати статистичну модель варіативності форм. Оскільки позначені точки на об'єкті описують форму, спершу маємо вирівняти всі набори точок в певні рамки координат за допомогою аналізу Прокруста та представити кожну з форм у вигляді вектора x. Далі застосуємо аналіз головних компонентів (Principal Component Analysis, PCA) до даних. Потім апроксимуємо будь-який приклад такою формулою:
x = x + Ps*bs
У попередній формулі, x - це середня форма, Ps - набір ортогональних режимів варіативності, а bs - набір параметрів форми. Щоб зрозуміти це краще, створимо простий застосунок у подальшому матеріалі частини, що дозволить працювати з PCA та моделями форми.
Навіщо взагалі використовувати PCA? Він дуже важливий, коли потрібно зменшити кількість параметрів моделі. Пізніше у розділі ми також побачимо користь цього процесу для пошуку на заданому зображенні. Детальніше з темою можна ознайомитися на ресурсі http://en.wikipedia.org/wiki/Principal_component_analysis :
PCA дозволяє представити користувачу зображення меншої розмірності, своєрідну "тінь" об'єкта, якщо подивитися на нього з найбільш інформативної точки зору. Це робиться лише за рахунок перших декількох головних компоненів для того, щоб зменшити розмірність трансформованих даних.
Все стає зрозумілим, якщо розглянути таке зображення:
Джерело: http://en.wikipedia.org/wiki/File:GaussianScatterPCA.png
Попередній скріншот зображає PCA багатовимірного розподілу Гауса з центром у позиції (2,3). Показалі вектори - це власні вектори матриці коваріації, зміщені таким чином, що їхні хвости мають середнє значення. Тобто, якщо ми бажаємо представити модель єдиним параметром, варто взяти напрямок з власного вектора, що вказує на верхню праву частину зображення. Крім цього, дещо змінюючи параметр, можемо екстраполювати дані та отримати значення, подібні до розшукуваних.
Перші кроки з PCA
Щоб відчути, як PCA здатне допомогти нам з моделлю обличчя, почнемо з активної моделі форми та протестуємо декілька параметрів.
Оскільки розпізнавання обличчя та відслідковування має історію вивчення, декілька баз даних облич доступні у відкритій форми для дослідження. Ми використаємо примірники з бази даних IMM. По-перше, спробуємо зрозуміти, як діє клас PCA в бібліотеці OpenCV. З документації можна зробити висновок, що клас PCA використовується для обчислення особливого базису для набору векторів, що складається з власних відповідників матриці коваріації, обрахованої для вхідного набору векторів. Цей клас також переносить вектори між старою та новою координатними системами за допомогою методів project та backproject. Цю нову координатну систему можна точно апроксимувати за рахунок лише декількох перших її компонентів. Це значить, що ми можемо представити початковий вектор високорозмірного простору значно коротшим вектором, що складається із спроєктованих координат на підпростір.
Оскільки нам потрібна параметризація в термінах декількох скалярних значень, частіше використовуватимемо метод backproject. Він дає на основі координат головних компонентів проєктованих векторів та реконструює початкові. Ми можемо отримати початкові, якщо здобудемо усі компоненти, але різниця буде надто малою, якщо врахуємо лише декілька компонентів; це одна із причин використання PCA. Оскільки потрібна певна варіативність оригінальних векторів, наші параметризовані скаляри зможуть екстраполювати початкові дані.
Крім того, клас PCA здатний здійснювати перетворення векторів між початковою та новою системами координат, що визначені базисами. Згідно з документацією, математично це означає, що ми обчислюємо проєкцію вектора на підпростір, сформований декількома власними векторами, які відповідають домінантним власним значенням матриці коваріації.
У нашому підході анотуватимемо зображення обличчя орієнтирами, необхідними для моделі розподілення точок (Point Distribution Model, PDM). Якщо ми маємо k вирівнених орієнтирів у двох розмірностях, описа форми набуває такого вигляду:
X = { x1, y1, x2, y2, …, xk, yk}
Варто зауважити, що для цього потрібні стабільні анотування на всіх примірниках. Наприклад, якщо ліва частина рота відмічена орієнтиром 3 на першому кадрі, це ж значення має бути використано для решти зображень.
Такі послідовності орієнтирів формують контури форми, яку можна визначити у вигляді вектора. Припустимо, що їх значення мають розподіл Гауса у цьому просторі, і ми можемо застосувати PCA для обчислення нормованих власних векторів та власних значень матриці коваріації для всіх тренувальних форм. Використавши верхні центральні власні вектори, створимо матрицю розмірності 2k * m, яку назвемо P. Таким чином кожен вектор описує головний режим змін вздовж набору.
Тепер можна визначити нову форму таким рівнянням:
X' = X' + Pb
Тут X' позначає середню форму серед усіх тренувальних зображень, ми лише усереднюємо кожен з орієнтирів, а b - це вектор масштабованих значень для кожного головного компонента. Це дає змогу створювати нові форми за рахунок модифікації значення b. На практиці b обирають в межах трьох стандарних дисперсій для того, щоб згенерована форма мала змогу потрапити в тренувальний набір.
Наступний скріншот зображає анотовані орієнтири рота для трьох різних кадрів:
Як можна бачити на попередньому малюнку, форми описані послідовністю орієнтирів. Для анотування тренувальних зображень можна скористатися GIMP чи ImageJ або створити просту програму на OpenCV. Припустимо, що користувач здійснив цей процес і зберіг точки як послідовності (x, y) позицій орієнтирів для всіх тренувальних зображень у текстовому файлі, який потім використаємо для аналізу PCA. Потім до першої стрічки файлу додамо два параметри, які позначатимуть кількість тренувальних кадрів та колонок для зчитування. Для k двовимірних точок це значення дорівнюватиме 2*k.
Наступні дані позначають екземпляр цього файлу, отриманий шляхом анотування трох зображень з бази даних IMM, значення k обрано рівним 5:
3 10
265 311 303 321 337 310 302 298 265 311
255 315 305 337 346 316 305 309 255 315
262 316 303 342 332 315 298 299 262 316
Маючи анотовані зображення, перетворимо ці дані в модель форми. Спочатку завантажимо їх у матрицю. Це можна зробити за рахунок функції loadPCA. Далі покажемо фрагмент коду, який її використовує:
PCA loadPCA(char* fileName, int& rows, int& cols,Mat& pcaset){
FILE* in = fopen(fileName,"r");
int a;
fscanf(in,"%d%d",&rows,&cols);
pcaset = Mat::eye(rows,cols,CV_64F);
int i,j;
for(i=0;i<rows;i++){
for(j=0;j<cols;j++){
fscanf(in,"%d",&a);
pcaset.at<double>(i,j) = a;
}
}
PCA pca(pcaset, // передаємо дані
Mat(), // ми не маємо заздалегідь обчисленого середнього вектора
// тому дозволимо PCA вирахувати його
CV_PCA_DATA_AS_ROW, // вказує на те, що вектори
// зберігаються у вигляді рядів матриці
// використовуємо CV_PCA_DATA_AS_COL, якщо вектори є
// колонками матриці)
pcaset.cols// визначаємо, скільки головних компонентів зберегти
);
return pca;
}
Зауважте, ми створюємо матрицю у стрічці pcaset = Mat::eye(rows,cols,CV_64F), виділивши достатньо місця для 2*k значень. Після двох циклів for, що формують матрицю, викличемо конструктор PCA з даними, які за необхідності можуть бути заздалегідь обчисленими середніми векторами. Ми також вказуємо, що вектори збережені у вигляді рядів матриці, і що ми бажаємо зберегти ту ж їх кількість, скільки маємо компонентів, хоч і використаємо лише декілька.
Заповнивши об'єкт PCA тренувальними даними, матимемо все, що потрібно для зворотньої проєкції форми згідно із заданими параметрами. Це можна зробити за рахунок виклику функції PCA.backproject, передавши у першому з них вектор ряду та отримавши спроєктований результат у другому.
Два попередні скріншоти показують різні конфігурації форми залежно від обраних значень параметрів. Жовті та зелені відповідають даним, а червоні - формі зі вказаними характеристиками.
Прикладом програми можна скористатися для експериментів з моделями активних форм, адже він дозволяє користувачу змінювати різні параметри моделі. Справедливо зазначити, що регулювання лише перших двох скалярів (які відповідають першому та другому режимам дисперсії) дозволяє отримати форму, дуже близьку до натренованих. Така варіативність допоможе нам у процесі побудови моделі AAM, оскільки дає змогу отримати інтерпольовані форми. Далі ми обговоримо триангуляцію, текстурування, AAM та пошук AAM.
Триангуляція
Оскільки спостережена форма може бути викривлена (наприклад, відкритий рот), потрібно повторно відобразити нормовану текстуру на середню форму та застосувати PCA до неї. Для цього необхідна триангуляція. Концепція дуже проста: ми створимо трикутники, що міститимуть анотовані точки, а потім відобразимо один трикутник на інший. Бібліотека OpenCV постачається зі зручною функцією cvCreateSubdivDelaunay2D, яка формує пусту триангуляцію Делоне. Це доволі ефективний алгоритм, що не враховуватиме малі трикутники.
Примітка
В математиці та обчислювальній геометрії триангуляція Делоне для набору P точок на площині - це така триангуляція DT(P), для якої жодна точка P не лежить всередині кола, описаного навколо кожного трикутника DT(P). Триангуляції Делоне максимізують мінімальне значення серед усіх кутів; вони не діють на малі трикутники. Алгоритм названо за ім'ям Бориса Делоне у праці 1934 року.
Після налаштування сітки Делоне можна скористатися функцією cvSubdivDelaunay2DInsert для вміщення точок. Подальший фрагмент коду вкаже, яким саме чином можна застосувати триангуляцію:
CvMemStorage* storage;
CvSubdiv2D* subdiv;
CvRect rect = { 0, 0, 640, 480 };
storage = cvCreateMemStorage(0);
subdiv = cvCreateSubdivDelaunay2D(rect,storage);
std::vector<CvPoint> points;
// налаштовуємо точки певним чином
...
// ітеруємо по точкам та додаємо їх у сітку Делоне
for(int i=0;i<points.size();i++){
float x = points.at(i).x;
float y = points.at(i).y;
CvPoint2D32f floatingPoint = cvPoint2D32f(x, y);
cvSubdivDelaunay2DInsert( subdiv, floatingPoint );
}
Враховуйте, що наші точки лежатимуть всередині прямокутної зони, що передається у вигляді параметра функції cvCreateSubdivDelaunay2D. Щоб сформувати сітку, маємо створити та налаштувати структуру для зберігання даних у пам'яті. Це показано в перших п'ятьох стрічках попереднього фрагмента коду. Потім, щоб створити триангуляцію, додамо точки за допомогою функції cvSubdivDelaunay2DInsert. Це відбувається у циклі for. Точки вже повинні бути налаштовані, оскільки саме вони відіграють роль вхідних даних. Наступний малюнок ілюструє, якою може бути триангуляція:
Цей знімок є результатом виводу попереднього коду для набору точок, що формують триангуляцію за допомогою алгоритму Делоне. Хоча створювати сітку методами OpenCV дуже зручно, ітерувати по трикутникам не так легко. Далі покажемо код, який ітерує по ребрам сітки:
void iterate(CvSubdiv2D* subdiv, CvNextEdgeType triangleDirection){
CvSeqReader reader;
CvPoint buf[3];
int i, j, total = subdiv->edges->total;
int elem_size = subdiv->edges->elem_size;
cvStartReadSeq((CvSeq*)(subdiv->edges), &reader, 0);
for(i = 0; i < total; i++){
CvQuadEdge2D* edge = (CvQuadEdge2D*)(reader.ptr);
if(CV_IS_SET_ELEM(edge)){
CvSubdiv2DEdge t = (CvSubdiv2DEdge)edge;
for(j=0;j<3;j++){
CvSubdiv2DPoint* pt = cvSubdiv2DEdgeOrg(t);
if(!pt) break;
buf[j] = cvPoint(cvRound(pt->pt.x), cvRound(pt->pt.y));
t = cvSubdiv2DGetEdge(t, triangleDirection);
}
}
CV_NEXT_SEQ_ELEM(elem_size, reader);
}
}
Маючи сітку, викличемо функцію cvStartReadSeq для налаштування об'єкта для зчитування ребер. Згідно з документацією OpenCV ми маємо таке визначення:
Фукнція налащтовує стан зчитувача. Після цього всі елементи від першого до останнього можна зчитати за допомогою послідовних викликів макроса CV_READ_SEQ_ELEM(read_elem, reader) у випадку прямого зчитування чи CV_REV_READ_SEQ_ELEM(read_elem, reader) - зворотнього. Обидва макроси розміщують фрагмент послідовності у змінній read_elem та переміщують вказівник зчитування на наступний елемент.
До альтернативних способів отримати наступний елемент відносять використання макросу CV_NEXT_SEQ_ELEM(elem_size, reader), це кращий вибір, якщо елементи послідовності мають великі розміри. У даному випадку використаємо CvQuadEdge2D* edge = (CvQuadEdge2D*) reader.ptr, щоб перетворити вказівник зчитування у вказівник на об'єкт типу CvQuadEdge2D. Макрос CV_IS_SET_ELEM перевіряє лише, встановлені окремі ребра чи ні. Маючи ребро, відшукаємо вихідну точку шляхом виклику функції cvSubdiv2DEdgeOrg. Щоб обробити регіони навколо трикутника, повторно викличемо cvSubdiv2DGetEdge та передамо напрям, наприклад CV_NEXT_AROUND_LEFT чи CV_NEXT_AROUND_RIGHT.
Викривлення текстури трикутника
Відтепер маємо можливість ітерувати по трикутникам сітки, тому можна знайти перетворення між трикутником з анотованого та викривленого зображень. Це корисно для відображення текстури оригінальної форми на змінену. Наступний фрагмент коду присвячений цьому процесу:
void warpTextureFromTriangle(Point2f srcTri[3], Mat originalImage,
Point2f dstTri[3], Mat warp_final){
Mat warp_mat(2, 3, CV_32FC1);
Mat warp_dst, warp_mask;
CvPoint trianglePoints[3];
trianglePoints[0] = dstTri[0];
trianglePoints[1] = dstTri[1];
trianglePoints[2] = dstTri[2];
warp_dst = Mat::zeros(originalImage.rows, originalImage.cols,
originalImage.type());
warp_mask = Mat::zeros(originalImage.rows, originalImage.cols,
originalImage.type());
/// отримуємо афінні перетворення
warp_mat = getAffineTransform(srcTri, dstTri);
/// застосовуємо афінні перетворення до вихідного зображення
warpAffine(originalImage, warp_dst, warp_mat, warp_dst.size());
cvFillConvexPoly(new IplImage(warp_mask), trianglePoints, 3,
CV_RGB(255,255,255), CV_AA, 0);
warp_dst.copyTo(warp_final, warp_mask);
}
У попередньому коді ми припускаємо, що вершини трикутника запаковані в масиві srcTri, а результат - у dstTri. Матриця warp_mat розмірності 2x3 використовується для отримання афінних перетворень з вихідних трикутників в отримані. Більше інформації задокументовано в описі функції cvGetAffineTransform бібліотеки OpenCV:
Функція cvGetAffineTransform обраховує матрицю афінних перетворень таким чином:
У цьому рівнянні destination(i) дорівнює (xi',yi'), source(i) рівний (xi, yi), а саме значення i - 0,1,2.
Обчисливши матрицю афінних перетворень, застосуємо їх до оригінального зображення. Це можна зробити за допомогою функції warpAffine. Оскільки ми не потребуємо змін усього кадру (лише нашого трикутника), можна скористатися маскою. Таким чином, остання стрічка копіює лише трикутники з оригінального зображення, що відповідають масці, створеній за допомогою функції cvFillConvexPoly.
Наступний малюнок ілюструє результат застосування процедури до кожного трикутника анотованого зображення. Врахуйте, трикутники повторно відображені на вирівнений кадр, розвернутий до спостерігача. Ця процедура використовується для створення статистичної текстури AAM.
Попередній малюнок показує результат викривлення усіх відображених трикутників з лівого зображення на усереднений кадр примірника.
Інстанціювання моделі - граємо з моделями активного вигляду
Цікава особливість AAM - це їхня можливість легко інтерполювати натреновану модель. Якщо відрегулювати декілька параметрів, можна пристосуватися до її потужних можливостей представлення об'єктів. Регулювання значень форми дає змогу отримати інші результати викривлення залежно від натренованих даних. З іншого боку, зі зміною параметрів вигляду змінюється й текстура базової форми. Викривлення перетворять кожен трикутник початкової форми у відповідний трикутник модифікованої, аби ми мали змогу синтезувати закритий рот над відкритим, як показано на наступному зображенні:
Попередній скріншот зображає синтезований закритий рот, отриманий за допомогою моделі активного вигляду, розміщений на поверхні іншого зображення. Таким чином можемо скомбінувати посмішку із задоволеним виразом обличчя, екстраполюючи натреновані зображення.
Знімок отримано шляхом регулювання трьох параметрів форми та трьох текстури. Саме це і є метою моделей активного вигляду. Приклад програми розроблений та доступний на ресурсі http://www.packtpub.com/ , читач може власноруч випробувати AAM. Створити нову модель дуже просто, достатньо лише змінити деякі параметри рівняння, визначені у цій частині.
Знайомство з алгоритмом PCA
Ви мали помітити, що дія AAM та вміщення точок покладаються на гнучкість пошуку найкращих співпадінь для заданого кадру моделі в позиція, відмінних він натренованої. Розглянемо це питання у наступній частині.
Пошук AAM та вміщення точок
Нова комбінована форми та модель текстури дозволяє описати зміну не тільки форми, а й вигляду обличчя. Тепер ми бажаємо знайти такі набір форм p та параметри вигляду λ, які розмістять модель якомога ближче до вхідного зображення I(x). Можемо обчислити похибку між інстанційованою моделлю та координатами заданого зображення на кадрі I(x) чи повторно відобразити точки на початковий вигляд та вирахувати відповідну різницю. Застосуємо другий підхід. У цьому разі потрібно мінімізувати таку функцію:
Тут S0 позначає набір пікселів x, рівних (x,y)T, що лежать всередині початкової форми AAM, A 0 (x) - текстура, A i (x) - отримані за допомогою PCA кадру вигляду, а W(x;p) - перетворення, що переводить пікселі вхідного зображення на кадр з базовою формою.
За роки вивчення проблеми для процедури мінімізації запропоновано декілька підходів. Першим з них є адитивний, за якого ∆p та ∆λ вирахувані як лінійні функції похибок зображення, а параметр форми p та вигляд λ оновлені як p i ← p i + ∆p i та λ i ← λ i +
∆λ i в i-тій ітерації. Хоча іноді можуть траплятися співпадіння, дельта не завжди залежить від поточних параметрів, а це, у свою чергу, може призводити до розбіжності.
Ще один підхід, заснований на алгоритмах градієнтного спуску, виявився занадто повільним, тому ми розглянули інший підхід пошуку збіжності. У такому разі оновлюємо не параметри, а цілі матриці викривлення. Цей спосіб, що називають підходом композиції, запропонований Аєном Метьюсом та Симоном Бейкером у відомій праці "Active Appearance Models Revisited". Деталі можна знайти на її сторінках, але найважливішим вкладом, який вона дала для вміщення, - перенесення найбільш складних обчислень на крок попередньої обробки, як показано на такому малюнку:
Оновлення відбувається у вигляді композиції кроків, як показано на етапі 9 попереднього малюнку. Рівняння (40) та (41) праці зображені далі:
Хоча вказаний алгоритм у загальних випадках зводиться досить якісно з позиції, близької до кінцевої, це може виявитися не так для значної різниці повороту, перенесення чи масштабування. Більше даних про збіжність можна надати за рахунок параметризації глобальних двовимірних перетворень подібності. Це рівняння 42, яке має такий вигляд:
У цьому рівнянні чотири параметри q = (a, b, t x, t y)T мають такі інтерпретації. Перша пара значень (a,b) пов'язана з масштабом k та поворотом θ: a дорівнює k cos θ - 1, а b = k sin θ. Друга пара (t x, t y) - це перенесення координат x та y, як запропоновано в праці "Active Appearance Models Revisited".
Використавши ще декілька математичних перетворень, можна застосувати описаний алгоритм для пошуку найкращого вміщеного зображення за допомогою глобальних 2D перетворень.
Композитні алгоритми викривлення мають декілька переваг, що підвищують їх продуктивність. У нашому випадку скористаємось тим, що описаний у праці "AAM Revisited",- алгоритмом зворотнього композитного проєктування. У цьому разі зміну вигляду в ході вміщення можна заздалегідь обрахувати чи спроєктувати, тим самим підвищивши продуктивність процедури AAM.
Наступний скріншот показує співпадіння між різними зображеннями набору даних MUCT за допомогою алгоритму зворотнього композиційного проєктування та вміщення AAM.
Далі показано успішні співпадіння - серед облич за межами тренувального набору AAM - за допомогою алгоритму вміщення шляхом композиційного проєктування назовні.
POSIT
Оцінивши двовимірні позиції орієнтирів, можемо отримати 3D позу моделі за допомогою POSIT. Поза P 3D об'єкта визначається у вигляді матриці повороту R розмірності 3х3 та вектора перенесення T, тому P дорівнює [ R | T ].
Примітка
Більшість матеріалу цієї частини базується на навчальних матеріалах POSIT від Джейвіера Берендіерена. З назви зрозуміло, що POSIT застосовує декілька ітерацій (IT) алгоритму оцінки пози з ортографічної проєкції та масштабу (POS). Гіпотеза, яка лежить в основі її роботи, вказує на можливість розпізнавання та порівняння на співпадіння чотирьох чи більше не-плоских особливих точок об'єкта на зображенні та їх відносне розміщення.
Головна ідея алгоритму полягає в тому, що ми можемо знайти непогане наближення до пози об'єкта, припустивши, що всі точки моделі лежать у тій же площині, оскільки їх значення глибини не надто змінюється між ними, якщо порівнювати з відстанню від камери до обличчя. Після обчислення початкової пози, матрицю повороту та вектор перенесення знайдемо з лінійної системи рівнянь. Потім застосуємо ітеративну процедуру апроксимації пози, щоб знайти більш точні масштабовані ортографічні проєкції ключових точок, які використаємо в ролі вхідних даних для алгоритму POSIT. Щоб отримати більше інформації, зверніться до праці ДеМентона "Model-Based Object Pose in 25 Lines of Code".
Занурюємось в POSIT
Для того, щоб змусити POSIT працювати як слід, потрібні щонайменше чотири точки тривимірної моделі, що не лежать на одній площині, та відповідні їм точки на зображенні. До цього додаємо критерій завершення (оскільки POSIT - це ітеративний алгоритм), тобто кількість ітерацій чи параметр відстані. Потім викликаємо функцію cvPOSIT, що формує матрицю повороту та вектор перенесення.
Для прикладу слідуватимемо навчальним матеріалам Джейвіера Берендіерена, який використовує POSIT для формування пози куба. Модель створена на основі чотирьох точок. Ми ініціалізуємо її таким кодом:
float cubeSize = 10.0;
std::vector<CvPoint3D32f> modelPoints;
modelPoints.push_back(cvPoint3D32f(0.0f, 0.0f, 0.0f));
modelPoints.push_back(cvPoint3D32f(0.0f, 0.0f, cubeSize));
modelPoints.push_back(cvPoint3D32f(cubeSize, 0.0f, 0.0f));
modelPoints.push_back(cvPoint3D32f(0.0f, cubeSize, 0.0f));
CvPOSITObject *positObject = cvCreatePOSITObject( &modelPoints[0],
static_cast<int>(modelPoints.size()) );
Власне модель створена за допомогою методу cvCreatePOSITObject, який повертає об'єкт CvPOSITObject, який потім використаємо у функції cvPOSIT. Будьте обережні, позу обрахуємо відносно першої точки моделі, тому її варто розмістити у початку координат. Далі маємо зберегти точки двовимірного зображення в іншому векторі. Пам'ятайте, вони мають знаходитися у масиві тієї ж розмірності, що й точки моделі; таким чином, i-та точка 2D зображення збігається з i-тою точкою 3D моделі. Нюанс полягає в тому, що початок системи координат, у якій розміщено точки двовимірного зображення, лежить у центрі, а тому його потрібно перенести. Може додати такі точки (звісно, вони змінюватимуться залежно від вибору користувача):
std::vector<CvPoint2D32f> srcImagePoints;
srcImagePoints.push_back( cvPoint2D32f( -48, -224 ) );
srcImagePoints.push_back( cvPoint2D32f( -287, -174 ) );
srcImagePoints.push_back( cvPoint2D32f( 132, -153 ) );
srcImagePoints.push_back( cvPoint2D32f( -52, 149 ) );
Тепер досить лише виділити пам'ять для матриць та сформувати критерій завершення, викликати функцію cvPOSIT, як показано в наступному фрагменті коду:
// оцінюємо позу
CvMatr32f rotation_matrix = new float[9];
CvVect32f translation_vector = new float[3];
CvTermCriteria criteria = cvTermCriteria(CV_TERMCRIT_EPS |
CV_TERMCRIT_ITER, 100, 1.0e-4f);
cvPOSIT( positObject, &srcImagePoints[0], FOCAL_LENGTH, criteria,
rotation_matrix, translation_vector );
Після завершення ітеративного процесу cvPOSIT збереже результати у змінних rotation_matrix та translation_vector. Наступний малюнок зображає додані точки у вигляді білих кіл, а також система координат з результатами повороту та перенесення:
Посилаючись на попередній скріншот, оглянемо вхідні точки та результати алгоритму POSIT:
Білі кола вказують на вхідні точки, а осі координат - отриману позу моделі. Впевніться, що ви використовуєте фокусну відстань, обраховану на етапі калібрування камери. Детальна інформація щодо процедури калібрування доступна в розділі 2 "Заснована на маркерах доповнена реальність на iPhone та iPad". Поточна реалізація POSIT дозволить працювати лише з квадратними пікселями, у такому випадку про фокусну довжину на осях x та y не може бути й мови.
Матриця повороту очікується у такому форматі:
[rot[0] rot[1] rot[2]]
[rot[3] rot[4] rot[5]]
[rot[6] rot[7] rot[8]]
Вектор перенесення повинен мати такий вигляд:
[trans[0]]
[trans[1]]
[trans[2]]
POSIT та модель голови
Для використання POSIT як інструменту для пози голови знадобиться 3D модель. Можливий варіант пропонує Інститут Систем та Робототехніки Університету Коймбра за адресою http://aifi.isr.uc.pt/Downloads/OpenGL/glAnthropometric3DModel.cpp . Власне опис моделі у файлі починається стрічкою:
float Model3D[58][3]= {{-7.308957,0.913869,0.000000}, ...
Модель можна побачити далі:
Попередній скріншот зображає підготовлену для POSIT тривимірну модель голови з 58 точок. Щоб алгоритм POSIT спрацював, маємо знайти співпадіння точок з проєкціями. Пам'ятайте, для POSIT потрібні як мінімум чотири тривимірні точки, що не лежать на одній площині, та відповідні їм двовимірні проєкції, їх маємо передати у вигляді параметрів, як вказано у частині "Занурюємось в POSIT". Алгоритм лінійний, якщо враховувати кількість співпадінь точок.
Далі зображено, як саме має бути здійснена перевірка на співпадіння.
Тут показані коректні точки тривимірної моделі голови та меш моделі активного вигляду.
Відслідковування з використанням вебкамери чи відеофайлу
Маючи інструменти з шістьма ступенями свободи для відслідковування обличчя, застосуємо їх до потоку відеокадрів чи файлу. Бібліотека OpenCV надає клас VideoCapture, яким можна скористатися таким чином (деталі знаходяться в частині "Отримуємо доступ до вебкамери" у розділі 1 "Ефект мультиплікації та зміна кольору шкіри для Android"):
#include "cv.h"
#include "highgui.h"
using namespace cv;
int main(int, char**)
{
VideoCapture cap(0);// отримаємо доступ до камери, можна вказати
// замість цього шлях до відеофайлу
if(!cap.isOpened()) // перевіряємо успішність результату
return -1;
AAM aam = loadPreviouslyTrainedAAM();
HeadModel headModel = load3DHeadModel();
Mapping mapping = mapAAMLandmarksToHeadModel();
Pose2D pose = detectFacePosition();
while(1)
{
Mat frame;
cap >> frame; // отримуємо новий кадр з камери
Pose2D new2DPose = performAAMSearch(pose, aam);
Pose3D new3DPose = applyPOSIT(new2DPose, headModel, mapping);
if(waitKey(30) >= 0) break;
}
// камера автоматично втратить налаштування в деструкторі
// VideoCapture
return 0;
}
Алгоритм працює таким чином. Об'єкт для захоплення відеопотоку налаштовується за допомогою оголошення VideoCapture cap(0), у такому разі використовується вебкамера за замовчуванням. Для роботи алгоритму, крім захоплення відеокадрів, потрібна натренована модель активного вигляду, яку завантажимо викликом функції loadPreviouslyTrainedAAM. Також зчитаємо модель тривимірної голови у змінну headModel та відображення орієнтирів на тривимірні точки - у mapping.
Завантаживши все, що потрібно для процедури, налаштуємо алгоритм на основі відомої пози, тобто відомої тривимірної позиції, повороту та набору параметрів AAM. Це можна зробити автоматично за допомогою добре задокументованого детектора обличчя на основі класифікатора особливих точок Хаара (більше деталей знаходиться в частині "Розпізнавання обличчя" у розділі 6 "Нецілісне відслідковування обличчя" або в документації каскадного класифікатора бібліотеки OpenCV), або можна вручну налаштувати позу з раніше анотованого кадру. Можна також скористатися підходом повного перебору, який виконає вміщення AAM для кожного прямокутника, оскільки він повільний лише для першого кадру. Під налаштуванням ми маємо на увазі пошук двовимірних орієнтирів на моделі активного вигляду шляхом регулювання їхніх параметрів.
Коли потрібні дані будуть збережені у змінних, активуємо головний цикл while. У ньому спершу запитаємо наступний захоплений кадр, викличемо процедуру вміщення моделі активного вигляду, щоб знайти орієнтири на наступному зображенні. Оскільки поточна позиція дуже важлива на цьому етапі, передамо її в якості параметра функції performAAMSearch(pose, aam). Знайшовши поточну позу шляхом мінімізації похибки зображень, отримаємо позиції орієнтирів, які можна передати на етап POSIT. Це відбувається у наступній стрічці, applyPOSIT (new2DPose, headModel, mapping). Нова двовимірна поза передається в якості параметра, як і попередньо завантажена модель голови та відображення. Після цього етапу ми зможемо візуалізувати будь-яку тривимірну модель в отриманій позі як зображення на системі координат чи модель у доповненій реальності. Вирахувавши орієнтири, можна досягнути більш цікавих ефектів за допомогою регулювання параметрів моделі, наприклад відкривання рота чи зміни позиції брови.
Оскільки ця процедура покладається на попередню позу для наступної оцінки, можна було б накопичувати похибки та зміщення від позиції голови. Іншим способом є повторне налаштування процедуру для кожного запуску з перевіркою порогу похибки зображення. Варто звернути увагу й на використані фільтри у ході відслідковування, бо можуть з'являтися певні артефакти. Простий фільтр середнього значення для координат перенесення та повороту дасть пристойні результати.
Висновки
У цьому розділі ми обговорювали, як можна скомбінувати моделі активного вигляду з алгоритмом POSIT, щоб отримати тривимірну позу голови. Ми розглянули спосіб створення, тренування та маніпуляції моделлю активного вигляду, який можна застосувати у будь-якій іншій сфері, наприклад медицині, обробці зображень чи індустрії. Крім знайомства з AAM, ми дізналися про сітку Делоне та триангульований меш. Ми також показали, як виконати відображення текстури на трикутники за допомогою функцій OpenCV. Ще однією цікавою темою виявилось вміщення моделі активного вигляду. Хоча був описаний лише зворотній композитний алгоритм проєктування, ми змогли легко скористатися результатом років досліджень цієї проблеми.
Ознайомившись з теорією та практикою AAM, ми поринули в деталі POSIT, щоб поєднати 2D та 3D виміри, за допомогою порівнянь вмістивши 3D форму між точками моделі. Ми завершили розділ, показавши, як застосувати всі інструменти для відслідковування обличчя в реальному часі шляхом розпізнавання, що формує 6 градусів свободи для пози голови - 3 для повороту і 3 для перенесення. Повний код розділу можна завантажити з ресурсу http://www.packtpub.com/.
Посилання
Active Appearance Models, T.F. Cootes, G. J. Edwards, and C. J. Taylor, ECCV, 2:484–498,
1998 (http://www.cs.cmu.edu/~efros/courses/AP06/Papers/cootes-eccv-98.pdf)
Active Shape Models – Their Training and Application, T.F. Cootes, C.J. Taylor, D.H. Cooper,
and J. Graham, Computer Vision and Image Understanding, (61): 38–59, 1995
(http://www.wiau.man.ac.uk/~bim/Papers/cviu95.pdf)
The MUCT Landmarked Face Database, S. Milborrow, J. Morkel, and F. Nicolls, Pattern
Recognition Association of South Africa, 2010 (http://www.milbo.org/muct/)
The IMM Face Database – An Annotated Dataset of 240 Face Images, Michael M. Nordstrom,
Mads Larsen, Janusz Sierakowski, and Mikkel B. Stegmann, Informatics and Mathematical
Modeling, Technical University of Denmark, 2004,
(http://www2.imm.dtu.dk/~aam/datasets/datasets.html)
Sur la sphère vide, B. Delaunay, Izvestia Akademii Nauk SSSR, Otdelenie Matematicheskikh
i Estestvennykh Nauk, 7:793–800, 1934
Active Appearance Models for Facial Expression Recognition and Monocular Head Pose
Estimation Master Thesis, P. Martins, 2008
Active Appearance Models Revisited, International Journal of Computer Vision, Vol. 60, No.
2, pp. 135 - 164, I. Mathews and S. Baker, November, 2004
(http://www.ri.cmu.edu/pub_files/pub4/matthews_iain_2004_2/matthews_iain_2004_2.pdf)
POSIT Tutorial, Javier Barandiaran (http://opencv.willowgarage.com/wiki/Posit)
Model-Based Object Pose in 25 Lines of Code, International Journal of Computer Vision, 15,
pp. 123-141, Dementhon and L.S Davis, 1995
(http://www.cfar.umd.edu/~daniel/daniel_papersfordownload/Pose25Lines.pdf)
Внимание! Этот перевод, возможно, ещё не готов.
Его статус: перевод редактируется
Переведено на Нотабеноиде
http://notabenoid.org/book/81521/501833
Переводчики: Atronax
Настройки
Готово:
