lunes, 11 de julio de 2011

UN POCO DE ELECTRÓNICA

En primer lugar quiero pedir disculpas a todo el mundo que me sigue por haber tardado tanto tiempo en publicar un nuevo post.  Pero por diversos temas no he podido.  Bueno es que me he puesto por mi cuenta, y claro entre unas cosas y otras no he tenido mucho tiempo para escribir, y menos para estudiar temas de visión. Bueno aunque en este post vamos a tratar un tema electrónico, el blog seguirá dando un trato especial a la visión, ya que es un tema del que quiero aprender todo lo que pueda. 
Bueno sin más dilación voy a contar de lo que trata este tema.  En este caso he hecho el control de un teclado matricial, bueno el código del pic esta hecho para detectar la tecla cero, para el resto de los casos es igual.
Para realizar el control del teclado he utilizado un PIC, en mi caso el 24FJ64GA002.  El datasheet de este PIC lo podéis encontrar aquí.  En la siguiente figura se ve el esquema del PIC con el significado de cada una de las 24 patitas, lo he sacado del datasheet.
 El esquema electrónico que he realizado es el siguiente.
En este esquema podemos ver el circuito de reset que está formado por dos resistencias R1 y R2, de 10 k y 100 ohmios respectivamente, este circuito nos mantiene a uno la pata MCLR que es la que resetearía el PIC si se pone a cero.  También podemos ver que hemos puesto condensadores C1 y C2 en las patas de alimentación de 100nF respectivamente, estos condensadores sirven para enviar a tierra todas las corrientes parásitas, ruidos, ect.  En el datasheet también dice que hay que poner un condensador de 10 micro faradios entre las patas 19 y 20, exactamente no se que hace pero lo dice el datasheet.  También podemos ver nuestro circuito que hace de reloj con un cristal de cuarzo y dos condensadores de 30 pF.
Para controlar el teclado matricial hemos conectado 4 patas el pic a las columnas y 4 patas del pic a las filas del teclado matricial. Las filas las hemos conectado también a través de unas resistencias a uno (VDD) de esta forma cuando pongamos una fila columna cero y pulsemos una tecla conectada a esa fila la columna correspondiente pasará a ser cero y así detectaremos la pulsación.
El código del PIC es el siguiente, programado en C.

#include <p24FJ64GA002.h>
#define FCY 40000000UL
#include <libpic30.h>

_CONFIG2(FNOSC_PRI   & FCKSM_CSDCMD & POSCMOD_EC );
_CONFIG1(JTAGEN_OFF & GCP_OFF & GWRP_OFF & BKBUG_ON & COE_ON &ICS_PGx1 & FWDTEN_OFF & WINDIS_OFF & FWPSA_PR128 & WDTPS_PS32768);

int main(int argc, char *argv[])
{
    TRISB=0x83FF;

    LATBbits.LATB14=0;
    LATBbits.LATB13=0;
    LATBbits.LATB12=1;
    LATBbits.LATB11=1;
    LATBbits.LATB10=1;
    while(1)
    {
        if (PORTBbits.RB7==0)
        {
            LATBbits.LATB14=1;
        //    __delay_ms(500);
        }
        else
        {
            LATBbits.LATB14=0;
        }

//        LATBbits.LATB14=1;
//        __delay_ms(500);
//        LATBbits.LATB14=0;
//        __delay_ms(500);

    }
}
Como podemos ver ponemos a uno todas las filas menos una y evaluamos el valor de la columna que lleva la tecla cero si esa columna está a uno entonces no ha sido pulsado si está a cero ha sido pulsado, la tecla cero.
Para controlar todo el teclado será cuestión de hacer un barrido por todas las filas y columnas y así detectar que tecla se ha pulsado. Esto lo dejo para vosotros, más adelante publicaré el código completo para controlar todo el teclado.

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

lunes, 24 de enero de 2011

Trabajo sobre Pixeles

