sábado, 13 de noviembre de 2010

RETO VII: CONTAR CARAS

Para este reto vamos a ir probando distintas soluciones hasta dar con la más adecuada, lo que vamos a intentar hacer es contar caras, o sea cada vez que alguien pasa contarlo.  Esto a priori parece fácil, pero si decimos cada vez que detectes cara, nos va a contar una por cada frame en la que aparece, con lo cual no tendría mucho sentido. Por lo tanto como primera aproximación vamos a intentar comparar imágenes si es la misma no la contamos si es una nueva la contamos.  Si esto no funciona bien tendremos que intentar otras cosas, como intentar obtener los puntos biométricos de la cara y comparar, o seguir una imagen a lo largo de su trayectoria.  De momento haremos esto para ver los resultados.

domingo, 7 de noviembre de 2010

SOLUCIÓN RETO VI: SEPARACIÓN DE LAS CARAS EN OTRAS IMAGENES

Para la resolución de este reto, me he creado una pequeña estructura, en la cual almaceno la imagen de la cara.  La estructura es la siguiente:

typedef struct _Rostro {
    struct _Rostro* Anterior; // secuencia previa
    struct _Rostro* Siguiente; // secuencia siguiente
    struct _Rostro* Primero; //Primer elemento de la lista
    int Nodo;
    int total; // Número total de elementos
    IplImage* Cara;  //Aqui guardamos la cara

}Rostro;
typedef Rostro *pRostro;
typedef Rostro *Rostros;

Podemos ver que nuestra intención es realizar una lista con las caras, para he definido una serie de funciones para tratar esta lista.

int ListaVacia(Rostros R);
void Insertar(Rostros *R, IplImage* Cara);
IplImage* MostrarRostro(Rostros *R,int nodo);

Con estas funciones controlaremos la lista.
El código fuente de estas funciones será el siguiente.

int ListaVacia(Rostros R) {
   return (R == NULL);
}
void Insertar(Rostros *R, IplImage* Cara){
    pRostro nuevo, anterior;
    /* Crear un nodo nuevo */
    nuevo = (pRostro)malloc(sizeof(Rostro));
    nuevo->Cara= Cara;
    if (ListaVacia(*R)){
        *R=nuevo;
        (*R)->Siguiente=NULL;
        (*R)->Anterior=NULL;
        (*R)->Cara=Cara;
        (*R)->Nodo=0;
        (*R)->total=1;
        (*R)->Primero=*R;
    }
    else
    {   
        anterior=(*R)->Primero;
        while (anterior->Siguiente!=NULL){
            anterior->total++;
            anterior=anterior->Siguiente;
        }
        anterior->total++;
        anterior->Siguiente=nuevo;
        nuevo->Anterior=anterior;
        nuevo->Siguiente=NULL;
        nuevo->Nodo=anterior->Nodo+1;
        nuevo->total=anterior->total;
        nuevo->Cara=Cara;
       
    }

}
IplImage* MostrarRostro(Rostros *R,int nodo){
   pRostro Caras = *R;

   if(ListaVacia(*R))  {
       printf("ERROR:  Lista Vacia\n");
       return NULL;
   }
   else {
      
      while(Caras->Nodo<nodo) {
         Caras = Caras->Siguiente;
     }
      return Caras->Cara;
   }
}

Bueno pero, como separamos nuestra cara de la imagen.  Para esto utilizaremos las siguientes funciones de OpenCV:

cvSetImageROI(IplImage*,CvSeq); 
cvCopy(IplImage* src,IplImage* des,NULL);
cvResetImageROI(IplImage*);

La primera, cvSetImageROI nos delimita la zona a copiar, la segunda función nos copia ese fragmento de imagen en la imagen des, y la tercera nos elimina la delimitación en la imagen, así la volvemos a tener completa otra vez.
Todo lo demás es exactamente igual al reto anterior.

El código fuente lo dejo aquí.

#include "cv.h"
#include "highgui.h"

#include <iostream>
#include <cstdio>
#include <string>

#include "Funciones.h"
#include "Estructuras.h"
using namespace std;

