Как найти небольшую иконку на скриншоте OpenCV

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

Пробовал искать с помощью сравнения шаблонов, в целом неплохо но при изменении масштаба иконки всё ломается, сделал несколько масштабов, тоже неплохо работает но при изменении разрешения самой игры работает уже не так стабильно.

Также пробовал ORB но он вообще не ищет кейпоинты пока не увеличу изображение самой иконки, да и ищет не точно. И вот хотел узнать есть ли какой-то более стабильный и универсальный способ поиска таких небольших иконок.

Размер иконки рыбки 33x35 Размер скриншота FullHd

Код для поиска при помощи сравнения шаблонов(C++):

std::vector<Mat> fishIcon;
const std::vector<double> SCALES = { 0.4, 0.5, 0.6, 0.8, 1.0, 1.2, 1.4 };

//Перебор всех масштабов пока не найдётся нужный
for (Mat ic : fishIcon) {
    if (FindObject(ic, screenshotGray, max_loc, result, true, 0.8)) {
        fishIconPosition = Point(max_loc.x, max_loc.y);
        found = true;
        break;
    }
}

//Код для создания нескольких мастштабов
for (double scale : SCALES) {
    Mat resIcon;
    resize(sourceIcon, resIcon, Size(0, 0), scale, scale);

    fishIcon.push_back(resIcon);
}

//Метод для поиска иконки на изображении
bool botActions::FindObject(Mat& templateForFind, Mat& screenshot, Point& maxLoc, Mat& result, bool debug, double confid) {
    matchTemplate(screenshot, templateForFind, result, TM_CCOEFF_NORMED);
    double minVal, maxVal;
    minMaxLoc(result, &minVal, &maxVal, NULL, &maxLoc);

    if (debug) {
        std::cout << maxVal << std::endl;
    }

    if (maxVal >= confid) {
        return true;
    }
    else {
        return false;
    }
}

Вот как тестировал ORB(Python):

import cv2
import numpy as np

template = cv2.imread("FishCrop.png", cv2.IMREAD_GRAYSCALE)
img = cv2.imread("Background.png", cv2.IMREAD_GRAYSCALE)

template = cv2.resize(template, None, fx=3, fy=3,interpolation=cv2.INTER_AREA)

orb = cv2.ORB.create(nfeatures=800)

kpTemplate, descTemplate = orb.detectAndCompute(template, None)
kpBackground, descBackground = orb.detectAndCompute(img, None)

bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.match(descTemplate, descBackground)
matches = sorted(matches, key = lambda x:x.distance)

matchRes = cv2.drawMatches(template, kpTemplate, img, kpBackground, matches[:15], None)

cv2.imshow("Result", matchRes)
cv2.waitKey(0)
cv2.destroyAllWindows()

Буду благодарен любым подсказкам

Иконка для поиска

Скриншот на котором ищу(Для примера)


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

Автор решения: Сергей Кох

Глядя на иконку напрашивается решение с детектированием прямоугольного контура с внутренним эллипсом. Я попробовал код из документации и, на данном фоне, при изменении положения движка > 170 детектора cv.Canny, остался выделенным только контур иконки.

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

Но надежней, я думаю использовать hierarchy контуров, для более точного определения иконки.

Но мне больше понравился, не сразу бросающийся в глаза, метод обратной проекции гистограммы. Если мы посмотрим на гистограмму иконки,

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

то увидим два характерных цветных пика. И применив второй код, находящийся на той же страницы документации, с функцией cv.calcBackProject(), я получил картинку,

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

которая позволяет с легкостью определить координаты иконки. При этом изменение масштаба и небольшой поворот слабо влияют на поиск, но вот изменение разрешения игры надо еще проверить.

→ Ссылка