Инструмент для поиска и сравнения изображений

💡 Усі статті, обговорення, новини про Java — в одному місці. Приєднуйтесь до Java спільноти!

Привет, всем! Хочу поделится инструментом, который я написала какое-то время назад, и вот только решила его выложить в публичный доступ, предварительно порефакторив его маленько. Написано сее добро на Maven, Java, Swing, OpenCV (с JavaCPP предустановками, JavaCV враперами — все для работы с OpenCV и подключено через Maven). У приложения есть интерфейс ввода в виде командной строки для запуска его из скриптов. Зоны ответственности приложения я разнесла по модулям: idiff-api — ядро приложения, не зависимое от конечного интерфейса; idiff-cmd — собственно консольный интерфейс. Все это находится в структуре Maven в виде структуры parent-child и всё это обильно полито соусом (шутка), поэтому со сборкой, управлением зависимостями и интеграцией в популярные среды разработки на Java проблем не должно быть. Собственно версии зависимостей управляются из parent модуля. Умеет он сравнивать изображения с отображением результата в GUI (формы написала на Java Swing) и сохранением его в файл ну и, собственно, осуществлять поиск по изображению на наличие искомого фрагмента. Все это можно выполнять с ожидаемыми очками, например: находить и, при желании, подсвечивать все области, в которых совпадение не меньше 90%. В обеих случаях я реализовала поддержку ROI — возможность искать как разницу между изображениями так и фрагмент только в указанных областях изображения. Найденные участки подсвечиваются на результирующем изображении, причем подсвечиваются и ROI (здесь, пожалуй, сохраню интригу, каким цветом... авось кто-то зайдет посмотреть только из-за этого...), если таковые были заданы. Применяла этот инструмент не часто но во всех случаях с саксесом и гордостью. Писать вздумала из-за того, что на то время, когда нужен был подобный функционал были проблемы с его наличием в общем доступе.

Из плюсов инструмента

  • Он работает и справляется с возложенными на него задачами
  • Исходный код приложения доступен по ссылке здесь

Из минусов инструмента

  • Использую нативные библиотеки OpenCV, из-за чего нужна сборка оного на других платформах кроме Windows (тут OpenCV идет уже с собранными библиотеками)
  • Сейчас я пока реализацию оставила для платформы windows-x86 (то есть для запуска приложения нужна 32 разрядная Java 6+). Для подключения, проверки и поддержки других платформ у меня нет и не было времени, хотя это должно быть не сложно при наличии времени и желания...

Из особенностей

  • Необходима Java 6+ x32
  • Apache Maven 3+

Из дополнительной информации

  • Для поиска фрагмента по изображению использую метод CV_TM_CCOEFF_NORMED библиотеки OpenCV — нормализированный метод, который помогает снизить влияние освещения между шаблоном и изображения

Что нужно, чтобы получить работающее приложение

  1. Заходим в директорию с проектом — idiff
  2. Выполняем команду: mvn clean install (при этом не забываем, что Java и Maven должны быть установлены)
  3. Берем по результату успешной сборки файл из idiff-cmd/target/idiff-cmd-1.0-dist.zip и распаковываем его куда душе угодно для дальнейших экспериментов

Структура сборки

  • conf — директория с конфигом (пока там только log4j.properties лежит, но с чем черт не шутит...)
  • lib — вспомогательные библиотеки для работы приложения
  • output — всякий вывод по умолчанию ложится в эту директорию
  • samples — заведомо сконфигурированные примеры для более плавного вхождения в тему
  • idiff-cmd-1.0.jar — собственно исполняемый Java архив (приложение)
  • opencv_core244.dll, opencv_highgui244.dll, opencv_imgproc244.dll — платформо-зависимое добро, доставшейся от OpenCV, без которого приложению будет совсем туго

Как запустить и получить первый результат

Для запуска и получения справки от приложения выполните команду из корневой директории приложения (место, куда вы распаковали приложение): java –jar idiff-cmd-1.0.jar [--help]

В результате увидим список возможный опций, с которыми приложение дружит.

