Технології комп’ютерного зору в UI-тестуванні. Частина 2

У першій частині ми розглянули теорію Computer Vision і познайомилися з бібліотекою OpenCV.

Далі буде наведений опис інструментів з фреймворку, створеного для демонстрації прикладів із цієї статті. Проєкт передбачає запуск на ОС Ubuntu або всередині Docker. Деталі налаштування середовища тут. Запуск на Windows і Mac також можливий за умови встановлення модуля OpenCV, але ці варіанти не тестувалися.

Ми визначили, що в 90% випадків UI-тестування фрагмент на екрані, який нам треба знайти та порівняти з еталоном, відображається без спотворень і перекриттів. Це різноманітні кнопки, іконки, перемикачі, текст, цифри, лінії графіків, фрагменти діаграм тощо. Для тестування таких елементів нам допоможе метод Template Matching і написаний на його основі скрипт.

Скрипт для пошуку темплейтів на картинці

Скрипт find_template_on_image.py приймає 4 параметри: «—template» — шлях до підготовленої заздалегідь картинки пошуку, «—image» — шлях до зображення, на якому треба знайти елементи, «—ratio» — порогове значення від 0 до 1 для налаштування точності (за промовчанням 0,65) та параметр, «—output» — шлях до картинки, в яку записуватиметься результат пошуку (знайдені темплейти на зображенні будуть обведені синім квадратом).

find_templates_on_img.py [-h] -t TEMPLATE -i IMAGE [-r RATIO] [-o OUTPUT]
Script to get number of occurrence template on image
arguments:
  -h, --help  	      Show this help message and exit
  -t, --template  TEMPLATE
                    	Template path. Path to template image
  -i, --image  IMAGE
                    	Main image path. Path to image
  -r, --ratio  RATIO
                    	Threshold ratio (0.01 - 1) Default 0.65 is a good
                    	value to start with, it detected all template with a
                    	minimum of false positives.
  -o, --output  OUTPUT
                    	Output path. Path to output image. Should contains
                    	extension equal to main img
python find_templates_on_img.py –t /templates/turn_right.png -i /images/screen.png
  -r 0.5 -o /tmp/test_out.png

Cпробуємо знайти координати іконки Google Maps у меню телефона. Готуємо темплейт із зображенням іконки Google Maps, також робимо скріншот з екрану телефона. Шляхи до картинок підставляємо в скрипт. Якщо вкажемо параметр —output, то збережеться картинка з обведеним місцем, де було знайдено темплейт. У консолі скрипт видає текст, що допомагає визначити, чи був темплейт локалізований на картинці. Наприклад, «Found» показує, в скількох пікселях результат порівняння перевищує вказаний поріг «Threshold». «Accepted points» містить координати цих точок.

Застосування find_template_on_image

Важливою є інформація, яка міститься в «Point clouds». На прикладі видно, що знайдено 555 точок, де перевищується порогове значення, але цієї інформації не досить, щоб сказати, скільки саме темплейтів знайдено, тому що були прийняті точки з результатом як 0,65, так і 0,999. Збільшуючи картинку з проміжним результатом, ми побачимо, що всі ці 555 пікселів розташовані всередині хмаринки довільного радіусу (червоне коло). Якщо обчислити відстані між кожною парою з усіх 555 точок, то ми знайдемо найбільшу відстань між визначеними точками. Цю величину можна порівняти з радіусом темплейта (половина найменшого з вимірів ширини чи висоти). Якщо найбільша відстань між знайденими точками менша за радіус темплейту, можемо сказати, що всі точки розташовані всередині однієї хмаринки, якщо ні, то необхідно знайти кількість хмаринок, в яких згруповані точки.

Зведення і групування точок на площині — доволі складна задача з розділу статистики, але нас цікавить не стільки розподіл по хмаринках, як їх кількість. Ми можемо побудувати метод (sparse_subset), що в залежності від радіуса хмаринки буде визначати кількість хмаринок, в які потрапляють точки. У методі формується масив, в якому опиняються тільки ті точки, які мають значну відстань (> r) від точок, що вже знаходяться в масиві. Кількість точок, що потраплять у результуючий масив, показує кількість хмаринок на площині. Водночас, кількість хмаринок дорівнює кількості темплейтів, знайдених на картинці.

Обчислення Point clouds

Скрипт для пошуку об’єктів на картинці

Другий скрипт find_objs_on_image.py використовується для пошуку об’єктів на картинці, наприклад, об’єктів на мапі. Його використання схоже з попереднім скриптом. Параметр «—query» приймає шлях до заздалегідь підготовленого зображення об’єкту, скріншот екрану приймає параметр «—train». Поріг «—ratio» використовується для задання точності. Параметр «—output» — для збереження результатів. На проміжному етапі скрипт використовує алгоритм пошуку темплейтів з низьким порогом для того, щоб визначити на всьому зображенні ті місця, де ми будемо застосовувати обчислення дескрипторів.

