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");
}
#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");
}
Pelota en el espacio HSV
Pelota segmentada
Pelota localizada
Hola, espero que estes bien.
ResponderEliminarEstuve 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?.
como se debe hacer?
ResponderEliminarespero tu respuesta.
Gracias!!! :)
Hola muy buenas, siento haber tardado tanto en contestar. Bueno si es posible binarizar la imagen con Thesolding utilizando algun metodo como OTSU.
ResponderEliminarLa 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
Gracias por la ayuda!
ResponderEliminarTengo otra duda con respecto a este codigo:
hlower =95;
hupper =105;
que indican estos valores?
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.
ResponderEliminarHola 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
ResponderEliminarMuchas gracias, comentarios como este me animan a seguir con el blog. A ver si pronto puedo publicar alguna cosica.
ResponderEliminar