shutterstock pixabay para paginas imágenes imagenes gratis freepik descargar buscar buscador banco ios image opencv image-processing image-compression

ios - para - pixabay



Comparar la imagen de origen con un banco de imágenes conocidas (5)

Estoy creando una aplicación para un padre de un amigo mío que lamentablemente sufrió un derrame cerebral y ya no puede hablar, leer ni deletrear. Sin embargo, puede dibujar dibujos bastante detallados.

Actualmente he construido una aplicación que puede procesar una imagen de un dibujo y detectar formas básicas. (Líneas, cuadrados y triángulos) La aplicación puede contar la cantidad de cada forma que se ha dibujado para que sepa la diferencia entre una imagen con dos cuadrados y una imagen con solo un cuadrado.

Esto coloca una gran cantidad de carga cognitiva en el usuario para recordar todas las combinaciones de formas y lo que significan. Actualmente estoy detectando los contornos en la imagen a través de.

findContours(maskMat, contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);

Lo que me gustaría lograr es que el usuario dibuje una forma, la agregue a un banco de dibujos conocidos y cada vez que dibuje una imagen, la aplicación procesa cada imagen conocida comparándola con la imagen de origen y guardando un valor de similitud. Luego, tomando el valor más alto de similitud, siempre que esté por encima de un umbral, se puede tomar ya que la imagen dibujada es la mejor imagen conocida.

He investigado la coincidencia de patrones de OpenCV y las plantillas, pero con resultados poco fiables.

Estoy pidiendo consejo sobre el mejor enfoque que proporcionará el resultado que estoy esperando.

Construí un video de promoción para mi conferencia universitaria para ilustrar mejor lo que hace la aplicación. Si estás interesado puedes verlo aquí. https://youtu.be/ngMUUIsLHoc

Gracias por adelantado.


  1. Recuperación de imágenes basadas en bocetos . Existe una literatura bastante extensa sobre cómo buscar imágenes reales usando bocetos como la consulta. No es exactamente lo que desea, pero probablemente se pueden adaptar algunas de las mismas técnicas para buscar bocetos utilizando un boceto como la consulta. Incluso pueden trabajar sin modificación.

  2. Reconocimiento automático de caracteres chinos escritos a mano (o sistemas de escritura similares). También hay bastante literatura sobre eso; El problema es similar, el sistema de escritura ha evolucionado a partir de bocetos de imágenes pero mucho más simplificado y estilizado. Intenta aplicar algunas de las mismas técnicas.

  3. El número, orden, ubicación de las líneas individuales es probablemente más informativo que el boceto terminado como imagen. ¿Hay alguna manera de capturar esto? Si su usuario está dibujando con un lápiz óptico, es probable que pueda grabar las trayectorias del lápiz óptico para cada línea. Esto tendrá mucho, mucho más contenido de información que la propia imagen. Piense en alguien dibujando (por ejemplo) un automóvil con los ojos cerrados. Siguiendo las trayectorias, fácilmente puedes imaginar que es un auto. Desde la imagen puede ser mucho más difícil.

  4. Si puede capturar líneas como se describe, entonces el problema de coincidencia puede reducirse, hasta cierto punto, al problema de hacer coincidir algunas de las líneas de la imagen A con las líneas más similares en la imagen B (posiblemente deformadas, desplazadas, etc.). También deben tener relaciones similares con otras líneas: si (por ejemplo) dos líneas se cruzan en la imagen A, deben cruzarse en la imagen B, y en un ángulo similar y en una ubicación similar a lo largo de la longitud de cada una. Para ser más robusto, idealmente debería tratar con cosas como dos líneas en una imagen que corresponden a una sola línea (combinada) en la otra.


En primer lugar, esta parece una gran aplicación. Y para un propósito fantástico. ¡Buen trabajo!

Para el detalle de su pregunta, después de haber visto el video, parece que un enfoque sería el siguiente:

1. Divida cada región de dibujo en (digamos) una cuadrícula de 3x3 y permita que cada región contenga una primitiva, digamos una línea vertical, una línea horizontal, un cuadrado, un círculo, un triángulo o nada. (Esto depende un poco del control motor de los padres de su amigo)

  1. Cuando se complete una imagen, detecte estos primitivos y codifique una (digamos) clave de 9 caracteres que se puede usar para recuperar la imagen apropiada. Por ejemplo, si el triángulo es T, el cuadrado es S y el guión bajo es subrayado, entonces el código para "Me voy a casa" según el video sería "_T__S____".

  2. Cuando se inicia una nueva imagen, puede detectar cada primitiva a medida que se dibuja y usarla para construir una clave de búsqueda donde la clave tiene ''?'' para personajes desconocidos. A continuación, puede recuperar rápidamente todas las coincidencias posibles de su base de datos.

