Сопоставление изображений на Android игровой доске - PullRequest
1 голос
/ 05 мая 2020

Первый вопрос: я ищу БЫСТРЫЙ подход для сопоставления изображений.

Теперь вариант использования: я разрабатываю детектор для обнаружения шара на Match-3 6x5 игровая доска для платформы android. У меня есть массив значка шара с фоном transparent , но шар на экране (снимок экрана) имеет другой цвет фона, возможно, другой размер. Мне нужно сравнить каждый шар на экране с моим набором значков (в частности, 69 значков), так что это 69x30 = 2070 шагов. Я попробовал ленивую реализацию и сгруппировал почти одинаковые значки вместе, чтобы уменьшить количество шагов, но все равно требуется много времени (максимум 10 секунд) для вычислений. Я также попытался проверить канал и глубину изображения, изменив размер изображений, чтобы они имели тот же размер, и настроить пороговое значение, но все равно не повезло.

Я пробовал Histogram Matching (seperate channel, grayscale), Template Matching (CCOEFF, SQDIFF, CCORR), AKAZE, ORB(unbounded, bounded), PHash все с использованием OpenCV, но сопоставление гистограммы и PHa sh дает мне ошибочный результат (слишком много ложных срабатываний), сопоставление шаблонов потребляет 10 с + (считается слишком медленным для ожидания пользователем), в то время как AKAZE и ORB дают лучший результат, чем все другие методы, но все равно требуется 6 с + на попытку. Есть ли какой-либо другой метод, который может помочь мне сократить время вычислений примерно до 1 с и дать лучший результат, учитывая, что худший сценарий - 2070 шагов?

Ссылки, которые я прочитал, сравнивая производительность различных алгоритмы сопоставления функций: Сравнительный анализ SIFT, SURF, KAZE, AKAZE, ORB и BRISK . Это показывает, что ORB и BRISK должны быть в среднем лучше, чем другие подходы по сравнению, в то время как AKAZE умеренно хорош для большинства случаев. Я удалил свой код сравнения гистограмм, так как он не очень полезен, но вы можете найти остальную его часть ниже.

Mat source = Utils.loadResource(this, R.drawable.orb_icon, Imgcodecs.CV_LOAD_IMAGE_UNCHANGED);
Mat tmp = new Mat();
Bitmap cropped_img = Bitmap.createBitmap(screenshot, x, y, width, height);
Utils.bitmapToMat(cropped_img, tmp);

//template matching code
int r_rows = source.rows() - tmp.rows() + 1;
int r_cols = source.cols() - tmp.cols() + 1;
Mat result = new Mat();

result.create(r_rows, r_cols, CvType.CV_32F);

Imgproc.matchTemplate(source, tmp, result, Imgproc.TM_CCOEFF_NORMED);
Core.MinMaxLocResult mmr = Core.minMaxLoc(result);
double maxVal = mmr.maxVal;
return maxVal;

//AKAZE
MatOfKeyPoint kp1 = new MatOfKeyPoint();
MatOfKeyPoint kp2 = new MatOfKeyPoint();
Mat desc1 = new Mat();
Mat desc2 = new Mat();
AKAZE akaze = AKAZE.create();
DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMING);
akaze.detectAndCompute(source, new Mat(), kp1, desc1);
akaze.detectAndCompute(tmp, new Mat(), kp2, desc2);
List<MatOfDMatch> knnMatches = new ArrayList<>();
matcher.knnMatch(desc1, desc2, knnMatches, 2);

float threshold = 0.7f;
int count = 0;
for(int i=0; i<knnMatches.size(); i++) {
    if(knnMatches.get(i).rows() > 1) {
        DMatch[] matches = knnMatches.get(i).toArray();
        if(matches[0].distance < threshold * matches[1].distance) {
            count++;
        }
    }
}

//ORB    
ORB orb = ORB.create();
DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMING);
MatOfKeyPoint kp1 = new MatOfKeyPoint();
MatOfKeyPoint kp2 = new MatOfKeyPoint();
Mat desc1 = new Mat();
Mat desc2 = new Mat();
orb.detectAndCompute(source, new Mat(), kp1, desc1);
orb.detectAndCompute(tmp, new Mat(), kp2, desc2);
List<MatOfDMatch> knnMatches = new ArrayList<>();
matcher.knnMatch(desc1, desc2, knnMatches, 2);

float threshold = 0.8f;
int count = 0;
for(int i=0; i<knnMatches.size(); i++) {
    if(knnMatches.get(i).rows() > 1) {
        DMatch[] matches = knnMatches.get(i).toArray();
        if(matches[0].distance < threshold * matches[1].distance) {
            count++;
        }
    }
}

//PHash
Mat hash_source = new Mat();
Mat hash_tmp = new Mat();
Img_hash.pHash(tmp, hash_tmp);
Img_hash.pHash(source, hash_source);
Core.norm(source, tmp, Core.NORM_HAMMING);

Изменить: как предлагается, ниже представлены игровое поле, изображение значка и образец скриншота шара. Game board

ICON orb icon против скриншота шара orb screenshot

Также вы можете наблюдать за симуляцией результат каждого подхода путем сравнения результата (наложение меньшего значка) над сферой на борту:

Сопоставление гистограммы

Histogram,

Соответствие шаблонов

Template Matching и

AKAZE (аналогично ORB)

AKAZE and ORB

После переноса инициализации переменной из моей функции сравнения в базовый класс, обнаружение ключевой точки и PHa sh изображений значков источника при инициализации класса, запуск функции обнаружения и вычисления в пакетном режиме с использованием списка для уменьшения количества вызовов отдельных функций. Процесс сопоставления изображений по-прежнему занимает более 4 с. Снижается расход времени, но точность остается большой проблемой. Вы можете увидеть мою стопку кучи внизу.

Profiling output

...