Найти картинку на картинке с java?

то, что я хочу, это проанализировать вход с экрана в виде изображений. Я хочу иметь возможность идентифицировать часть изображения в большем изображении и получить его координаты внутри большей картины. Пример:

box

должен быть расположен в

big picture

и результатом будет верхний правый угол изображения в большой картине и нижняя левая часть в большой картине. Как вы можете видеть, белая часть картина не имеет значения, что мне в основном нужно, это просто зеленая рамка. Есть ли библиотека, которая может сделать что-то подобное для меня? Время выполнения на самом деле не проблема.

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

вопрос был задан несколько раз так, как кажется, без единого ответа. Я нашел, что нашел решение в http://werner.yellowcouch.org/Papers/subimg/index.html . К сожалению, это на C++ , и я ничего не понимаю. Было бы неплохо иметь реализацию Java на SO.

2 ответов


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

возможные подходы

в этом случае проблема тесно связана с концепциями обработки сигналов кросс-корреляции и свертка, который часто реализуется с помощью ФФТ как это очень быстро (его в названии!). Это то, что было использовано в подход вы связан и библиотека FFTW библиотека может быть полезна при попытке такой реализации, поскольку она имеет оболочки для Java. Использование кросс-корреляции работает довольно хорошо, как видно из этой вопрос, а также знаменитый Уолдо вопрос.

другой вариант-использовать не все пиксели для сравнения, а только те функции, которые легче найти и, скорее всего, будут уникальными. Для этого потребуется функция дескриптор like просеять, серф или один из многих другие. Вам нужно будет найти все функции в обоих изображениях, а затем искать функции, которые имеют аналогичные позиции в изображении шаблона. При таком подходе я предлагаю вам использовать JavaCV.

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

если вы не используете внешнюю библиотеку, самым простым методом в Java было бы то, что я бы назвал подходом грубой силы, хотя он немного медленный. Подход грубой силы просто включает в себя поиск всего изображения для субрегиона, который лучше всего соответствует изображению, которое вы ищете. Я объясню этот подход подробнее. Сначала нужно определить, как определить сходство между двумя одинаковыми изображениями. Это может сделайте путем суммировать разницы между цветами пиксела который требует определения для разницы между значениями RGB.

цветовой схожести

один из способов определения разницы между двумя значениями RGB используется евклидово расстояние:

sqrt( (r1-r2)^2 + (g1-g2)^2 + (b1-b2)^2 )

существуют различные цветовые пространства, чем RGB, которые можно использовать, но поскольку ваше подизображение, скорее всего, почти идентично (а не просто визуально похоже), это должно сработать. Если у вас есть цветовое пространство ARGB и вы не хотите, чтобы полупрозрачные пиксели влияли на ваши результаты, вы можете использовать:

a1 * a2 * sqrt( (r1-r2)^2 + (g1-g2)^2 + (b1-b2)^2 )

что даст меньшее значение, если цвета имеют прозрачность (при условии a1 и a2 между 0 и 1). Я бы предложил использовать прозрачность вместо белых областей и использовать формат файла PNG, поскольку он не использует сжатие с потерями, которое тонко искажает цвета в изображении.

сравнение изображений

для сравнения изображений одинакового размера вы можете суммировать разницу между их отдельными пикселями. Эта сумма является мерой разницы, и вы можете искать область на изображении с самой низкой мерой разницы. Становится сложнее, если вы даже не знаете, содержит ли изображение подизображение, но это будет указано лучшим совпадением с высокой разностной мерой. Если вы хотите, вы также можете нормализовать разностная мера лежит между 0 и 1 путем деления ее на размер подизображения и максимально возможную разность RGB (sqrt(3) с евклидовым расстоянием и значениями RGB от 0 до 1). Тогда ноль будет идентичным совпадением, а все, что близко к единице, будет как можно более различным.

реализация грубой силы

вот простая реализация, которая использует подход грубой силы для поиска изображения. С помощью ваших примеров изображений он нашел местоположение в (139,55) чтобы быть верхним левым местоположением региона с лучшим совпадением (что выглядит правильно). Для работы на моем компьютере потребовалось от 10 до 15 секунд, а нормализованная разностная мера местоположения была около 0,57.

 /**
 * Finds the a region in one image that best matches another, smaller, image.
 */
 public static int[] findSubimage(BufferedImage im1, BufferedImage im2){
   int w1 = im1.getWidth(); int h1 = im1.getHeight();
   int w2 = im2.getWidth(); int h2 = im2.getHeight();
   assert(w2 <= w1 && h2 <= h1);
   // will keep track of best position found
   int bestX = 0; int bestY = 0; double lowestDiff = Double.POSITIVE_INFINITY;
   // brute-force search through whole image (slow...)
   for(int x = 0;x < w1-w2;x++){
     for(int y = 0;y < h1-h2;y++){
       double comp = compareImages(im1.getSubimage(x,y,w2,h2),im2);
       if(comp < lowestDiff){
         bestX = x; bestY = y; lowestDiff = comp;
       }
     }
   }
   // output similarity measure from 0 to 1, with 0 being identical
   System.out.println(lowestDiff);
   // return best location
   return new int[]{bestX,bestY};
 }

 /**
 * Determines how different two identically sized regions are.
 */
 public static double compareImages(BufferedImage im1, BufferedImage im2){
   assert(im1.getHeight() == im2.getHeight() && im1.getWidth() == im2.getWidth());
   double variation = 0.0;
   for(int x = 0;x < im1.getWidth();x++){
     for(int y = 0;y < im1.getHeight();y++){
        variation += compareARGB(im1.getRGB(x,y),im2.getRGB(x,y))/Math.sqrt(3);
     }
   }
   return variation/(im1.getWidth()*im1.getHeight());
 }

 /**
 * Calculates the difference between two ARGB colours (BufferedImage.TYPE_INT_ARGB).
 */
 public static double compareARGB(int rgb1, int rgb2){
   double r1 = ((rgb1 >> 16) & 0xFF)/255.0; double r2 = ((rgb2 >> 16) & 0xFF)/255.0;
   double g1 = ((rgb1 >> 8) & 0xFF)/255.0;  double g2 = ((rgb2 >> 8) & 0xFF)/255.0;
   double b1 = (rgb1 & 0xFF)/255.0;         double b2 = (rgb2 & 0xFF)/255.0;
   double a1 = ((rgb1 >> 24) & 0xFF)/255.0; double a2 = ((rgb2 >> 24) & 0xFF)/255.0;
   // if there is transparency, the alpha values will make difference smaller
   return a1*a2*Math.sqrt((r1-r2)*(r1-r2) + (g1-g2)*(g1-g2) + (b1-b2)*(b1-b2));
 }

Я не смотрел, но, возможно, одна из этих библиотек обработки изображений Java также может быть полезна:

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


то, что вы хотите, это найти блок изображения по маске / границам.

Это можно сделать без внешней библиотеки. На низком уровне каждая картинка представляет собой матрицу чисел, ваша маска также является матрицей чисел. Вы можете просто линейно сканировать большую матрицу и найти область, которая следует правилам, определенным вашей маской.

пример:

большой матрицей:

1 0 1 1 1 1 
0 1 0 1 0 0
0 0 0 1 1 1
0 1 1 0 0 0

Маска:

1 1 1
1 0 0
1 1 1

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

в реальной проблеме у вас не будет набора чисел [0, 1] но гораздо больше - byte например ([0, 256]). Чтобы алгоритм работал лучше, matched означает не точное совпадение чисел, но возможно с некоторыми отклонениями +-5 или что-то в этом роде.