Por ejemplo, si el usuario dibuja un triángulo en la región media superior, esto se codificaría como ''? T ???????'' y esto coincidiría con ''_T__S____'' así como ''_TT______''

Si no es posible obligar al usuario a dibujar en una región más pequeña de la pantalla, aún puede almacenar una clave de codificación que represente las posiciones relativas de cada primitiva.

Para hacer esto, puede calcular el centro de masa de cada primitivo, ordenarlos de izquierda a derecha, de arriba a abajo y luego almacenar alguna representación de sus posiciones relativas, por ejemplo, un triángulo sobre un cuadrado puede ser TVS donde V significa que S está debajo T, un triángulo a la izquierda de un cuadrado podría ser T

Espero que esto ayude.

¡Buena suerte!


Recomiendo encarecidamente usar el modelo de bolsa de palabras .

Si desea utilizar un modelo como este para reconocer, por ejemplo, una bicicleta, subdividiría la imagen en sus partes (silla de montar, weels, cuadro, dirección). Si ve otra imagen que contiene dos weels, un sillín y un marco, es probable que se ajuste a la imagen de la bicicleta (ya que algunas de las palabras son similares). El problema con este enfoque es que la relación espacial de las palabras no se tiene en cuenta.

En este caso específico, creo que esto funcionaría razonablemente bien, ya que los bocetos tienden a ser una combinación de muchos objetos más pequeños (reconocibles). En lugar de tener que detectar una bicicleta todo el tiempo, puede reutilizar las partes de detección más pequeñas (por ejemplo, una rueda) para otras cosas que necesitan ser detectadas (motocicleta, automóvil, etc.).

Edición : OpenCV incluso tiene una implementación para el modelo de bolsa de palabras. Se puede encontrar here


Un método que puede ayudar, debido a cuánto tiempo ha estado en uso esta biblioteca y qué tan bien parece mantenerse, es usar ImageMagick. Déjame demostrarte.

1 instale el Pod usando

pod ''ImageMagick'', ''6.8.8-9''

2 en el viewController o cualquier otra vista que desee para hacer sus comparaciones entre imágenes, importe lo siguiente:

#import <wand/MagickWand.h>

4 Cree la excepción "Macro" en modo fácil para verificar errores sin tener que codificar el método de excepción cada vez que necesite verificar las excepciones:

#define ThrowWandException(wand) { / char * description; / ExceptionType severity; / / description = MagickGetException(wand,&severity); / (void) fprintf(stderr, "%s %s %lu %s/n", GetMagickModule(), description); / description = (char *) MagickRelinquishMemory(description); / exit(-1); / }

3 crear un método de comparación para comparar dos imágenes:

-(void)compareTwoImages:(UIImage*)firstImage secondImage:(UIImage*)secondImage comparitorSize:(size_t)comparitorSize { double diff1, diff2, diff3, diff4, diff5, diff6, diff7, diff8, diff9, diff10, diff11, diff12; MagickWandGenesis(); MagickWand *magick_wand_1 = NewMagickWand(); NSData * dataObject1 = UIImagePNGRepresentation(firstImage); MagickBooleanType status1; status1 = MagickReadImageBlob(magick_wand_1, [dataObject1 bytes], [dataObject1 length]); if (status1 == MagickFalse) { ThrowWandException(magick_wand_1); } MagickWandGenesis(); MagickWand *magick_wand_2 = NewMagickWand(); NSData * dataObject11 = UIImagePNGRepresentation(secondImage); MagickBooleanType status11; status11 = MagickReadImageBlob(magick_wand_2, [dataObject11 bytes], [dataObject11 length]); if (status11 == MagickFalse) { ThrowWandException(magick_wand_2); } MagickScaleImage(magick_wand_2, comparitorSize, comparitorSize); MagickScaleImage(magick_wand_1, comparitorSize, comparitorSize); MagickWandGenesis(); MagickWand *magick_wand_3 = NewMagickWand(); MagickCompareImages(magick_wand_1, magick_wand_2, UndefinedMetric, &diff1); MagickCompareImages(magick_wand_1, magick_wand_2, AbsoluteErrorMetric, &diff2); MagickCompareImages(magick_wand_1, magick_wand_2, MeanAbsoluteErrorMetric, &diff3); MagickCompareImages(magick_wand_1, magick_wand_2, MeanErrorPerPixelMetric, &diff4); MagickCompareImages(magick_wand_1, magick_wand_2, MeanSquaredErrorMetric, &diff5); MagickCompareImages(magick_wand_1, magick_wand_2, PeakAbsoluteErrorMetric, &diff6); MagickCompareImages(magick_wand_1, magick_wand_2, PeakSignalToNoiseRatioMetric, &diff7); MagickCompareImages(magick_wand_1, magick_wand_2, RootMeanSquaredErrorMetric, &diff8); MagickCompareImages(magick_wand_1, magick_wand_2, NormalizedCrossCorrelationErrorMetric, &diff8); MagickCompareImages(magick_wand_1, magick_wand_2, FuzzErrorMetric, &diff10); MagickCompareImages(magick_wand_1, magick_wand_2, UndefinedErrorMetric, &diff11); MagickCompareImages(magick_wand_1, magick_wand_2, PerceptualHashErrorMetric, &diff12); NSLog(@"UndefinedMetric: %.21f", diff1); NSLog(@"AbsoluteErrorMetric: %.21f", diff2); NSLog(@"MeanAbsoluteErrorMetric: %.21f", diff3); NSLog(@"MeanErrorPerPixelMetric: %.21f", diff4); NSLog(@"MeanSquaredErrorMetric: %.21f", diff5); NSLog(@"PeakAbsoluteErrorMetric: %.21f", diff6); NSLog(@"PeakSignalToNoiseRatioMetric: %.21f", diff7); NSLog(@"RootMeanSquaredErrorMetric: %.21f", diff8); NSLog(@"NormalizedCrossCorrelationErrorMetric: %.21f", diff9); NSLog(@"FuzzErrorMetric: %.21f", diff10); NSLog(@"UndefinedErrorMetric: %.21f", diff11); NSLog(@"PerceptualHashErrorMetric: %.21f", diff12); DestroyMagickWand(magick_wand_1); DestroyMagickWand(magick_wand_2); DestroyMagickWand(magick_wand_3); MagickWandTerminus(); }

5 observe la salida al depurador (obviamente, necesitará otro método que use algún tipo de monitor de "trilla" para determinar qué nivel muestra una "exacta o cercana a la coincidencia" en comparación con lo que considerará una coincidencia usted mismo). Además, NOTA MUY IMPORTANTE, la razón por la que tengo una variable "size_t" para el tamaño en las entradas del método anterior es porque no puede comparar imágenes de diferentes tamaños, por lo que primero debe cambiar el tamaño de las imágenes que está comparando con un tamaño que sienta. es "razonable" y luego se cambiará el tamaño de ambas imágenes usando ImageMagick para luego comparar las imágenes:

Ejemplo 1:

[self compareTwoImages:[UIImage imageNamed:@"book.png"] secondImage:[UIImage imageNamed:@"book.png"] comparitorSize:32];

[76233: 1364823] UndefinedMetric: 0.866871957624008593335

[76233: 1364823] AbsoluteErrorMetric: 0.000000000000000000000

[76233: 1364823] MeanAbsoluteErrorMetric: 0.00000000000000000000000

[76233: 1364823] MeanErrorPerPixelMetric: 0.000000000000000000000 **

[76233: 1364823] MeanSquaredErrorMetric: 0.000000000000000000000

[76233: 1364823] PeakAbsoluteErrorMetric: 0.00000000000000000000000

[76233: 1364823] PeakSignalToNoiseRatioMetric: inf

[76233: 1364823] RootMeanSquaredErrorMetric: 0.866871957624008593335

[76233: 1364823] NormalizedCrossCorrelationErrorMetric: 0.000000000000000000000

[76233: 1364823] FuzzErrorMetric: 0.000000000000000000000

[76233: 1364823] UndefinedErrorMetric: 0.866871957624008593335

[76233: 1364823] PerceptualHashErrorMetric: 0.00000000000000000000

Ejemplo 2:

[self compareTwoImages:[UIImage imageNamed:@"book.png"] secondImage:[UIImage imageNamed:@"arrow.png"] comparitorSize:32];

[76338: 1368754] UndefinedMetric: 0.074585376822533272501

[76338: 1368754] AbsoluteErrorMetric: 795.000000000000000000000

[76338: 1368754] MeanAbsoluteErrorMetric: 0.314410045058480136504

[76338: 1368754] MeanErrorPerPixelMetric: 328395.000000000000000000000

[76338: 1368754] MeanSquaredErrorMetric: 0.245338692857198115149

[76338: 1368754] PeakAbsoluteErrorMetric: 1.000000000000000000000

[76338: 1368754] PeakSignalToNoiseRatioMetric: 6.102339529383479899138

[76338: 1368754] RootMeanSquaredErrorMetric: 0.074585376822533272501

[76338: 1368754] NormalizedCrossCorrelationErrorMetric: 0.000000000000000000000

[76338: 1368754] FuzzErrorMetric: 0.571942529580490965913

[76338: 1368754] UndefinedErrorMetric: 0.074585376822533272501

[76338: 1368754] PerceptualHashErrorMetric: 1827.005561849247442296473

Aquí está sucediendo bastante matemáticamente. No tengo tiempo para explicar todas estas variables, pero es suficiente decir que esta comparación entre dos imágenes utiliza algunos métodos muy conocidos para comparar dos imágenes. Tendrá que recogerlo desde aquí y probar las estadísticas para personalizar esto a su gusto y elegir un umbral de error que se adapte a sus propósitos.

Explicación rápida:

ImageMagick es una biblioteca de procesamiento de imágenes probada en la batalla, y aunque estos métodos enumerados anteriormente son una especie de "caja negra", esta caja negra le permite ahorrar tiempo frente a algo que utiliza OpenCV. ImageMagick ya ha establecido algunos algoritmos de procesamiento de imágenes muy buenos y es esta historia junto con el "este método funciona", que es uno de los mayores beneficios de ImageMagick, considerando lo que se necesitaría para desarrollar su propia imagen. Biblioteca / métodos de reconocimiento / procesamiento. (por cierto, no estoy afiliado a ImageMagick, estoy muy contento con el producto, eso es todo)

Los métodos utilizados en los métodos de Objective-C anteriores son métodos que se derivan de la Biblioteca de ImageMagick para IOS. Tendrá que leer sobre estos métodos, pero para que sepa, están escritos en C y no en Objective-C, lo que significa que parte de esto es algo ajeno a la ejecución de la E / S de imágenes del molino u otras bibliotecas de procesamiento. . Sin embargo, la única parte que veo que es difícil de entender (suponiendo que una persona es nueva en el código C) es cómo entender qué hace el símbolo "&" frente a algunas variables. Aparte de esto, existe el problema de declarar y usar estructuras, pero esto puede responderse fácilmente mediante el Desbordamiento de pila, hay mucha información sobre esto aquí.

No me gusta la idea de que tengas que escribir tu propia biblioteca o algoritmos de procesamiento de imágenes para una aplicación como esta. Creo que estás haciendo algo muy bueno por una persona necesitada y creo que ayudará a un gran número de personas si puedes bloquear un MVP y creo que usar ImageMagick te ayudará a llegar mucho antes que antes. necesita volver a escribir algunas de las cosas que ImageMagick ya hace para sus propios fines.

Una última cosa a tener en cuenta, ImageMagick se basa en algunas bibliotecas de C de muy bajo nivel. Necesitaría realizar una comparación de procesamiento de algún tipo para determinar el rendimiento de ImageMagick en los dispositivos IOS en comparación con algo como Image I / O. Sin embargo, tengo la sensación de que estas dos bibliotecas de procesamiento de imágenes comparten la misma velocidad y funcionalidad de procesamiento de imágenes. Corríjame si alguien está seguro de que Image I / O es definitivamente más rápido en las pruebas, pero simplemente le digo esto para que sepa que esta NO ES su instalación de POD promedio, esta es una pieza de maquinaria robusta que por las gracias de algunos desarrolladores de IOS hicieron una versión de IOS básicamente usando CoccoaPods. Más allá de esto, ImageMagick se usa en todas las plataformas informáticas y es en gran medida una herramienta de línea de comandos.

Estadísticas, lo que significan los números:

Estos son los recursos que necesitará para comprender las matemáticas detrás de las estadísticas que se muestran arriba en la salida del depurador:

Este primero es antiguo, pero sigue siendo relevante:

http://www.ict.griffith.edu.au/anthony/info/graphics/image_comparing

Éste parece estar al día:

http://www.imagemagick.org/Usage/compare/

Ya sea que elija usar esta biblioteca u otra, buena suerte, me gusta a dónde va con esta aplicación y su demostración se ve increíble.

EDITAR: Casi olvido decirle la parte más importante aquí ... si las estadísticas devuelven todos los "ceros" para todo excepto para "UndefinedMetric", "PeakSignalToNoiseRatioMetric", "RootMeanSquaredErrorMetric", y "UndefinedErrorMetric", entonces es más probable que no tenga ¡un partido!


Descripción basada en el enfoque

Basado en su video, asumo que usted está más interesado en comparar dibujos lineales y no en bocetos detallados. Para los dibujos lineales podría pensar en el siguiente enfoque basado en la descripción. La descripción propuesta se basa en la relación y no dependería de los tamaños / dimensiones absolutos de la forma y también debería manejar las variaciones bastante bien.

Cálculo de una descripción para la forma

Debe calcular una descripción para la forma normalizada que sea resistente a pequeños cambios entre diferentes instancias. Existe una vasta literatura sobre la coincidencia de formas y la recuperación basada en bocetos, como se mencionó en la respuesta anterior, así que no lo repetiré. Suponiendo que las formas con las que está tratando son líneas o formas poligonales, el siguiente enfoque relativamente simple debería funcionar.

  1. Detecta todas las líneas en la imagen usando Hough Transform. Un ejemplo muy detallado se da here . Es posible que las líneas dibujadas a mano no sean completamente rectas y que no las detecten como una sola línea, pero sí múltiples segmentos diferentes con diferentes pendientes e intersecciones. Puede restringir el boceto solo en forma de líneas o usar un enfoque de ajuste de línea que se ajuste a una sola línea recta para una línea ligeramente irregular dibujada a mano.

  2. Ordena las líneas según las coordenadas X primero y luego las coordenadas Y.

  3. Recorra las líneas de izquierda a derecha, de abajo a arriba (o cualquier orden fijo) y calcule las siguientes propiedades para cada línea.

  4. Cree un vector de características concatenando los valores anteriores, por ejemplo, si el boceto tenía tres líneas, el vector de características tendría la forma { theta1, theta2, theta3, lengthRatio1, lengthRatio2, lengthRatio3, segmentRatio1, segmentRatio2, segmentRatio3 }

Formas de base de datos y consulta coincidentes

Como se explicó anteriormente, puede crear las representaciones de vectores de características para todas las formas en su base de datos, así como cualquier nueva consulta. Ahora puede escribir una función simple que calcula la distancia entre dos vectores. A continuación se da un pseudocódigo.

int numLines; // computed using hough transform vector<float> featureVector1(vec_size); vector<float> featureVector1(vec_size); ... // feature vectors computed as explained// // Compute error between two vectors // float angleError = 0.0f, lengthRatioError = 0.0, segmentRatioError = 0.0; float diff = 0.0; // (0,numLines-1) elements of the vector are angles for(int i=0; i < numLines; i++) { diff = abs(featureVector1[i] - featureVector2[i]); angleError += diff; } diff = 0.0; // (numLines,2numLines-1) elements of the vector are length ratios for(int i=numLines; i < 2*numLines-1; i++) { diff = abs(featureVector1[i] - featureVector2[i]); lengthRatioError += diff; } diff = 0.0; // (2*numLines,3numLines-1) elements of the vector are segment ratios // These values should be zero for no intersection for(int i=numLines; i < 2*numLines-1; i++) { diff = abs(featureVector1[i] - featureVector2[i]); segmentRatioError += diff; } // Weights for errors - you should play around with these. float w1 = 1.0, w2 = 1.0, w3 = 1.0; totalError = w1*angleError + w2*lengthRatioError + w3*segmentRatioError;

totalError coincidir la función de consulta con todas las funciones de la base de datos y busque la forma con la distancia mínima ( totalError ). Si la distancia está por debajo de un umbral, declare una coincidencia; de lo contrario, declare la consulta como una nueva forma y agregue a la base de datos.