viernes, 20 de enero de 2012

Reconocimiento de patrones con OpenCV - Parte 1

La visión artificial, es algo que, aunque parezca muy especifico y a todos nos suene del mundo de la robotica o de la inteligencia artificial, cada vez nos vemos más obligados a avanzar en esa dirección. El éxito de los dispositivos moviles (bien telefonos, tablets..)  Android o Apple, se debe en gran medida al "juego" que da tener en un mismo dispositivo camara, acelerometro y GPS, esta combinación abre un abanico de posibilidades mucho más allá de sacar fotos y guiarnos por carretera. 
Por esta razón decido empezar este blog con un pequeño tutorial para reconocer imagenes, bien seas programador de iPhone, Android o Windows creo que puede serte interesante. 


Reconociendome la cara con mi Samsung Galaxy S


¿Que vamos a conseguir?


Reconocer patrones dentro de una imagen. Esto se puede extrapolar a un frame de un video y conseguir detectar objetos a través del webcam o de la cámara del movil.


Los objetos a reconocer puede ser solamente uno ( lo que nos facilitará muchismo el trabajo), o pueden ser una clase de objetos. Me explico, si buscamos un logo, este va a ser siempre igual, solamente tendremos que centrarnos en posicion, tamaño o angulo en el que pueda aparecer. Pero, si por ejemplo, hacemos un detector de caras como el de la imagen, estaremos buscando no una imagen, sino los rasgos comunes de cientos de caras que usemos para crear el clasificador. 


En esta primera parte veremos lo más importante, como crear un clasificador de OpenCV, en la segunda parte, veremos ejemplos de uso y a que funciones llamar para hacer cosillas interesantes con el. De todas formas una vez conseguido el clasificador, con seguir la documentación OpenCV no tendrás ningún problema.


Por otra parte, intento que este tutorial sirva para el que nunca hizo nada similar y empiece de cero en la visión artificial, por eso intento explicarlo de forma sencilla, aunque muchos otros, seguramente consideren determinadas explicaciones como obviedades. Seas de un perfil u otro, vamos al tema!


Empezamos...


Vamos a utilizar OpenCV, que son unas librerías de Intel para Inteligencia Artificial. OpenCV, si tenéis tiempo a profundizar con ellas se pueden llegar a hacer cosas increibles, pero nos vamos a centrar en lo que nos interesa. 
Lo bajamos de aquí: http://sourceforge.net/projects/opencvlibrary/  (de las librerías de código hay versiones específicas para Android y iPhone, aunque la generación del clasificador es independiente de la plataforma)


Entre otras aplicaciones, encontraremos opencv_createsamples.exe y opencv_haartraining.exe que serán las unicas dos que encesitemos para el proceso de construcción del clasificador.


Un clasificador no es otra cosa que un archivo xml con toda la información que OpenCV necesita para identificar objetos. No es otra cosa que una red neuronal, pero que afortunadamente, OpenCV nos crea y entrena "casi" automaticamente.


A partir de ahora vamos a hablar de fotos negativas, que seán las que no incluya el objeto a buscar, y fotos positivas que si lo incluyen. Tanto unas como otras, estarán en escala de grises. Bien pues no nos asustemos pero vamos a necesitar unas cuantas, ma o menos 5000 negativas y 2000 positivas para conseguir resultados decentes... ¿Que como las conseguimos? Bien, las negativas muy sencillo, podeis encontrar por Internet packs de fotos desde estadios de futbol, hasta fotos de la segunda guerra mundial..todo vale, si nos hacemos con unas cuantas podemos multiplicarlas facilmente con cualquier programa de tratamiento digital en masa, por ejemplo, creando negativos, espejo, volteado..etc, que se parezcan o no, no es importante. Es más las fotos de nuestras vacaciones son perfectamente validas. Eso si, aseguraros de que en estas imagenes no aparezca el objeto a buscar.
La colección de imagenes que suelo utilizar estaban en Megaupload.. no pude elegir peor día... De todas formas, seguro que no os costará haceros con imagenes negatiavas.


Una vez con la colección de imágenes, debemos realizar un listado que llamaremos negativas.dat , que incluya la ruta de todas las imágenes en el siguiente formato:


C:\Imgs\neg1.jpg
C:\Imgs\neg2.jpg
C:\Imgs\neg3.jpg
C:\Imgs\neg4.jpg
C:\Imgs\neg5.jpg


La forma más sencilla de crearlo es mediante algún programa que nos automatice el proceso, desde aqui os recomiendo por su sencillez, PrintFolders


Imagenes positivas..¿De donde?


Llegados a este punto debemos conseguir las imágenes positivas, es muy importante saber que es lo que buscamos exactamente:

  • Una imagen única: un logo, una silueta, un escudo de equipo, una señal de tráfico...Cualquier cosa que no admita más variación que la del ángulo, tamaño y luz.
  • Un patrón con rasgos comunes: Personas, maletas, matriculas.. Comparten rasgos comunes, pero debemos contemplar los rasgos que diferencian a unos de otros, por lo que la creación del clasificador será a partir de imágenes distintas.
En cualquiera de los casos, el proceso es similar, en este caso necesitamos un fichero positivas.dat , pero con cierta información extra, las dos primeras columnas son las coordenadas de esa imagen donde se encuentra el objeto a buscar, y las dos siguientes el tamaño del mismo:
                                    x      y     w   h
C:\Imgs\pos1.jpg 100 160 25 25
C:\Imgs\pos2.jpg 95  140 20 22
C:\Imgs\pos3.jpg 54  100 12 15
C:\Imgs\pos4.jpg 150 45  16 16
C:\Imgs\pos5.jpg 165 150 25 25


Ahora es donde viene la diferencia, si tenemos una imagen única, podremos generar automáticamente las imágenes positivas a partir, mismamente, de las negativas. En cambio, si lo que buscamos es un patrón de rasgos comunes, deberemos hacernos con esas imágenes.


Para generar automáticamente las imágenes, usaremos la aplicación opencv_createsamples ya comentada antes:


opencv_createsamples
   -img logo.png -> La imagen patrón
   -num 2000 -> Numero de imágenes positivas que queremos crear
   -bg negativas.dat -> Imagenes de entrada (usamos la lista de las negativas)
   -info positivas.dat -> Nombre para el fichero salida
   -maxxangle 0.3 -> Máximos ángulos aleatorios
   -maxyangle 0.6
   -maxzangle 0
   -maxidev 100 ->Maxima variación de intensidad
   -bgcolor 0 ->Color de fondo
   -w 20 -> Ancho
   -h 20 -> Alto


Recomiendo hacer la prueba con un numero de imágenes bajo, unas 100 para comprobar que los resultados generados son lo que esperamos, si es así repetirlo con las 2000 o las que queramos, de todas formas si las imágenes no son muy grandes, no suele ser un proceso lento.


En el caso de que busquemos patrones de características, la cosa se nos complica, más que complicar, se nos hace un poco más pesada... 
Si, por ejemplo hiciéramos el proceso anterior con una sola imagen de cara, el clasificador tendería a reconocer únicamente la cara de esa persona, por ello debemos hacernos con cientas de imágenes de caras. Para este ejemplo no tendriamos ningún problema ya que podriamos recurrir a la Face Recognition Database , pero para la mayoría de casos tendriamos que buscarnos la vida por nuestra cuenta.


Un programa muy interesante es el PositiveBuilder, pongo como curiosidad que cuando busqué el link me encontré con esto  (que quereis, me hizo gracia..). El programa lo podeis bajar de aqui. Necesitareis tener VisualStudio instalado.
Este programilla, a partir de un video (de cualquier formato) o de una carpeta de imagenes, nos va mostrando los frames (o las imagenes) uno por uno y nosotros le indicaremos donde está el objeto buscado o si por el contrario no aparece y ha de ser considerada como imagen negativa




Este programa nos generará al cabo de un par de horillas (depende de lo rápido que seamos con el mouse) la ansiada colección de imágenes positivas junto al positivas.dat que utilizaremos en la creación del clasificador


Ya esta todo.. A entrenar!!


Llego el momento de crear el clasificador, el exito en este proceso, nos llevará directamente a la obtención del fichero xml final que cualquier programa que use OpenCV podrá utilizar.


Primero debemos crear un vector con las imagenes positivas, esto es rápido y sencillo:


