Mostrando entradas con la etiqueta ARcodigo. Mostrar todas las entradas
Mostrando entradas con la etiqueta ARcodigo. Mostrar todas las entradas

lunes, 19 de mayo de 2014

Mejoramos nuestra aplicación RA: estabilizamos el tracking

Os presento ahora una importante mejora que podemos introducir en nuestros programas de Realidad Aumentada: se trata de mejorar el tracking de marcadores para conseguir una imagen capturada más estabilizada. Para ello, utilizaremos la función histórica de ARToolKit.



ARToolKit incorpora una función de tratamiento del histórico de percepciones para estabilizar el tracking. Este histórico se implementa en una función alternativa a arGetTransMat que, en realidad, utiliza únicamente la percepción anterior, llamada arGetTransMatCont. Mediante el uso de esta función se elimina gran parte del efecto de registro tembloroso.

programa historico
Salida del programa con histórico

La función histórica requiere como parámetro la percepción anterior y no podrá utilizarse hasta que no dispongamos (al menos) de una percepción. Por esta razón, es necesario llamar primero a la función sin histórico arGetTransMat y, con la primera percepción, llamar a la función de histórico arGetTransMatCont. Esto lo conseguimos modificando ligeramente el código de la función mainLoop, una vez detectado el marcador, de la siguiente forma:

   if(k != -1) {   // Si ha detectado el patron en algun sitio... obtenemos
                   // transformacion relativa entre marca y la camara real
      if (useCont && contAct) {
            arGetTransMatCont(&marker_info[k], patt_trans, p_center,
            p_width, patt_trans);
            printf ("Usando historico!!!\n");
         }
      else {
            useCont = 1;      // En la siguiente iteracion lo podemos usar!
            arGetTransMat(&marker_info[k], p_center, p_width, patt_trans);
            printf ("Sin historico...\n");
         }
      draw();       // Dibujamos los objetos de la escena
  } else {
      useCont = 0;
      printf ("Reset Historico (fallo de deteccion)\n");
  } 