Для быстрого старта уже есть список предустановленных аргументов:

  • java -jar idiff-cmd-1.0.jar --find-diff-roi-sample — поиск разницы в пределах ROI с выводом на скрин
  • java -jar idiff-cmd-1.0.jar --find-diff-sample — поиск разницы по всему изображению с выводом на скрин
  • java -jar idiff-cmd-1.0.jar --find-template-in-source-image-roi-sample — поиск фрагмента в пределах ROI с выводом на скрин
  • java -jar idiff-cmd-1.0.jar --find-template-in-source-image-sample — поиск фрагмента по всему изображению с выводом на скрин

Продвинутые команды

  1. Сравниваем два изображения (опции командной строки: image1 и image2), одинаковых размерами с выводом результата на Swing форму (show-result). По клику в любом месте Swing формы происходит переключение между исходным изображением и целевым. По наведению на выделенный фрагмент изображения в Swing форме — всплывает подсказка с деталями найденной области (очки, координаты, ширина, высота). В результате на целевом изображении подсветка (result-image) — желтая (с очками попадания), на исходном (result-source-image) — зеленая (без очков попадания). Все результаты подсвечиваются.

    java -jar idiff-cmd-1.0.jar --image1 samples/image1.png --image2 samples/image2.png --match-type DIFFERENCES --show-result --result-image target.png --result-source-image target-source.png
  2. Команда #1 с аргументом --rois 247,137,325,35 — ищет разницу только в выделенной прямоугольной области (ROI) с координатами x:247, y:137 и width:325, height:35
  3. Команда #1 с аргументом --match-similarity 0.7 — ищет разницу, с точностью меньшей 0.7 (70%)
  1. Для поиска фрагмента (image2) на изображения (image1) с точностью не меньшей 0.9 (90%), с выводом результата на Swing форму (show-result), и последующим сохранением результата с подсветкой в target.png (result-image) используем следующую команду:
    java -jar idiff-cmd-1.0.jar --image1 samples/sourceImage.jpg --image2 samples/templateImage.jpg --match-type TEMPLATES --show-result --result-image target.png --match-similarity 0.9

Рекомендации

  • если хотите использовать приложение в скрипте, тогда просто уберите опцию show-result (не покажет вам Swing форму, а результат сохранит)
  • при сравнении оба изображения обязательно должны иметь одинаковый размер
  • переключение между режимами поиска фрагмента и разницы осуществляется опцией match-type. За поиск фрагмента отвечает режим — TEMPLATES, за поиск разницы — DIFFERENCES соответственно

P.S.
У самой было в свое время много идей по поводу этого инструмента, но они лишь остались у меня в фантазиях, так как реальность сурова...

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


Спасибо за внимание.

👍ПодобаєтьсяСподобалось0
До обраногоВ обраному0
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Отличная работа!
Я думаю, что проделанную работу можно ещё усовершенствовать:
* Сделать развёрнутое описание (вроде того, что тут в посте) в README корня репозитория прямо на github. Если после этого тут просто оставить ссылку, то информации будет примерно столько же, но захват аудитории в разы больше.
* Добавить описание API, если вы считаете, что кому-то может быть полезно использование кода как библиотеки.
Я с Java особо не работаю, потому не в курсе, как там принято описывать API библиотек, но наверно какой-то javadoc c примерами использования стал бы решающим при завоевании ещё десятка-другого сердец пользователей.

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

Давным давно возникла такая же проблема. Решил в лоб: построении цифровой подписи для картинок (игнорирование цвета и освещения, понятное дело мой вариант «глупее», чем в OpenCV). Решение помогло в поиске похожих изображений по огромной таблице. Подробнее тут: leopard.in.ua/...-in-postgresql

Прочла. Спасибо большое, Леша, за интересный труд! Отлично написана статья!

Да не за что. Мне вариант с OpenCV нравится. Задача, которую мне было тяжело решить там, но я думаю можно с помощью этого подхода — это поиск, что одно изображение есть частью другого (например вырезали кусок и изменили цветовую гамму). Но понятное дело — это совсем друга задача :)

А можно попросить какой-нибудь скриншот или схему того,
как оно работает и что может сделать?
Какие-то интерестные фичи?