void main()
{
     static CvScalar colores[] = {
             {{0,0,255}}, {{0,128,255}},{{0,255,255}},{{0,255,0}},
            {{255,128,0}},{{255,255,0}},{{255,0,0}}, {{255,0,255}}};//Colores para dibujar

   
     double t;
     bool Primera= true;
     CvHaarClassifierCascade* cascade = (CvHaarClassifierCascade*)cvLoad( "haarcascade_frontalface_alt.xml",0, 0, 0 );
     CvMemStorage* storage = cvCreateMemStorage(0);
     //Creamos la ventana
     cvNamedWindow( "Imagen", CV_WINDOW_AUTOSIZE );
     //Capturamos la cámara
     CvCapture* capture= cvCreateCameraCapture(0);   
     assert( capture != NULL ); 
     IplImage* Frame;
     IplImage* gris;
     IplImage* RostroDetectado;
     Rostros ListaRostros=NULL;
     CvSeq* objects;
   

   
   
     int i,j,k,l;
     string nombreVentana;
     cvSetCaptureProperty(capture,CV_CAP_PROP_FRAME_WIDTH,300);
     cvSetCaptureProperty(capture,CV_CAP_PROP_FRAME_HEIGHT,300);

     Frame=cvQueryFrame(capture);  //Capturamos la primera imagen
     

     if(Frame)  //Si hay frame
     {
         gris = cvCreateImage( cvSize(Frame->width,Frame->height), 8, 1 );
        
         while(1)
        {
            //Preparación de la imagen
            cvCvtColor( Frame, gris, CV_BGR2GRAY );
           
            //Capturamos el tiempo inicial
            t = (double)cvGetTickCount();
            objects= Deteccion_y_dibujo(gris, cascade, storage);  //Aqui ya no me rellena el objects la segunda vez
            //Capturamos el tiempo final y le restamos el inicial
            t = (double)cvGetTickCount() - t;

             //Dibujamos cuadros al rededor de los objetos
            //Con lo cual este for no me lo hace
           

            for( i = 0; i < (objects ? objects->total : 0); i++ ) {
                CvRect* r = (CvRect*)cvGetSeqElem( objects, i );
                cvRectangle(
                Frame,
                cvPoint(r->x,r->y),
                cvPoint(r->x+r->width,r->y+r->height),
                colores[i%8],
                2
                 );
               RostroDetectado = cvCreateImage(cvSize(r->width, r->height), gris->depth, gris->nChannels);
               cvSetImageROI(gris,*r);
               cvCopy(gris,RostroDetectado,NULL);
               cvResetImageROI(gris);
               Insertar(&ListaRostros, RostroDetectado);
             


            }   
           
            //Visualizamos el tiempo
           printf( "Tiempo deteccion = %g ms\n",t/((double) cvGetTickFrequency() *1000.) );
            //Dibujamos la imagen
            cvShowImage( "Imagen", Frame);
            for( l = 0; l < (ListaRostros ? ListaRostros->total : 0); l++ )
                {  
                    nombreVentana="Imagen";
                    j=l;
                    do
                    {                   
                         k=j%10;
                         nombreVentana+=(char)(k+48);
                         j=j/10;
                    }
                    while (j/10!=0);
                    cvNamedWindow(nombreVentana.data(), CV_WINDOW_AUTOSIZE );
                    cvShowImage( nombreVentana.data(), MostrarRostro(&ListaRostros,l));
                }
               
           
            char c = cvWaitKey
            if(c==27) break;
            BorrarLista(&ListaRostros);
            Frame=cvQueryFrame(capture);  //Capturamos el resto de las imagenes
           
           
        }
        cvReleaseImage( &gris );
     }
     cvReleaseImage(&Frame);


CvSeq*  Deteccion_y_dibujo( IplImage* gris,CvHaarClassifierCascade* cascade,CvMemStorage* storage )
{
     CvSeq*  objects;
     cvEqualizeHist( gris, gris );
   
     //Detección si las hay
     cvClearMemStorage( storage );
     objects = cvHaarDetectObjects(
            gris,
            cascade,
            storage,
            1.1,
            2,
            0 /*CV_HAAR_DO_CANNY_PRUNING*/,
            cvSize(30, 30));
           
    return objects;   
   
}
 

En este código se puede ver el empleo de la función 

cvSetCaptureProperty(capture,CV_CAP_PROP_FRAME_WIDTH,300); la cual uso para cambiar el ancho del frame, y la función.
cvSetCaptureProperty(capture,CV_CAP_PROP_FRAME_HEIGHT,300); la cual utilizo para cambiar el alto, del frame.  

Así de esta forma puedo llegar a un compromiso entre reconocimiento y velocidad de procesado.

Bueno como siempre si alguien tiene una solución mejor que la proponga, así aprendemos todos.

lunes, 1 de noviembre de 2010

RETO VI: SEPARACIÓN DE LAS CARAS EN OTRAS IMÁGENES

Muy buenas a todos, después de casi un mes desaparecido, vuelvo a escribir.  Para este reto, propongo, coger el algoritmo de segmentación de caras y separar las caras en otras imágenes para posteriormente visualizarlas en la pantalla.  De esta forma, ya podremos hacer lo que queramos con las caras.  Reconocerlas, contarlas, lo que sea.

viernes, 8 de octubre de 2010

SOLUCIÓN RETO V: SEGMENTACIÓN DE CARAS CON UNA CÁMARA

La solución a este reto es muy similar a la del reto anterior, seguiremos utilizando la función cvHaarDetectObjects de la misma manera que en el reto anterior.  La diferencia es que en este utilizamos una cámara, o sea que estamos reconociendo las caras en un montón de fotografías, una detrás de otra.  Esta función devuelve una secuencia, que es donde se encuentran las caras, esta secuencia es del tipo CvSeq, que es una estructura definida de este tipo:
typedef struct CvSeq {
    int flags; // misceláneo de banderas
    int header_size; // Tamaño y secuencia de la cabecera
    CvSeq* h_prev; // secuencia previa
    CvSeq* h_next; // secuencia siguiente
    CvSeq* v_prev; // Previsión de las secuencia 2n
    CvSeq* v_next // Previsión de la secuancia 2n siguiente
    int total; // Número total de elementos
    int elem_size; // Tamaño de la secuencia en byte
    char* block_max; // Máximo contorno en el último bloquie
    char* ptr; // Puntero de escritura actual
    int delta_elems; // Cuantos punteros asignados cuando la secuencia crece
    CvMemStorage* storage; //Cuando la secuencia es almacenada
    CvSeqBlock* free_blocks; // Lista libre de bloques
    CvSeqBlock* first; // Puntero al primer elemnto de bloque
}
Los parámetros de entrada de esta función son los siguientes; la imagen en tonalidad de grises, un puntero a un dato de tipo  CvHaarClassifierCascade* que no es nada más que el archivo donde están los parámetros de reconocimiento, un parámetro del tip CvMemStorage* que es una especie de almacén, más adelante explicaré este elemento, cuando lo entienda mejor je je je, el factor de escala, el número de vecinos, unas banderas y el tamaño mínimo.  
Lo que hace esta función ya lo esplique un poco por encima en el reto 4, tampoco vamos a entrar en muchos detalles, hay mucha más información en el libro learning openCV, lo malo que esta en ingles, pero es muy simple de leer.
Como siempre os dejo mi código, más adelante iremos profundizando un poco más.  Cuando vaya yo aprendiendo, je je je.


#include "cv.h"
#include "highgui.h"

#include <iostream>
#include <cstdio>

void Deteccion_y_dibujo( IplImage* imagen,IplImage* gris,CvHaarClassifierCascade* cascade,CvMemStorage* storage);

void main()
{
     double t;
     bool Primera= true;
     CvHaarClassifierCascade* cascade = (CvHaarClassifierCascade*)cvLoad( "haarcascade_frontalface_alt.xml",0, 0, 0 );
     CvMemStorage* storage = cvCreateMemStorage(0);
     //Creamos la ventana
     cvNamedWindow( "Imagen", CV_WINDOW_AUTOSIZE );
     //Capturamos la cámara
     CvCapture* capture= cvCreateCameraCapture(0);   
     assert( capture != NULL ); 
     IplImage* Frame;
     IplImage* gris

     while(1)
     {
        Frame=cvQueryFrame(capture);  //Capturamos la primera imagen
        if(!Frame) break;  //Si no hay frame salimos
        if (Primera)
        {
            gris = cvCreateImage( cvSize(Frame->width,Frame->height), 8, 1 );
            Primera= false;
        }

        //Capturamos el tiempo inicial
        t = (double)cvGetTickCount();
        Deteccion_y_dibujo(Frame,gris, cascade, storage);
        //Capturamos el tiempo final y le restamos el inicial
        t = (double)cvGetTickCount() - t;
        //Visualizamos el tiempo
        printf( "Tiempo deteccion = %g ms\n", t/((double)cvGetTickFrequency()*1000.) );
        //Dibujamos la imagen
        cvShowImage( "Imagen", Frame);
   
        char c = cvWaitKey(33);
        if(c==27) break;
     }
     cvReleaseImage(&Frame);
     cvReleaseImage( &gris );

  
}

void Deteccion_y_dibujo( IplImage* imagen,IplImage* gris,CvHaarClassifierCascade* cascade,CvMemStorage* storage  )
{
     static CvScalar colores[] = {
        {{0,0,255}}, {{0,128,255}},{{0,255,255}},{{0,255,0}},
        {{255,128,0}},{{255,255,0}},{{255,0,0}}, {{255,0,255}}};//Colores para dibujar
   
     //Preparación de la imagen
     cvCvtColor( imagen, gris, CV_BGR2GRAY );
     cvEqualizeHist( gris, gris );
   
     //Detección si las hay
     cvClearMemStorage( storage );
     CvSeq* objects = cvHaarDetectObjects(
            gris,
            cascade,
            storage,
            1.1,
            2,
            0 /*CV_HAAR_DO_CANNY_PRUNING*/,
            cvSize(30, 30));
     //Dibujamos cuadros al rededor de los objetos
     for(int i = 0; i < (objects ? objects->total : 0); i++ ) {
            CvRect* r = (CvRect*)cvGetSeqElem( objects, i );
            cvRectangle(
            imagen,
            cvPoint(r->x,r->y),
            cvPoint(r->x+r->width,r->y+r->height),
            colores[i%8],
            2
            );
     }
   
}


También os dejo un video.

martes, 28 de septiembre de 2010

RETO V: SEGMENTACIÓN DE CARAS CON UNA CÁMARA

En este reto vamos a hacer lo mismo que en el reto IV pero esta vez empleando una cámara web.  Segmentaremos las caras para posteriormente marcarlas en la imagen con un cuadrado.
Para hacer esto vamos a utilizar la función Deteccion_y_dibujo, modificandola para que nos diga también el tiempo que ha tardado en reconocer la cara.  La función main la modificaremos para capturar las imágenes de la cámara web.
En principio parece un reto muy sencillo, ya que lo complicado está hecho.

domingo, 26 de septiembre de 2010

SOLUCIÓN RETO IV: SEGMENTACIÓN DE CARAS EN UNA IMAGEN

Muy buenas a todos, al final después de muchas búsquedas, he encontrado la solución a este reto.   La solución es más fácil de lo que yo pensaba, ya que ya estaba hecha en OpenCV.  Gracias al libro "Learnin OpenCV" de Gary Bradski y Adrian Kaehler, de la editorial O'reilly, lo he encontrado.

En OpenCV existe una función llamada cvHaarDetectObjects, que reconoce objetos utilizando el clasificador de Viola-Jones.  

  1. Usa las características de entrada Haar-Like:  un umbral aplicando sumas y restas de regiones rectangulares de la imagen.
  2. Es una técnica de integración de imagen que permite una rápida del valor de la región rectangular o regiones giradas 45º.  Esta estructura de datos es utilizada para acelerar los cálculos de las características de las entradas.
  3. Se utiliza para crear una clasificación de las características por alta detección y débil rechazo.
  4. Esta organizado en un débil clasificador de nodos en una cascada de rechazos. En otras palabras, en el primer clasificador seleccionamos la mejor región detectada que contiene el objeto, permitiendo errores en la detección, en el siguiente nodo la segundo mejor detección.  En un modo de test un objeto es detectado solo si ha superado toda la cascada de nodos.
Para utilizar esta función necesitamos entrenar la función cvHaarDetectObjects, en este reto no la hemos entrenado, lo dejamos para retos posteriores, ya que dentro del directorio data, de openCV encontramos el archivo haarcascade_frontalface_alt.xml  que es el que he utilizado.  Si es que al final OpenCV lo tiene todo resuelto, je je je.

A continuación dejo el código que he generado.

#include "cv.h"
#include "highgui.h"

#include <iostream>
#include <cstdio>



void Deteccion_y_dibujo( IplImage* imagen,CvHaarClassifierCascade* cascade,CvMemStorage* storage, double escala);





void main()
{
     CvHaarClassifierCascade* cascade = (CvHaarClassifierCascade*)cvLoad( "haarcascade_frontalface_alt.xml",0, 0, 0 );
     CvMemStorage* storage = cvCreateMemStorage(0);
     IplImage* imagen = cvLoadImage("HPIM1403.jpg");
     IplImage* imagenPequeña =cvCreateImage(cvSize((int)((imagen->width*25)/100) , (int)((imagen->height*25)/100) ),imagen->depth, imagen->nChannels );
     cvResize(imagen,imagenPequeña,CV_INTER_NN);
     Deteccion_y_dibujo(imagenPequeña, cascade, storage,1 );
     //Creamos la ventana
     cvNamedWindow( "Imagen", CV_WINDOW_AUTOSIZE );
     //Dibujamos la imagen
     cvShowImage( "Imagen", imagenPequeña);
     
     cvWaitKey(0);
     cvReleaseImage(&imagen);

  
}

void Deteccion_y_dibujo( IplImage* imagen,CvHaarClassifierCascade* cascade,CvMemStorage* storage, double escala  )
{
     static CvScalar colores[] = {
        {{0,0,255}}, {{0,128,255}},{{0,255,255}},{{0,255,0}},
        {{255,128,0}},{{255,255,0}},{{255,0,0}}, {{255,0,255}}};//Colores para dibujar
     
     //Preparación de la imagen
     IplImage* gris = cvCreateImage( cvSize(imagen->width,imagen->height), 8, 1 );
     IplImage* Imagen_Pequeña = cvCreateImage(
                cvSize( cvRound(imagen->width/escala), cvRound(imagen->height/escala)), 8, 1);
     cvCvtColor( imagen, gris, CV_BGR2GRAY );
     cvResize( gris, Imagen_Pequeña, CV_INTER_LINEAR );
     cvEqualizeHist( Imagen_Pequeña, Imagen_Pequeña );
     
     //Detección si las hay
     cvClearMemStorage( storage );
     CvSeq* objects = cvHaarDetectObjects(
            Imagen_Pequeña,
            cascade,
            storage,
            1.1,
            2,
            0 /*CV_HAAR_DO_CANNY_PRUNING*/,
            cvSize(30, 30));
     //Dibujamos cuadros al rededor de los objetos
     for(int i = 0; i < (objects ? objects->total : 0); i++ ) {
            CvRect* r = (CvRect*)cvGetSeqElem( objects, i );
            cvRectangle(
            imagen,
            cvPoint(r->x,r->y),
            cvPoint(r->x+r->width,r->y+r->height),
            colores[i%8]
            );
     }
     cvReleaseImage( &gris );
     cvReleaseImage( &Imagen_Pequeña );
}

Aquí pongo unas fotografías en las que muestro los resultados.


Bueno, en los próximos días publicare el siguiente reto.  Espero que no me cueste tanto como este je je je.

jueves, 16 de septiembre de 2010

RETO IV: SEGMENTACIÓN DE CARAS EN UNA IMAGEN

Hola a todos muy buenas, siento mucho haberme retrasado a la hora de escribir este nuevo reto.  Para este reto vamos a intentar segmentar las caras de una fotografía, tal como hacen las cámaras de fotos, que para enfocar reconocen el lugar donde hay una cara.  Bueno para esto buscaremos la posición de los ojos.  De momento no puedo decir mucho, ya que solo he investigado un poquito el tema.   Pero bueno, a ver si en una semana lo solucionamos entre todos.