Utilizamos la variable  useCont (definida anteriormente) para indicarnos si ya podemos utilizar la función con histórico o no, en función de que tengamos capturado o no el primer frame. Esta variable de comportamiento booleano se tendrá que poner a false (valor 0) cuando no se detecte la marca. De este modo, si el usuario activó el uso del histórico (y ya tenemos al menos una percepción previa de la marca, utilizamos la función con histórico. En otro caso, llamaremos a la función de detección sin histórico y activaremos el uso del histórico para la siguiente llamada.

En nuestro programa, imtroducimos también una función de callback de teclado que permite activar el uso del histórico mediante la tecla h:

  // ======== keyboard ================================================
  static void keyboard(unsigned char key, int x, int y) {
  switch (key) {
      case 'H': case 'h':
                if (contAct) {contAct = 0; printf("Historico Desactivado\n");}
                else {contAct = 1; printf("Historico Activado\n");} 
                break;
      case 0x1B: case 'Q': case 'q':
                cleanup(); exit(1); 
                break;
      }
   }


Salida del programa

ARToolKit
Salida del programa 
Esta es la salida del programa y los siguientes enlaces permiten descargar el código y el marcador utilizado (imagen para impresión y datos para el programa) y los ficheros de inicialización de la cámara. Todos los ficheros deberán estar en ...artoolkit\bin\Data.

código del programa histórico
marcador meditel
ficheros cámara

miércoles, 14 de mayo de 2014

Modificamos nuestra aplicación RA para detectar varios patrones

Con unos sencillos cambios podemos modificar nuestra primera aplicación de Realidad Aumentada para que reconozca en la escena varios marcadores, asociando a cada uno de ellos una imagen tridimensional diferente. Os mostraré cómo reconocer cinco marcadores para dibujar cinco objetos diferentes. Podéis descargaros el código y los ficheros necesarios mediante los links correspondientes en este post.



A la hora de realizar una aplicación de Realidad Aumentada que sea capaz de reconocer diferentes patrones asignando imágenes u objetos 3D distintos a cada uno de ellos, tendremos que tener en cuenta las siguientes cambios: (en la imagen se ve la salida del programa ejemplo para dos marcadores distintos)


  1. Cargaremos un fichero de datos que contenga información sobre varios patrones en lugar del fichero de patrón ya utilizado pattMeditel.
  2. Utilizaremos, también, una estructura de datos asociada a los patrones diferente; que permita almacenar varios patrones.
  3. Modificaremos la función de dibujo draw para poder dibujar diferentes objetos en función del patron detectado.

Carga del fichero de datos de los patrones

El fichero de datos que contiene información de todos los marcadores es mi_object_data (es un fichero de texto con una estructura muy sencilla que contiene, para cada marcador, su nombre, el nombre de su fichero de datos y la anchura y centro del marcador; puedes descargarlo aquí). Para cargarlo en el programa, primero definiremos las variables:

   // ==== Fichero Object Data =========================================
   char            *model_name = "Data/mi_object_data";
   ObjectData_T    *object;

   int             objectnum;

A continuación, en la función init, cargamos el fichero mediante una función de ARToolKit definida en object.c llamada read_ObjData a la que le pasamos el fichero de datos y el número de marcadores definidos en él. Los datos referentes a las marcas quedan almacenados en una estructura de datos ObjectData_T específica de ARToolKit. Aquí os dejo el código que hace esto:

  // Cargamos el fichero mi_object_data 
  if ((object = read_ObjData(model_name, &objectnum)) == NULL) exit(0);
  printf("Objectfile num = %d\n", objectnum);


Detección de patrones


ARToolKit puede ahora tratar de identificar varios patrones en la rutina arDetectMarker. Puesto que ahora la detección es para múltiples marcadores, una vez detectado una marca con mayor fiabilidad, habrá que comprobar si se trata de un patrón conocido (presente en el fichero cargado). En este ejemplo, dibujamos también sus bordes cuando esto ocurre. Por otra parte, también es necesario mantener la transformación específica para cada uno de los marcadores detectados. La parte de código que hace esto será:

   // busca patrones conocidos en los marcadores detectados y dibuja un cuadrado
   // de otro color sobre los patrones reconocidos
   for (i = 0; i < objectnum; i++) {
      k = -1;
      for (j = 0; j < marker_num; j++) {
      if (object[i].id == marker_info[j].id) {   //si reconoce un patron
          glColor3f(0.0, 1.0, 0.0);
          argDrawSquare(marker_info[j].vertex, 0, 0);
              if (k == -1) k = j;
          else       // Vemos donde detecta el patron con mayor fiabilidad
           if (marker_info[k].cf < marker_info[j].cf) k = j;
         }
      }
       if (k == -1) {
          object[i].visible = 0;
          continue;
          }

       // calculamos la transformada para cada patron reconocido.
       object[i].visible = 1;
       arGetTransMat(&marker_info[k], object[i].marker_center, 
       object[i].marker_width,
       object[i].trans);
    }

Hay una bandera del visiblitdad asociada con cada marcador, y se realizará una nueva transformación para cada marcador detectado.


Dibujado del objeto asociado al marcador

Para dibujar el objeto asociado al marcador detectado, se llama a la función draw con la estructura ObjectData_T  y el número de objetos como parámetros. Dentro de esta función, se convertimos la matriz de transformación a formato openGL para cada marcador detectado:

    for (i = 0; i < objectnum; i++) {
      if (object[i].visible == 0) continue;
      argConvGlpara(object[i].trans, gl_para);  
          draw_object(object[i].id, gl_para);
    }

Finalmente, dibujamos el objeto asociado al marcador detectado a través de la subrutina draw_object que, mediante la anidación de funciones if selecciona el objeto para cada marcador (pondré solo un ejemplo):

    if (obj_id == 0){
       glMaterialfv(GL_FRONT, GL_SPECULAR, mat_flash_collide);
       glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient_collide);
       glTranslatef(0.0, 0.0, 30.0);
       glutSolidSphere(30, 12, 6);    // dibujamos una esfera
    }


Salida del programa