Muy buenas, hace unos días una lectora me ha preguntado como trabajo con pixeles, como aplico la transformada de hough, y estas cosas, pues para esto he decidido crear un nuevo post, en el que explico todo esto.
Cuando aplico la transformada de hough, yo realmente no trabajo sobre pixeles directamente, si no que lo hace OpenCV por mi.  La función que aplico es esta, 
CvSeq* cvHoughCircles(
CvArr* image,    //Imagen en escala de grises
void* circle_storage, //Almacenamiento
int method,   //método
double dp,  //Resolución del acumulador
double min_dist, //Minima distancia entre circulos
double param1 = 100,  //Umbral del algoritmo de Cany
double param2 = 300,  //Acumulador del umbral del algoritmo de canny
int min_radius = 0,  //Minimo radio de un circulo
int max_radius = 0  //Máximo radio de un circulo
);
Esta función me devuelve una secuencia, que son los círculos que tengo en la imagen, para esto introducimos una imagen en blanco y negro (image), a esta imagen el propio algoritmo de la función aplica un detector de bordes y posteriormente la ecuación de la circunferencia par detectar los círculos, los cuales los guarda en el cirgle_storage y yo los capturo con    float* p = (float*) cvGetSeqElem( results, i ); a la cual le paso el CvSeq que me ha devuelta la función cvHoughCircles. Es lo bueno que tiene OpenCV que el trabajo arduo con pixeles viene resuelto.
Bien pero si quiero trabajar directamente con pixeles tenemos que conocer como guarda openCV una imagen, este la guarda como si fuera un vector y para capturar cada pixel necesitamos una serie de datos que estan en la imagen.
Entonces en la imagen tenemos el ancho, el alto, el paso y los canales, esta información la necesitaremos y los propios datos.  Par explicarlo pongo un ejemplo:
IplImage* frame;
height= frame->height;
width= frame->width;
step= frame->widthStep/sizeof(uchar);
channels   = frame->nChannels;
data = (uchar *)frame->imageData;
for(i = 0; i <height; i++ ) {
          for(j = 0; j <width; j++ ) {
                        data_hsv[i*step+j*channels+1]
                        data_hsv[i*step+j*channels+2]
                        data_hsv[i*step+j*channels+3]

}
}
donde 1 2 y 3 es el RGB de color.

     
 

jueves, 6 de enero de 2011

SOLUCIÓN RETO VIII:QUITAR EL FONDO

Después de haber trabajado mucho en esto me doy por rendido, he intentado aplicar muchas soluciones pero ninguna me daba buenos resultados, la que mejor me ha parecido aunque iba muy lento en mi ordenador de forma que era inviable, me imagino que por el poder de cálculo de mi ordenador, es un método que utiliza codebook, página 278 del libro learning OpenCV, si ha alguno se le ocurre otras mejores formas de utilizar éste o cualquier otro método que me lo diga.  Yo sigo trabajando en esto paralelamente a otros retos que iremos resolviendo, cuando tenga algo mejor lo publicaré.

jueves, 2 de diciembre de 2010

RETO VIII:QUITAR EL FONDO

En este reto vamos a quitar el fondo a nuestras imágenes de la cámara, ayudados por las funciones de OpenCV que realizan esto mismo.  Esto lo haremos buscando las similitudes entre píxeles.  De esta forma quitaremos de nuestra imagen todos los elementos que no nos importan, en el caso que llevamos en estos días son todos los elementos que no son caras, y que pertenecen al fondo de la imagen.  En mi caso a veces tenemos falsos positivos con partes del fondo, en concreto unos cuadros que hay en la pared de mi cuarto, si antes de intentar detectar caras los suprimo de la imagen pues ya no me darán un falso positivo, bueno o eso espero je je je.

sábado, 27 de noviembre de 2010

SOLUCIÓN RETO VII: CONTAR CARAS

Muy buenas, ya tenemos una solución al reto de contar caras.  Para esta solución me he modificado la estructura,y en vez de utilizar listas he utilizado un vector.  La estructura es la siguiente:

struct Rostro {
    bool HayCara; // Número total de elementos
    centro Pcentral;
    IplImage* Cara;  //Aqui guardamos la cara
    bool HayConincidencia;
    int ContadorCoincidencia;
    int ContadorNoCoincidencia;

};


