Выявление 2 одинаковых изображений с использованием Java - PullRequest
10 голосов
/ 26 марта 2009

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

Существует ли какая-либо библиотека или утилита Java, которая может определить, имеют ли 2 изображения одинаковое содержание (т. Е. На уровне пикселей).

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

Ответы [ 10 ]

8 голосов
/ 26 марта 2009

Я делал нечто очень похожее на это раньше в Java и обнаружил, что класс PixelGrabber в пакете API java.awt.image чрезвычайно полезен (если в этом нет необходимости).

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

Если вы обнаруживали сходство, вам нужно использовать метод усреднения в той или иной форме, как указано в ответе на этот вопрос

Если вы можете, также ознакомьтесь с главой 7 тома 2 по Java для Horstman (8-е издание), потому что есть целый ряд примеров преобразования изображений и тому подобного, но, опять же, обязательно изучите java.awt.image пакет, потому что вы должны найти, что почти все подготовлено для вас:)

G'luck!

5 голосов
/ 26 марта 2009

В зависимости от того, как подробно вы хотите получить с ним:

  • скачать изображение
  • при загрузке генерирует хеш для него
  • создать каталог, в котором имя каталога является значением хеш-функции (если каталог не существует)
  • если каталог содержит 2 или более файлов, сравните размеры файлов
  • если размеры файлов одинаковы, тогда выполняется сравнение байтов изображения с байтами изображений в файле
  • если байты уникальны, то у вас есть новое изображение

Независимо от того, хотите ли вы сделать все это или нет, вам нужно:

  • скачать изображения
  • сделать побайтовое сравнение изображений

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

4 голосов
/ 26 марта 2009

Посмотрите на класс MessageDigest. По сути, вы создаете его экземпляр, а затем передаете ему серию байтов. Байты могут быть байтами, непосредственно загруженными из URL, если вы знаете, что два «одинаковых» изображения будут тем же файлом / потоком байтов. Или, если необходимо, вы можете создать BufferedImage из потока, а затем извлечь значения пикселей, например:

  MessageDigest md = MessageDigest.getInstance("MD5");
  ByteBuffer bb = ByteBuffer.allocate(4 * bimg.getWidth());
  for (int y = bimg.getHeight()-1; y >= 0; y--) {
    bb.clear();
    for (int x = bimg.getWidth()-1; x >= 0; x--) {
      bb.putInt(bimg.getRGB(x, y));
    }
    md.update(bb.array());
  }
  byte[] digBytes = md.digest();

В любом случае MessageDigest.digest () в конечном итоге дает вам байтовый массив, который является «подписью» изображения. Вы можете преобразовать это в шестнадцатеричную строку, если это полезно, например, для помещения в HashMap или таблицу базы данных, например ::100100

StringBuilder sb = new StringBuilder();
for (byte b : digBytes) {
  sb.append(String.format("%02X", b & 0xff));
}
String signature = sb.toString();

Если содержимое / изображение из двух URL-адресов дает вам одинаковую подпись, значит, это одно и то же изображение.

Редактировать: Я забыл упомянуть, что, если вы хэшируете значения пикселей, вы, вероятно, захотите включить размеры изображения в хеш. (Точно так же: записать два целых числа в 8-байтовый ByteBuffer, затем обновить MessageDigest с помощью соответствующего 8-байтового массива.)

Другое дело, что кто-то упоминал, что MD5 не является устойчивым к столкновениям . Другими словами, существует методика построения нескольких байтовых последовательностей с одним и тем же хешем MD5 без необходимости использовать метод "грубой силы" методом проб и ошибок (где в среднем вы ожидаете попробовать около 2 ^ 64 или 16 миллиардов миллиардов файлов до попадания в коллизию). Это делает MD5 неподходящим , где вы пытаетесь защитить от этой модели угрозы . Если вы не обеспокоены случаем, когда кто-то может сознательно попытаться обмануть вашу дубликатную идентификацию, и вы просто беспокоитесь о вероятности дублирования хеша "случайно", тогда MD5 абсолютно в порядке. На самом деле, это не только хорошо, это на самом деле немного чрезмерно - как я уже сказал, в среднем вы ожидаете один «ложный дубликат» после примерно 16 миллиардов миллиардов файлов. Или, другими словами, у вас может быть, скажем, миллиард файлов, и вероятность столкновения будет очень близка к нулю.

