Jesus Llor

Phd - Solution Architect

Calibrar una cámara con OpenCV: Fácil, sencillo y …

admin - 24/04/2008

1.- Imágenes origen:

En primer lugar se realizan una serie de fotos con la cámara que vamos a utilizar. Las fotos se realizaran a un patrón, este patrón será un tablero de ajedrez, con un determinado número de cuadrados, en este caso 8×8, aunque se podrían realizar con otras combinaciones (Por ejemplo, 8×7, 5×4). Las fotos serán realizadas desde distintos ángulos para poder así realizar un buen calibrado de la imagen.

Las fotos de origen utilizadas son las siguientes:










2.- Find chess corner:

A continuación se crean una imagen a para cada imagen original a la que llamaremos “findChessBoardCornerGuesses”, que buscara los puntos interiores en el tableros y los marcara con colores



El resultado para el resto de imágenes:










3.- Backprojecting:

Puesto que el “cvCalibrate()” devuelve no solamente los parámetros de la cámara fotográfica, sino que también aporta la información de rotación/translación para cada imagen, podemos proyectar fácilmente coordenadas de la escena 3D en la imagen. Esto es una prueba muy buena si los datos de la calibración están correctos, puesto que backprojected los puntos de la esquina de la escena se deben emparejar los que están en la imagen.

4.- Undistorting:

En el backprojection se asume que la imagen no está torcida. Para computar imágenes no deformadas se pasan los parámetros obtenidos de la cámara fotográfica en una función de OpenCV llamada “cvUndistortOnce()”. Esta función combina la imagen de una manera que las líneas paralelas en la imagen son paralelas en la escena.



Cada cámara es diferente, por lo que los resultados de la calibración pueden distar mucho de unas cámaras a otras. Los resultados obtenidos han sido:

Coeficientes de distorsión = [ -0.1866 0.4870 0.0038 0.0014 ]

Matriz Cámara =

801.1770 0.0000 310.3586

0.0000 806.3867 261.5120

0.0000 0.0000 1.0000

El código fuente utilizado es el siguiente:

Llamada a la función:

calibrar(“C:/Calibracion/88new”,”.jpg”,9,7,7);

Parametro1: Directorio donde se encuentran las imágenes originales. (Ex. “C:/Calibracion/88new”)

Parametro2: Extensión del formato de imagen. (Ex. “.jpg”,”.bmp”, …)

Parametro3: Número de imágenes utilizadas. (Ex. “9”)

Parametro 4 y 5:Número de cuadrados del tablero de ajedrez en horizontal y vertical -1. ( Ex. En este caso es un tablero de 8×8 por lo que hay que introducir 7 y 7)

Función:

int calibrar(char* b,char* e,int nI,int xc,int yc )
{
int xCorners = 0;
int yCorners = 0;
int iImg;
int width;
int height;
int numImages=nI;

sprintf(base, “%s”, b);
sprintf(end, “%s”, e);
numImages = nI;
xCorners = xc;
yCorners = yc;

printf(“Calibrando …\n”);

cornersArray = new CvPoint2D32f[xCorners*yCorners*numImages];
corners3d = new CvPoint3D32f[xCorners*yCorners*numImages];
int *cornersFound = new int[numImages];
distortion = new float[4];
camera_matrix = new float[9];
translation_vectors = new float[3*numImages];
rotation_matrices = new float[9*numImages];
InitCorners3d(corners3d, xCorners, yCorners, numImages);
cvNamedWindow(“Image”, 0);
cvNamedWindow(“Undistorted”, 0);
CvPoint2D32f *nextCornersArray = cornersArray;

for (iImg = 0; iImg < numImages; iImg++)
{
// Load all the new images into there data structures
sprintf(filename, “%s%d%s”, base, iImg, end);
printf(“Procesando imagen imagen: %s\n”,filename);
inicio = cvLoadImage(filename);
width = inicio->width;
height = inicio->height;
if (inicio->depth != IPL_DEPTH_8U)
{
fprintf(stderr, “image, %s, depth not IPL_DEPTH_8U\n”,filename);
exit(1);
}

bw = cvCreateImage(cvSize(inicio->width, inicio->height),IPL_DEPTH_8U, 1);
cvConvertImage(inicio, bw);
threshold = cvCreateImage(cvSize(inicio->width,inicio->height), IPL_DEPTH_8U, 1);
// Feedback of images loaded for sanity
cvShowImage(“Image”, inicio);
// Start calibration of points
cornersFound[iImg] = xCorners * yCorners;
// nextCornersArray is a pointer into cornersArray to the lastslot open
// this will overflow if cvchessboard ever gives us morecorners then expected
int bResult = cvFindChessBoardCornerGuesses(bw, threshold,NULL, cvSize(xCorners, yCorners), nextCornersArray, &cornersFound[iImg]);
cvFindCornerSubPix(bw, nextCornersArray, cornersFound[iImg],cvSize(5,5), cvSize(-1,-1), cvTermCriteria(CV_TERMCRIT_ITER, 100, 0.1));
// more visual feedback to confirm correctness
DrawCircles(inicio, nextCornersArray, cornersFound[iImg],“Image”, -1);
cvShowImage(“Image”, inicio);
sprintf(filename, “%s%d%s%s”, base, iImg,“_findChessBoardCornerGuesses”, end);
cvSaveImage(filename, inicio);
if (!bResult)
{
fprintf(stderr, “Did not find expected number of points… bailing\n”);
cvWaitKey(0);
}
printf(“Fin imagen: %s\n”,filename);
cvWaitKey(0);

// update so that nextCornersArray points to the next open slotin cornersArray
nextCornersArray += cornersFound[iImg];
cvReleaseImage(&inicio);
cvReleaseImage(&bw);
cvReleaseImage(&threshold);
}
cvCalibrateCamera(numImages, cornersFound, cvSize(width, height),
cornersArray, corners3d, distortion, camera_matrix,
translation_vectors,
rotation_matrices, 0);
PrintIntrinsics(distortion, camera_matrix);
CvPoint2D32f *backprojPts2d = ConvertWorldToPixel(corners3d, numImages,
cornersFound, camera_matrix, translation_vectors, rotation_matrices);
for (iImg = 0; iImg < numImages; iImg++)
{
// Load all the new images into there data structures
sprintf(filename, “%s%d%s”, base, iImg, end);
inicio = cvLoadImage(filename);
undistort = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U,3);
cvUnDistortOnce(inicio, undistort, camera_matrix, distortion, 1);
DrawCircles(inicio, &backprojPts2d[iImg*cornersFound[iImg]],cornersFound[iImg], “Image”, 1);
DrawCircles(undistort, &backprojPts2d[iImg*cornersFound[iImg]], cornersFound[iImg], “Undistorted”, 1);
sprintf(filename, “%s%d%s%s”, base, iImg, “_backproject”, end);
cvSaveImage(filename, inicio);
sprintf(filename, “%s%d%s%s”, base, iImg,“_backproject_undistorted”, end);
cvSaveImage(filename, undistort);
cvWaitKey(0);
cvReleaseImage(&inicio);
cvReleaseImage(&undistort);
}
cvWaitKey(0);
cvDestroyWindow(“Image”);

delete cornersArray;
delete corners3d;
delete cornersFound;
delete distortion;
delete camera_matrix;
delete translation_vectors;
delete rotation_matrices;

printf(“Fin Calibración”);
return 0;
}

Nota:

Este ejemplo ha sido realizado en Visual Studio 2005 con las librerías de OpenCV para saber como configurarlo para poder usar este código visita el articulo Configurar OpenCV para usar en Visual Studio 2005

Be Sociable, Share!