find_objs_on_img.py [-h] -q QUERY -t TRAIN [-r RATIO] [-o OUTPUT]
Script to get number of occurrence template on image with additional checks.
Feature detection and matching used for making final decision.
arguments:
  -h, --help    	Show this help message and exit
  -q, --query  QUERY
                    	Query image path. Path to query image
  -t, --train  TRAIN
                    	Train image path. Path to train image
  -r, --ratio  RATIO
                    	Threshold ratio (0.01 - 1) Default 0.65 is a good
                    	value to start with, it detected all template with a
                    	minimum of false positives.
  -o, --output  OUTPUT
                    	Output path. Path to output image. Should contains
                    	extension equal to main img
python find_objs_on_img.py -q /positions/car_on_the_road.png -t /images/screen.png
  -r 0.5 -o /tmp/test_out.png

Розглянемо роботу скрипту на прикладі тестування зображення локації з офісом Intellis за адресою Kyrylivska 39 на мапі Google з результатами пошуку тексту «Intellis Kyrylivska 39». Заздалегідь підготуємо скріншот для параметра —train і локацію офісу для параметра —query. У консолі отримуємо інформацію про кількість знайдених темплейтів «Clouds found». Якщо темплейт знайдено, то з нашого скріншоту буде вирізано фрагмент (за розмірами нашого темплейту) й обчислено ключові точки та дескриптори. Далі в консолі присутні блоки «Accepted points» і «Rejected points», які показують, чи містили ці фрагменти об’єкти, зображені на query-зображенні. У прикладі бачимо — на картинці знайдено 1 темплейт, у ньому знайдено 17 ключових точок (mtc), які при порівнянні дали 11 результатів, що пройшли тест Девіда Лоу (gd).

Також перевірено координати цих точок, і 10 з 11 мали схожі значення (cmn). На основі цієї інформації можна зробити висновок, що на скріншоті присутній об’єкт, зображений на query-зображенні. Система налаштована таким чином, що позитивне рішення приймається тоді, коли знайдено більше 3-х точок, які пройшли тест Девіда Лоу, і при цьому їхні координати на фрагменті зображення мають збігатися з координатами на query-зображенні (cmn >= 3). Також кількість таких точок має бути більше ніж 20% від кількості знайдених ключових точок (cmn >= 0.2* mtc). На виході маємо зображення, на якому червоний квадрат показує, що знайдено темплейт, а синій квадрат (з кружечком у центрі), — що знайдено об’єкт.

Використання find_objs_on_image. Позитивний сценарій

Також розглянемо негативний тест. Спробуємо перевірити, чи буде знайдено локацію з офісом Intellias за адресою Kyrylivska 39 на мапі Google з результатами пошуку тексту «Intellis Kyrylivska 15». Як ми бачили в першій частині, результат був позитивний у випадку використання алгоритму пошуку темплейтів.

Отже, в консолі бачимо, що знайдено 1 темплейт, але при детальній перевірці було виявлено, що фрагмент, який вирізано зі скріншоту за розмірами темплейту не містить об’єктів, зображених на query-зображенні. Кількість знайдених точок була так само 17, але тест Девіда Лоу та перевірку координат пройшли всього 2. Тобто умова cmn >=3 && cmn > mtc * 0.2 не виконується. У результаті бачимо червоний квадрат знайденого темплейту, але синього квадрату немає.

Використання find_objs_on_image. Негативний сценарій

Скрипти, які ми розглянули, можна застосовувати самостійно, наприклад при відладці UI-тестів або при написанні окремих user flow сценаріїв. Також, для більшої ефективності вони можуть бути інтегровані в тестовий фреймворк.

Тестовий фреймворк

Демопроєкт — це класичний приклад BDD-фреймворку на Ruby для тестування Android-застосунків з тою відмінністю, що всі маніпуляції із застосунком виконує ADB (Android Debug Bridge), без використання драйверів Appium, Selenium, Calabash або інших. У драйвері немає потреби, адже роль контролера стану застосунку виконують скрипти перевірки зображень экрану find_template_on_image.py, find_objs_on_image.py. Сценарії тестів написані мовою Gherkin, що транслюється в код на Ruby.

На найнижчому рівні код запускає bash, який або напряму керує застосунком через ADB (запустити застосунок, ввести текст, скопіювати файли, видалити кеш), або запускає наші OpenCV Python скрипти, аналізує їхні результати й робить висновок про потрібні подальші дії (тап по координатах, дропдаун, свайп тощо). Таку архітектуру не можна назвати зручною (як мінімум, дві мови програмування Ruby + Python, а також Bash і Gherkin), але вона показує, що скрипти OpenCV можна інтегрувати в будь-який фреймворк на Java, JS, Ruby тощо.

Схема фреймворку для тестування Android з інтегрованим OpenCV

