Método simple y rápido para comparar imágenes por similitud

192

Necesito una forma simple y rápida de comparar dos imágenes por similitud. Es decir, quiero obtener un valor alto si contienen exactamente lo mismo pero pueden tener un fondo ligeramente diferente y pueden ser movidos / redimensionados por unos pocos píxeles.

(Más concreto, si eso importa: una imagen es un icono y la otra imagen es una subárea de una captura de pantalla y quiero saber si esa subárea es exactamente el icono o no).

Tengo OpenCV a mano pero todavía no estoy tan acostumbrado.

Una posibilidad que pensé hasta ahora: Divide ambas imágenes en 10x10 celdas y para cada una de esas 100 celdas, compara el histograma de color. Entonces puedo establecer un valor de umbral compuesto y si el valor que obtengo está por encima de ese umbral, supongo que son similares.

Todavía no lo he intentado, pero creo que sería lo suficientemente bueno. Las imágenes ya son bastante similares (en mi caso de uso), por lo que puedo usar un valor de umbral bastante alto.

Supongo que hay docenas de otras posibles soluciones para esto que funcionarían más o menos (ya que la tarea en sí misma es bastante simple, ya que solo quiero detectar similitudes si realmente son muy similares). ¿Qué sugieres?


Hay algunas preguntas muy relacionadas / similares sobre cómo obtener una firma / huella digital / hash de una imagen:

Además, me topé con estas implementaciones que tienen tales funciones para obtener una huella digital:

Algunas discusiones sobre hashes de imágenes perceptuales: aquí


Un poco tópico: existen muchos métodos para crear huellas digitales de audio. MusicBrainz , un servicio web que proporciona búsqueda de canciones basada en huellas digitales, tiene una buena visión general en su wiki . Están utilizando AcoustID ahora. Esto es para encontrar coincidencias exactas (o en su mayoría exactas). Para encontrar coincidencias similares (o si solo tiene algunos fragmentos o mucho ruido), eche un vistazo a Echoprint . Una pregunta SO relacionada está aquí . Entonces parece que esto está resuelto para el audio. Todas estas soluciones funcionan bastante bien.

Aquí hay una pregunta algo más genérica sobre la búsqueda difusa en general . Por ejemplo, hay hashing sensible a la localidad y búsqueda de vecinos más cercanos .

Albert
fuente
1
¿Tal vez la huella digital de la imagen podría ayudar? stackoverflow.com/questions/596262/…
GWW
La métrica de Wasserstein, también conocida como Distancia de movimiento de tierra (EMD), es algo que la gente parece no saber, pero le daría más o menos lo que quiere aquí.
mmgp
3
posible duplicado de la comparación
sashoalm
Hola, se me ocurrió dHash mejorado - lo llamé IDHash: github.com/Nakilon/dhash-vips
Nakilon

Respuestas:

107

¿Se puede transformar la captura de pantalla o el icono (escalado, girado, sesgado ...)? Hay bastantes métodos en mi cabeza que podrían ayudarte:

  • Distancia euclidiana simple como lo menciona @carlosdc (no funciona con imágenes transformadas y necesita un umbral).
  • Correlación cruzada (normalizada) : una métrica simple que puede usar para comparar áreas de imagen. Es más robusto que la simple distancia euclidiana, pero no funciona en imágenes transformadas y nuevamente necesitará un umbral.
  • Comparación de histogramas : si utiliza histogramas normalizados, este método funciona bien y no se ve afectado por las transformaciones afines. El problema es determinar el umbral correcto. También es muy sensible a los cambios de color (brillo, contraste, etc.). Puedes combinarlo con los dos anteriores.
  • Detectores de puntos / áreas sobresalientes , como MSER (regiones extremas máximamente estables) , SURF o SIFT . Estos son algoritmos muy robustos y pueden ser demasiado complicados para su tarea simple. Lo bueno es que no tiene que tener un área exacta con un solo icono, estos detectores son lo suficientemente potentes como para encontrar la combinación correcta. Una buena evaluación de estos métodos se encuentra en este artículo: Detectores locales de características invariables: una encuesta .

La mayoría de estos ya están implementados en OpenCV; consulte, por ejemplo, el método cvMatchTemplate (utiliza la coincidencia de histogramas): http://dasl.mem.drexel.edu/~noahKuntz/openCVTut6.html . Los detectores de área / punto sobresalientes también están disponibles; consulte Detección de funciones de OpenCV .

