Асинхронный поиск изображения на экране с помощью Trio - PullRequest
0 голосов
/ 25 мая 2020

Я пытаюсь адаптировать этот модуль для поддержки асинхронного выполнения при поиске большого количества изображений на одном и том же снимке экрана в заданное время. Я как бы новичок в кодировании asyn c, и после долгих исследований я выбрал Trio для этого (из-за его крутизны и простоты).

Дело в следующем:

  • Функция получает список путей изображений
  • На каждой итерации она делает снимок экрана и пытается найти изображения в массив (лучше для производительности, если мы не будем делать новый снимок экрана для каждой попытки в массиве)
  • Если он найдет, возвращает путь к изображению и его координаты
  • Сделайте это полностью опять же, потому что на экране может появиться какое-то изображение

Я собираюсь использовать это в другом проекте с поддержкой asyn c с Trio, поэтому я пытаюсь его преобразовать.

Это моя попытка:


def image_search(image, precision=0.8, pil=None):
    if pil is None:
        pil = pyautogui.screenshot()
    if is_retina:
        pil.thumbnail((round(pil.size[0] * 0.5), round(pil.size[1] * 0.5)))
    return most_probable_location(pil, image, precision)

async def multiple_image_search_loop(images, interval=0.1, timeout=None, precision=0.8):
    async def do_search():
        while True:
            pil = pyautogui.screenshot()
            for image in images:
                if pos := image_search(image, precision, pil):
                    return {
                        "position": pos,
                        "image": image
                    }
            await trio.sleep(interval)

    if timeout:
        with trio.fail_after(timeout):
            return await do_search()
    else:
        return await do_search()

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

Это не так уж плохо, если нет разницы в производительности, потому что цель состоит в том, чтобы сделать эту функцию полезной в контексте asyn c, без блокировки на все время, пока она ищет изображения, но если я мог бы оптимизировать вещи, так будет лучше.

Может было бы лучше, если бы вместо awaiting после поиска по всем изображениям адаптировать image_search() с вызовом trio.sleep() и открыть детскую на главной функции? (используя внутри него метод trio.start_soon() для каждого изображения в массиве). Это уменьшило бы блокировку другого проекта, который я собираюсь использовать, но на поиск изображения уйдет больше времени, верно?

1 Ответ

2 голосов
/ 26 мая 2020

Trio не будет напрямую распараллеливать код, связанный с процессором, таким образом. «Фреймворк asyn c» означает, что он просто использует один поток ЦП, распараллеливая операции ввода-вывода и сетевых операций. Если вы вставите несколько вызовов в await trio.sleep(0), это позволит Trio чередовать поиск изображений с другими задачами, но это не ускорит поиск изображений.

Однако вы можете захотеть сделать следующее: используйте отдельный поток. Я предполагаю, что ваш код, вероятно, проводит большую часть своего времени в opencv, а opencv, вероятно, отбрасывает GIL? Таким образом, использование потоков, вероятно, позволит вам запускать свой код одновременно на нескольких процессорах, а также позволит выполнять другие задачи asyn c одновременно. Для управления подобными потоками Trio позволяет выполнять await trio.to_thread.run_sync(some_sync_function, *args), который запускает some_sync_function(*args) в потоке. Если вы запускаете несколько таких вызовов одновременно в детской, то вы будете использовать несколько потоков. не отменяется, поэтому таймауты и т. д. c. не вступят в силу до завершения вызова. Чтобы обойти это, вы можете убедиться, что отдельные вызовы не блокируются слишком долго. , потому что, если пользователь хочет добавить тайм-аут, он может записать блок with вокруг вашей функции так же легко, как передать аргумент. Таким образом, вам не придется загромождать API повсюду аргументами тайм-аута.

...