La salida de este programa detectando dos marcadores diferentes, con sus correspondientes objetos asociados (en este caso una esferarosa y un conjunto esfera-toroide azul) puede verse en la imagen siguiente:

salida varios marcadores
Salida del programa de detección de varios marcadores


miércoles, 7 de mayo de 2014

Mi primera aplicación de Realidad Aumentada

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.

  1. #ifdef _WIN32
  2. #include <windows.h>
  3. #endif
  4. #include <GL/gl.h>
  5. #include <GL/glut.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <AR/gsub.h>
  9. #include <AR/video.h>
  10. #include <AR/param.h>
  11. #include <AR/ar.h>
  12.  
  13.  
  14. // ==== Configuracion de la camara ===============
  15. #ifdef _WIN32
  16. char                *vconf = "Data\\WDM_camera_flipV.xml";
  17. #else
  18. char                *vconf = "";
  19. #endif
  20.  
  21. // ==== Definicion de constantes y variables globales ===============
  22. int    patt_id;                                // Identificador de la marca
  23. double patt_trans[3][4];                       // Matriz de transformacion de la marca
  24.  
  25. // ==== Definicion de funciones =====================================
  26. void print_error (char *error) {  printf(error); exit(0); }
  27.  
  28. // ======== cleanup =================================================
  29. static void cleanup(void) {  
  30.   arVideoCapStop();                      // Para captura de video y libera recursos
  31.   arVideoClose();
  32.   argCleanup();
  33. }
  34.  
  35. // ======== draw ====================================================
  36. static void draw( void ) {
  37.   double  gl_para[16];                   // Matriz 4x4 de OpenGL
  38.   GLfloat mat_ambient[]     = {0.0, 0.0, 1.0, 1.0};
  39.   GLfloat light_position[]  = {100.0,-200.0,200.0,0.0};
  40.  
  41.   argDrawMode3D();                       // Activa modo presentacion 3D
  42.   argDraw3dCamera(0, 0);                 // Activa vista de la camara 3D
  43.   glClear(GL_DEPTH_BUFFER_BIT);          // Limpia buffer de profundidad
  44.   glEnable(GL_DEPTH_TEST);
  45.   glDepthFunc(GL_LEQUAL);
  46.  
  47.   argConvGlpara(patt_trans, gl_para);   // Convierte la matriz de la marca
  48.   glMatrixMode(GL_MODELVIEW);           // para ser usada por OpenGL
  49.   glLoadMatrixd(gl_para);               // Carga l matriz
  50.  
  51.   // Dibuja el objeto 3D
  52.   glEnable(GL_LIGHTING);  glEnable(GL_LIGHT0);
  53.   glLightfv(GL_LIGHT0, GL_POSITION, light_position);
  54.   glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
  55.     glTranslatef(0.0, 0.0, 60.0);
  56.     glRotatef(90.0, 1.0, 0.0, 0.0);
  57.     glutSolidTeapot(80.0);
  58.   glDisable(GL_DEPTH_TEST);
  59. }
  60.  
  61. // ======== init ====================================================
  62. static void init( void ) {
  63.   ARParam  wparam, cparam;        // Parametros intrinsecos de la camara
  64.   int xsize, ysize;               // Tamaño del video de camara (pixels)
  65.  
  66.   // Abre dispositivo de video
  67.   if (arVideoOpen(vconf) < 0) exit(0); 
  68.   if(arVideoInqSize(&xsize, &ysize) < 0) exit(0);
  69.  
  70.   // Carga los parametros intrinsecos de la camara
  71.   if(arParamLoad("data/camera_para.dat", 1, &wparam) < 0)  
  72.     print_error ("Error en carga de parametros de camara\n");
  73.  
  74.   arParamChangeSize(&wparam, xsize, ysize, &cparam);
  75.   arInitCparam(&cparam);                       // Inicializa la camara con "cparam"
  76.  
  77.   // Carga la marca
  78.   if((patt_id=arLoadPatt("data/pattMeditel")) < 0)
  79.     print_error ("Error en carga de patron\n");
  80.  
  81.   argInit(&cparam, 1.0, 0, 0, 0, 0);   // Abre la ventana grafica
  82. }
  83.  
  84. // ======== mainLoop ================================================
  85. static void mainLoop(void) {
  86.   ARUint8 *dataPtr;                     // contenedor de frame activo
  87.   ARMarkerInfo *marker_info;            // informacion del marcador
  88.   int marker_num, j, k;
  89.  
  90.   double p_width     = 120.0;           // Ancho del patron (marca)
  91.   double p_center[2] = {0.0, 0.0};      // Centro del patron (marca)
  92.  
  93.   // Captura un frame de la camara de video
  94.   if((dataPtr = (ARUint8 *)arVideoGetImage()) == NULL) {
  95.     // Si devuelve NULL es porque no hay un nuevo frame listo
  96.     arUtilSleep(2);  return;  // Duerme el hilo 2ms y salimos
  97.   }
  98.  
  99.   argDrawMode2D();
  100.   argDispImage(dataPtr, 0,0);    // Dibuja lo que ve la camara
  101.  
  102.   // Detecta la marca en el frame capturado (return -1 si error y sale de programa)
  103.   if(arDetectMarker(dataPtr, 100, &marker_info, &marker_num) < 0) {
  104.     cleanup(); exit(0);  
  105.   }
  106.  
  107.   arVideoCapNext();      // Captura siguiente frame
  108.  
  109.   // Detecta el patron con mayor fiabilidad
  110.   for(j = 0, k = -1; j < marker_num; j++) {
  111.     if(patt_id == marker_info[j].id) {
  112.       if (k == -1) k = j;
  113.       else if(marker_info[k].cf < marker_info[j].cf) k = j;
  114.     }
  115.   }
  116.  
  117. if(k != -1) {                   // Si detecta el patron obtiene transformación
  118. // relativa entre la marca y la cámara real
  119.     arGetTransMat(&marker_info[k], p_center, p_width, patt_trans);
  120.     draw();                       // Dibuja los objetos de la escena
  121.   }
  122.  
  123.   argSwapBuffers();                     // Cambia el buffer con lo que tenga dibujado
  124. }
  125.  
  126. // ======== Main ====================================================
  127. int main(int argc, char **argv) {
  128.   glutInit(&argc, argv);                 // Crea la ventana OpenGL con Glut
  129.   init();                               
  130.  
  131.   arVideoCapStart();                    // Crea un hilo para captura de video y
  132.   argMainLoop( NULL, NULL, mainLoop );  // asocia callbacks...
  133.   return (0);
  134. }

