Постоянное реагирование на объект

Написал такой код, он нажимает кнопку "стрелка влево", если видит на выбранной области картинку красного цвета:

import cv2
import numpy as np
import pyautogui
import time

# Функция для нахождения картинки на экране
def find_image(target_img, screen_img):
    result = cv2.matchTemplate(screen_img, target_img, cv2.TM_CCOEFF_NORMED)
    return np.where(result >= 1)  # Порог для совпадения

# Параметры области захвата экрана
capture_area = (320, 830, 900, 900)  # (x, y, width, height)

# Загрузка изображения для поиска
target_image = cv2.imread('red.png')  # Изображение, которое ищем
key_to_press = 'left'  # Клавиша, которую нужно нажать

while True:
    # Захват экрана
    screenshot = pyautogui.screenshot(region=capture_area)
    screen_image = cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2BGR)

    # Поиск изображения на экране
    locations = find_image(target_image, screen_image)

    if len(locations[0]) > 0:
        print("Изображение найдено!")
        pyautogui.press(key_to_press)  # Нажимаем клавишу
        time.sleep(1)  # Задержка, чтобы не нажимать слишком быстро

    time.sleep(0.1)  # Задержка перед следующим сканированием

Предыдущую ошибку исправил, теперь программа запускается, но условие почему-то выполняется постоянно, и постоянно нажимается кнопка "стрелка влево" и print("Изображение найдено"), даже когда условие не должно выполняться.

Подскажите, пожалуйста, в чем ошибка?


Ответы (1 шт):

Автор решения: Amgarak

Распечатайте print(len(locations)) -> получите в консоль 2.

Следовательно даже при отсутствии найденного изображения ваше условие всегда вернёт True:

if len(locations[0]) > 0: # True всегда

Дело в том, что np.where() возвращает кортеж из двух массивов - один массив для координат по оси Y и один для координат по оси X.

И даже если оба массива в кортеже будут пустые, то len(locations) всё равно вернёт 2.

Изображения не найдены.
(array([], dtype=int64), array([], dtype=int64))

Вообще не вижу смысла использовать np.where, вы же ищите не группу одинаковых изображений на скриншоте, а один конкретный объект. Фактически вас устроит один результат с самым максимальным совпадением.

Доработал для вас пример, обратите внимание - добавил нахождение координат найденного объекта, если вдруг захочется на него нажать.

Код:

import cv2
import numpy as np
import pyautogui
import time

capture_area = (320, 830, 900, 900)  
x, y, _, _ = capture_area 

template = cv2.imread(r'C:\Users\Amgarak\Desktop\j.png') 
threshold = 0.8 

while True:
    screenshot = pyautogui.screenshot(region=capture_area)
    screenshot = cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2BGR)

    result = cv2.matchTemplate(screenshot, template, cv2.TM_CCOEFF_NORMED)
    _, max_val, _, max_loc = cv2.minMaxLoc(result)

    if max_val >= threshold:
        print(f"Изображение найдено! {max_val}")
        h, w = template.shape[:2]
        
        bottom_right = (max_loc[0] + w, max_loc[1] + h) 
        cv2.rectangle(screenshot, max_loc, bottom_right, (0, 255, 0), 2)  
        
        center_x = x + (max_loc[0] + w // 2)
        center_y = y + (max_loc[1] + h // 2)
        print(f"Центр совпадения на экране: x={center_x}, y={center_y}")
        
        cv2.imshow('Result', screenshot)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

        pyautogui.press('left')
        break
    else:
        print(f"Изображение не найдено.")

    time.sleep(0.1) 

Вывод:

введите сюда описание изображения

Изображение не найдено.
Изображение не найдено.
Изображение найдено! 1.0
Центр совпадения на экране: x=509, y=943

Если нужно найти все места шаблона на изображении, то придётся добавить ещё и фильтрацию, так как np.where может вернуть кучу близких по значению совпадений для одной и той же области.

Одним словом - можно обнаружить одно и то же место несколько раз с небольшим смещением.

Код:

import cv2
import numpy as np
import pyautogui
import time

capture_area = (320, 830, 900, 900)  
x, y, _, _ = capture_area  

template = cv2.imread(r'C:\Users\Amgarak\Desktop\j.png')   
threshold = 0.8  
min_distance = max(template.shape[:2]) * 0.5 # Минимальное расстояние между совпадениями (в пикселях)

while True:
    screenshot = pyautogui.screenshot(region=capture_area)
    screenshot = cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2BGR)

    result = cv2.matchTemplate(screenshot, template, cv2.TM_CCOEFF_NORMED)
    locations = np.where(result >= threshold)

    if len(locations[0]) > 0:
        print(f"Найдено {len(locations[0])} совпадений")
        h, w = template.shape[:2]  
        filtered_locations = []  

        for loc in zip(*locations[::-1]):  
            too_close = False
            for filtered_loc in filtered_locations:
                if abs(filtered_loc[0] - loc[0]) < min_distance and abs(filtered_loc[1] - loc[1]) < min_distance:
                    too_close = True
                    break
            
            if not too_close:
                filtered_locations.append(loc)

        print(f"Отфильтровано до {len(filtered_locations)} уникальных совпадений")

        for top_left in filtered_locations:
            bottom_right = (top_left[0] + w, top_left[1] + h)
            cv2.rectangle(screenshot, top_left, bottom_right, (0, 255, 0), 2)

            center_x = x + (top_left[0] + w // 2)
            center_y = y + (top_left[1] + h // 2)
            print(f"Центр совпадения на экране: x={center_x}, y={center_y}")
        
        cv2.imshow('Result', screenshot)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

        pyautogui.press('left')
        break
    else:
        print(f"Изображения не найдены.")

    time.sleep(0.1)  

Вывод:

введите сюда описание изображения

Изображения не найдены.
Изображения не найдены.
Найдено 18 совпадений
Отфильтровано до 2 уникальных совпадений
Центр совпадения на экране: x=797, y=916
Центр совпадения на экране: x=798, y=991

P.S. В своих задачах, я обычно устанавливаю пороговое значение на 0.8 - 0.9 так как добиться 1 получиться далеко не всегда.

Так же обратите внимание, что в pyautogui уже есть удобная реализация работы с OpenCV - Пример.

→ Ссылка