Esta estructura contiene las siguientes variables hay cara, para saber si hay cara en esa posición del vector o no, Pcentral para conocer el punto central de la cara, Cara es la imagen de la cara, realmente esta para contar caras no lo necesitamos, pero por si en un futuro queremos visualizarla.  HayCoincidencia es una variable que me dice si anteriormente ha estado esta cara, en base a su posición.  Contador de NoCoincidencia es por si no está, y antes si.  Esto es por si nos pierde la imagen por lo que sea que no se olvide de esa cara.
Pcentral es una variable de tipo centro.  Este tipo lo he definido así:

struct centro{
    int x;
    int y;
};

Principalmente en el algoritmo lo que hacemos es lo siguiente. 
  1. Comparamos la distancia entre la imagen anterior y la imagen actual, si es menor a un determinado valor suponemos que es la misma.
  2. Si es la  misma contamos una coincidencia.
  3. Sin no es la misma contamos una no coincidencia
  4. Si hay un numero de veces coincidencia es una cara que nos interesa.
  5. Si ha pasado varias veces y no hay coincidencia suponemos que se ha ido la cara.

El algoritmo en lenguaje C es el siguiente:

           for( l = 0; l < 10; l++ )
            {
                //Comprobamos si hay coincidencia, si hay coincidencia y esta se ha repetido una serie de veces contamos cara

                for( m = 0; m < 10 ; m++ )
                {  
                        if ((ListaRostros[l].HayCara==true)&& (ListaRostrosAnterior[m].HayCara==true))
                        {
                            if(((((ListaRostros[l].Pcentral.x-ListaRostrosAnterior[m].Pcentral.x)^2+(ListaRostros[l].Pcentral.y-ListaRostrosAnterior[m].Pcentral.y)^2)^(1/2))<750)&& !ListaRostros[l].HayConincidencia&& !ListaRostrosAnterior[m].HayConincidencia)
                            {
                                ListaRostros[l].HayConincidencia=true;
                                ListaRostrosAnterior[m].HayConincidencia=true;
                                ListaRostros[l].ContadorCoincidencia=ListaRostrosAnterior[m].ContadorCoincidencia+1;
                                if (ListaRostros[l].ContadorCoincidencia==5)
                                {
                                    NumeroCaras++;
                                }
                            }
                        }
                       
                }
                //Si no hay coincidencia contamos
                if (!ListaRostros[l].HayConincidencia)
                {
                    ListaRostros[l].ContadorCoincidencia=0;
                    ListaRostros[l].ContadorNoCoincidencia++;

                }
               
            }
            //Si no hay coincidencia y esta es menor de 3 entonces guardamos la imagen
            for( m = 0; m < 10; m++ )
            {
                if (!ListaRostrosAnterior[m].HayConincidencia)
                {
                    ListaRostrosAnterior[m].ContadorNoCoincidencia++;
                    ListaRostrosAnterior[m].ContadorCoincidencia=0;
                    if (ListaRostrosAnterior[m].ContadorNoCoincidencia<3)
                    {
                        l=0;
                        while ((ListaRostros[l].HayCara)&& l<10) l++;
                        if (l<10) ListaRostros[l]=ListaRostrosAnterior[m];

                    }
                }
                   
            }
            //Copiamos la imagen en imagen anterior
            for( i = 0; i < 10; i++ )
            {
                ListaRostrosAnterior[i]=ListaRostros[i];
            }

Los resultados de este algoritmo no los he evaluado todavía, tasas de acierto y de fallo.  Parece que funciona bastante bien, aunque habría que mejorarlo, sobre todo en los falsos positivos, a veces me reconoce caras donde no las hay, para esto miraremos algoritmos que nos quitan el fondo con openCV.  También comentar que el algoritmo es parametrizable, podemos cambiar la distancia para comprobar si es la misma cara, y el numero de veces que cuenta coincidencia y el numero de veces que no.
Pongo un vídeo


En le vídeo podemos ver, que reconoce una vez y me cuenta una sola vez.  También se ve la aparición de falsos positivos, esto nos puede fastidiar un poco la cuenta, hay que ver como los eliminamos, probaremos, eliminando el fondo.  Además luego desaparezco de la imagen y vuelvo, como me he ido y el vuelto pues me cuenta otra vez.

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.

