Mi primera aplicación de Realidad Aumentada es un sencillo programa que, una vez compilado, es capaz de reconocer en la escena un marcador generado por nosotros para renderizar sobre él una imagen tridimensional, en mi caso una tetera. Podéis descargaros el código y los ficheros necesarios al final del post.
A continuación os muestro el código de este programa que utilizaré para explicar la estructura y funcionamiento básico de un programa de Realidad Aumentada. El programa reconoce en la escena un marcador ARToolkit creado por nosotros (en otra entrada mostraré como hacerlo) y renderizar sobre él una imagen tridimensional.
- #ifdef _WIN32
- #include <windows.h>
- #endif
- #include <GL/gl.h>
- #include <GL/glut.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <AR/gsub.h>
- #include <AR/video.h>
- #include <AR/param.h>
- #include <AR/ar.h>
- // ==== Configuracion de la camara ===============
- #ifdef _WIN32
- char *vconf = "Data\\WDM_camera_flipV.xml";
- #else
- char *vconf = "";
- #endif
- // ==== Definicion de constantes y variables globales ===============
- int patt_id; // Identificador de la marca
- double patt_trans[3][4]; // Matriz de transformacion de la marca
- // ==== Definicion de funciones =====================================
- void print_error (char *error) { printf(error); exit(0); }
- // ======== cleanup =================================================
- static void cleanup(void) {
- arVideoCapStop(); // Para captura de video y libera recursos
- arVideoClose();
- argCleanup();
- }
- // ======== draw ====================================================
- static void draw( void ) {
- double gl_para[16]; // Matriz 4x4 de OpenGL
- GLfloat mat_ambient[] = {0.0, 0.0, 1.0, 1.0};
- GLfloat light_position[] = {100.0,-200.0,200.0,0.0};
- argDrawMode3D(); // Activa modo presentacion 3D
- argDraw3dCamera(0, 0); // Activa vista de la camara 3D
- glClear(GL_DEPTH_BUFFER_BIT); // Limpia buffer de profundidad
- glEnable(GL_DEPTH_TEST);
- glDepthFunc(GL_LEQUAL);
- argConvGlpara(patt_trans, gl_para); // Convierte la matriz de la marca
- glMatrixMode(GL_MODELVIEW); // para ser usada por OpenGL
- glLoadMatrixd(gl_para); // Carga l matriz
- // Dibuja el objeto 3D
- glEnable(GL_LIGHTING); glEnable(GL_LIGHT0);
- glLightfv(GL_LIGHT0, GL_POSITION, light_position);
- glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
- glTranslatef(0.0, 0.0, 60.0);
- glRotatef(90.0, 1.0, 0.0, 0.0);
- glutSolidTeapot(80.0);
- glDisable(GL_DEPTH_TEST);
- }
- // ======== init ====================================================
- static void init( void ) {
- ARParam wparam, cparam; // Parametros intrinsecos de la camara
- int xsize, ysize; // Tamaño del video de camara (pixels)
- // Abre dispositivo de video
- if (arVideoOpen(vconf) < 0) exit(0);
- if(arVideoInqSize(&xsize, &ysize) < 0) exit(0);
- // Carga los parametros intrinsecos de la camara
- if(arParamLoad("data/camera_para.dat", 1, &wparam) < 0)
- print_error ("Error en carga de parametros de camara\n");
- arParamChangeSize(&wparam, xsize, ysize, &cparam);
- arInitCparam(&cparam); // Inicializa la camara con "cparam"
- // Carga la marca
- if((patt_id=arLoadPatt("data/pattMeditel")) < 0)
- print_error ("Error en carga de patron\n");
- argInit(&cparam, 1.0, 0, 0, 0, 0); // Abre la ventana grafica
- }
- // ======== mainLoop ================================================
- static void mainLoop(void) {
- ARUint8 *dataPtr; // contenedor de frame activo
- ARMarkerInfo *marker_info; // informacion del marcador
- int marker_num, j, k;
- double p_width = 120.0; // Ancho del patron (marca)
- double p_center[2] = {0.0, 0.0}; // Centro del patron (marca)
- // Captura un frame de la camara de video
- if((dataPtr = (ARUint8 *)arVideoGetImage()) == NULL) {
- // Si devuelve NULL es porque no hay un nuevo frame listo
- arUtilSleep(2); return; // Duerme el hilo 2ms y salimos
- }
- argDrawMode2D();
- argDispImage(dataPtr, 0,0); // Dibuja lo que ve la camara
- // Detecta la marca en el frame capturado (return -1 si error y sale de programa)
- if(arDetectMarker(dataPtr, 100, &marker_info, &marker_num) < 0) {
- cleanup(); exit(0);
- }
- arVideoCapNext(); // Captura siguiente frame
- // Detecta el patron con mayor fiabilidad
- for(j = 0, k = -1; j < marker_num; j++) {
- if(patt_id == marker_info[j].id) {
- if (k == -1) k = j;
- else if(marker_info[k].cf < marker_info[j].cf) k = j;
- }
- }
- if(k != -1) { // Si detecta el patron obtiene transformación
- // relativa entre la marca y la cámara real
- arGetTransMat(&marker_info[k], p_center, p_width, patt_trans);
- draw(); // Dibuja los objetos de la escena
- }
- argSwapBuffers(); // Cambia el buffer con lo que tenga dibujado
- }
- // ======== Main ====================================================
- int main(int argc, char **argv) {
- glutInit(&argc, argv); // Crea la ventana OpenGL con Glut
- init();
- arVideoCapStart(); // Crea un hilo para captura de video y
- argMainLoop( NULL, NULL, mainLoop ); // asocia callbacks...
- return (0);
- }
Inicialización (función init)
Marcador meditel |
Se carga el patrón asociado a la marca (línea 78). Finalmente, en la línea 81 se abre la ventana de OpenGL mediante argInit (de la librería auxiliar Gsub de ARToolKit), pasándole como primer parámetro la configuración de la cámara. El segundo parámetro indica el factor de zoom (en este caso, sin zoom).
Bucle principal
El primer lugar, se recupera un frame de la cámara de vídeo mediante la función arVideoGetImage (la llamada devuelve un puntero a un buffer donde se encuentra la imagen capturada). Si se llama a la función con mayor frecuencia de la soportada por la cámara, se duerme el hilo 2ms (línea 96) y se vuelve a ejecutar el mainLoop.
A continuación, se dibuja en la ventana (en modo 2D) el buffer que acabamos de recuperar de la cámara (línea 100) para que arDetectMarker (línea 103) localice las marcas en el buffer de entrada. El segundo parámetro de valor 100 se corresponde con el valor umbral de binarización de la imagen (a blanco y negro, explicaremos el proceso de binarización de la marca en una entrada posterior). Esta función nos devuelve en el tercer parámetro un puntero a una lista de estructuras de tipo ARMarkerInfo, que contienen información sobre las marcas detectadas (junto con un grado de fiabilidad de la detección), y como cuarto parámetro el número de marcas detectadas.
De esta forma, ARToolKit nos devuelve “posibles” posiciones para cada una de las marcas detectas. Incluso cuando estamos trabajando con una única marca, es común que sea detectada en diferentes posiciones (por ejemplo, si hay algo parecido a un cuadrado negro en la escena). ¿Cómo elegimos la correcta en el caso de que tengamos varias detecciones? ARToolKit asocia a cada percepción una probabilidad de que lo percibido sea una marca, en el campo cf (confidence value). Como se puede comprobar, todos los campos de la estructura ARMarkerInfo se refieren a coordenadas 2D, por lo que aún no se ha calculado la posición relativa de la marca con la cámara.
Campos de la estructura ARMarkerInfo |
Así, en las líneas 109-115 se guarda en la variable k el índice de la lista de marcas detectadas aquella percepción que tenga mayor probabilidad de ser la marca (cuyo valor de fiabilidad sea mayor). Mediante la llamada a arGetTransMat (línea 119) se obtiene la matriz de transformación relativa entre la marca y la cámara (matriz 3x4 de doubles); es decir, se obtiene la traslación y rotación de la cámara con respecto de la marca detectada. Para ello es necesario especificar el centro y ancho de la marca. Esta matriz será finalmente convertida al formato de matriz homogénea de 16 componentes utilizada por OpenGL mediante la llamada a argConvGlpara en la línea 47. Finalmente, se renderizará una imagen 3D mediante primitivas OpenGL para uso en aplicaciones de Realidad Aumentada. (líneas 51-58).
Finalización y función Main
En la función cleanup se liberan los recursos al salir de la aplicación. Se hace uso de funciones de ARToolKit para detener la cámara de vídeo, y limpiar las estructuras de datos internas de ARToolKit. En la función main se registran los callbacks mediante la función argMainLoop. En este ejemplo, se pasa como primer y segundo parámetro NULL (correspondientes a los manejadores de ratón y teclado respectivamente). Por su parte, se asocia la función que se estará llamando constantemente en el bucle principal mainLoop.
Salida del programa
Salida del programa |
código del programa
marcador meditel
ficheros cámara
No hay comentarios:
Publicar un comentario