Comparar similitud de imágenes usando OpenCV con Python


Estoy tratando de comparar una imagen con una lista de otras imágenes y devolver una selección de imágenes (como las imágenes de búsqueda de Google) de esta lista con hasta un 70% de similitud.

Tengo este código en este post y cambio para mi contexto

# Load the images
img =cv2.imread(MEDIA_ROOT + "/uploads/imagerecognize/armchair.jpg")

# Convert them to grayscale
imgg =cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

# SURF extraction
surf = cv2.FeatureDetector_create("SURF")
surfDescriptorExtractor = cv2.DescriptorExtractor_create("SURF")
kp = surf.detect(imgg)
kp, descritors = surfDescriptorExtractor.compute(imgg,kp)

# Setting up samples and responses for kNN
samples = np.array(descritors)
responses = np.arange(len(kp),dtype = np.float32)

# kNN training
knn = cv2.KNearest()
knn.train(samples,responses)

modelImages = [MEDIA_ROOT + "/uploads/imagerecognize/1.jpg", MEDIA_ROOT + "/uploads/imagerecognize/2.jpg", MEDIA_ROOT + "/uploads/imagerecognize/3.jpg"]

for modelImage in modelImages:

    # Now loading a template image and searching for similar keypoints
    template = cv2.imread(modelImage)
    templateg= cv2.cvtColor(template,cv2.COLOR_BGR2GRAY)
    keys = surf.detect(templateg)

    keys,desc = surfDescriptorExtractor.compute(templateg, keys)

    for h,des in enumerate(desc):
        des = np.array(des,np.float32).reshape((1,128))

        retval, results, neigh_resp, dists = knn.find_nearest(des,1)
        res,dist =  int(results[0][0]),dists[0][0]


        if dist<0.1: # draw matched keypoints in red color
            color = (0,0,255)

        else:  # draw unmatched in blue color
            #print dist
            color = (255,0,0)

        #Draw matched key points on original image
        x,y = kp[res].pt
        center = (int(x),int(y))
        cv2.circle(img,center,2,color,-1)

        #Draw matched key points on template image
        x,y = keys[h].pt
        center = (int(x),int(y))
        cv2.circle(template,center,2,color,-1)



    cv2.imshow('img',img)
    cv2.imshow('tm',template)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

Mi pregunta es, ¿cómo puedo comparar la imagen con la lista de imágenes y obtener solo las imágenes similares? ¿Hay algún método para hacer esto?

Author: Community, 2012-11-14

4 answers

Le sugiero que eche un vistazo a la distancia del motor de la tierra (EMD) entre las imágenes. Esta métrica da una idea de lo difícil que es transformar una imagen en escala de grises normalizada en otra, pero se puede generalizar para imágenes en color. Un muy buen análisis de este método se puede encontrar en el siguiente documento:

Robotics.stanford.edu/~rubner/papers/rubnerIjcv00.pdf

Se puede hacer tanto en la imagen completa como en el histograma (que es realmente más rápido que la imagen completa método). No estoy seguro de qué método permite una comparación completa de imágenes, pero para la comparación de histogramas puede usar el cv .Función CalcEMD2 .

El único problema es que este método no define un porcentaje de similitud, sino una distancia sobre la que se puede filtrar.

Sé que este no es un algoritmo de trabajo completo, pero sigue siendo una base para él, así que espero que ayude.

EDITAR:

Aquí hay una parodia de cómo funciona el EMD en principio. La idea principal es tener dos matrices normalizadas (dos imágenes en escala de grises divididas por su suma), y definir una matriz de flujo que describa cómo se mueve el gris de un píxel a otro desde la primera imagen para obtener la segunda (se puede definir incluso para una no normalizada, pero es más difícil).

En términos matemáticos, la matriz de flujo es en realidad un tensor cuadridimensional que da el flujo desde el punto (i, j) de la imagen antigua hasta el punto (k,l) de la nueva, pero si aplanas tus imágenes puedes transformarlo en una matriz normal, sólo un poco más difícil de leer.

Esta matriz de flujo tiene tres restricciones: cada término debe ser positivo, la suma de cada fila debe devolver el mismo valor del píxel de destino y la suma de cada columna debe devolver el valor del píxel inicial.

Dado esto, usted tiene que minimizar el costo de la transformación, dada por la suma de los productos de cada flujo de (i,j) a (k,l) para la distancia entre (i,j) y (k,l).

Parece un poco complicado en palabras, así que aquí está el código de prueba. La lógica es correcta, no estoy seguro de por qué el solucionador de scipy se queja de ello (quizás deberías buscar openOpt o algo similar):

#original data, two 2x2 images, normalized
x = rand(2,2)
x/=sum(x)
y = rand(2,2)
y/=sum(y)

#initial guess of the flux matrix
# just the product of the image x as row for the image y as column
#This is a working flux, but is not an optimal one
F = (y.flatten()*x.flatten().reshape((y.size,-1))).flatten()

#distance matrix, based on euclidean distance
row_x,col_x = meshgrid(range(x.shape[0]),range(x.shape[1]))
row_y,col_y = meshgrid(range(y.shape[0]),range(y.shape[1]))
rows = ((row_x.flatten().reshape((row_x.size,-1)) - row_y.flatten().reshape((-1,row_x.size)))**2)
cols = ((col_x.flatten().reshape((row_x.size,-1)) - col_y.flatten().reshape((-1,row_x.size)))**2)
D = np.sqrt(rows+cols)

D = D.flatten()
x = x.flatten()
y = y.flatten()
#COST=sum(F*D)