domingo, 12 de septiembre de 2010

SOLUCIÓN RETO III: HACER MÁS FIABLE EL RECONOCIMIENTO DE LA PELOTA

Muy buenas a todos,  ya he conseguido hacer más robusto el algoritmo, para ello, reconocemos el color amarillo de la imagen y la segmentamos.

Para encontrar el color amarillo, nos hemos basado en el espacio de color HSV, del ingles Hue, Saturation, Value -Tonalidad, Saturación, Valor.  

La tonalidad nos representa el tipo del color, por ejemplo el rojo es 0, el verde 120.
La saturación representa la pureza del color, contra menos es su valor menos tonalidad de gris tendrá.
El valor del color es el brillo.

El algoritmo que nos queda es el siguiente:  Primero pasamos la imagen al espacio HSV, después buscamos los puntos donde tenemos el color de nuestra pelota y en una imagen creada en blanco y negro vamos poniendo valor blanco para los puntos de nuestra pelota y valor negro para el resto.  Después dibujamos nuestro circulo donde tenemos la pelota, para esto he utilizado el algoritmo de hough, aunque lo podríamos haber hecho de muchas maneras, esta ya la tenia hecha. 

El código es el siguiente
#include <cv.h>
#include <highgui.h>
#include <math.h>

void main()
{

    cvNamedWindow("Reco Pelota HSV",CV_WINDOW_AUTOSIZE);  //Creamos una ventana
    cvNamedWindow("Reco Pelota RGB",CV_WINDOW_AUTOSIZE);
    cvNamedWindow("Reco Pelota Binearizada",CV_WINDOW_AUTOSIZE);
    cvNamedWindow("Reco Pelota Segmentada",CV_WINDOW_AUTOSIZE);

    CvCapture* capture= cvCreateCameraCapture(0);    //Capturamos la cámara
    assert( capture != NULL ); 
    IplImage* frame;
    IplImage* frameBN;
    IplImage* frameHSV;
    IplImage* frameSeg;


    int height , width , step , channels , k = 1;
    int step_bn , channels_bn;
    int step_fra , channels_fra;
    int step_seg , channels_seg;
    uchar *data_hsv , *data_bn, *data, *data_seg;
    int i,j;

    int percent=200;
    CvSeq* results;
    bool Primera= true;

    int hlower =95;
    int hupper =105;
    int Saturation=0;
    int Brightness=0;


    int Dilate = 10;
    int Erode = 5;

   
    while(1)
    {
        frame=cvQueryFrame(capture);  //Capturamos la primera imagen
        if(!frame) break;  //Si no hay frame salimos
        if (Primera)
        {
            //Creamos la imagen
            frameHSV = cvCreateImage( cvGetSize(frame), IPL_DEPTH_8U, 3 );
            frameBN = cvCreateImage( cvGetSize(frame), IPL_DEPTH_8U, 1 );
            frameSeg=cvCreateImage( cvGetSize(frame), IPL_DEPTH_8U, 3 );

            // Obtenemos atributos de la imagen HSV
            height= frameHSV->height;
            width= frameHSV->width;
            step= frameHSV->widthStep/sizeof(uchar);
            channels   = frameHSV->nChannels;

            step_bn   = frameBN->widthStep/sizeof(uchar);
            channels_bn = frameBN->nChannels;

            step_fra   = frame->widthStep/sizeof(uchar);
            channels_fra = frame->nChannels;

            step_seg   = frameSeg->widthStep/sizeof(uchar);
            channels_seg = frameSeg->nChannels;
   
            //Creamos el lugar donde se almacenaran los circulos en el algoritmo de hough
            CvMemStorage* storage = cvCreateMemStorage(0);

            Primera=false;
        }
        cvCvtColor(frame,frameHSV,CV_RGB2HSV);
       
        // Obtenemos los valores RGB de la Imagen
        data_hsv = (uchar *)frameHSV->imageData;
        data_bn = (uchar *)frameBN->imageData;
        data = (uchar *)frame->imageData;
        data_seg=(uchar *)frameSeg->imageData;

        // Recorremos la imagen
        for(i = 0; i <height; i++ ) {
          for(j = 0; j <width; j++ ) {

                if (((data_hsv[i*step+j*channels])>= hlower)&& ((data_hsv[i*step+j*channels]) <= hupper)){
                    if (data_hsv[i*step+j*channels+1]>= Saturation) {
                        if (data_hsv[i*step+j*channels+2]>= Brightness) {
                            // Coloreamos el pixel en blanco
                            data_bn[i*step_bn+j*channels_bn] =255;

                            data_seg[i*step_seg+j*channels_seg] = data[i*step_fra+j*channels_fra] ;
                             data_seg[i*step_seg+j*channels_seg+1] = data[i*step_fra+j*channels_fra+1];
                            data_seg[i*step_seg+j*channels_seg+2] = data[i*step_fra+j*channels_fra+2];

                        }
                        else
                        {
                            // Coloreamos el pixel en negro
                             data_bn[i*step_bn+j*channels_bn] = 0;
                           
                             data_seg[i*step_seg+j*channels_seg] = 0;
                             data_seg[i*step_seg+j*channels_seg+1] = 0;
                             data_seg[i*step_seg+j*channels_seg+2] = 0;


                        }
                    }
                    else{
                        // Coloreamos el pixel en negro
                        data_bn[i*step_bn+j*channels_bn] = 0;

                          data_seg[i*step_seg+j*channels_seg] = 0;
                        data_seg[i*step_seg+j*channels_seg+1] = 0;
                        data_seg[i*step_seg+j*channels_seg+2] = 0;

                    }
                }
                else{
                    // Coloreamos el pixel en negro
                     data_bn[i*step_bn+j*channels_bn] = 0;

                     data_seg[i*step_seg+j*channels_seg] = 0;
                     data_seg[i*step_seg+j*channels_seg+1] = 0;
                     data_seg[i*step_seg+j*channels_seg+2] = 0;
                }
          }
        }
        // Erocionar y Dilatar , para la eliminacion de pixeles perdidos
        cvErode(frameBN,frameBN,0,Erode);
        cvDilate( frameBN,frameBN,0,Dilate);

        cvErode(frameSeg,frameSeg,0,Erode);
        cvDilate( frameSeg,frameSeg,0,Dilate);



       

        //Suavizamos la imagen
        //cvSmooth(frameBN, frameBN, CV_GAUSSIAN, 5, 5 );
        //Aplicamos el algoritmo de Hough para circulos
        results = cvHoughCircles(
            frameBN,        //Imagen en escala de grises, no hace falta aplicar ni canny ni sobel ya que lo invoca la función
            storage,            //El lugar donde almacena circulos
            CV_HOUGH_GRADIENT,    //El metodo
            1,                    //Resolución del acumulador
            frameBN->width/10,    //Mínima distancia entre dos circulos
            12,                //Umbral del algoritmo canny
            12,                //Acumulador del umbral
            1,                //Minimo radio
            300                    //Máximo radio
            );
        //Dibujamos las circunferencias de color rojo en la imagen en color
        for( int i = 0; i < results->total; i++ ) {
            float* p = (float*) cvGetSeqElem( results, i );
            CvPoint pt = cvPoint( cvRound( p[0] ), cvRound( p[1] ) );
            cvCircle(
                frame,
                pt,
                cvRound( p[2] ),
                CV_RGB(0xff,0x00,0x00),
                2);
            }
       

        cvShowImage("Reco Pelota HSV",frameHSV);
        cvShowImage("Reco Pelota RGB",frame);
        cvShowImage("Reco Pelota Binearizada",frameBN);
        cvShowImage("Reco Pelota Segmentada",frameSeg);

        char c = cvWaitKey(33);
        if(c==27) break;
    }
    cvReleaseCapture(&capture);
    cvDestroyWindow("Reco Pelota HSV");
    cvDestroyWindow("Reco Pelota RGB");
    cvDestroyWindow("Reco Pelota Binearizada");
    cvDestroyWindow("Reco Pelota Segmentada");


}

