Первый вопрос: я ищу БЫСТРЫЙ подход для сопоставления изображений.
Теперь вариант использования: я разрабатываю детектор для обнаружения шара на 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);
Изменить: как предлагается, ниже представлены игровое поле, изображение значка и образец скриншота шара.
ICON против скриншота шара
Также вы можете наблюдать за симуляцией результат каждого подхода путем сравнения результата (наложение меньшего значка) над сферой на борту:
Сопоставление гистограммы
,
Соответствие шаблонов
и
AKAZE (аналогично ORB)
После переноса инициализации переменной из моей функции сравнения в базовый класс, обнаружение ключевой точки и PHa sh изображений значков источника при инициализации класса, запуск функции обнаружения и вычисления в пакетном режиме с использованием списка для уменьшения количества вызовов отдельных функций. Процесс сопоставления изображений по-прежнему занимает более 4 с. Снижается расход времени, но точность остается большой проблемой. Вы можете увидеть мою стопку кучи внизу.