Если вы обеспокоены изложенной моделью угрозы (т. Е. Вы думаете, что кто-то может сознательно посвятить процессорное время созданию файлов, чтобы обмануть вашу систему), тогда решение состоит в том, чтобы использовать более сильный хеш. Java поддерживает SHA1 из коробки (просто замените «MD5» на «SHA1»). Теперь это даст вам более длинные хэши (160 бит вместо 128 бит), но с учетом современных знаний делает невозможным обнаружение коллизий.

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

2 голосов
/ 26 марта 2009

Вы также можете создать MD5-подпись файла и игнорировать повторяющиеся записи. Не поможет вам найти похожие изображения.

1 голос
/ 25 января 2012

Вы можете сравнивать изображения, используя:

1) простое попиксельное сравнение

Это не даст очень хороших результатов при некотором сдвиге, повороте, изменении освещения, ...

2) Относительно простой, но более продвинутый подход

http://www.lac.inpe.br/JIPCookbook/6050-howto-compareimages.jsp

3) Более продвинутые алгоритмы

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

1 голос
/ 26 марта 2009

рассчитать MD5, используя что-то вроде этого:

MessageDigest m=MessageDigest.getInstance("MD5");
m.update(image.getBytes(),0,image.length());
System.out.println("MD5: "+new BigInteger(1,m.digest()).toString(16));

Поместите их в хэш-карту.

1 голос
/ 26 марта 2009

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

Если, конечно, вы не заинтересованы в идентификации похожих изображений.

0 голосов
/ 26 января 2016

Я написал чистую библиотеку Java только несколько дней назад. Вы можете указать его путем к каталогу (включая подкаталог), и он перечислит дубликаты изображений в списке с абсолютным путем, который вы хотите удалить. Кроме того, вы также можете использовать его, чтобы найти все уникальные изображения в каталоге.

Он использовал awt api для внутреннего использования, поэтому его нельзя использовать для Android. Поскольку у imageIO есть проблемы с чтением большого количества новых типов изображений, я использую банку с двенадцатью обезьянами, которая используется внутри.

https://github.com/srch07/Duplicate-Image-Finder-API

Jar с внутренними зависимостями можно скачать с https://github.com/srch07/Duplicate-Image-Finder-API/blob/master/archives/duplicate_image_finder_1.0.jar

API также может найти дубликаты среди изображений разных размеров.

0 голосов
/ 28 марта 2009

Проверьте заголовки ответа и запросите значение заголовка HTTP , если оно есть. ( RFC2616: ETag ) Они могут быть одинаковыми для идентичных изображений, поступающих с вашего целевого веб-сервера. Это связано с тем, что значение ETag часто представляет собой дайджест сообщения, такой как MD5, который позволит вам воспользоваться уже выполненными вычислениями веб-сервера. Это может потенциально позволить вам даже не загружать изображение!

for each imageUrl in myList
    Perform HTTP HEAD imageUrl
    Pull ETag value from request
    If ETag is in my map of known ETags
       move on to next image
    Else
       Download image
       Store ETag in map

Конечно, ETag должен присутствовать, а если нет, то идея просто тост. Но, может быть, у вас есть тянуть с администраторами веб-сервера?

0 голосов
/ 27 марта 2009

Хеширование уже предлагается, и распознать, если два файла идентичны, очень легко, но вы сказали, что уровень пикселей. Если вы хотите распознать два изображения, даже если они в разных форматах (.png / .jpg / .gif / ..) и даже если они были масштабированы, я предлагаю: (с использованием библиотеки изображений и если изображение среднего / большого размера без значков 16x16):

  1. масштабировать изображение до определенного фиксированного размера, это зависит от образцов
  2. преобразовать его в оттенки серого, используя преобразование RGB-YUV для исследования и взяв Y оттуда (очень легко) 3 Выполните расстояние Хемминга для каждого изображения и установите порог, чтобы определить, являются ли они одинаковыми или нет.

Вы получите сумму всех серых пикселей обоих изображений, которые вы получите, если разница

-

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...