#cost function
fun = lambda F: sum(F*D)
jac = lambda F: D
#array of constraint
#the constraint of sum one is implicit given the later constraints
cons  = []
#each row and columns should sum to the value of the start and destination array
cons += [ {'type': 'eq', 'fun': lambda F:  sum(F.reshape((x.size,y.size))[i,:])-x[i]}     for i in range(x.size) ]
cons += [ {'type': 'eq', 'fun': lambda F:  sum(F.reshape((x.size,y.size))[:,i])-y[i]} for i in range(y.size) ]
#the values of F should be positive
bnds = (0, None)*F.size

from scipy.optimize import minimize
res = minimize(fun=fun, x0=F, method='SLSQP', jac=jac, bounds=bnds, constraints=cons)

La variable res contiene el resultado de la minimización...pero como dije no estoy seguro de por qué se queja de una matriz singular.

El único problema con este algoritmo es que no es muy rápido, por lo que no es posible hacerlo bajo demanda, pero hay que realizarlo con paciencia en el creación del conjunto de datos y almacenar en algún lugar los resultados

 23
Author: EnricoGiampieri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-29 11:40:54

Escribí un programa para hacer algo muy similar tal vez hace 2 años usando Python/Cython. Más tarde lo reescribí para Ir a conseguir un mejor rendimiento. La idea base proviene de findimagedupes IIRC.

Básicamente calcula una "huella dactilar" para cada imagen, y luego compara estas huellas dactilares para que coincidan con imágenes similares.

La huella digital se genera redimensionando la imagen a 160x160, convirtiéndola en escala de grises, agregando algo de desenfoque, normalizándola y luego redimensionándola a 16x16 monocromo. Al final tienes 256 bits de salida: esa es tu huella digital. Esto es muy fácil de hacer usando convert:

convert path[0] -sample 160x160! -modulate 100,0 -blur 3x99 \
    -normalize -equalize -sample 16x16 -threshold 50% -monochrome mono:-

(El [0] en path[0] se usa solo para extraer el primer fotograma de GIFs animados; si no estás interesado en tales imágenes, simplemente puedes eliminarlo.)

Después de aplicar esto a 2 imágenes, tendrá 2 (256 bits) huellas dactilares, fp1 y fp2.

La puntuación de similitud de estas 2 imágenes se calcula mediante XORing estos 2 valores y contando los bits establecidos en 1. Para hacer este conteo de bits, puede usar la función bitsoncount() de esta respuesta :

# fp1 and fp2 are stored as lists of 8 (32-bit) integers
score = 0
for n in range(8):
    score += bitsoncount(fp1[n] ^ fp2[n])

score será un número entre 0 y 256 indicando cuán similares son tus imágenes. En mi aplicación lo divido por 2.56 (normalizar a 0-100) y he encontrado que las imágenes con una puntuación normalizada de 20 o menos son a menudo idénticas.

Si desea implementar este método y usarlo para comparar muchas imágenes, yo fuertemente sugiero que use Cython (o simplemente C) tanto como sea posible: el XORing y el conteo de bits es muy lento con enteros puros de Python.

Lo siento mucho, pero ya no puedo encontrar mi código Python. En este momento solo tengo una versión de Go, pero me temo que no puedo publicarlo aquí (estrechamente integrado en algún otro código, y probablemente un poco feo ya que fue mi primer programa serio en Go...).

También hay una muy buena función "buscar por similitud" en GQview/Geeqie; su fuente es aquí.

 10
Author: Schnouki,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:00:41

Se está embarcando en un problema masivo, conocido como "recuperación de imágenes basada en contenido", o CBIR. Es un campo masivo y activo. Todavía no hay algoritmos terminados o enfoques estándar, aunque hay muchas técnicas, todas con diferentes niveles de éxito.

Incluso la búsqueda de imágenes de Google no hace esto (todavía) - hacen búsquedas de imágenes basadas en texto-por ejemplo, buscar texto en una página que es como el texto que buscaste. (Y estoy seguro de que están trabajando en el uso de CBIR; es el santo grial para muchos investigadores de procesamiento de imágenes)

Si tiene un plazo ajustado o necesita hacer esto y trabajar pronto... uy.

Aquí hay un montón de documentos sobre el tema:

Http://scholar.google.com/scholar?q=content+basado+imagen + recuperación

Generalmente tendrá que hacer algunas cosas:

  1. Extraer características (ya sea en puntos de interés locales, o globalmente, o de alguna manera, TAMIZAR, NAVEGAR, histogramas, etc.)
  2. Cluster / construir un modelo de distribuciones de imágenes

Esto puede implicar descriptores de características, síntesis de la imagen, aprendizaje de instancia múltiple . sucesivamente.

 10
Author: Pete,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-27 14:59:24

Para una implementación más simple de la Distancia de Earth Mover (también conocida como Distancia de Wasserstein) en Python, podría usar Scipy:

from scipy.stats import wasserstein_distance
from scipy.ndimage import imread
import numpy as np

def get_histogram(img):
  '''
  Get the histogram of an image. For an 8-bit, grayscale image, the
  histogram will be a 256 unit vector in which the nth value indicates
  the percent of the pixels in the image with the given darkness level.
  The histogram's values sum to 1.
  '''
  h, w = img.shape
  hist = [0.0] * 256
  for i in range(h):
    for j in range(w):
      hist[img[i, j]] += 1
  return np.array(hist) / (h * w)

a = imread('a.jpg')
b = imread('b.jpg')
a_hist = get_histogram(a)
b_hist = get_histogram(b)
dist = wasserstein_distance(a_hist, b_hist)
print(dist)
 0
Author: duhaime,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-31 21:18:57