Які тести присутні в демопроєкті? У файлі зі сценаріями samples.feature є два тести на перевірку локації в Google Maps. Тести «Location Office Intellias Kyrylivska 39», «Location Office Intellias Kyrylivska 15» використовують описаний алгоритм пошуку об’єкта на зображенні. На малюнку також зазначені темплейти, які треба знайти та клікнути перед тим, як виконувати перевірку локації.

Темплейти та локація для тестування застосунку Google Maps

Крім того, у файлі є тести на перевірку функціоналу аплікації Google Fit. У тесті «Steps Number Of Active Days For The Last 7 Days» перевіряється діаграма кількості активних днів за минулий тиждень. Зображення активного дня збережено як темплейт, кількість знайдених темплейтів і є кількістю активних днів.

Темплейти для тестування кількості активних днів у застосунку Google Fit

В іншому тесті «Activity Cardio Check if day plan is done» перевіряється, чи діаграми прогресу активностей мають певну форму. Коли план не виконано, діаграма має круглу форму та показує прогрес (синя діаграма). Якщо план виконано, діаграма приймає форму восьмикутника. Тож для прийняття рішення досить перевірити, чи діаграма має кути. Через те що в середині діаграми є фото, перевірка робиться на краях зображення.

Темплейти для тестування кількості активних днів у застосунку Google Fit

Підготовка тестових даних

Яким чином вибираються темплейти чи query-зображення? Насправді, процес дуже схожий на написання xpath чи css селекторів для елементів при класичному тестуванні. Перш за все, треба визначити, що саме необхідно перевірити у тесті — наявність елемента, його форму, статус, положення відносно інших елементів? Якщо тестується присутність(відсутність) елемента, то можна знехтувати формою і положенням і підготувати фрагмент що містить найбільш виражені деталі елементу (прямокутник 1). Якщо важлива форма, то темплейт має містити границі елементу і його фон (прямокутник 2). У цьому випадку зміна фону буде призводити до помилок, але таким чином можна протестувати функціонал зміни тем оформлення. Взаємне розміщення можна протестувати, якщо темплейт буде містити групу елементів (прямокутник 3).

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

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

Підготовка темплейту та query-зображення

Декілька слів про перформанс. Для скріншотів мого телефону розміром 1080×1920 а також скріншотів нашої навігаційної системи 1920×720, пошук темплейтів триває 0,4–0,8 секунд і 0,1 секунди на обчислення дескрипторів і пошук об’єктів. Методи працюють у циклах очікування, де кожен цикл приблизно дорівнює 1 сек. Тобто сканування екрана відбувається щосекунди й це накладає обмеження у використанні скриптів у тестуванні динамічних анімацій або відеоігор. Якщо зміни будуть занадто швидкі, система може пропустити момент, коли не екрані буде правильне зображення.. Проте скрипти дозволяють перевіряти локації на мапі при русі машини зі швидкістю до 60 км/год.

Висновки

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

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

Computer Vision — це ще один інструмент в арсеналі автоматизатора, який відкриває нові шляхи до написання тестів і тим самим дозволяє суттєво збільшити тестове покриття.

Більше інформації про особливості роботи із зображеннями в OpenCV ви можете знайти за такими лінками:

LinkedIn

11 комментариев

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

Я так розумію, якшо змінити, до прикладу телефон на планшет з більшим screen resolution то прийдеться всі baseline вирізати наново, або бавитись з налаштування точності ? Як таку проблему вирішуєте ? Маєш може приклади на Java (бо шо в python, шо в ruby я повний Зеленскій) ?

:) Нажаль, на джаві нема, але там все досить зрозуміло тож вам буде нескладно розібратися. Насправді, вирізати baseline прийдеться через інші причини — різна клавіатура, різні дизайни аплікацій для різних версій Андроїд (7 — 9 — 10).
Щодо screen resolution отримую багато питань, тому планую оновити репозиторій та додати в скрипти функціонал підбору зуму для матчингу. Логіка така, якщо темплейт чи об’єкт не знайдено, то зменшуємо темплейт на 5% і пробуємо ще раз...і так поки не зменшимо до 50% а потім збільшуємо до 150%. Це вирішує проблему різних screen resolution, але швидкість пошуку істотно падає. Якщо елемент там є, то скрипт його знаходить за ~1сек, але якщо нема, то проходять всі етапи зумингу і в залежності від кроку зуму(2% — 5% — 10%) результат що емемент не знайдено отримуємо до 5сек.

find_template_on_image.py — поиск максимумов, расположенных не более, чем на радиус r, можно было сделать за две простые операции: сделать dilate с радиусом r и дальше оставить только те точки исходного массива, что совпадают с dilate.

Дякую. Це слушна думка

Гарний доклад. Багато часу вклав?

:) Десь з Нового року готував.

Цей коментар не рахується, бо був проплачений

0_o што?)) Де пруф?) баланс рахунку не змінився)

не парся, Ромі ніхто вже давно не вірить і так

Подписаться на комментарии