Mastering OpenCV with Practical Computer Vision Projects / Опановуюємо OpenCV через практичні проекти комп'ютерного зору: Розділ 1. Ефект мультиплікації та зміна кольору шкіри для Android
| Английский оригинал | Перевод на украинский | |
|---|---|---|
Сожалеем, но текст оригинала доступен только зарегистрированным пользователям. |
Щоб користувач точно зрозумів, куди слід помістити обличчя, виведемо екран повідомлення! Atronax 13.02.21 в 10:16 |
|
|
// Напишемо текст з ефектом згладжування. Atronax 13.02.21 в 10:19 |
||
|
Маючи контур обличчя, ми можемо розмістити його над зображенням за допомогою альфа змішування та об'єднати мультиплікацію з намальованим контуром: Atronax 13.02.21 в 10:57 |
||
|
Реалізація зміни кольору шкіри Atronax 13.02.21 в 10:58 |
||
|
Цікавою особливістю функції floodFill() бібліотеки OpenCV є те, що вона може здійснювати заливку у вихідне зображення, не змінюючи при цьому вхідне. Завдяки цьому ми зможемо отримати маску для керування кольором пікселів без обов'язкової зміни яскравості чи насиченості, формуючи більш реалістичне зображення, аніж у випадку однакового зеленого кольору для всіх пікселів шкіри (через що важливі деталі обличчя буде втрачено). Atronax 13.02.21 в 11:19 |
||
|
Зміна кольору шкіри не так ефективна у просторі кольорів RGB. Нам важливо, аби яскравість мала змогу змінюватися залежно від позиції на обличчі, а відтінок залишався тим же. Простір RGB не відокремлює Atronax 13.02.21 в 11:27 |
||
|
яскравість від кольору. По-перше, можна використати простір HSV (відтінок, насиченість, яскравість), адже він розмежовує ці характеристики. На жаль, простір HSV надбудовує значення відтінку над червоною компонентою, а оскільки шкіра має здебільшого рожевий колір, нам доведеться працювати з відтінками менше ніж 10 і більше ніж 90 відсотків, адже і ті, й інші червоні. Тому замість цього ми використаємо простір Y'CrCb (варіант YUV, що підтримується OpenCV), тому що він виокремлює яскравість та має єдиний діапазон значень для типового кольору шкіри, а не два. Майте на увазі, що більшість камер, зображень та відео на ділі використовують деякі види просторів YUV до того, як будуть конвертовані в RGB, тому у багатьох випадках ви отримаєте зображення YUV без необхідності конвертувати його власноруч. Atronax 13.02.21 в 11:30 |
||
|
Оскільки ми бажаємо, щоб режим прибульця був схожим на мультиплікацію, ми застосуємо фільтр вже після того, як матимемо зображення з даним ефектом; іншими словами, матимемо доступ до зменшеного кольорового зображення, створеного двопрохідним фільтром та повнорозмірною маскою меж. Алгоритм розпізнавання шкіри часто працює краще у випадках низької роздільної здатності (або низькочастотних сигналів замість високочастотних з шумом). Тому роботу здійснюватимемо над тим же зменшеним зображенням, як і двопрохідний фільтр (половина ширини і половина висоти). Конвертуємо картину в простір кольорів YUV: Atronax 13.02.21 в 11:54 |
||
|
Mat yuv = Mat(smallSize, CV_8UC3); Atronax 13.02.21 в 11:54 |
||
|
Ми також маємо зменшити маску меж, щоб вона мала ті ж розміри, що й зображення картини. Є певна складність у роботі з функцією floodFill() бібліотеки OpenCV, а саме, коли відбувається запис маски в окреме зображення, воно повинно також мати границю шириною 1 піксель, тому якщо на вхід подано кадр місткістю WxH пікселів, маска має містити (W+2)x(H+2) пікселів. Але функція floodFill() дозволяє також налаштувати маску межами, які її алгоритм ні в якому разі не перетне. Скористаємось цією особливістю в надії, що заливка не вийде за контури обличчя. Atronax 13.02.21 в 12:37 |
||
|
Таким чином, нам необхідно сформувати два зображення: маску меж з розмірами WxH і таку ж, але розмірами (W+2)x(H+2), бо вона включатиме границі навколо неї. При роботі з бібліотекою можливо мати декілька об'єктів cv::Mat (чи заголовків) що відносяться до тих же даних, чи навіть один об'єкт cv::Mat, що позначає частину основного cv::Mat. Тому замість виділення двох окремих кадрів чи копіювання пікселів меж, виділимо єдине зображення маски включно з границею і створимо додатковий об'єкт cv::Mat розмірами WxH (який лише вказує на зону інтересу в масці заливки без границі). Іншими словами, існує єдиний масив пікселів розмірами (W+2)x(H+2), але два об'єкти cv::Mat, один з яких відповідає цілісному зображенню (W+2)x(H+2), а інше - фрагменту WxH в його центрі: Atronax 13.02.21 в 13:08 |
||
|
int sw = smallSize.width; Atronax 13.02.21 в 13:25 |
||
|
Маска меж (показана з лівого боку наступного малюнку) повна гострих і слабких меж; але нам потрібні лише гострі, тому застосуємо бінарний поріг (результатом якого є середнє зображення). Щоб з'єднати прогалини між межами, ми скомбінуємо морфологічні оператори dilate() та erode(), інакше кажучи, виконаємо оператор "замикання", що сформує праву частину малюнку: Atronax 13.02.21 в 13:29 |
||
|
const int EDGES_THRESHOLD = 80; Atronax 13.02.21 в 13:29 |
||
|
Як зазначено раніше, нам необхідно виконати заливку в багатьох точках обличчя, щоб упевнитися, що включені різні можливі кольори та відтінки. Виберемо 6 точок на носі, щоках і лобі, як показано з лівого боку наступного малюнку. Зауважте, ці значення залежать від раніше намальованого контуру обличчя: Atronax 13.02.21 в 13:32 |
||
|
int const NUM_SKIN_POINTS = 6; Atronax 13.02.21 в 13:32 |
||
|
Тепер треба знайти слушні нижні та верхні границі для заливки. Майте на увазі, цей процес виконується в просторі кольорів Y'CrCb, тому по суті ми вирішуємо, наскільки може змінюватися яскравість, червоний та синій компоненти. Яскравість має змінюватися досить сильно, щоб включати тіні, освітлення та відблиск, але кольори повинні залишатися приблизно тими ж: Atronax 13.02.21 в 13:38 |
||
|
const int LOWER_Y = 60; Atronax 13.02.21 в 13:41 |
||
|
Ліве зображення наступного малюнку зображає 6 позицій для заливки (показані у вигляді синіх кіл), на правому можна бачити згенеровану маску, у якій шкіра має сірий колір, а межі - білий. Майте на увазі, що зображення з правого боку модифіковане для цієї книги таким чином, щоб пікселі шкіри (зі значенням 1) можна було роздивитися. Atronax 13.02.21 в 13:46 |
||
|
Зображення маски (показане з правого боку попереднього малюнку) відтепер має пікселі з такими значеннями: Atronax 13.02.21 в 13:53 |
||
|
Далі зображені початковий кадр з лівого боку та прибулець з ефектом мультиплікації з правого, на якому мінімум 6 частин обличчя відтепер зелені! Пам'ятайте, ми зробили шкіру не тільки зеленою, але й більш яскравою (аби бути схожими на прибульця, що світиться у темряві). Якщо потрібно змінити лише колір, без жодних втручань у яскравість, використайте інші методи, наприклад додайте 70 до зеленої компоненти й відніміть те ж значення від червоної та синьої, або конвертуйте зображення в HSV за допомогою cvtColor(src, dst, "CV_BGR2HSV_FULL") та налаштуйте відтінок і насиченість. Atronax 13.02.21 в 14:04 |
||
|
Переносимо настільну версію програми на Android Atronax 13.02.21 в 14:04 |
||
|
Застосунок працює як треба на ПК, тому приступимо до створення версії для Android чи iOS. Подані нижче деталі специфічні для Android, але їх можна використати на iOS, для Apple iPhone, iPad та інших подібних пристроїв. При розробці для Android користуватися OpenCV можна напряму з Java, але результат навряд чи буде настільки ж ефективним, як нативний код C/C++, ще й не дозволить запускати той же код на мобільному. Тому рекомендовано користуватися C/C++ для розробки більшості програм OpenCV для Android (читачі, які бажають користуватися виключно Java, можуть звернути увагу на JavaCV (автор Samuel Audet), доступну за адресою http://code.google.com/p/javacv/, це дасть змогу запускати на ПК той же код, що й на Android). Atronax 13.02.21 в 14:20 |
||
|
Примітка Atronax 13.02.21 в 14:43 |
||
|
Налаштування Android-проєкту, що використовує OpenCV Atronax 13.02.21 в 14:45 |
||
|
Портування OpenCV на Android кожного року суттєво змінюється, як і метод для доступу до камери, тому книга - не найкращий спосіб дізнатися, як її правильно налаштувати. Читачеві краще знайти інструкції на сторінці http://opencv.org/platforms/android.html, щоб відрегулювати та збудувати нативний (з використанням NDK) Android-застосунок з OpenCV. В бібліотеці є приклад проєкту з назвою Sample3Native, що отримує доступ до камери та виводить модифіковане зображення. Цей приклад корисний як основа застосунку, що буде розроблений далі, тому читачам варто з ним ознайомитися (зараз доступний за адресою http://docs.opencv.org/doc/tutorials/introduction/android_binary_package/android_binary_package_using_with_NDK.html). Потім ми модифікуємо основу так, щоб він зміг оброблювати кадри ефектом мультиплікації та виводити результат. Atronax 13.02.21 в 15:00 |
||
|
Якщо розробка OpenCV програм для Android здається вам складною, наприклад ви отримуєте помилки компіляції чи камера видає чорні кадри, спробуйте знайти рішення на цих сайтах: Atronax 13.02.21 в 15:09 |
||
|
Формати кольору для обробки зображень на Android Atronax 13.02.21 в 15:09 |
||
|
Під час розробки для ПК ми мали справу з форматом пікселя BGR, адже вхідні дані з камери, зображення чи відеофайлу мали саме цей формат, вихідні, до речі, теж. Але при розробці для мобільних пристроїв доведеться конвертувати нативні формати самостійно. Atronax 13.02.21 в 15:24 |
||
|
Формат кольору для вводу з камери Atronax 13.02.21 в 15:36 |
||
|
Формат кольору для виводу на екран Atronax 13.02.21 в 15:38 |
||
|
У коді Sample3Native змінна mbgra позначає кольорове зображення, що виводитиметься на пристрої Android, у форматі BGRA. Формат OpenCV за замовчуванням - це BGR (зворотний порядок байтів, аніж при RGB), а BGRA всього лише додає невикористаний байт на кінці пікселя, тобто кожен з них зберігається у вигляді "синій, зелений, червоний, невикористаний". Можна або виконати обробку в форматі BGR, а потім конвертувати BGR в BGRA до того, як вивести його на екран, або відрегулювати код обробки так, щоб він міг підтримувати формат BGRA замість або в доповнення до BGR. Досить часто це легко зробити в OpenCV, багато з його функцій здатні оброблювати BGRA зображення, але треба впевнитися, що ви створюєте зображення з тією ж кількістю каналів, що й вхідне, визначивши за допомогою функції Mat::channels() його значення. Крім цього, якщо у коді ви працюєте з пікселями напряму, доведеться відокремити код обробки 3-канального BGR та 4-канальних зображень BGRA. Atronax 13.02.21 в 15:55 |
||
|
Примітка Atronax 13.02.21 в 16:02 |
||
|
Як зазначено раніше, кадр камери прибуває до коду у форматі YUV420sp, а відбувати має як BGRA. Тому, якщо ми підготуємо об'єкти cv::Mat для вводу та виводу, нам доведеться лише конвертувати YUV420sp в BGRA з використанням cvtColor(). Щоб мати можливість користуватися кодом C/C++ в застосунку Java для Android, потрібні особливі імена функцій JNI, що збігаються з іменами класу та пакету Java, що використає функцію JNI, у такому форматі: Atronax 13.02.21 в 16:10 |
||
|
Почнемо з функції C/C++ ShowPreview(), яку потім використаємо з класу Java CartoonifierView в пакеті Cartoonifier. Додайте її в jni\jni_part.cpp: Atronax 13.02.21 в 16:35 |
||
|
// Показати звичайний кадр камери без модифікацій. Atronax 13.02.21 в 16:39 |
||
|
Хоча код виглядає досить складним, на перший погляд, перші дві стрічки функції лише дають нативний доступ до даних масивів Java, наступні дві будують об'єкти cv::Mat над заданими буферами пікселів (таким чином, вони не розміщують нові зображення, а дозволяють myuv отримати доступ до пікселів в масиві _yuv), а останні дві звільняють нативну прив'язку, яку ми встановили для масивів Java. Єдиною реальною роботою є конвертація формату YUV у BGRA, тому ця функція є лише основою, яку можна використати для створення нових. Тепер розширимо код аналізом та модифікацією BGRA об'єкта cv::Mat до виводу на екран. Atronax 13.02.21 в 16:51 |
||
|
Примітка Atronax 13.02.21 в 17:16 |
||
|
Конвертувати зображення з формату BGRA в BGR до етапу обробки, виконати алгоритм над форматом BGR, а потім повернути результат в BGRA, яким можна скористатися в Android. Atronax 14.02.21 в 07:17 |
||
|
Спершу краще компілювати й перевірити показану раніше функцію ShowPreview() на ПК, щоб мати можливість повернутися у випадку більш пізніх проблем з кодом C/C++. Щоб викликати її з Java, додамо нативне оголошення поряд з Java-версією CartoonifyImage() в нижній частині CartoonifyView.java: Atronax 14.02.21 в 07:40 |
||
|
Використовуємо код ефекту мультиплікації в застосунку Android NDK Atronax 14.02.21 в 08:42 |
||
|
1. Додайте cartoon.cpp (та ImageUtils_0.7.cpp, якщо потрібні інструменти для налагодження) в LOCAL_SRC_FILES, але врахуйте, що вони знаходяться в каталозі desktop замість jni за замовчуванням. Тому включіть це після LOCAL_SRC_FILES := jni_part.cpp: Atronax 14.02.21 в 08:54 |
||
|
2. Додайте шлях для пошуку заголовків, аби NDK зміг знайти cartoon.h: Atronax 14.02.21 в 09:05 |
||
|
3. У верхній частині файлу jni\jni_part.cpp замініть #include <vector> на: Atronax 14.02.21 в 09:07 |
||
|
4. Додайте у файл JNI-функцію CartoonifyImage(), код обробки ефектом мультиплікації. Почнемо з дублювання створеної раніше функції ShowPreview(), яка лише виводить на екран кадри з камери без модифікацій. Примітьте, що конвертація відбувається з формату YUV420sp в BGR, адже ми не бажаємо працювати із зображеннями BGRA: Atronax 14.02.21 в 09:17 |
||
|
Модифікуємо зображення камери фільтром мультиплікації. Atronax 14.02.21 в 09:20 |
||
|
// у формат OpenCV's за замовчуванням. Atronax 14.02.21 в 09:25 |
||
|
5. Попередній код не змінює зображення, однак ми бажаємо застосувати фільтр мультиплікації, розроблений раніше у цьому розділі. Тому доєднаємо виклик наявної функції cartoonifyImage() з файлу cartoon.cpp застосунку для ПК. Замініть тимчасову стрічку коду displayedFrame = mbgr на: Atronax 14.02.21 в 09:30 |
||
|
6. Це все! Збудуйте рішення (Eclipse має скомпілювати код C/C++, використовуючи ndk-build) та запустіть його на мобільному пристрої. Ви маєте отримати робочу версію програми Cartoonifier для Android (на початку розділу є приклад, як має виглядати результат)! Якщо виявляться проблеми з компіляцією чи запуском, поверніться на попередні кроки та виправте помилки (можете ознайомитись з кодом, що йде разом з книжкою). Перевірте наступні кроки, коли впораєтесь з цим. Atronax 14.02.21 в 10:05 |
||
|
Вона має працювати з вводом даних, такими як зміна режимів між скетчем, картиною, виглядом страховидла чи прибульця. Додамо їх в меню Android. Atronax 14.02.21 в 10:19 |