Karel Petranek
fuente
1
Se puede escalar o mover ligeramente. Además, el fondo del icono será diferente. Intenté la comparación de histogramas pero obtuve muchos falsos positivos. También probé la distancia euclidiana, pero eso también da demasiados falsos positivos (pero tal vez pueda mejorar un poco el manejo del valor alfa en el icono). Lo intentaré un poco más; de lo contrario, comprobaré MSER, SURF o SIFT.
Albert
1
Otra idea: ¿no funcionaría si utilizara la comparación de histograma de las imágenes después de aplicar un operador sobel? Eso solo compararía la similitud de los bordes. Puede o no funcionar, dependiendo de qué tan "nervioso" sea el fondo.
Karel Petranek
44

Me enfrento a los mismos problemas recientemente, para resolver este problema (algoritmo simple y rápido para comparar dos imágenes) de una vez por todas, contribuyo con un módulo img_hash a opencv_contrib, puedes encontrar los detalles en este enlace .

El módulo img_hash proporciona seis algoritmos hash de imágenes, bastante fáciles de usar.

Ejemplo de códigos

origen lenaorigen lena

desenfoque lenadesenfoque lena

cambiar el tamaño de lenacambiar el tamaño de lena

shift lenashift 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));
}

En este caso, ColorMomentHash nos da el mejor resultado

  • ataque de desenfoque gaussiano: 0.567521
  • ataque de turno: 0.229728
  • redimensionar ataque: 0.229358

Pros y contras de cada algoritmo

Rendimiento bajo diferentes ataques

El rendimiento de img_hash también es bueno

Comparación de velocidad con la biblioteca PHash (100 imágenes de ukbench) calcular el rendimiento rendimiento de comparación

Si desea conocer los umbrales recomendados para estos algoritmos, consulte esta publicación ( http://qtandopencv.blogspot.my/2016/06/introduction-to-image-hash-module-of.html ). Si le interesa saber cómo mido el rendimiento de los módulos img_hash (incluida la velocidad y los diferentes ataques), consulte este enlace ( http://qtandopencv.blogspot.my/2016/06/speed-up-image-hashing-of -opencvimghash.html ).

StereoMatching
fuente
11

¿La captura de pantalla contiene solo el icono? Si es así, la distancia L2 de las dos imágenes podría ser suficiente. Si la distancia L2 no funciona, el siguiente paso es probar algo simple y bien establecido, como: Lucas-Kanade . Lo que estoy seguro está disponible en OpenCV.

carlosdc
fuente
La subárea contiene exactamente solo el icono (con un fondo aleatorio) o algo diferente. Quiero ver qué caso es. Sin embargo, puede ser ligeramente desplazado o redimensionado, es por eso que no estaba seguro de poder mirar la distancia (en cualquier norma). Pero intentaré con una versión reducida.
Albert
6

Si desea obtener un índice sobre la similitud de las dos imágenes, le sugiero a partir de las métricas el índice SSIM. Es más consistente con el ojo humano. Aquí hay un artículo al respecto: Índice de similitud estructural

También se implementa en OpenCV, y se puede acelerar con GPU: OpenCV SSIM con GPU

Milan Tenk
fuente
5

Si puede estar seguro de tener una alineación precisa de su plantilla (el icono) con la región de prueba, entonces cualquier suma de diferencias de píxeles anterior funcionará.

Si la alineación solo va a estar un poco apagada, entonces puede pasar las dos imágenes con cv :: GaussianBlur antes de encontrar la suma de las diferencias de píxeles.

Si la calidad de la alineación es potencialmente pobre, recomendaría un histograma de gradientes orientados o uno de los algoritmos convenientes de detección / descripción de puntos clave de OpenCV (como SIFT o SURF ).

rcv
fuente
4

Si para hacer coincidir imágenes idénticas, codifique la distancia 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
}

Rápido. Pero no es robusto a los cambios en la iluminación / punto de vista, etc. Fuente

Kiran
fuente
2

Si desea comparar la imagen por similitud, le sugiero que use OpenCV. En OpenCV, hay pocas coincidencias de características y coincidencias de plantillas. Para la coincidencia de características, hay un detector SURF, SIFT, FAST, etc. Puede usar esto para detectar, describir y luego hacer coincidir la imagen. Después de eso, puede usar el índice específico para encontrar el número de coincidencias entre las dos imágenes.

Hua Er Lim
fuente
1
usted dijo "Después de eso, puede usar el índice específico para encontrar el número de coincidencias entre las dos imágenes". ¿Cuál puede ser el número mínimo de coincidencias entre las dos imágenes para decir que "contais" el mismo objeto?
Inês Martins