Простой и быстрый метод сравнения изображений на сходство

195

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

(Более конкретно, если это имеет значение: одно изображение - это значок, а другое изображение - это часть снимка экрана, и я хочу знать, является ли эта часть в точности значком или нет.)

У меня есть OpenCV, но я все еще не привык к нему.

Одна возможность, о которой я думал до сих пор: разделите оба изображения на 10x10 ячеек и для каждой из этих 100 ячеек сравните цветную гистограмму. Затем я могу установить некоторое придуманное пороговое значение, и если полученное мной значение выше этого порога, я предполагаю, что они похожи.

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

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


Есть несколько очень связанных / похожих вопросов о получении подписи / отпечатка пальца / хэша из изображения:

Кроме того, я наткнулся на эти реализации, которые имеют такие функции для получения отпечатка пальца:

Некоторые дискуссии о перцептивных хэшах изображений: здесь


Немного оффтоп: существует множество методов для создания аудио отпечатков пальцев. MusicBrainz , веб-сервис, который обеспечивает поиск песен по отпечаткам пальцев, имеет хороший обзор в своей вики . Сейчас они используют AcoustID . Это для поиска точных (или наиболее точных) совпадений. Чтобы найти похожие совпадения (или если у вас есть только несколько фрагментов или много шума), взгляните на Echoprint . Связанный вопрос SO здесь . Похоже, это решено для аудио. Все эти решения работают неплохо.

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

Альберт
источник
1
Может быть, отпечаток изображения может помочь? stackoverflow.com/questions/596262/…
GWW
1
Метрика Вассерштейна, также известная как расстояние земного движителя (EMD), - это то, о чем люди, похоже, не знают, но дает в значительной степени то, что вы хотите здесь.
mmgp
Привет, я придумал улучшенный dHash - я назвал его IDHash: github.com/Nakilon/dhash-vips
Nakilon

Ответы:

110

Можно ли преобразовать снимок экрана или значок (масштабировать, повернуть, наклонить ...)? У меня в голове довольно много методов, которые могли бы вам помочь:

  • Простое евклидово расстояние, упомянутое @carlosdc (не работает с преобразованными изображениями, и вам нужен порог).
  • (Нормализованная) Кросс-корреляция - простая метрика, которую вы можете использовать для сравнения областей изображения. Это более надежно, чем простое евклидово расстояние, но не работает с преобразованными изображениями, и вам снова понадобится порог.
  • Сравнение гистограмм - если вы используете нормализованные гистограммы, этот метод работает хорошо и не зависит от аффинных преобразований. Проблема в том, чтобы определить правильный порог. Он также очень чувствителен к изменению цвета (яркости, контрастности и т. Д.). Вы можете комбинировать его с двумя предыдущими.
  • Детекторы заметных точек / областей - такие как MSER (Максимально стабильные экстремальные области) , SURF или SIFT . Это очень надежные алгоритмы, которые могут оказаться слишком сложными для вашей простой задачи. Хорошо то, что вам не обязательно иметь точную область с одним значком, эти детекторы достаточно мощные, чтобы найти правильное совпадение. Хорошая оценка этих методов содержится в этой статье: Локальные инвариантные детекторы признаков: обзор .

Большинство из них уже реализовано в OpenCV - см., Например, метод cvMatchTemplate (использует сопоставление гистограмм): http://dasl.mem.drexel.edu/~noahKuntz/openCVTut6.html . Также доступны детекторы выдающихся точек / областей - см. Обнаружение функций OpenCV .

Карел Петранек
источник
1
Его можно масштабировать или немного перемещать. Также будет отличаться фон значка. Я попробовал сравнить гистограммы, но получил много ложных срабатываний. Я также пробовал евклидово расстояние, но это также дает слишком много ложных срабатываний (но, возможно, я смогу немного улучшить обработку альфа-значения в значке). Я попробую еще немного, иначе я проверю MSER, SURF или SIFT.
Альберт
1
Еще одна идея - не сработало бы, если бы вы использовали гистограмму сравнения изображений после применения оператора Собеля? Вот бы только сравнить сходство граней. Может работать или не работать, в зависимости от того, насколько "резкий" фон.
Карел Петранек
46

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

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

Пример кодов

происхождение Ленапроисхождение Лена

размытие ленаразмытие лена

изменить размер Леныизменить размер Лены

сдвиг Ленасдвиг Лена

#include <opencv2/core.hpp>
#include <opencv2/core/ocl.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/img_hash.hpp>
#include <opencv2/imgproc.hpp>

#include <iostream>

