domingo, 13 de marzo de 2011

RETO IX: OBTENER EL CENTROIDE DE UNA PELOTA

Muy buenas en el próximo reto vamos a volver con las pelotas de frontenis, y vamos a intentar hallar la posición del centroide de las pelotas.  En una primera idea, primero obtendremos como siempre el perímetro de la pelota, para posteriormente calcular el centroide. Bueno como siempre si a alguien se le ocurre formas de como hacerlo que escriba un comentario.

domingo, 6 de marzo de 2011

SOLUCIÓN RETO VIII 2:QUITAR EL FONDO

Bueno al final lo he conseguido.   El método que voy a contar ahora aparece en el libro learning openCV de Gary Bradski &Adrian kaehler, es el Averaging Background Method (Método de promedio de fondo).  Básicamente aprende la media y la desviación estándar de cada pixel como su modelo del fondo.

En primer lugar se crean punteros a varias imágenes de ceros y estadísticas de mantenimiento que se utilizaran a lo largo del proceso.

//Almacenamiento global
//
//Float,Imagen de tres canales
//
IplImage *IavgF,*IdiffF, *IprevF, *IhiF, *IlowF;
IplImage *Iscratch,*Iscratch2;
//Float, Imagen de un canal
//
IplImage *Igray1,*Igray2, *Igray3;
IplImage *Ilow1, *Ilow2, *Ilow3;
IplImage *Ihi1, *Ihi2, *Ihi3;
// Byte, Imagen de un canal
//
IplImage *Imaskt;
//Cuenta el numero de imágenes para la media posterior
//
float Icount;
Después creamos todas estas imagenes, esto lo hacemos con esta función a la cual le pasamos nuestra imagen.

void AllocateImages( IplImage* I )
{
    CvSize sz = cvGetSize( I );
    IavgF = cvCreateImage( sz, IPL_DEPTH_32F, 3 );
    IdiffF = cvCreateImage( sz, IPL_DEPTH_32F, 3 );
    IprevF = cvCreateImage( sz, IPL_DEPTH_32F, 3 );
    IhiF = cvCreateImage( sz, IPL_DEPTH_32F, 3 );
    IlowF = cvCreateImage( sz, IPL_DEPTH_32F, 3 );
    Ilow1 = cvCreateImage( sz, IPL_DEPTH_32F, 1 );
    Ilow2 = cvCreateImage( sz, IPL_DEPTH_32F, 1 );
    Ilow3 = cvCreateImage( sz, IPL_DEPTH_32F, 1 );
    Ihi1 = cvCreateImage( sz, IPL_DEPTH_32F, 1 );
    Ihi2 = cvCreateImage( sz, IPL_DEPTH_32F, 1 );
    Ihi3 = cvCreateImage( sz, IPL_DEPTH_32F, 1 );
    cvZero( IavgF );
    cvZero( IdiffF );
    cvZero( IprevF );
    cvZero( IhiF );
    cvZero( IlowF );
    Icount = 0.00001; //Protección contra la división por cero
    Iscratch = cvCreateImage( sz, IPL_DEPTH_32F, 3 );
    Iscratch2 = cvCreateImage( sz, IPL_DEPTH_32F, 3 );
    Igray1 = cvCreateImage( sz, IPL_DEPTH_32F, 1 );
    Igray2 = cvCreateImage( sz, IPL_DEPTH_32F, 1 );
    Igray3 = cvCreateImage( sz, IPL_DEPTH_32F, 1 );
    Imaskt = cvCreateImage( sz, IPL_DEPTH_8U, 1 );
    cvZero( Iscratch );
    cvZero( Iscratch2 );
}

Después aprendemos la estadística del fondo para más de una imagen. O sea llamaremos esta función unas cuantas veces.  Yo la he llamado unas 500.  Al principio del algoritmo, así aprendemos el fondo.  En este momento no debe aparecer ningún primer plano.
void accumulateBackground( IplImage *I )
{
    static int first = 1; // nb. Not thread safe
    cvCvtScale( I, Iscratch, 1, 0 ); // convert to float
    if( !first )
    {
        cvAcc( Iscratch, IavgF );
        cvAbsDiff( Iscratch, IprevF, Iscratch2 );
        cvAcc( Iscratch2, IdiffF );
        Icount += 1.0;
    }
    first = 0;
    cvCopy( Iscratch, IprevF );
}
Una vez que tenemos acumulados unos cuantos frames, creamos nuestro modelo estadístico del fondo de la imagen.
void createModelsfromStats()
{
    cvConvertScale( IavgF, IavgF,( double)(1.0/Icount) );
    cvConvertScale( IdiffF, IdiffF,(double)(1.0/Icount) );
    //Nos aseguramos de que la diferencia siempre es a alguna cosa
    cvAddS( IdiffF, cvScalar( 1.0, 1.0, 1.0), IdiffF );
    setHighThreshold( 7.0 );
    setLowThreshold( 6.0 );
}

A partir de aquí ya podemos ir evaluando frame a frame nuestras imagenes e ir separando el fondo del primer plano.  Para esto nos crearemos una máscara, negro fondo y blanco primer plano, y a partir de aquí lo podremos utilizar.
void setHighThreshold( float scale )
{
    cvConvertScale( IdiffF, Iscratch, scale );
    cvAdd( Iscratch, IavgF, IhiF );
    cvSplit( IhiF, Ihi1, Ihi2, Ihi3, 0 );
}
void setLowThreshold( float scale )
{
    cvConvertScale( IdiffF, Iscratch, scale );   
    cvSub( IavgF, Iscratch, IlowF );
    cvSplit( IlowF, Ilow1, Ilow2, Ilow3, 0 );
}

Con esta función es con la que creamos la máscara que discrimina el fondo del primer plano.

void backgroundDiff(IplImage *I, IplImage *Imask)
{
   
    cvCvtScale(I,Iscratch,1,0); // To float;
    cvSplit( Iscratch, Igray1,Igray2,Igray3, 0 );
    //Canal 1
    //
    cvInRange(Igray1,Ilow1,Ihi1,Imask);
    //Canal 2
    //
    cvInRange(Igray2,Ilow2,Ihi2,Imaskt);
    cvOr(Imask,Imaskt,Imask);
    //Canal 3
    //
    cvInRange(Igray3,Ilow3,Ihi3,Imaskt);
    cvOr(Imask,Imaskt,Imask);
    //Invertimos el resultado
    //
    cvSubRS( Imask, cvScalar(255,255,255), Imask);   
}
Al final del todo liberamos la memoria de todas las imágenes.

void DeallocateImages()
{
    cvReleaseImage( &IavgF);
    cvReleaseImage( &IdiffF );
    cvReleaseImage( &IprevF );
    cvReleaseImage( &IhiF );
    cvReleaseImage( &IlowF );
    cvReleaseImage( &Ilow1 );
    cvReleaseImage( &Ilow2 );
    cvReleaseImage( &Ilow3 );
    cvReleaseImage( &Ihi1 );
    cvReleaseImage( &Ihi2 );
    cvReleaseImage( &Ihi3 );
    cvReleaseImage( &Iscratch );
    cvReleaseImage( &Iscratch2 );
    cvReleaseImage( &Igray1 );
    cvReleaseImage( &Igray2 );
    cvReleaseImage( &Igray3 );
    cvReleaseImage( &Imaskt);
}

Si queréis saber más sobre este método está descrito en el libro Learning OpenCV de Gary Bradski y Adrian Kaehler