Простой и быстрый способ сравнить изображения на предмет сходства - PullRequest
177 голосов
/ 16 ноября 2010

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

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

У меня есть OpenCV под рукой, но я до сих пор не привык к этому.

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

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

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


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

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

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


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

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

Ответы [ 7 ]

99 голосов
/ 17 ноября 2010

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

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

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

36 голосов
/ 03 июля 2016

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

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

Пример кода

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

blur lena размытие Лена

resize lena изменить размер лена

shift lena смена лены

#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

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

Performance under different attacks

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

Сравнение скорости с библиотекой PHash (100 изображений из ukbench) compute performance comparison performance

Если вы хотите узнать рекомендуемые пороговые значения для этих алгоритмов, проверьте этот пост (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).

10 голосов
/ 17 ноября 2010

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

5 голосов
/ 12 сентября 2013

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

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

4 голосов
/ 18 ноября 2010

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

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

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

3 голосов
/ 04 апреля 2014

Если для сопоставления идентичных изображений - код для расстояния 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
}

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

2 голосов
/ 19 января 2013

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

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