Mastering OpenCV with Practical Computer Vision Projects / Опановуюємо OpenCV через практичні проекти комп'ютерного зору: Розділ 1. Ефект мультиплікації та зміна кольору шкіри для Android
| Английский оригинал | Перевод на украинский | |
|---|---|---|
Сожалеем, но текст оригинала доступен только зарегистрированным пользователям. |
Надаємо зображенню ефект мультиплікації у відповідь на дотик до екрана Atronax 14.02.21 в 11:42 |
|
|
Щоб показати зображення з камери (поки користувач не забажає застосувати ефект мультиплікації до обраного кадру), можна просто викликати створену раніше JNI-функцію ShowPreview(). До застосування фільтру також знадобиться почекати на події дотику. Нам потрібно обробити зображення, коли користувач дотикнеться до екрана, тому зробимо мітку, що вкаже на потребу застосувати фільтр до наступного кадру, і лише коли її буде звільнено, вивід на екран продовжиться. Це значитиме, що оброблене зображення виведеться лише на долі секунди, а потім зміниться на наступний кадр камери. Тому знадобиться інша мітка, завдяки якій зображення не зникатиме декілька секунд до того, як наступні змінять його; зрештою, це дасть змогу користувачу роздивитися подібні кадри. Atronax 14.02.21 в 12:34 |
||
|
1. Додаємо наступний заголовок у верхній частині файлу CartoonifierApp.java з Atronax 14.02.21 в 12:35 |
||
|
2. Модифікуємо визначення класу у верхній частині файлу CartoonifierApp.java: Atronax 14.02.21 в 12:36 |
||
|
3. Вміщуємо наступний код в нижню частину функції onCreate(): Atronax 14.02.21 в 12:38 |
||
|
4. Додаємо функцію onTouch() для обробки подій дотику: Atronax 14.02.21 в 12:44 |
||
|
5. Тепер визначимо функцію nextFrameShouldBeSaved() в файлі CartoonifierView.java: Atronax 14.02.21 в 12:44 |
||
|
// Оброблюємо наступний кадр камери та зберігаємо його замість виводу на екран Atronax 14.02.21 в 12:46 |
||
|
6. Запишемо наступні змінні у верхній частині класу CartoonifierView: Atronax 14.02.21 в 12:48 |
||
|
7. Функція processFrame() класу CartoonifierView тепер здатна перемикати режим між звичайними та обробленими зображеннями, але потрібно впевнитися, що немодифіковані кадри виводитимуться лише якщо наразі не показана версія з ефектом мультиплікації. Тому змінимо функцію processFrame() так: Atronax 14.02.21 в 12:55 |
||
|
if (!bSaveThisFrame) { Atronax 14.02.21 в 13:21 |
||
|
8. Тепер програма має скомпілюватися та запуститися. Перевіримо, чи добре вона працює. Atronax 14.02.21 в 13:22 |
||
|
Зберігаємо зображення у файл та виводимо в галереї Android Atronax 14.02.21 в 13:23 |
||
|
Ми збережемо вихідний результат у вигляді файлу PNG та покажемо його в галереї зображень Android. Остання розроблена для використання з файлами JPEG, але даний формат не підходить для мультиплікації з чіткими кольорами та межами, тому ми застосуємо цікавий спосіб додати зображення PNG до галереї. Створимо Java функцію savePNGImageToGallery(), що допоможе виконати це завдання. В нижній частині функції processFrame(), показаної раніше, бачимо створення об'єкту Android Bitmap із вхідних даних; тому нам потрібен спосіб зберегти його у вигляді файлу PNG. Функція imwrite() OpenCV Java API може бути використана з цією метою, але це вимагатиме прив'язки обох версій API (Java і C/C++, як робить це приклад "tutorial-4-mixed" бібліотеки OpenCV4Android). Оскільки OpenCV Java API не потрібен нам для будь-чого іншого, наступний код покаже лише як зберегти файли PNG, використовуючи Android API замість OpenCV Java API: Atronax 14.02.21 в 13:46 |
||
|
1. Клас Bitmap для Android здатний зберігати файли у форматі PNG, тому використаємо його. Також підберемо ім'я файлу. Використаємо поточні дату і час, що дозволить зберегти багато зображень та вказати користувачу, коли саме вони створені. Вмістіть це точно перед стрічкою "return bmp" функції processFrame(): Atronax 14.02.21 в 13:57 |
||
|
2. Додамо цей код до верхньої частини CartoonifierView.java: Atronax 14.02.21 в 14:02 |
||
|
3. Помістіть цей код в класі CartoonifierView, у верхній частині: Atronax 14.02.21 в 14:04 |
||
|
4. Додайте це у функцію nextFrameShouldBeSaved() класу CartoonifierView: Atronax 14.02.21 в 14:05 |
||
|
5. Реалізуйте функцію savePNGImageToGallery() в класі CartoonifierView: Atronax 14.02.21 в 14:09 |
||
|
// Відкриваємо файл. Atronax 14.02.21 в 14:15 |
||
|
6. Застосункам Android потрібен дозвіл на встановлення, якщо вони планують зберігати файли на пристрої. Для цього помістіть таку стрічку у файл AndroidManifest.xml поряд з подібною, що запитує дозвіл на доступ до камери: Atronax 14.02.21 в 14:21 |
||
|
7. Скомпілюйте та запустіть програму! Якщо ви торкнетесь екрану, щоб зберегти фотографію, через деякий час оброблене фільтром зображення з'явиться на екрані (можливо, через 5 чи 10 секунд). Як тільки-но воно буде показано, це значитиме, що відбувся запис на SD-карту та в галерею. Завершіть роботу з програмою Cartoonifier, відкрийте галерею Android та перегляньте альбом. Ви маєте побачити оброблені зображення у форматі PNG повної роздільної здатності екрану. Atronax 14.02.21 в 14:36 |
||
|
Показуємо повідомлення Android про збережене зображення Atronax 14.02.21 в 14:38 |
||
|
1. Додайте наступний код у верхню частину файлу CartoonifierView.java: Atronax 14.02.21 в 14:39 |
||
|
2. Додайте це поряд з верхньою частиною файлу CartoonifierView: Atronax 14.02.21 в 14:41 |
||
|
3. Помістіть це всередині блоку if під викликом функції savePNGImageToGallery() в processFrame(): Atronax 14.02.21 в 14:44 |
||
|
4. Реалізуйте функцію showNotificationMessage() в класі CartoonifierView: Atronax 14.02.21 в 14:50 |
||
|
// Закриємо попередні оповіщення, щоб виводити їх Atronax 14.02.21 в 14:57 |
||
|
5. Знову ж таки, скомпілюємо та запустимо застосунок! Тепер, якщо ви натиснете на екран, щоб зберегти наступний кадр, буде показано оповіщення. Якщо повідомлення має з'явитися до обробки зображення, а не після цього тривалого процесу, здійсніть виклик функції showNotificationMessage() перед cartoonifyImage(), та перемістіть код для генерації рядків дати й часу, щоб для оповіщення та назви реального файлу залишалися однаковими. Atronax 14.02.21 в 15:08 |
||
|
Змінюємо режим мультиплікації через панель меню Android Atronax 14.02.21 в 15:09 |
||
|
Дозволимо користувачу змінювати режим через меню: Atronax 14.02.21 в 15:10 |
||
|
2. Розміщуємо наступні внутрішні змінні в клас CartoonifierApp: Atronax 14.02.21 в 15:11 |
||
|
3. Додайте код наступних функцій в CartoonifierApp: Atronax 14.02.21 в 15:19 |
||
|
4. Помістіть наступні внутрішні змінні в CartoonifierView: Atronax 14.02.21 в 15:20 |
||
|
5. Додайте наступні функції в CartoonifierView: Atronax 14.02.21 в 15:21 |
||
|
6. Значення режимів потрібно передати в JNI-код функції cartoonifyImage(), тому відправимо їх як аргументи. Модифікуємо оголошення Java-функції CartoonifyImage() в CartoonifierView: Atronax 14.02.21 в 15:23 |
||
|
7. Тепер змінимо код Java таким чином, щоб передати поточні значення режимів в processFrame(): Atronax 14.02.21 в 15:25 |
||
|
8. JNI оголошення функції CartoonifyImage() у файлі jni\jni_part.cpp тепер матиме вигляд: Atronax 14.02.21 в 15:26 |
||
|
9. Тепер маємо передати режим в код C/C++ файлу cartoon.cpp з JNI-функції в jni\jni_part.cpp. Під час розробки для Android в кожен момент часу можна показувати одне вікно, на відміну від ПК, коли зручно оперувати кількома для налагодження. Тому замість булевої мітки вкажемо номер, що становитиме 0 для звичайного режиму програми, 1 для налагодження на мобільних пристроях (створення GUI OpenCV завжди викличе проблеми!) та 2 для налагодження версії для ПК (у якої можна створювати стільки вікон, скільки забажається): Atronax 14.02.21 в 15:38 |
||
|
10. Оновлюємо реалізацію C/C++ у файлі cartoon.cpp: Atronax 14.02.21 в 15:39 |
||
|
11.Також змінюємо оголошення C/C++ у файлі cartoon.h: Atronax 14.02.21 в 15:40 |
||
|
12. Компілюємо та запускаємо код; потім тестуємо кнопки меню в нижній частині вікна. Майте на увазі, скетч зображений в режимі реального часу, а картина формується зі значною затримкою через використання двопрохідного фільтра. Atronax 14.02.21 в 15:43 |
||
|
Зменшуємо випадковий перцевий шум на скетчі Atronax 15.02.21 в 08:37 |
||
|
Більшість камер сучасних смартфонів і планшетів створюють значну кількість шуму. Частіше за все це некритично, але у випадку застосування фільтру Лапласових меж 5х5 вплив надто сильний. Маска меж (показана у вигляді режиму скетчу) матиме тисячі невеликих скупчень, які називають перцевим шумом, кожна частка якого складатиметься з кількох чорних пікселів, розміщених поряд на білому фоні. Дотепер використано Медіанний фільтр, який непогано видаляє перцевий шум у більшості випадків, але в нашому він не достатньо ефективний. Маска меж переважно має чистий білий фон (зі значенням 255) з деякими чорними межами (0) та точки шуму (того ж рівня). Можна використати стандартний морфологічний оператор, але він видалить також і межі. Замість цього застосуємо ручний фільтр, що працюватиме з малими чорними фрагментами, повністю оточеними білими пікселями. Це дозволить спекатися більшості шуму, не сильно змінюючи власне межі. Atronax 15.02.21 в 08:38 |
||
|
Знайдемо на зображенні чорні пікселі, і для кожного з них перевіримо квадрат розмірами 5х5 навколо нього, щоб перевірити, чи не є границя повністю білою. Якщо це дійсно так, маємо острівець чорного шуму, тому заповнимо поточний блок білими пікселями й таким чином видалимо його. Для простоти, в нашому фільтрі 5х5 проігноруємо два граничні пікселі навколо зображення та залишимо їх як є. Atronax 15.02.21 в 09:04 |
||
|
Наступний малюнок зображає оригінальний кадр з планшета Android з лівого боку, режим скетчу посередині (на якому можна помітити малі чорні точки перцевого шуму), а з правого боку - результат обробки, на якому шкіра виглядає чистішою: Atronax 15.02.21 в 09:08 |
||
|
Наступний блок коду можна назвати функцією removePepperNoise(). Для простоти вона редагує вхідне зображення: Atronax 15.02.21 в 09:09 |
||
|
void removePepperNoise(Mat &mask) Atronax 15.02.21 в 09:10 |
||
|
// Перевіряємо, чи це не чорний піксель, що оточений Atronax 15.02.21 в 09:16 |
||
|
// Оскільки ми тільки-но заповнили весь блок 5x5 Atronax 15.02.21 в 09:20 |