opencv_createsamples
   -info positivas.dat -> Fichero de entrada (imagenes positivas)
   -vec positivas.vec -> Nombre que le damos al fichero de salida (vector)
   -num 2000 -> Numero de imagenes. Muy importante que sea exactamente el numero de imágenes del que disponemos, sino las partirá sin enterarnos.
   -w 20 -> Tamaño exacto que pusimos en la creación de las muestras
   -h 20




Y por último, entrenamos al clasificador:


opencv_haartraining
-data cascadeFaces -> Nombre que le damos al clasificador
-vec positivas.vec -> Vector
-nonsym / -sym -> Simetria, si el objeto a buscar es simétrico verticalmente será un entrenamiento más rápido
-bg negativas.dat -> Negativas
-npos 2000 -> Numero de imagenes positivas (Importantisimo que se el mismo)
-nneg 5000 -> Numero de imagenes negativas (Ya no tan importante)
-w 20 -> Ancho (Tamaño exacto al usado en la createsamples)
-h ->Alto (Tamaño exacto al usado en la createsamples)
-nstages 20 -> Numero de fases (A más fases, mejor clasificador)
-nsplits 1 -> Numero de nodos por fase
-minhitrate 0.999 -> Éxito necesario
-maxfalsealarm 0.5 -> Errores máximo por fase
-mem 1700 -> Memoria dedicada
-mode ALL -> El modo ALL es el recomendado por la documentación


NOTAS IMPORTANTES:

  • El entrenamiento, y por consiguiente el clasificador está estructurado en forma de árbol, para construir un clasificador robusto debería tener por lo menos dos nodos por fase (nsplits), pero a consecuencia de dicha estructura el tiempo de computación crecerá exponencialmente según avancen las fases. 
  • Parámetros muy altos harán bloquearse al PC, nunca debemos usar toda la memoria del ordenador.
  • La cantidad de variables que existen independientes de estos parámetros (tamaño de las imágenes, procesador, tamaño del patrón, numero de imágenes positivas y negativas...)  en este proceso hace imposible hacerse una idea de lo que va a tardar, por eso es bueno empezar con valores modestos e ir incrementándolos, teniendo en cuenta que una buena estimación para que el clasificador sea robusto es más o menos una semana de entrenamiento, siendo menos importante cuales fueron los parámetros que se mejoraron.
  • Podemos empezar por ejemplo con 2 splits, 0.998 de minhitrate, y 14 stages (muy orientativo..)
  • No esta preparado para procesadores de más de un núcleo, este problema hace que el entrenamiento vaya más rápido en mi portatil que en mi AMD-X4, es más mientras se ejecuta se puede comprobar en el administrador de tareas que está usando exactamente un 25%.



En pocos dias la segunda parte, en la que veremos como ponerlo en practica con un ejemplo completo para Android. Saludos

6 comentarios:

  1. Hola, publicaste la segunda parte para Android?

    ResponderEliminar
  2. Hola,
    Muchas gracias por tu esfuerzo en explicarlo, me has adelantado el trabajo, y me has dado ideas nuevas, para empezar a juguetear con la librería. Ya estaba abandonando toda esperanza.

    Yo lo quiero integrar en wxdev c++ (todavía no lo consigo), para calcular puntos faciales (realmente se necesitaría orto-pantomo-grafías), para posicionamiento de puntos craneales y cálculo de ángulos. Para luego (es para nota), recalcular las posiciones y dar un resultado angular óptimo para un tto individual. Pero el primer paso como dices es reconocimiento de las estructuras...

    Un saludo.

    ResponderEliminar
  3. Hola, se que ha pasado tiempo sobre esto y quizás ya no trabajes con ello, pero si tienes la segunda parte de lo que tenias pensado colgar, te estaría muy agradecido, pues estoy leyendo algo sobre esto y me siento bastante perdido y agobiado.

    un saludo

    ResponderEliminar
  4. Hola una pregunta, ¿aproximadamente que tiempo tarda usando 1000 imagenes negativas y 250 positivas?

    ResponderEliminar
  5. hola el link del programa que obtiene las imágenes ya no esta disponible ,..... podrías por favor subir el archivo

    ResponderEliminar
  6. hola amigo tengo unas dudas referente al tema, me podrías suministrar su correo electrónico para contactarme.. Gracias

    ResponderEliminar