Los vídeos que dejo son: Pelota binearizada
Pelota en el espacio HSV


Pelota segmentada


Pelota localizada

martes, 7 de septiembre de 2010

RETO III: HACER MÁS FIABLE EL RECONOCIMIENTO DE LA PELOTA

Bueno, como se puede ver en el vídeo de la solución del reto II, el reconocimiento de la pelota no es muy fiable, en cuanto nos movemos un poco empieza a reconocer otras cosas que no son la pelota, o no reconoce la pelota.  En este reto vamos a intentar decirle al ordenador, que es una pelota y que no es:  En nuestro caso, vemos que la pelota es de color amarillo, con lo cual, vamos a intentar reconocer todo lo que es amarillo dentro de los círculos que encontremos en el algoritmo de hough, de esta forma descartaremos todo lo que no es una pelota.

Parece fácil, pero lo de hacer más robusto un algoritmo, a mi juicio es lo más complicado.  Pero no por esto vamos a dejar de intentarlo.  
Como siempre en este blog se aceptan sugerencias.


domingo, 5 de septiembre de 2010

SOLUCIÓN RETO II: RECONOCER UNA PELOTO Y SEGUIRLA

Este reto me está costando más de lo que me había planteado en un principio, pero bueno voy a publicar las soluciones que tengo hasta ahora.  De momento no sigo a la pelota pero la reconozco tanto en una foto como en un con la cámara.  Esto lo hago de la misma manera que el reto uno, con el algoritmo de hough.  
Para la fotografía este el  código que he hecho, como siempre he utilizado las librerías de OpenCV:

#include <cv.h>
#include <highgui.h>
#include <math.h>
void main ()
{   
    int percent=25;
    // Leemos la imagen
    IplImage* imagen = cvLoadImage("DSCN1886.jpg", CV_LOAD_IMAGE_GRAYSCALE);
    //Leemos imagen en color
    IplImage* imagenColor = cvLoadImage("DSCN1886.jpg");
    //Tenemos que reducir el tamaño de la imagen
    IplImage* imagenPequeña =cvCreateImage(cvSize((int)((imagen->width*percent)/100) , (int)((imagen->height*percent)/100) ),imagen->depth, imagen->nChannels );
    cvResize(imagen,imagenPequeña,CV_INTER_NN);
    //Lo mismo con la imagen en color   
    IplImage* imagenPequeñaColor =cvCreateImage(cvSize((int)((imagenColor->width*percent)/100) , (int)((imagenColor->height*percent)/100) ),imagenColor->depth, imagenColor->nChannels );
    cvResize(imagenColor,imagenPequeñaColor,CV_INTER_NN);

    //Creamos el lugar donde se almacenaran los circulos en el algoritmo de hough
    CvMemStorage* storage = cvCreateMemStorage(0);
    //Suavizamos la imagen
    cvSmooth(imagenPequeña, imagenPequeña, CV_GAUSSIAN, 5, 5 );
    //Aplicamos el algoritmo de Hough para circulos
    CvSeq* results = cvHoughCircles(
        imagenPequeña,        //Imagen en escala de grises, no hace falta aplicar ni canny ni sobel ya que lo invoca la función
        storage,            //El lugar donde almacena circulos
        CV_HOUGH_GRADIENT,    //El metodo
        2,                    //Resolución del acumulador
        imagenPequeña->width/10,    //Mínima distancia entre dos circulos
        150,                //Umbral del algoritmo canny
        150,                //Acumulador del umbral
        80,                //Minimo radio
        140                    //Máximo radio
        );
    //Dibujamos las circunferencias de color rojo en la imagen en color
    for( int i = 0; i < results->total; i++ ) {
        float* p = (float*) cvGetSeqElem( results, i );
        CvPoint pt = cvPoint( cvRound( p[0] ), cvRound( p[1] ) );
        cvCircle(
            imagenPequeñaColor,
            pt,
            cvRound( p[2] ),
        CV_RGB(0xff,0x00,0x00),
        2

        );
    }
    //Creamos la ventana
    cvNamedWindow( "Imagen", CV_WINDOW_AUTOSIZE );
    //Dibujamos la imagen
    cvShowImage( "Imagen", imagenPequeñaColor);

    //Esperamos indefinidamente hasta que se cierre la ventana
    cvWaitKey(0);
    //Borramos las imagen
    cvReleaseImage(&imagen);
    cvReleaseImage(&imagenColor);
    cvReleaseImage(&imagenPequeñaColor);
    cvReleaseImage(&imagenPequeña);
    //Destruimos la ventana
    cvDestroyWindow("Imagen");
}

