¿Cómo puedo evaluar qué tan similares son dos imágenes con OpenCV?

141

¿OpenCV admite la comparación de dos imágenes, devolviendo algún valor (tal vez un porcentaje) que indica qué tan similares son estas imágenes? Por ejemplo, el 100% se devolvería si se pasara la misma imagen dos veces, el 0% se devolvería si las imágenes fueran totalmente diferentes.

Ya leí muchos temas similares aquí en StackOverflow. También hice un poco de Google. Lamentablemente, no pude encontrar una respuesta satisfactoria.

Boris
fuente
Consulte también las respuestas en stackoverflow.com/questions/4196453/…
B. Vaya el

Respuestas:

208

Este es un tema enorme, con respuestas de 3 líneas de código a revistas de investigación completas.

Voy a describir las técnicas más comunes y sus resultados.

Comparación de histogramas

Uno de los métodos más simples y rápidos. Propuesto hace décadas como un medio para encontrar similitudes de imágenes. La idea es que un bosque tendrá mucho verde y una cara humana mucho rosa, o lo que sea. Entonces, si compara dos imágenes con bosques, obtendrá una similitud entre los histogramas, porque tiene mucho verde en ambos.

Desventaja: es demasiado simplista. Un plátano y una playa se verán iguales, ya que ambos son amarillos.

Método OpenCV: compareHist ()

Comparación de plantillas

Un buen ejemplo aquí matchTemplate encuentra una buena coincidencia . Convoluciona la imagen de búsqueda con la que se está buscando. Por lo general, se usa para encontrar partes de imágenes más pequeñas en una más grande.

Desventajas: solo devuelve buenos resultados con imágenes idénticas, mismo tamaño y orientación.

Método OpenCV: matchTemplate ()

Coincidencia de funciones

Considerada una de las formas más eficientes de hacer búsquedas de imágenes. Se extraen varias características de una imagen, de manera que garantiza que las mismas características se reconocerán nuevamente incluso cuando se rotan, escalan o se sesgan. Las características extraídas de esta manera pueden compararse con otros conjuntos de características de imagen. Se considera que otra imagen que tiene una alta proporción de las características que coinciden con la primera representa la misma escena.

Encontrar la homografía entre los dos conjuntos de puntos le permitirá también encontrar la diferencia relativa en el ángulo de disparo entre las imágenes originales o la cantidad de superposición.

Hay una serie de tutoriales / ejemplos de OpenCV sobre esto, y un buen video aquí . Todo un módulo OpenCV (features2d) está dedicado a él.

Desventajas: puede ser lento. No es perfecto


En el sitio de preguntas y respuestas de OpenCV , estoy hablando de la diferencia entre los descriptores de características, que son geniales al comparar imágenes completas y descriptores de textura, que se utilizan para identificar objetos como rostros humanos o automóviles en una imagen.

Sam
fuente
para comparar imágenes similares que solo tienen unas pocas imágenes distintas (por ejemplo, un nuevo objeto movido a la misma vista), también puede trabajar con absdiff codota.com/code/java/methods/org.opencv.core.Core/absdiff Umbralizar el resultado produce una máscara que le permite resaltar las regiones que cambiaron de escena a escena.
Max F.
34

Si para hacer coincidir imágenes idénticas (mismo tamaño / orientación)

// 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
}

Fuente

Kiran
fuente
12

La solución de Sam debería ser suficiente. Utilicé la combinación de la diferencia de histograma y la coincidencia de plantillas porque ningún método me funcionaba el 100% de las veces. Sin embargo, le di menos importancia al método de histograma. Así es como lo he implementado en un script simple de Python.

import cv2

class CompareImage(object):

    def __init__(self, image_1_path, image_2_path):
        self.minimum_commutative_image_diff = 1
        self.image_1_path = image_1_path
        self.image_2_path = image_2_path

    def compare_image(self):
        image_1 = cv2.imread(self.image_1_path, 0)
        image_2 = cv2.imread(self.image_2_path, 0)
        commutative_image_diff = self.get_image_difference(image_1, image_2)

        if commutative_image_diff < self.minimum_commutative_image_diff:
            print "Matched"
            return commutative_image_diff
        return 10000 //random failure value

    @staticmethod
    def get_image_difference(image_1, image_2):
        first_image_hist = cv2.calcHist([image_1], [0], None, [256], [0, 256])
        second_image_hist = cv2.calcHist([image_2], [0], None, [256], [0, 256])

        img_hist_diff = cv2.compareHist(first_image_hist, second_image_hist, cv2.HISTCMP_BHATTACHARYYA)
        img_template_probability_match = cv2.matchTemplate(first_image_hist, second_image_hist, cv2.TM_CCOEFF_NORMED)[0][0]
        img_template_diff = 1 - img_template_probability_match

        # taking only 10% of histogram diff, since it's less accurate than template method
        commutative_image_diff = (img_hist_diff / 10) + img_template_diff
        return commutative_image_diff


    if __name__ == '__main__':
        compare_image = CompareImage('image1/path', 'image2/path')
        image_difference = compare_image.compare_image()
        print image_difference
Priyanshu Chauhan
fuente
No entiendo bien Python. Pero, ¿qué es el tipo 'commutative_image_diff'? cv.Mat o doble. Si es cv.Mat, compare 'commutative_image_diff <self.minimum_commutative_image_diff' cómo funciona o cuál es el propósito de esta comparación. ¿Me lo puedes explicar?
BulletRain
1

Un poco fuera de tema pero útil es el numpyenfoque pitónico . Es robusto y rápido, pero solo compara píxeles y no los objetos o datos que contiene la imagen (y requiere imágenes del mismo tamaño y forma):

Un enfoque muy simple y rápido para hacer esto sin openCV y cualquier biblioteca para visión por computadora es normalizar las matrices de imágenes mediante

import numpy as np
picture1 = np.random.rand(100,100)
picture2 = np.random.rand(100,100)
picture1_norm = picture1/np.sqrt(np.sum(picture1**2))
picture2_norm = picture2/np.sqrt(np.sum(picture2**2))

Después de definir ambas imágenes normadas (o matrices), puede sumar la multiplicación de las imágenes que desea comparar:

1) Si compara imágenes similares, la suma devolverá 1:

In[1]: np.sum(picture1_norm**2)
Out[1]: 1.0

2) Si no son similares, obtendrá un valor entre 0 y 1 (un porcentaje si multiplica por 100):

In[2]: np.sum(picture2_norm*picture1_norm)
Out[2]: 0.75389941124629822

Tenga en cuenta que si tiene imágenes en color, debe hacerlo en las 3 dimensiones o simplemente comparar una versión en escala de grises. A menudo tengo que comparar grandes cantidades de imágenes con contenido arbitrario y esa es una forma muy rápida de hacerlo.

Franz
fuente
2
Hola, acabo de seguir tu paso pero descubrí que la parte de normalización no podía obtener el resultado adecuado. El resultado final es mucho mayor que 1.0. ¿Alguna idea?
G_cy