Algoritmo para comparar dos imágenes en C #

Estoy escribiendo una herramienta en C # para encontrar imágenes duplicadas. Actualmente creo una sum de comprobación MD5 de los archivos y los comparo.

Desafortunadamente mis imágenes pueden ser

  • girado por 90 grados
  • tener diferentes dimensiones (imagen más pequeña con el mismo contenido)
  • tener diferentes compresiones o tipos de archivos (por ejemplo, artefactos jpeg, ver abajo)

enter image description here enter image description here

¿Cuál sería el mejor enfoque para resolver este problema?

Aquí hay un enfoque simple con un hash de imágenes de 256 bits (MD5 tiene 128 bits)

  1. cambiar el tamaño de la imagen a 16×16 píxeles

16x16 redimensionado

  1. reduce los colores a negro / blanco (lo que equivale a verdadero / falso en esta salida de la consola)

enter image description here

  1. lee los valores booleanos en List – este es el hash

Código :

 public static List GetHash(Bitmap bmpSource) { List lResult = new List(); //create new image with 16x16 pixel Bitmap bmpMin = new Bitmap(bmpSource, new Size(16, 16)); for (int j = 0; j < bmpMin.Height; j++) { for (int i = 0; i < bmpMin.Width; i++) { //reduce colors to true / false lResult.Add(bmpMin.GetPixel(i, j).GetBrightness() < 0.5f); } } return lResult; } 

Lo sé, GetPixel no es tan rápido, pero en una imagen de 16x16 píxeles no debería ser el cuello de botella.

  1. compare este hash con los valores hash de otras imágenes y agregue una tolerancia. (número de píxeles que pueden diferir del otro hash)

Código:

 List iHash1 = GetHash(new Bitmap(@"C:\mykoala1.jpg")); List iHash2 = GetHash(new Bitmap(@"C:\mykoala2.jpg")); //determine the number of equal pixel (x of 256) int equalElements = iHash1.Zip(iHash2, (i, j) => i == j).Count(eq => eq); 

Entonces este código puede encontrar imágenes iguales con:

  • diferentes formatos de archivo (por ejemplo, jpg, png, bmp)
  • rotation (90, 180, 270), flip horizontal / vertical - cambiando el orden de iteración de i y j
  • diferentes dimensiones (se requiere el mismo aspecto)
  • compresión diferente (se requiere tolerancia en caso de pérdida de calidad como artefactos jpeg): puede aceptar una igualdad del 99% para que sea la misma imagen y un 50% para ser una imagen diferente.
  • color cambiado a geyscaled y al revés (porque el brillo es independiente del color)

Actualización / Mejoras:

después de usar este método por un tiempo noté algunas mejoras que se pueden hacer

  • reemplazando GetPixel para un mayor rendimiento
  • usando la miniatura de la versión en lugar de leer toda la imagen para mejorar el rendimiento
  • en lugar de establecer 0.5f para que difiera entre claro y oscuro, use el brillo medio distintivo de los 256 píxeles. De lo contrario, se supone que las imágenes oscuras / claras son las mismas y permite detectar imágenes que tienen un brillo cambiado.
  • si necesita cálculos rápidos , use bool[] o List si necesita almacenar muchos valores hash con la necesidad de guardar memoria, use un Bitarray porque un booleano no se almacena en un bit, ¡se necesita un byte !

Puede consultar Algoritmo para comparar dos imágenes para ver los métodos disponibles para la comparación de imágenes.

A menos que desee recrear los algoritmos completos por su cuenta, debe intentar usar bibliotecas ya existentes o una parte mínima de su código (siempre que su licencia sea correcta para usted).

Para una implementación de C # de código abierto de Detección de bordes y algoritmos de visión por computadora relacionados, puede probar EmguCV que es un contenedor de OpenCV.

Después de remuestrear las imágenes a una resolución común, puede usar una Descomposición Wavelet y comparar los coeficientes de esta descomposición en lugar de las imágenes mismas. Comparando solo los primeros N coeficientes hará que este método sea más robusto para el ruido y otros artefactos.

Hay varias implementaciones de C # para wavelets disponibles. Un ejemplo es https://waveletstudio.codeplex.com/

Interesante pregunta, la comparación de imágenes no es tan difícil dado que,

  1. Esas imágenes son las mismas (la primera no es una sección de la segunda o viceversa)
  2. Las imágenes solo se rotan en múltiplos de 90 grados

Una forma de hacer la comparación sería,

  1. Cambiar el tamaño de ambas imágenes a la diamencia de tamaño más bajo
  2. Aplique detección de bordes en cada imagen que resulte en una imagen en blanco y negro (o una matriz de 0 y 1)
  3. Compare los mapas de bits resultantes (mantenga primero uno inmóvil, y gire el segundo uno 90 grados 3 veces) y calcule el% de píxeles coincidentes y obtenga el valor más alto

Ahora bien, si el valor entra dentro de un valor razonable, digamos 90% (probablemente tenga que determinar haciendo algunos experimentos), entonces puede asumir con seguridad que ambos son iguales, pero esto no va a funcionar si,

  1. Incluso si algunos píxeles differece en la esquina, por ejemplo, la segunda imagen se recorta desde la primera
  2. Las imágenes se rotan a no ser múltiplos de 90 grados (aunque esto no es muy probable)