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.

2 comentarios:

  1. Hola. soy el Juan Carlos de la bici. La verdad es que tienen buena pinta los artículos el problema es que yo programo un poquito en ASM y tardaré un poco en aprender C pero así tendré un aliciente para estudiar.

    ResponderEliminar
  2. Muy buenas Juan Carlos, eso está bien siempre es bueno tener alicientes para aprender. Y no te preocupes tengo en mente hacer algo que algun PIC algun dia, en esa nos podras enseñar.

    ResponderEliminar