Найти тему

Первые шаги навстречу компьютерному зрению или python+opencv+adb = автоматизация для андроида

Часто бывает в повседневной работе приходится сталкиваться с монотонными однообразными задачами. Монотонная работа изрядно надоедает, усталость приходит быстро. Хочется отвлечься, передохнуть после рабочего дня.
Открываем свой смартфон, запускаем игру и... минут 15 тратим на закрытие приставучих баннеров с акциями и другим "привлекательным" контентом. А если хочется улучшить свой игровой процесс добавлением новых вещей, тот тут либо к "привлекательным" акциям прибегать и тратить кровно заработанные, либо смотреть еще 15-20 минут горы рекламы. А там уже ни сил, ни желания играть нет. Знакомо? Эта статья как раз о том, как можно сэкономить свое время, отдав смартфон для просмотра рекламы своему личному боту!

Как сделать?

Реализовывать автоматический просмотр рекламы в игре мы будем достаточно топорно и громоздко, но зато просто! Громоздко - так это потому что для реализации нам понадобится не только смартфон, но и ПК (или еще один смартфон с OTG, хотя не проверялось), который будет подключаться к смартфону через ADB (usb-кабель или wifi - различие будет только в одной строчке кода). Ну а топорно, потому что алгоритм будет примерно следующий:
1) получаем скриншот со смартфона
2) распознаем на скриншоте все интересующие нас элементы
3) в зависимости от того, какие элементы видим выполняем нажатие в определенную область экрана.

Можно лучше нельзя

Сразу хочу оговориться о том, что для решения такого типа задачи есть множество других вариантов, при том лучших по скорости, количеству ресурсов и удобству использования. Но у каждого есть свои НО. Для демонстрации поиска картинки в картинке с применением библиотеки OpenCV выбранный подход будет показательным. Хотя считаю нужно упомянуть так же про то, что получать элементы с экрана можно с помощью uiautomator, а если говорить про windows, то через winAPI и дескрипторы можно делать много интересных вещей. Но сейчас мы будем считать что всего этого нет и есть только "экран", "мышка" и OpenCV.

Что же такое этот OpenCV?

Если кратко и своими словами - код, который позволяет достаточно просто взаимодействовать с изображениями. Если чуть сложнее - библиотека алгоритмов компьютерного зренияобработки изображений и численных алгоритмов общего назначения с открытым кодом (Материал из Википедии — свободной энциклопедии).

Приступаем к реализации

1) Получаем скриншот со смартфона.
Самым простым будет использование ADB и команды "adb exec-out screencap -p > screenshot.png". Выполняется долго, но работает просто. После выполнения получаем файл screenshot.png на ПК. Напишем функцию для загрузки изображения на ПК и после в представление OpenCV:

-2

2) Распознаем на скриншоте все интересующие нас элементы.
С OpenCV это делается буквально одной строкой. Но прежде чем найти интересующие нас элементы мы должны их подготовить. Делаем скриншоты и вырезаем элементы. Получаем что-то вроде этого:

-3

И прописываем в коде, чтобы знать какие у нас есть: target_images:np.ndarray=['ad_enable', 'ruletka', 'ruletka_attemp', 'ruletka_end', 'get', 'ok', 'black_market', 'discount_special', 'ad_exit_next0', 'ad_exit_next1', 'ad_exit_next2', 'ad_exit0', 'ad_exit1', 'ad_exit2', 'box_open', 'main_menu', 'main_menu_exit', 'discount_banner0', 'discount_banner1', 'discount_banner2', 'back', 'bronze_box'].

Добавляем функцию для загрузки целевых картинок:

-4

Половинные размеры понадобятся позже для вычисления центра элементов.

Добавляем распознавание на скриншоте целевых изображений:

-5

Самое важное здесь это функция cv2.matchTemplate, в которой происходит сравнение скриншота с шаблоном (целевым изображением). И как ее продолжение функция cv2.minMaxLoc, которая берет наш результат корреляции и возвращает 4-кортеж, который включает минимальное значение корреляции, максимальное значение корреляции, (x, y)-координату минимального значения и (x, y)-координату максимального значения соответственно.

После выполнения recognize_screenshot() алгоритм образно будет видеть скриншоты как-то так (а мы в логе увидим то, что в подписях под скриншотами):