Постараюсь что-то более развернутое написать чуть попозже по приложению, а то со временем туго сейчас. Фичи приложения:

  1. поиск в изображении заданного фрагмента (хороший туториал, правдо для Python, но сути не меняет: Template Matching)
  2. поиск и подсветка разницы между двумя изображениями одинакового размера
  3. выполнение первого и второго в пределах указанного региона (с использованием ROI)
  4. выполнение первого и второго с заданным максимальным количеством найденных областей в результате (limit)
  5. выполнение первого и второго с заданными границами оценки (matching score)
  6. выполнение первого и второго с последующим сохранением результата в файл с подсветкой
  7. выполнение первого и второго с последующим выводом на экран с возможностью по клику мыши переключатся между двумя изображениями (в случае поиска разниц в двух изображениях) и выводом подсказке по указанной области при наведении на область курсором (в подсказке: координаты, размеры, очки совпадения). Удобно использовать при отладки

Именно это я и хотел увидеть, спасибо.

Спасибо и Вам, Володя, за комментарий.
Извиняюсь за битую линку на туториал по Template Matching’у. Вот он: opencv-python-tutroals.readthedocs.org/...e_matching.html

Вы главное не описали — насколько визуально Вас удовлетворяет качество сравнения?
Возможно ли использовать его в каких-либо прикладных целях и в каких, по Вашему мнению?

Главная интрига то в цвете прямоугольничка :-D Абажаю женщин в роли водителей и программистов )))))))

Использовала это приложения только в целях сравнения одинаковых по размеру скринов для тестирования Web UI на предмет дефектов. Делала это таким образом: получала скрины на заведомо проверенном UI (Selenium FirefoxDriver API, например, расширяя интерфейс TakesScreenshot делает возможным создания скриншота страницы благодаря методу getScreenshotAs). Выполняла это кодом (который где-то затерялся, но его воспроизвести не сложно при наличии времени), который из внешнего источника (БД, файл CSV и др.) получал список URL’ов, и пробегался по ним, делая скрины с метаинформацией о странице (в формате XML с таким-же названием, как и исходное изображение, как вариант) и далее использовала эти данные в качестве эталонных при тестировании тех-же URL’ов после внесенных изменений разработчиками. Метод очень действенный, но и очень чувствительный благодаря тексту, который может изменится в блоках страницы, после чего мы можем получить в результате существенную разницу между эталонным и текущим скрином... Но с этим относительно легко бороться, если есть настроенное окружение, в котором данные не изменяются (используется один и тотже набор данных, как и во время снятия эталонов, и порядок отображения этих данных не меняется), соответственно грешить остается только на верстку при расхождениях. Я думаю, что такой метод можно применять также для тестирования движков браузеров, так как идея остается такая же.

Кстати, чуть не забыла. Также можно проверять из скрина только указанные блоки благодаря функции ROI.
Делать это можно, например, с помощью кода:

  • Переход от координат элемента к Selenium WebElement’у:
    WebElement webElement = (WebElement) ((JavascriptExecutor) firefoxDriver). .executeScript(String.format("return document.elementFromPoint(%s, %s)", webElementLocation.x, webElementLocation.y));
  • Переход от Selenium WebElement’а к координатам его на странице (а следовательно и на скрине странице):
    Point location = webElement.getLocation(); Point webElementLocation = webElement.getLocation();
P.S.
Переход от Selenium WebElement’а к координатам его на странице может потребоваться, если мы собираемся сначала найти желаемый элемент на странице с помощью ById, ByClassName, ByLinkText, ByName, ByCssSelector, ByTagName, ByXPath и может как-то еще (в Page Objects, на сколько я знаю, используются теже API при поиске Selenium WebElement’а, что и из переданного Web Driver’а благодаря прокси объекту, который возвращается этим вызовом: PageFactory.initElements(new FirefoxDriver(), PageObjectInstance.class); - см. исходный код, и Page Objects туториал, как вариант), если я что-то не пропустила, и в дальнейшем использовать его координаты для работы с его расположением на скрине.
Ну, на самом деле, эти куски кода больше экспромт, потому как последнее время у меня несколько другие задачи на проекте, поэтому я думаю, что это можно будет допилять отдельным модулем к приложению, и посмотреть, как оно будет работать финально.
Спасибо.

Спасибо :-) Но на роль спасателя не претендую, так как с ДОУ, вроде как, все нормально

Підписатись на коментарі