35 Comments

  1. Roberto viernes,26 septiembre, 2008 at 13:08

    Hola Jesús,
    He estado leyendo tu blog y me parece muy interesante.
    Te quería pedir ayuda con un problema que tengo con Opencv a ver si tu puedes solucionarmelo.
    Estoy trabajando con OpenCV bajo Windows para mi proyecto fin de carrera, ya tengo bastante avanzado el programa, pero ahora quería capturar video a partir de una webcam en vez de archivo como hasta ahora.
    He probado programas sencillos de captura de imágenes por webcam pero no me funciona ninguno.
    Se abre una nueva ventana en gris y se enciende la lucecita de la cámara como para empezar a funcionar, pero se cierra la ventana y finaliza.
    Mi cámara es una PHILIPS SPC210NC.
    Si puedes echarme una mano te lo agradecería mucho.
    Saludos

  2. Lennin sábado,27 septiembre, 2008 at 1:05

    Hola, está bueno tu blog, lástima que no lo viera antes de pelear tanto con las funciones de calibración de Opencv!
    También tengo unas dudas, para mi trabajo de grado pretendo hacer una navegación visual. Ya he calibrado mi cámara con un código parecido, aunque aqui llega la primera, veo que en tu código los valores de corners3d son iguales a los de cornersArray, esto a que se debe? ¿puedo ingresarle los valores XYZ reales? pues yo ennumeré las casillas de la matriz (corners3d)para X e Y con Z constante y parece que funciona.

  3. admin lunes,29 septiembre, 2008 at 9:26

    Para Roberto:

    He estado mirando y no se como lo estarás intentando pero yo cogí las imagenes directamente desde la webcam sin necesidad de programas adicionales. Creo recordar que se listan las webcam conectadas al ordenador, en mi caso con solo una conectada en la función “capture = cvCaptureFromCAM( 0 );” yo indicaba 0. Pero recuerda que tomar algo de la webcam es como coger fotogramas uno a uno por eso tengo un “for(;;)” que va cogiendo imagenes sin parar “frame = cvRetrieveFrame(capture);” hasta que halla un break. Tal vez lo que te pase es que solo estes cogiendo la primera imagen sea y ya no vuelvas a pedir más. No se si alguna de estas cosas sera tu problema. te pongo el codigo fuente de mi funcion webcam haber si con eso te aclaras mejor. Decirte que yo utilize el EYE TOY de la playstation como webcam, por lo que con cualquiera supongo debería funcionar bien.

    int webcam(int argc, _TCHAR* argv[])
    {

    IplImage *img = 0,*frame,*image = 0;;
    bool primera =true,renovar=false;
    CvCapture* capture;

    printf(“procesando imagen …\n”);
    printf( “Opciones: \n\tr – Renovar imagen inicial\n”);
    printf( “\td – Guardar tamaño en Array distancias\n”);
    printf( “\tv – Mostrar Array distancias\n”);
    printf( “\tESC – Salir del programa\n”);
    capture = cvCaptureFromCAM( 0 );

    cvCreateTrackbar( “alfa”, “Webcam”, &alfa, 200, 0);
    cvSetMouseCallback( “Webcam”, on_mouse, 0 );

    for(;;)
    {

    if(!cvGrabFrame(capture)){ // capture a frame
    printf(“Could not grab a frame\n\7”);
    exit(0);
    }

    frame = cvRetrieveFrame(capture); // retrieve the captured frame

    if( !frame )
    break;
    if( !image )
    image = cvCreateImage( cvSize(frame->width,frame->height),
    IPL_DEPTH_8U, frame->nChannels );
    if( frame->origin == IPL_ORIGIN_TL )
    cvCopy( frame, image, 0 );
    else
    cvFlip( frame, image, 0 );

    if(primera == true || renovar == true)
    {
    original = cvCreateImage( cvSize(frame->width,frame->height),
    IPL_DEPTH_8U, frame->nChannels );
    primera = false;
    renovar = false;
    time(&tiempo);
    printf(“Imagen original en tiempo: %s”,ctime(&tiempo));
    cvCopy(image,original, 0 );
    }

    cvCopy(draw(original,image,alfa),image);

    cvShowImage( “Webcam”, image );
    car = cvWaitKey(10);
    if( (char)car == 27 )
    break;
    switch( (char) car )
    {
    case ‘r’:
    renovar = true;
    break;
    case ‘d’:
    distancia(5);
    printf(“Distancia guardada en tiempo: %s”,ctime(&tiempo));
    break;
    case ‘v’:
    verDist();
    break;
    default:
    ;
    }

    }
    cvReleaseImage(&image);
    cvReleaseCapture(&capture);

    return 0;
    }

  4. admin lunes,29 septiembre, 2008 at 9:54

    Para Lennin:

    No he entendido exactamente la primera pregunta, supongo que se debe a que es un tablero cuadrado con mismo número de columnas y filas.

    Y para la segunda supongo que poner el valor de Z constante y no ponerlo debe ser lo mismo para función aunque no se ahora mismo como funciona internamente.

  5. Kostas lunes,3 noviembre, 2008 at 15:04

    Hola Jesús

    La función InitCorners3d(corners3d, xCorners, yCorners, numImages) qué código contiene?

    He probado la función calibrar, y 8 de las 9 imágenes que pones, pero no me da el mismo resultado que a tí… algo me falla.

    Me puedes enviar el código completo?

    Gracias, Kostas

  6. natalia viernes,27 marzo, 2009 at 15:10

    Hola Jesus necesito si es que se pudiese me podrias dar tu mail para realizarte unas consultas
    Gracias
    Natalia

  7. Pedro lunes,17 agosto, 2009 at 23:45

    Que tal, una consulta tengo problemas con tu codigo, ya que me sale errores sobre objetos que no estan declarados. No se si habras obviado la parte de declaraciones o tal vez estoy cometiendo un error.

    Agradeceria su podrias ayudarme.

    Gracias de Antemano.

  8. admin lunes,24 agosto, 2009 at 10:19

    Para Pedro, Aquí lo tienes:

    #include “stdafx.h”
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include

    #include
    #include #include
  9. liz miércoles,9 septiembre, 2009 at 16:30

    hola, una consulta: con los datos obtenidos de tu programa, cómo puedo pasar de coordenadas en pixeles a coordenadas en centímetros por ejm.

  10. admin jueves,10 septiembre, 2009 at 8:17

    Pues supongo que habrá muchas maneras, pero a mi se me ocurre coger una imagen y con un for recorrer los pixels y contar cuales son de cada color, ose que cada vez que cambie de color el pixel iniciar de nuevo la cuenta para saber cuantos pixels tiene cada cuadro. Sabiendo lo que mide un cuadro al final deberías tener un calculo aproximado.

  11. andres miércoles,13 enero, 2010 at 21:45

    Jesus, te felicito por tu blog. Sabes la informacion que me has brindado me ha ayudado para mi trabajo de final de carrera.

    Muchas gracias. Viejo

  12. admin jueves,14 enero, 2010 at 0:15

    Me alegro Andres para eso estamos.

  13. jeison viernes,15 enero, 2010 at 23:09

    jesus, cordial saludo
    Presento un problema con tu codigo al compilar me arroja un error de que “InitCorners3d, DrawCircles,PrintIntrinsics, ConvertWorldtopixel ” no estan decladaras. Mi pregunta es como las declaro que son estas variables.

  14. admin sábado,16 enero, 2010 at 0:52

    Buenas jeison, son funciones no variables, te las envio al email.

  15. andres miércoles,3 febrero, 2010 at 21:48

    EStoy iniciando en openCv…
    Me parece genial tu blog… me has ahorrado mucho tiempo…
    Tambien he tenido inconvenientes a la ahora de correr el programa…
    Me lo puedes enviar completo al email…
    te lo agradeceria mucho…

  16. Antonio miércoles,31 marzo, 2010 at 1:50

    Hola felicidades por tu blog……
    EStoy iniciando en openCv…
    Me parece genial tu blog… me has ahorrado mucho tiempo…
    Tambien he tenido inconvenientes a la ahora de correr el programa…
    Me lo puedes enviar completo al email…
    te lo agradeceria mucho…

  17. Ana jueves,10 junio, 2010 at 19:34

    Hola Jesús, fantástico tu blog.

    He intentado implementar lo mismo pero a mi manera. Probando tus imágenes me sale la misma matriz de la cámara que a ti, pero con un vector de coeficientes de distorsión diferente. Asique, supongo que he de tener algo mal. Podrías mandarme también el resto del código?

    Por otra parte, no entiendo muy bien cómo defines los puntos ‘corners3D’ que entiendo que son las posiciones en milímetros de los puntos a buscar de la imágen objeto del tablero no?

    Gracias por todo!

  18. carlos martes,14 septiembre, 2010 at 17:28

    hey chavos, soy nuevo en esto de open vpn y todas sus funciones, tengo un problema a resolver necesito capturar la imagen de una botella y verificar si tiene tapa o no, hasta ahora no he logrado mas que intentar muchas cosas pero sin resultado, alguien con mucha experiencia tiene alguna informacion que me sirva?

  19. alvaro miércoles,13 octubre, 2010 at 19:00

    Hola Jesus, tengo el mismo problema que jeison tuvo en su momento, para las funciones que no incluyes en el post marca error, podrías enviarmelas a mi también por correo por favor? un saludo y muy buen trabajo!

  20. PEDRO sábado,13 noviembre, 2010 at 2:13

    Hola, estoy con un proyecto de vision y uso opencv en borland c++, mi problema es el siguente, al capturar una imagen con mi camara microsoft lifecam vx6000 me aparese la imagen en negro y no logro corregir el problema. al probar con la webcam de mi laptop funciona bien, pero necesito usar la webcam vx6000, ya reinstale los driver y todo y no pasa nada, espero me puedan ayudar. gracias

  21. admin lunes,15 noviembre, 2010 at 11:04

    Buenas Pedro,

    La has utilizado con algún otro programa y te pasa lo mismo, no tengo ni idea de lo que puede ser.

  22. jesus manuel viernes,14 enero, 2011 at 21:33

    Hola amigo:

    Mira tengo una web cam y quiero que al momento
    que se esté viendo el video en un picturebox de visual studio la camara tengo un efecto de zoom. Te agradecería mucho tu respuesta

  23. Mike jueves,1 septiembre, 2011 at 17:32

    Hola, te agradeceria mucho si pudieras enviarme las funciones que haz enviado a Jeison y Alvaro, ya que me marca el mismo error que a ellos, Muchas gracias, Gran Trabajo.

  24. lalo martes,20 septiembre, 2011 at 8:37

    Una pregunta para que sirve calibrar una cámara??? no entiendo bien

    y otra pregunta, como le hago para que mi webcam al grabar en opencv no obtenga tanto ruido, es que si dejo un flujo de imagenes se ve como cada pixel cambia ligeramente su valor respecto al frame anterior, dando un efecto como de que todo se mueve.. o es normal???

    Por ejemplo con una cámara de video cuando uno graba se ve la constancia de valores en los pixeles, pero en mi webcam no ocurre eso.. por favor ayudenme.. a lo mejor la calibración responde mi pregunta gracias

  25. admin martes,20 septiembre, 2011 at 8:42

    Buenas Lalo,

    Aqui tienes la respuesta a tu pregunta http://dialnet.unirioja.es/servlet/articulo?codigo=2512800.

    Sobre el tema del ruido de los pixeles que comentas no tengo ni idea de como solucionarlo, a ver si alguien puede ayudar.

  26. Laura jueves,8 diciembre, 2011 at 14:46

    Hola Jesús,
    Estoy realizando un proyecto de fin de carrera en el que tengo que utilizar capturas de cámara. Me gustaría que me enviases el código de las funciones que faltan como a otros compañeros para poder ejecutar tu código.

    Muchísimas gracias, me estás siendo de mucha ayuda

  27. andres moreno viernes,13 enero, 2012 at 17:59

    Buenos dias,

    Hace ya 7 meses vego trabajando con opencv con visual c++ y windows forms muy parecido a windows consola, si a alguien le interesa los apuntes que tengo, puede escribirme al correo andmore018@gmail.com, con gusto trataré de ayudarlos.

  28. admin viernes,13 enero, 2012 at 22:17

    Muchas gracias por el ofrecimiento Andrés.

    Saludos

  29. Jorge lunes,11 junio, 2012 at 18:16

    Hola, estoy haciendo mi trabajo de fin de carrera y necesito un algoritmo para calibrar y no he podido ejecutar este codigo. Por favor si pudieran mandarme al correo todo lo necesario para ejecutarlo y cualquier otra cosa que me pudiese ayudar como codigo, URLs, documentacion y demas.
    Muchas gracias de antemano a todos por el granito de arena aportado. Saludos

  30. ijuanjo lunes,20 agosto, 2012 at 19:32

    Me lo puedes enviar a mi??
    Y por qué no lo publicas, pues de lo contrario creo que a nadie le funciona el código

  31. Pablo jueves,8 noviembre, 2012 at 20:08

    Tengo el mismo problema de las funciones “InitCorners3d, DrawCircles,PrintIntrinsics, ConvertWorldtopixel, no se si tambien me las pudieras mandar a mi. Muchas Gracias

  32. Pablo jueves,8 noviembre, 2012 at 20:09

    Tengo el mismo problema con las funciones “InitCorners3d, DrawCircles,PrintIntrinsics, ConvertWorldtopixel, no se si tambien me las puedas mandar a mi, muchas gracias

  33. Cristian miércoles,12 marzo, 2014 at 16:25

    Hola jesus,espero que leas este mensaje… necesito hacer la calibracion de unas camaras para mi proyecto de tesis y cuando intento ejecutar tu codigo me sale el error de que no estan definidas las funciones “InitCorners3d, DrawCircles, PrintIntrinsics, ConvertWorldtopixel”
    podrias pasarme el codigo de las mismas para poder calibrar nis camaras.
    saludos

  34. Ricardo jueves,24 septiembre, 2015 at 19:11

    Tengo el mismo problema con las funciones “InitCorners3d, DrawCircles,PrintIntrinsics, ConvertWorldtopixel, no se si tambien me las puedas mandar a mi, muchas gracias.
    Estoy buceando por internet y al final me voy a tener que instalar openframeworks en linux 32bits…
    Aún así muchas gracias, gran trabajo.

  35. admin viernes,25 septiembre, 2015 at 7:00

    Enviado Ricardo 🙂

Leave a Comment

You must be logged in to post a comment.

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Traductor Google

Alicante, Spain

"There is no spoon"

The Matrix