void compute(cv::Ptr<cv::img_hash::ImgHashBase> algo)
{
    auto input = cv::imread("lena.png");
    cv::Mat similar_img;

    //detect similiar image after blur attack
    cv::GaussianBlur(input, similar_img, {7,7}, 2, 2);
    cv::imwrite("lena_blur.png", similar_img);
    cv::Mat hash_input, hash_similar;
    algo->compute(input, hash_input);
    algo->compute(similar_img, hash_similar);
    std::cout<<"gaussian blur attack : "<<
               algo->compare(hash_input, hash_similar)<<std::endl;

    //detect similar image after shift attack
    similar_img.setTo(0);
    input(cv::Rect(0,10, input.cols,input.rows-10)).
            copyTo(similar_img(cv::Rect(0,0,input.cols,input.rows-10)));
    cv::imwrite("lena_shift.png", similar_img);
    algo->compute(similar_img, hash_similar);
    std::cout<<"shift attack : "<<
               algo->compare(hash_input, hash_similar)<<std::endl;

    //detect similar image after resize
    cv::resize(input, similar_img, {120, 40});
    cv::imwrite("lena_resize.png", similar_img);
    algo->compute(similar_img, hash_similar);
    std::cout<<"resize attack : "<<
               algo->compare(hash_input, hash_similar)<<std::endl;
}

int main()
{
    using namespace cv::img_hash;

    //disable opencl acceleration may(or may not) boost up speed of img_hash
    cv::ocl::setUseOpenCL(false);

    //if the value after compare <= 8, that means the images
    //very similar to each other
    compute(ColorMomentHash::create());

    //there are other algorithms you can try out
    //every algorithms have their pros and cons
    compute(AverageHash::create());
    compute(PHash::create());
    compute(MarrHildrethHash::create());
    compute(RadialVarianceHash::create());
    //BlockMeanHash support mode 0 and mode 1, they associate to
    //mode 1 and mode 2 of PHash library
    compute(BlockMeanHash::create(0));
    compute(BlockMeanHash::create(1));
}

В этом случае ColorMomentHash дает лучший результат

  • атака размытия по Гауссу: 0,567521
  • сдвиг атаки: 0,229728
  • изменение размера атаки: 0.229358

Плюсы и минусы каждого алгоритма

Производительность при разных атаках

Производительность img_hash тоже хорошая

Сравнение скорости с библиотекой PHash (100 изображений из ukbench) производительность вычислений сравнение производительности

Если вы хотите узнать рекомендуемые пороговые значения для этих алгоритмов, проверьте этот пост ( http://qtandopencv.blogspot.my/2016/06/introduction-to-image-hash-module-of.html ). Если вам интересно, как мне измерить производительность модулей img_hash (включая скорость и различные атаки), проверьте эту ссылку ( http://qtandopencv.blogspot.my/2016/06/speed-up-image-hashing-of -opencvimghash.html ).

StereoMatching
источник
11

На скриншоте есть только значок? Если это так, то может хватить расстояния L2 между двумя изображениями. Если расстояние L2 не работает, следующий шаг - попробовать что-нибудь простое и хорошо известное, например: Лукас-Канаде . Я уверен, что это доступно в OpenCV.

Carlosdc
источник
Подобласть содержит либо только значок (со случайным фоном), либо что-то другое. Я хочу посмотреть, в каком именно случае. Хотя он может быть немного смещен или изменен в размере, поэтому я не был уверен, могу ли я просто смотреть на расстояние (в любой норме). Но я попробую с уменьшенной версией.
Альберт
6

Если вы хотите получить индекс схожести двух изображений, я предлагаю вам по метрикам индекс SSIM. Это больше соответствует человеческому глазу. Вот статья об этом: Индекс структурного сходства

Он также реализован в OpenCV и может быть ускорен с помощью графического процессора: OpenCV SSIM с графическим процессором

Милан Тенк
источник
5

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

Если выравнивание будет только крошечным, то вы можете пропустить оба изображения с помощью cv :: GaussianBlur, прежде чем найти сумму разностей пикселей.

Если качество выравнивания потенциально низкое, я бы порекомендовал либо гистограмму ориентированных градиентов, либо один из удобных алгоритмов обнаружения / дескриптора ключевых точек (например, SIFT или SURF ) OpenCV .

rcv
источник
4

Если для сопоставления одинаковых изображений - код для расстояния L2

// Compare two images by getting the L2 error (square-root of sum of squared error).
double getSimilarity( const Mat A, const Mat B ) {
if ( A.rows > 0 && A.rows == B.rows && A.cols > 0 && A.cols == B.cols ) {
    // Calculate the L2 relative error between images.
    double errorL2 = norm( A, B, CV_L2 );
    // Convert to a reasonable scale, since L2 error is summed across all pixels of the image.
    double similarity = errorL2 / (double)( A.rows * A.cols );
    return similarity;
}
else {
    //Images have a different size
    return 100000000.0;  // Return a bad value
}

Быстро. Но не устойчив к изменениям освещения / точки обзора и т. Д. Источник

Kiran
источник
2

Если вы хотите сравнить изображения на сходство, я предлагаю вам использовать OpenCV. В OpenCV есть несколько функций сопоставления и сопоставления шаблонов. Для сопоставления функций существуют детекторы SURF, SIFT, FAST и т. Д. Вы можете использовать это для обнаружения, описания и сопоставления изображения. После этого вы можете использовать определенный индекс, чтобы найти количество совпадений между двумя изображениями.

Хуа Эр Лим
источник
1
вы сказали: «После этого вы можете использовать определенный индекс, чтобы найти количество совпадений между двумя изображениями». какое может быть минимальное количество совпадений между двумя изображениями, чтобы сказать, что они «содержат» один и тот же объект?
Inês Martins