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

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

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

Я OpenCV под рукой, но я все еще не привыкла.

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

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

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


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

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

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


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

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

7 ответов


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

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

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


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

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

пример коды

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

blur lenaразмытие Лена!--6-->

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
  • shift attack: 0.229728
  • изменение размера атаки: 0.229358

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

Performance under different attacks

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

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

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


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


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

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

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


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

Он также реализован в OpenCV, и его можно ускорить с помощью GPU:OpenCV SSIM с GPU


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

быстро. Но не робастно к изменениям в освещении / точке зрения etc. источник


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