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

7 comentarios:

  1. Hola, espero que estes bien.
    Estuve revisando tu codigo y queria saber si es posible binarizar la imagen con Thresolding, utilizando algun metodo como OTSU y asi obtener el histograma de la imagen de la pelota?.

    ResponderEliminar
  2. como se debe hacer?
    espero tu respuesta.
    Gracias!!! :)

    ResponderEliminar
  3. Hola muy buenas, siento haber tardado tanto en contestar. Bueno si es posible binarizar la imagen con Thesolding utilizando algun metodo como OTSU.
    La definición de treshold es
    double cvThreshold(
    CvArr* Src,
    CvArr* dst,
    double threshold,
    double max_value,
    int threshold_type
    );
    El threshold_type puede ser
    CV_THRESH_BINARY
    CV_THRESH_BINARY_INV
    CV_THRESH_TRUNC
    CV_THRESH_TOZERO_INV
    CV_THRESH_TOZERO
    Buscando por internet he visto que puede haber otro parámetro, en el libro de OpenCV que tengo yo no aparece pero lo pongo, todo es probar.
    THRESH_OTSU

    ResponderEliminar
  4. Gracias por la ayuda!

    Tengo otra duda con respecto a este codigo:

    hlower =95;
    hupper =105;
    que indican estos valores?

    ResponderEliminar
  5. Muy buenas de nuevo. Como sabras el espacio de color se puede representar de muchas maneras, con el RGB; Rojo, verde y Azul. HSV; tonalidad, saturación y brillo. Pues muy bien la H o sea la tonalidad es el color, y nosotros estamos buscando un color de tonalidad amarilla, pues eso son los valores, la tonalidad de la pelota esta entre esos valores.

    ResponderEliminar
  6. Hola sabes me llamo emanuel y soy de Ingenieria en computacion, me llamo mucho la atencion tu reto, y ahora bien si lo encontre es por algo o no jaja, bueno en si si ya que mi proyecto es sobre eso deteccion de una figura, y junto con ella seguirla, asi bien una vez encontrada es pintarla, pero creeeme que tu programa me fue de mucha ayuda. GRACIAS. Y que buen blog

    ResponderEliminar
  7. Muchas gracias, comentarios como este me animan a seguir con el blog. A ver si pronto puedo publicar alguna cosica.

    ResponderEliminar