Img: ad_enable, location: x:1132, y:823
Img: ad_exit1, location: x:2208, y:56
Img: ad_exit2, location: x:2208, y:56
Img: discount_banner0, location: x:1134, y:114
Img: ad_enable, location: x:1132, y:823 Img: ad_exit1, location: x:2208, y:56 Img: ad_exit2, location: x:2208, y:56 Img: discount_banner0, location: x:1134, y:114
Img: ad_enable, location: x:368, y:842
Img: ad_exit1, location: x:2208, y:56
Img: ad_exit2, location: x:2208, y:56
Img: discount_banner0, location: x:1134, y:114
Img: ad_enable, location: x:368, y:842 Img: ad_exit1, location: x:2208, y:56 Img: ad_exit2, location: x:2208, y:56 Img: discount_banner0, location: x:1134, y:114
Img: main_menu, location: x:1075, y:1015
Img: main_menu, location: x:1075, y:1015
Img: ad_enable, location: x:446, y:817
Img: discount_special, location: x:836, y:147
Img: back, location: x:113, y:1025
Img: ad_enable, location: x:446, y:817 Img: discount_special, location: x:836, y:147 Img: back, location: x:113, y:1025
Img: ruletka, location: x:1133, y:156
Img: ruletka_end, location: x:1132, y:1010
Img: back, location: x:119, y:1013
Img: ruletka, location: x:1133, y:156 Img: ruletka_end, location: x:1132, y:1010 Img: back, location: x:119, y:1013
Img: black_market, location: x:1131, y:56
Img: box_open, location: x:635, y:744
Img: back, location: x:113, y:1025
Img: black_market, location: x:1131, y:56 Img: box_open, location: x:635, y:744 Img: back, location: x:113, y:1025

Так же в функции recognize_screenshot() есть отдельная проверка шаблона 'ad_enable'. Она понадобилась из-за того, что кнопка с данным шаблоном может быть неактивной, но отличается от активной лишь яркостью, что собственно и проверяет составное условие (1-что это 'ad_enable', 2-был найден шаблон, 3-вариант кнопки с левым расположением значка, 4-с правым).

3) В зависимости от того, какие элементы видим выполняем нажатие в определенную область экрана.

Для отправки нажатия на экран использовать будем так же ADB. Команда выглядит так: 'adb shell input tap 3 5', где 3 и 5 - координаты x, y соответственно.

Сами решения когда и куда нажимать - скучная алгоритмика и зависит от того, для чего и какой алгоритм работы мы хотим.
В данном случае при запуске закрываются все окна кроме тех, что с рекламой. Реклама просматривается всегда. После рекламы забираем призы (подтвердить вознаграждение и нажать "ок"). После попадание на главный экран (меню) игры сначала идем в рулетку, крутим пока не кончатся попытки. После идем в "специальное" и из вкладок просматриваем так же все, что доступно. После идем в коробки и открываем бесплатные пока есть разрешение на просмотр. Когда просмотрели все что было выходим в главное меню. Алгоритм завершает работу. Дополнительно если при просмотре рекламы не можем найти кнопку выхода после
timeout_ad=90 секунд, то нажимаем в ту область, где она должна быть. Выглядит этот алгоритм так:

длинный и скучный
длинный и скучный

В цикле в основном методе (main) пытаемся получить скриншот, если получили, распознаем все что знаем и принимаем действия, а после выполнения действий даем время на анимацию (загрузку активити). Собственно на этом все.

Что можно было сделать иначе?

Ну тут целый список:
1) Искать только те шаблоны, которые должны быть на текущем экране
2) Получение скриншотов сразу в память в
raw виде. Т.е. без промежуточного сохранения в файл и последующего чтения из файла.
3) Поиск шаблонов только в предполагаемых местах. Очевидно, что многие значки и кнопки всегда в одной области, а для некоторых достаточно считать цвет пикселя, чтобы понять есть ли в этой области экрана предполагаемый.
4) Использовать шаблоны с маской. Поможет избавиться от нескольких вариаций значка в зависимости от фона.
5) Для каждого шаблона свой порог распознавания. Например для крестиков. Для них важна форма, а не цвет или стиль обводки.

Исходный код можно найти на моем GitHub: проект wr_bot.

—————————————————————————

Спасибо, что дочитали статью!

Подпишитесь пожалуйста на мой канал "Заметки Электроника | Alexander.Chad", этим Вы очень сильно поможете мне. Канал существует только за счет наличия и участия подписчиков.

Если Вам понравился материал - поддержите его лайком или даже донатом (ЮMoney). Есть что сказать? Оставьте комментарий! Это тоже будет помощью.

Сейчас канал нуждается в Вас как никогда прежде!