После внезапного обогащения энтузиастов, которые поиграли в начале года в приложение Notcoin в телеграм, подобные проекты стали расти как грибы. Да и грибников заметно поприбавилось. Но в данной статье мы не будем касаться тем блокчейна или финансов, а рассмотрим простой пример применения компьютерного зрения для фарма поинтов в самом популярном, после Notcoin, проекте - хомяке комбате. Название явно на что-то намекает, но да ладно.
Это не первый проект, который я автоматизирую, и не самый нуждающийся в этом. Да и без компьютерного зрения с автоматизацией хомяка можно спокойно обойтись. Но с ним, во-первых, интереснее, а во-вторых - это просто хороший пример с минимумом строк кода для демонстрации возможностей библиотеки cv2. Статья, соответственно, предназначена для энтузиастов и начинающих специалистов.
Начнем с того, что мы установим все необходимые зависимости и импортируем их в свой проект. Вот они, слева направо.
import pyautogui
import keyboard
import cv2
import numpy as np
import timeС помощью pyautogui наш бот будет управлять мышью. Keyboard пригодится для назначения горячих клавиш, чтобы управлять работой бота. cv2 наградит бота зрением, пусть и компьютерным, с помощью которого тот будет находить совпадения с искомым изображением. А numpy пригодится для работы с большими массивами, но тут он почти для галочки, не бойтесь. Модуль time тоже понадобится, чтобы ставить таймауты в работе программы.
Далее напишем небольшую конструкцию- переключатель.
def change():
global work
work = not work
work = FalseФункция change при вызове всего лишь меняет значение переменной work, которая будет использована в бесконечном цикле. И если work будет False, работа нашего кода будет останавливаться. И наоборот запускаться, в противоположном случае.
Кстати, забыл упомянуть, что разработчики проекта, над которым мы сейчас проводим эксперимент, большие молодцы, и убрали возможность пользоваться приложением на десктопных устройствах. Поэтому для его запуска понадобится эмулятор Android.
Теперь определим основную логику работы бота:
Он ищет совпадение с изображением полностью заполненной энергии.
Если находит совпадение, ищет изображение монеты и кликает на неё энное количество раз.
И всё это работает в бесконечном цикле.
Значит со скриншота приложения необходимо вырезать две области, которые помечены красным, и разместить их в отдельные файлы, конечно же.

Теперь напишем функцию для кликов по монетке. Она будет принимать путь к исходному изображению, а так же порог чувствительности для компьютерного зрения и интервал (таймаут) в секундах.
def click(template_path, threshold=0.8, interval=0.0001):
template = cv2.imread(template_path, 0)
w, h = template.shape[::-1]
screenshot = pyautogui.screenshot()
screenshot = cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2BGR)
# Переводим скриншот в оттенки серого.
gray_screenshot = cv2.cvtColor(screenshot, cv2.COLOR_BGR2GRAY)
# Находим изображение на экране.
result = cv2.matchTemplate(gray_screenshot, template, cv2.TM_CCOEFF_NORMED)
loc = np.where(result >= threshold)
# Кликаем по найденным координатам.
for pt in zip(*loc[::-1]):
center_x = pt[0] + w // 2
center_y = pt[1] + h // 2
for _ in range(260):
pyautogui.doubleClick(center_x, center_y)
breakПеременной template будет присвоено исходное изображение монетки, но в оттенках серого, так как мы указали в параметрах 0. Это необходимость, так как в оттенках серого компьютер зрит лучше. Сразу вычисляем высоту и ширину исходника, и присваиваем переменным. А далее по ходу исполнения кода он делает скриншот, сравнивает с исходником, получает координаты области с совпадением, и делает 260 даблкликов по ней. Координаты я ищу немного кривовато и в итоге loc содержит большой массив, из которого я использую лишь самые первые координаты, после чего цикл прерываю. Но лучше сделать не смог, извините.
А теперь напишем аналогичную функцию, но с задачей искать совпадение с картинкой полной энергии, после чего вызывать функцию click.
def find(template_path, threshold=0.9, interval=1):
keyboard.add_hotkey('`', change)
# Загружаем изображение, которое мы хотим найти на экране. 0- в градациях серого.
template = cv2.imread(template_path, 0)
w, h = template.shape[::-1]
try:
while True:
if work:
# Получаем скриншот экрана.
screenshot = pyautogui.screenshot()
screenshot = cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2BGR)
# Переводим скриншот в оттенки серого.
gray_screenshot = cv2.cvtColor(screenshot, cv2.COLOR_BGR2GRAY)
# Находим изображение на экране.
result = cv2.matchTemplate(gray_screenshot, template, cv2.TM_CCOEFF_NORMED)
loc = np.where(result >= threshold)
for _ in zip(*loc[::-1]):
click('coin.bmp')
break
time.sleep(interval)
except KeyboardInterrupt:
print('\nВыход из программы')В целом всё аналогично. Добавил лишь ожидание горячей клавиши, чтобы можно было остановить программу в любое время нажатием на тильду (Ё). Ну и для красоты заключил в try-except.
И это всё. Пишем последние строки и запускаем скрипт (не забыв нажать на Ё для запуска логики в цикле).
if __name__ == "__main__":
find('energy.bmp')По сути, этот код многофункционален, и его без труда, с минимальными изменениями, можно переделать под любые другие задачи. На всякий случай оставлю и полную версию кода:
import pyautogui
import keyboard
import cv2
import numpy as np
import time
def change():
global work
work = not work
work = False
def find(template_path, threshold=0.9, interval=1):
keyboard.add_hotkey('`', change)
# Загружаем изображение, которое мы хотим найти на экране. 0- в градациях серого.
template = cv2.imread(template_path, 0)
w, h = template.shape[::-1]
try:
while True:
if work:
# Получаем скриншот экрана.
screenshot = pyautogui.screenshot()
screenshot = cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2BGR)
# Переводим скриншот в оттенки серого.
gray_screenshot = cv2.cvtColor(screenshot, cv2.COLOR_BGR2GRAY)
# Находим изображение на экране.
result = cv2.matchTemplate(gray_screenshot, template, cv2.TM_CCOEFF_NORMED)
loc = np.where(result >= threshold)
for _ in zip(*loc[::-1]):
click('coin.bmp')
break
time.sleep(interval)
except KeyboardInterrupt:
print('\nВыход из программы')
def click(template_path, threshold=0.8, interval=0.0001):
template = cv2.imread(template_path, 0)
w, h = template.shape[::-1]
screenshot = pyautogui.screenshot()
screenshot = cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2BGR)
# Переводим скриншот в оттенки серого.
gray_screenshot = cv2.cvtColor(screenshot, cv2.COLOR_BGR2GRAY)
# Находим изображение на экране.
result = cv2.matchTemplate(gray_screenshot, template, cv2.TM_CCOEFF_NORMED)
loc = np.where(result >= threshold)
# Кликаем по найденным координатам.
for pt in zip(*loc[::-1]):
center_x = pt[0] + w // 2
center_y = pt[1] + h // 2
for _ in range(260):
pyautogui.doubleClick(center_x, center_y)
break
if __name__ == "__main__":
find('energy.bmp')Не судите меня строго по этому скрипту, большую часть жизни я вообще бегал с пистолетиком, и пристрастился к разработке сравнительно недавно, поэтому всего лишь юный падаван в возрасте. Хотя могу писать и большие скучные штуки, но вот писать о них получится дольше, чем сам код. А если будут вопросы- добро пожаловать в телеграм. У меня там небольшой клуб по интересам.