Hola muy buenas a todos, ya se que daba una semana para resolver el primer reto, pero bueno, al final ha sido más fácil de lo que pensaba. Esto ha sido así gracias a que la librería OpenCV ya tiene implementados un montón de algoritmos, y entre ellos el de reconocer círculos en una imagen, el algoritmo de openCVHough para círculos. utilizado es la transformada de
La primitiva de esta función se la siguiente:
CvSeq* cvHoughCircles(
CvArr* image,
void* circle_storage,
int method,
double dp,
double min_dist,
double param1 = 100,
double param2 = 300,
int min_radius = 0,
int max_radius = 0
);
Esta función nos devuelve un puntero con los datos de la circunferencias encontradas, punto central y radio.
Los parámetros son en este orden, la imagen, esto indica si es un array de almacenamiento o una memoria (realmente esto no lo entiendo muy bien, continuaré estudiandolo je je je), el siguiente parámetro es el método, normalmente es CV_HOUGH_GRADIENT, el parámetro dp es la resolución del acumulador, y el siguiente parámetro es la distancia mínima entre dos círculos. Los dos parámetros siguientes son el umbral y el acumulador del umbral del algoritmo de canny, y los dos últimos parámetros son el mínimo y el máximo radio de las circunferencias.
Bien vale, para resolver el problema he seguido esta estructura,
- Leer imagen, la imagen en escala de grises.
- Suavizar imagen
- Aplicar el algoritmo de hough para circulos
- Dibujar los círculos de la imagen.
A parte he leído también la imagen en color para así dibujar los círculos sobre esta, y he tenido que escalarla, ya que si no las imagenes se me hacían muy grandes.
El código en C que he creado es el siguiente:
#include <cv.h>
#include <highgui.h>
#include <math.h>
void main ()
{
int percent=25;
// Leemos la imagen
IplImage* imagen = cvLoadImage("DSCN1882.jpg", CV_LOAD_IMAGE_GRAYSCALE);
//Leemos imagen en color
IplImage* imagenColor = cvLoadImage("DSCN1882.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
100, //Umbral del algoritmo canny
100, //Acumulador del umbral
100, //Minimo radio
200 //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)
);
}
//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 he obtenido es:
 |
Imagen con las dos monedas detectadas |
|
|
Bueno esta es mi solución, os invito a proponer otras soluciones, seguramente mucho mejores que esta.