La imagen que me dio fue la siguiente:
Solución, encontrar las pelotas en una foto.





Lo que más trabajo me está dando es la parte de la cámara, como no tengo nada para dejar la cámara fija, ya que la distancia de la cámara al plano de la pelota es muy importante, ya veis que en el la función que llama al algoritmo de hough le digo el mínimo y el máximo radio de la pelota, pues bien este cambia si me acerco con la cámara o me alejo.  En siguientes retos intentaremos solucionar este problemilla.  
#include <cv.h>
#include <highgui.h>
#include <math.h>

void main()
{
    int width;
    int height;
    cvNamedWindow("Reco Pelota",CV_WINDOW_AUTOSIZE);  //Creamos una ventana
    CvCapture* capture= cvCreateCameraCapture(0);    //Capturamos la cámara
    assert( capture != NULL ); 
    IplImage* frame;
    IplImage* frameBN;
    IplImage* frameGrande;
    int percent=200;
    while(1)
    {
        frame=cvQueryFrame(capture);  //Capturamos la primera imagen
        if(!frame) break;  //Si no hay frame salimos
        //Tenemos que ampliar el tamaño de la imagen
        frameGrande =cvCreateImage(cvSize((int)((frame->width*percent)/100) , (int)((frame->height*percent)/100) ),frame->depth, frame->nChannels );
        cvResize(frame,frameGrande,CV_INTER_NN);
        //Transformamos la imagen en BN
        width = frameGrande->width; 
        height = frameGrande->height;
        frameBN = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U,1); 
        cvCvtColor(frameGrande,frameBN,CV_RGB2GRAY);//Transformamos la imagen en blanco y negro
        //Creamos el lugar donde se almacenaran los circulos en el algoritmo de hough
        CvMemStorage* storage = cvCreateMemStorage(0);
        //Suavizamos la imagen
        cvSmooth(frameBN, frameBN, CV_GAUSSIAN, 5, 5 );
        //Aplicamos el algoritmo de Hough para circulos
        CvSeq* results = cvHoughCircles(
            frameBN,        //Imagen en escala de grises, no hace falta aplicar ni canny ni sobel ya que lo invoca la función
            storage,            //El lugar donde almacena circulos
            CV_HOUGH_GRADIENT,    //El metodo
            2,                    //Resolución del acumulador
            frameBN->width/10,    //Mínima distancia entre dos circulos
            50,                //Umbral del algoritmo canny
                                50,                //Acumulador del umbral
            150,                //Minimo radio
            200                    //Máximo radio
            );

        for( int i = 0; i < results->total; i++ ) {
            float* p = (float*) cvGetSeqElem( results, i );
            CvPoint pt = cvPoint( cvRound( p[0] ), cvRound( p[1] ) );
            cvCircle(
                frameGrande,
                pt,
                cvRound( p[2] ),
                CV_RGB(0xff,0x00,0x00),
                2);
            }

        cvShowImage("Reco Pelota",frameGrande);
        char c = cvWaitKey(33);
        if(c==27) break;
    }
    cvReleaseCapture(&capture);
    cvDestroyWindow("Reco Pelota");
}

Más adelante iré mejorando esta solución .  Lo que hay que mejorar es que siempre detecte la pelota, o sea hacer algo para que mi cámara este quieta, je je je.  Hay que tener en cuenta que la iluminación también es muy importante, otra cosa que no controlo desde mi despacho.  También tendríamos que implementar algoritmos para decirle que hay círculos que no son la pelota, falsos positivos.   Dejo el vídeo a continuación.
En retos posteriores, intentaremos mejorar este algoritmos con otras técnicas.