Cualquier aplicación que use ARToolKit tendrá la estructura de programa similar a la siguiente: tres grandes bloques inicialización, bucle principal y finalización (en el código están coloreados en azul, verde y rojo respectivamente):

proceso AR


Inicialización (función init)
Primero abre el dispositivo de vídeo con arVideoOpen (línea 67) y se obtiene el tamaño de la fuente de vídeo mediante arVideoInqSize (línea 68) (se guarda en dos variables entero el ancho y alto del vídeo). A continuación, arParamLoad (línea 71) carga en una estructura ARParam (tercer parámetro) los parámetros intrínsecos de la cámara obtenidos en la etapa de calibración (fichero genérico camera_para.dat válido para la mayoría de webcams; aunque lo ideal es trabajar con un fichero que contenga los parámetros de nuestra cámara para obtener   resultados más precisos).
patron_meditel
Marcador meditel
Veremos cómo crear este fichero específico para cada cámara en una entrada posterior en ARexperience. Después, arParamChangeSize (línea 74) modifica los parámetros intrínsecos para la resolución en píxels con la que trabajará la cámara y se cargan en las estructuras de datos internas de ARToolKit, mediante la llamada a arInitCparam (línea 75).

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.

ARMarkerInfo
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


ARToolKit
Salida del programa 
Esta es la salida del programa y los siguientes enlaces permiten descargar el código y el marcador utilizado (imagen para impresión y datos para el programa) y los ficheros de inicialización de la cámara. Todos los ficheros deberán estar en ...artoolkit\bin\Data.

código del programa
marcador meditel
ficheros cámara