UNIVERSIDAD DE CÓRDOBA E SCUELA P OLITÉCNICA SUPERIOR Ingeniería en Informática PROYECTO DE FIN DE CARRERA Desarrollo de una librería para acceder a ArUco desde Java M ANUAL T ÉCNICO Autor: José Carlos Garrido Pérez Director: Prof. Rafael Muñoz Salinas Córdoba, 8 de septiembre de 2014 UNIVERSIDAD DE CÓRDOBA E SCUELA P OLITÉCNICA SUPERIOR Ingeniería en Informática PROYECTO DE FIN DE CARRERA Desarrollo de una librería para acceder a ArUco desde Java M ANUAL T ÉCNICO Autor: José Carlos Garrido Pérez Director: Prof. Rafael Muñoz Salinas Córdoba, 8 de septiembre de 2014 Prof. Rafael Muñoz Salinas, Profesor Titular de Universidad del área de Conocimiento de Ciencias de la Computación e Inteligencia Artificial y adscrito al Departamento de Informática y Análisis Numérico de la Universidad de Córdoba, INFORMA Que el presente Proyecto de Fin de Carrera de Ingeniería en Informática, titulado Desarrollo de una librería para acceder a ArUco desde Java, ha sido realizado, bajo su dirección, por José Carlos Garrido Pérez, reuniendo, a su juicio, las condiciones exigidas en este tipo de trabajos. Y para que conste, firma el presente informe en Córdoba, a día 8 de septiembre de 2014 EL DIRECTOR EL ALUMNO Rafael Muñoz Salinas José Carlos Garrido Pérez A mi hermano y a mis padres Índice de contenidos Índice de contenidos I Índice de tablas V Índice de figuras VII 1. Introducción y objetivos 1 1.1. Presentación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.2. Problema real . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 1.3. Problema técnico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 1.3.1. Funcionamiento . . . . . . . . . . . . . . . . . . . . . . . . . . 3 1.3.2. Entorno . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 1.3.2.1. Interfaz con el usuario . . . . . . . . . . . . . . . . . . 4 1.3.2.2. Entorno de programación . . . . . . . . . . . . . . . . 4 1.3.2.3. Ambiente de ejecución . . . . . . . . . . . . . . . . . 4 1.3.2.4. Vida esperada . . . . . . . . . . . . . . . . . . . . . . 4 1.3.2.5. Ciclo de mantenimiento . . . . . . . . . . . . . . . . . 4 1.3.2.6. Competencia . . . . . . . . . . . . . . . . . . . . . . . 5 1.3.2.7. Aspecto externo . . . . . . . . . . . . . . . . . . . . . 5 1.3.3. Calidad y fiabilidad . . . . . . . . . . . . . . . . . . . . . . . . . 5 1.3.4. Programa de tareas . . . . . . . . . . . . . . . . . . . . . . . . . 5 1.3.5. Pruebas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 1.3.6. Seguridad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.4. Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.4.1. Objetivo principal . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.4.2. Objetivos secundarios . . . . . . . . . . . . . . . . . . . . . . . 7 2. Antecedentes 9 2.1. Aplicaciones de Realidad Aumentada . . . . . . . . . . . . . . . . . . . 9 2.2. Visión Artificial: OpenCV . . . . . . . . . . . . . . . . . . . . . . . . . 13 2.3. Realidad Aumentada: librerías existentes . . . . . . . . . . . . . . . . . . 14 I Índice de Contenidos 2.4. ArUco: visión general . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 2.4.1. Clase Board . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 2.4.2. Clase BoardConfiguration . . . . . . . . . . . . . . . . . . . . . 17 2.4.3. Clase BoardDetector . . . . . . . . . . . . . . . . . . . . . . . . 17 2.4.4. Clase CameraParameters . . . . . . . . . . . . . . . . . . . . . . 17 2.4.5. Clase CvDrawingUtils . . . . . . . . . . . . . . . . . . . . . . . 17 2.4.6. Clase FiducidalMarkers . . . . . . . . . . . . . . . . . . . . . . 18 2.4.7. Clase Marker . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 2.4.8. Clase MarkerCandidate . . . . . . . . . . . . . . . . . . . . . . . 18 2.4.9. Clase MarkerDetector . . . . . . . . . . . . . . . . . . . . . . . 18 2.4.10. Estructura MarkerInfo . . . . . . . . . . . . . . . . . . . . . . . 18 2.5. Programas de prueba de la librería ArUco . . . . . . . . . . . . . . . . . 18 2.5.1. arucoBoardPix2meters . . . . . . . . . . . . . . . . . . . . . . . 19 2.5.2. arucoCreateBoard . . . . . . . . . . . . . . . . . . . . . . . . . 19 2.5.3. arucoCreateMarker . . . . . . . . . . . . . . . . . . . . . . . . . 19 2.5.4. arucoSelectoptimalmarkers . . . . . . . . . . . . . . . . . . . . . 19 2.5.5. arucoSimple . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 2.5.6. arucoSimpleBoard . . . . . . . . . . . . . . . . . . . . . . . . . 19 2.5.7. arucoTest . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 2.5.8. arucoTestBoard . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 2.5.9. arucoTestBoardGl . . . . . . . . . . . . . . . . . . . . . . . . . 20 2.6. Librerías basadas en ArUco . . . . . . . . . . . . . . . . . . . . . . . . . 20 2.6.1. js-aruco . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 2.7. Otros proyectos fin de carrera . . . . . . . . . . . . . . . . . . . . . . . . 20 2.7.1. Simulador gráfico de operaciones sobre ficheros . . . . . . . . . . 20 2.8. Justificación del proyecto . . . . . . . . . . . . . . . . . . . . . . . . . . 21 3. Restricciones y recursos 23 3.1. Factores iniciales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 3.2. Factores estratégicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 3.2.1. Entorno de desarrollo: NetBeans . . . . . . . . . . . . . . . . . . 24 3.2.2. Modo de acceso a las funciones de ArUco: JNI . . . . . . . . . . 24 3.3. Recursos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 3.3.1. Recursos de hardware . . . . . . . . . . . . . . . . . . . . . . . 25 3.3.1.1. Recursos de hardware para el desarrollo . . . . . . . . 25 3.3.1.2. Recursos de hardware para uso del programa . . . . . . 25 3.3.2. Recursos de software . . . . . . . . . . . . . . . . . . . . . . . . 25 3.3.3. Recursos humanos . . . . . . . . . . . . . . . . . . . . . . . . . 26 II Índice de Contenidos 4. Especificación de requisitos 27 4.1. Requisitos funcionales . . . . . . . . . . . . . . . . . . . . . . . . . . . 5. Especificación del modelo de clases 27 29 5.1. Análisis de clases Java . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 5.1.1. Clase Board . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 5.1.2. Clase BoardConfiguration . . . . . . . . . . . . . . . . . . . . . 32 5.1.3. Clase BoardDetector . . . . . . . . . . . . . . . . . . . . . . . . 35 5.1.4. Clase CameraParameters . . . . . . . . . . . . . . . . . . . . . . 38 5.1.5. Clase CvDrawingUtils . . . . . . . . . . . . . . . . . . . . . . . 41 5.1.6. Clase FiducidalMarkers . . . . . . . . . . . . . . . . . . . . . . 43 5.1.7. Clase Marker . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 5.1.8. Clase MarkerDetector . . . . . . . . . . . . . . . . . . . . . . . 49 5.1.9. Clase MarkerInfo . . . . . . . . . . . . . . . . . . . . . . . . . . 54 5.2. Análisis de los ficheros C++ del “wrapper” . . . . . . . . . . . . . . . . 55 5.2.1. Handle.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 5.2.2. JBoard.cpp y JBoard.h . . . . . . . . . . . . . . . . . . . . . . . 57 5.2.3. JBoardConfiguration.cpp y JBoardConfiguration.h . . . . . . . . 58 5.2.4. JBoardDetector.cpp y JBoardDetector.h . . . . . . . . . . . . . . 59 5.2.5. JCameraParameters.cpp y JCameraParameters.h . . . . . . . . . 60 5.2.6. JCvDrawingUtils.cpp y JCvDrawingUtils.h . . . . . . . . . . . . 62 5.2.7. JFiducidalMarkers.cpp y JFiducidalMarkers.h . . . . . . . . . . . 63 5.2.8. JMarker.cpp y JMarker.h . . . . . . . . . . . . . . . . . . . . . . 63 5.2.9. JMarkerDetector.cpp y JMarkerDetector.h . . . . . . . . . . . . . 65 5.2.10. JMarkerInfo.cpp y JMarkerInfo.h . . . . . . . . . . . . . . . . . 66 5.3. Análisis de los programas Java de ejemplo . . . . . . . . . . . . . . . . . 67 5.3.1. arucoCreateBoard . . . . . . . . . . . . . . . . . . . . . . . . . 67 5.3.2. arucoCreateMarker . . . . . . . . . . . . . . . . . . . . . . . . . 68 5.3.3. arucoSimple . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 5.3.4. arucoSimpleBoard . . . . . . . . . . . . . . . . . . . . . . . . . 70 6. Diagramas de secuencia 73 6.1. Creación de un objeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 6.2. Llamada a un método con modificación de parámetros . . . . . . . . . . 74 6.3. Llamada a un método que escribe en disco . . . . . . . . . . . . . . . . . 75 6.4. Llamada a un método que lee desde el disco . . . . . . . . . . . . . . . . 76 7. Diagrama de paquetes 79 7.1. Diagrama general de paquetes . . . . . . . . . . . . . . . . . . . . . . . III 81 Índice de Contenidos 8. Pruebas 8.1. Clases de Java . . . . . . . . . . . . . . . . . . . . . . . 8.2. “Wrapper” en C++ . . . . . . . . . . . . . . . . . . . . 8.3. Programas de prueba . . . . . . . . . . . . . . . . . . . 8.4. Pruebas de funcionamiento en varios sistemas operativos . . . . 83 84 87 89 89 9. Conclusiones y futuras mejoras 9.1. Conclusiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9.2. Futuras mejoras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 91 92 Bibliografía 93 IV . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Índice de tablas 2.1. Comparativa entre los antecedentes y el proyecto . . . . . . . . . . . . . V 21 Índice de figuras 1.1. Ejemplo de funcionamiento de ArUco . . . . . . . . . . . . . . . . . . . 2 1.2. Logotipo de la librería de visión artificial OpenCV . . . . . . . . . . . . 2 1.3. Planificación temporal del proyecto fin de carrera . . . . . . . . . . . . . 6 2.1. Captura de pantalla de learnAR . . . . . . . . . . . . . . . . . . . . . . . 10 2.2. Captura de pantalla de Google Sky Map . . . . . . . . . . . . . . . . . . 10 2.3. Captura de pantalla de BlippAR . . . . . . . . . . . . . . . . . . . . . . 11 2.4. Captura de pantalla de Ingress . . . . . . . . . . . . . . . . . . . . . . . 12 2.5. Fotografía de un teléfono móvil ejecutando Word Lens . . . . . . . . . . 13 2.6. Coche autónomo Stanley . . . . . . . . . . . . . . . . . . . . . . . . . . 14 2.7. Logotipo de la librería de Realidad Aumentada ARLab . . . . . . . . . . 15 2.8. Ejemplo de un marcador de la librería de Realidad Aumentada GRATF . . 16 2.9. Tablero de marcadores de ArUco . . . . . . . . . . . . . . . . . . . . . . 17 2.10. Tutorial de GraFich . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 5.1. Diagrama de clases de Board . . . . . . . . . . . . . . . . . . . . . . . . 32 5.2. Diagrama de clases de BoardConfiguration . . . . . . . . . . . . . . . . 35 5.3. Diagrama de clases de BoardDetector . . . . . . . . . . . . . . . . . . . 38 5.4. Diagrama de clases de CameraParameters . . . . . . . . . . . . . . . . . 41 5.5. Diagrama de clases de CvDrawingUtils . . . . . . . . . . . . . . . . . . 42 5.6. Diagrama de clases de Marker . . . . . . . . . . . . . . . . . . . . . . . 49 5.7. Diagrama de clases de MarkerDetector . . . . . . . . . . . . . . . . . . . 54 5.8. Diagrama de clases de MarkerInfo . . . . . . . . . . . . . . . . . . . . . 55 5.9. Imagen con el tablero resultado de arucoCreateBoard . . . . . . . . . . . 68 5.10. Marcador con el identificador 23 creado por arucoCreateMarker . . . . . 69 5.11. Imagen resultado de ejecutar arucoSimple . . . . . . . . . . . . . . . . . 70 5.12. Imagen resultado de ejecutar arucoSimpleBoard . . . . . . . . . . . . . . 71 6.1. Diagrama de secuencia del constructor de MarkerDetector . . . . . . . . 74 6.2. Diagrama de secuencia de la función detect de MarkerDetector . . . . . . 75 6.3. Diagrama de secuencia de la función saveToFile de Board . . . . . . . . 76 6.4. Diagrama de secuencia de la función readFromFile de Board . . . . . . . 77 VII Índice de Figuras 7.1. Diagrama de paquetes de la librería . . . . . . . . . . . . . . . . . . . . . VIII 81 Capítulo 1 Introducción y objetivos 1.1. Presentación El termino Realidad Aumentada, como suele ocurrir en ingeniería, tiene diferentes definiciones. Algunas de ellas entienden la Realidad Aumentada como un caso particular de Realidad Virtual, mientras que otras entienden que es justo lo contrario. Sin embargo, es importante notar que, al contrario que ocurre en la Realidad Virtual, el entorno no es completamente suprimido, sino que tiene un papel muy importante en la aplicación. De hecho, es complementado con la información virtual[1]. Si se sigue con la comparación, puede observarse fácilmente que es más complicado controlar un entorno real que un entorno completamente sintético (como es el caso de la Realidad Virtual). De manera general, puede entenderse que en la Realidad Aumentada se integra el entorno con la información sintética. Ha de existir una relación entre estos dos “mundos” que, normalmente, es de tipo espacial. Para poder observar la información virtual sobre el entorno real hemos de usar un expositor (por ejemplo una pantalla) y un software que interprete los patrones o coordenadas del mundo real para poder situar en él la información virtual. Para localizar la escena en la que se encuentra el usuario puede utilizarse una cámara o un sensor GPS. Entre las numerosas aplicaciones de la realidad aumentada se encuentran la educación, accesibilidad o el ocio. Con aplicaciones que pueden desde traducir carteles a nuestro idioma hasta crear un mundo virtual “superpuesto” con el real. En el capítulo siguiente se comentarán algunos de estos ejemplos más en detalle. Una vez se ha expuesto lo que es la Realidad Aumentada y se han mostrado algunos ejemplos de su posible uso, se puede entender mejor el sentido de ArUco[7]. Dentro de la Universidad de Córdoba se ha desarrollado esta librería que se presenta como una librería de Realidad Aumentada minimalista basada en OpenCV[8] y cuyas principales funcionalidades son la detección de marcadores y tableros de marcadores con una línea de código en C++. Cuenta con la posibilidad de utilizar hasta 1024 marcadores distintos. Lógicamente, también permite la creación de estos marcadores y tableros. 1 Capítulo 1. Introducción y objetivos Además, la librería cuenta con varios ejemplos para que los usuarios menos expertos puedan aprender a trabajar con ella. La aplicación anteriormente comentada, learnAR, utiliza un sistema basado en marcadores como el que se puede crear usando ArUco, por lo que aplicaciones similares podrían ser desarrolladas usando la librería cordobesa. A continuación, se muestra una imagen en la que se puede apreciar un tablero con varios marcadores que han sido detectados mediante el uso de ArUco: Figura 1.1: Ejemplo de funcionamiento de ArUco Esta librería ha sido desarrollada en C++ utilizando OpenCV y puede ser utilizada solamente en programas desarrollados en ese lenguaje. OpenCV es una librería de visión artificial bajo licencia BSD. En los últimos años, los sistemas de visión artificial están desarrollándose a gran velocidad y con aplicaciones tan variadas e interesantes como el reconocimiento facial, el reconocimiento óptico de caracteres, la búsqueda en imágenes, los coches autónomos, la robótica, etc. La visión artificial es un campo que permite a un computador adquirir, procesar y analizar imágenes con el objetivo de que “entienda” las imágenes que se le están proporcionando. Figura 1.2: Logotipo de la librería de visión artificial OpenCV 2 Capítulo 1. Introducción y objetivos El lenguaje de programación Java[9] es un lenguaje de programación orientado a objetos, cuya sintaxis está basada en C y C++. Este lenguaje es normalmente interpretado, aunque también puede ser compilado. En el caso de la interpretación, Java es una de las alternativas multi-plataforma más extendidas hasta ahora. Además, permite la creación de “applets” que pueden ser integrados en páginas web, por lo que supuso una “revolución” en Internet ya que hasta su aparición sólo se podía ver en internet el contenido generado por HTML, el cual es un lenguaje bastante pobre en lo que a cálculo se refiere y que sólo está pensado para dar formato al texto e imágenes que vemos en una web, así como para enlazar unas páginas con otras. Por estos factores se encuentra una necesidad de poder acceder a la librería ArUco desde el lenguaje de programación Java, con el objetivo de llegar a un mayor número de programadores y usuarios, contar con programas con una mayor portabilidad y poder ser utilizados en “applets” en internet. Existen diversas formas de acceder a código nativo desde Java siendo JNI una de las más utilizadas y documentadas en la actualidad, por lo que se tiene en cuenta esta tecnología para realizar este proyecto. 1.2. Problema real El problema real que se desea resolver con el desarrollo del presente proyecto de fin de carrera es el siguiente: la librería ArUco goza de popularidad en el mundo de la informática y se está utilizando con lenguajes como C y C++. Sin embargo, el lenguaje Java es muy utilizado en todo el mundo y tiene la gran ventaja de ser multiplataforma, por lo que se estima necesario la creación de un wrapper para poder acceder a las funciones de ArUco desde código escrito en Java. Además de realizar el wrapper se entiende como necesaria la creación de algunos programas de ejemplo cuyo objetivo será tanto didáctico como demostrativo del funcionamiento de la librería. 1.3. Problema técnico 1.3.1. Funcionamiento La librería ha de ofrecer las siguientes posibilidades: Creación de marcadores Creación de tableros Detección de marcadores Detección de tableros 3 Capítulo 1. Introducción y objetivos Funciones que permitan dibujar señales que demuestren el reconocimiento de los marcadores y tableros, tales como sus bordes, identificadores, ejes tridimensionales, etc. Utilidades que demuestren el funcionamiento y uso de las funciones anteriormente comentadas Ser utilizada en programas realizados con lenguaje Java La ejecución de las funciones ha de realizarse en lenguaje nativo 1.3.2. Entorno 1.3.2.1. Interfaz con el usuario Dado que esta librería está dirigida a profesionales del mundo de la informática que ya han de contar con unos conocimientos previos la interfaz con el usuario será lo más parecida a la librería ArUco posible. 1.3.2.2. Entorno de programación El programa va a ser realizado en el entorno de programación NetBeans en su versión 7.3.1, que es de gran utilidad, dadas las herramientas de las que provee al programador, tales como depurador de código, realce de sintaxis, fácil navegación, etc. 1.3.2.3. Ambiente de ejecución Al ser una librería desarrollada en lenguaje Java se espera que sea ejecutada en cualquier máquina que cuente con la Máquina Virtual de Java SE. 1.3.2.4. Vida esperada Se espera que cuente con una vida esperada que vaya desde media a larga aunque, lógicamente, nuestro objetivo sería que la librería tuviera una vida tan larga como la utilización de técnicas de Realidad Aumentada. Sin embargo, con el avance de la informática, es lógico pensar que estas técnicas mejorarán o se crearán otras nuevas y que estos futuros cambios deberían ser incluidos en nuestra librería para que siguiera siendo utilizada. Por otra parte, el uso y la prueba de la librería pondrán de manifiesto necesidades de mejora que tendrán que ser resueltas en el futuro. 1.3.2.5. Ciclo de mantenimiento La librería que se pretende desarrollar será fruto de un Proyecto Fin de Carrera y, por tanto, su autor no será responsable de su mantenimiento en el futuro. Sin embargo, se va 4 Capítulo 1. Introducción y objetivos a utilizar un diseño de clases y elaborar una documentación que facilitará revisiones y ampliaciones en el futuro. 1.3.2.6. Competencia En el capítulo Antecedentes, se nombran algunas de las librerías de realidad aumentada existentes en la actualidad. Cabe resaltar que pocas pueden utilizarse con el lenguaje Java sin ser de pago. 1.3.2.7. Aspecto externo El soporte utilizado para el transporte y distribución del software será CD-R. Este soporte contendrá instrucciones para su instalación, uso, funcionamiento, ejecución, etc. Por supuesto, también contendrá la librería y los programas de ejemplo. Además, se podrá descargar un paquete desde internet con el mismo contenido. 1.3.3. Calidad y fiabilidad Es muy importante maximizar la calidad de nuestra librería, porque esta característica está muy demandada hoy en día. Por esta razón, vamos a utilizar herramientas para la creación del software que ya están contrastadas a nivel mundial, tales como NetBeans y la Máquina Virtual de Java. En cuanto a la fiabilidad, será maximizada gracias a las pruebas a las que será sometida la librería para intentar minimizar la cantidad de errores que puedan ocurrir por haber tenido fallos en el diseño, la planificación o la codificación. 1.3.4. Programa de tareas La estimación de tiempos para las tareas es la siguiente: Estudio del problema: aunque durante todo el proyecto se continuará con el estudio del problema y los lenguajes utilizados se estima que habría que dedicar unas dos semanas al inicio del proyecto para poder comenzar a trabajar. Análisis y diseño: una vez realizado el estudio del problema se estima que habría que dedicar unas tres semanas a tareas de análisis y diseño. Implementación: se estima que esta fase puede durar unas doce semanas, realizando tareas de las dos siguientes fases también. Pruebas: aunque se irán realizando pruebas a medida que se realice la implementación, se estima que deberían dedicarse unas dos semanas a las pruebas. 5 Capítulo 1. Introducción y objetivos Documentación: se irá realizando documentación durante todo el proyecto pero se dedicarán unas dos semanas al final a la realización de esta tarea mientras se realizan las últimas pruebas. Teniendo en cuenta que algunas de las actividades pueden intercalarse se considera que la duración del proyecto puede ser cercana a las 18 semanas si se entiende que se tendrá una dedicación total al mismo. En el siguiente diagrama se muestra la planificación temporal: Figura 1.3: Planificación temporal del proyecto fin de carrera 1.3.5. Pruebas Se deberá realizar una fase de pruebas, entre las que se encuentran las siguientes: Pruebas de unidad: pruebas de las clases por separado. Pruebas de integración: pruebas que recaerán sobre la unión de las distintas clases de la aplicación. Pruebas del software: pruebas de funcionamiento de la librería una vez terminada. Pruebas del sistema: se probará la comunicación y cohesión de la librería con el soporte de hardware y el sistema operativo. La descripción de cada prueba deberá incluir: El objetivo de la prueba. El problema detectado, si lo hay. La solución adoptada, en su caso. El capítulo de Pruebas informará sobre las pruebas realizadas a la librería informática desarrollada. 6 Capítulo 1. Introducción y objetivos 1.3.6. Seguridad Se ha de garantizar la seguridad, tanto sobre el hardware en el que se ejecute la librería, como sobre el software (Sistema Operativo y otros programas) que se encuentre instalado en él. La seguridad es un aspecto muy importante hoy en día. Se intentará controlar lo máximo posible las operaciones que puedan suponer un problema para la seguridad del sistema informático en el que se ejecute nuestra librería. Algunas de estas operaciones son: las lecturas o escrituras en el disco duro, el acceso a memoria, las violaciones de segmento, etc. La librería se diseñará y codificará teniendo en cuenta que debe controlar estas operaciones para que no afecten a la estabilidad del sistema. En cuanto a la licencia del producto, el software que se desarrollará se establece como una librería totalmente libre de distribución, copia y uso, debido a su carácter estrictamente académico; es por eso que carece de cualquier ánimo de lucro o carácter privativo, no existiendo protección contra su libre distribución. Para evitar cualquier acción malintencionada ligada con la copia o la reproducción total o parcial, así como cualquier remuneración económica por su distribución, la aplicación se atiene a la licencia GPL de GNU. 1.4. Objetivos 1.4.1. Objetivo principal El objetivo principal del presente proyecto de fin de carrera es realizar un wrapper mediante JNI que permita utilizar las funciones de la librería ArUco desde Java. 1.4.2. Objetivos secundarios El objetivo principal del proyecto se puede descomponer en los siguientes objetivos secundarios: Realizar una librería con las siguientes características: • Posibilidad de ser utilizada en Java. • Posibilidad de ofrecer las mismas funcionalidades que ArUco, siendo las más importantes: ◦ Creación de marcadores ◦ Creación de tableros ◦ Detección de marcadores ◦ Detección de tableros 7 Capítulo 1. Introducción y objetivos ◦ Funciones que permitan dibujar señales que demuestren el reconocimiento de los marcadores y tableros, tales como sus bordes, identificadores, ejes tridimensionales, etc. • La ejecución del código debe realizarse en lenguaje nativo, directamente en la propia ArUco. • La librería ha de venir acompañada con varios ejemplos que muestren su utilidad y funcionamiento, con el objetivo de que puedan ser utilizados con una función didáctica. • Como resultado final debe obtenerse tanto una librería para sistemas Linux como para sistemas Windows. 8 Capítulo 2 Antecedentes 2.1. Aplicaciones de Realidad Aumentada Para tomar mayor consciencia de la Realidad Aumentada y las aplicaciones que tiene se han descargado y ejecutado algunas aplicaciones de diferentes ámbitos. Ejemplos de ello son las siguientes: Educación: añadir información virtual a una escena real abre muchas posibilidades en cuanto al aprendizaje. Existen ya ejemplos bastante interesantes de ello como los que se detallan a continuación: • learnAR[2]: es una herramienta de aprendizaje que utiliza la realidad aumentada para convertir la experiencia de aprendizaje en interactiva. Contiene diversos paquetes que van desde los idiomas hasta la geometría, pasando por la biología o la química. Esta aplicación utiliza la detección de marcadores para mostrar los elementos virtuales en pantalla. En la siguiente imagen puede apreciarse cómo detecta un marcador para mostrar, sobre una imagen real, los órganos de manera virtual: 9 Capítulo 2. Antecedentes Figura 2.1: Captura de pantalla de learnAR • Google Sky Map[3]: con esta aplicación gratuita se pueden obtener conocimientos de astronomía. Permite conocer el nombre de estrellas, planetas y constelaciones, además de ayudar a buscar en el cielo las estrellas o planetas que nos interesen gracias a su función de búsqueda, que sepuede apreciar en la siguiente imagen: Figura 2.2: Captura de pantalla de Google Sky Map Publicidad: es un campo en el que se están produciendo muchos desarrollos. Ya existen aplicaciones capaces de mostrar un trailer de una película simplemente si se 10 Capítulo 2. Antecedentes enfoca un cartel de la misma con la cámara del móvil o mostrar un modelo 3D de un reloj y permitir colocarlo virtualmente en la muñeca del usuario si se enfoca un anuncio del mismo. Algunos ejemplos de este tipo de aplicaciones son: • blippAR [4]: esta aplicación está disponible para Android, iOS y Windows Phone. Su objetivo es el de ofrecer contenido adicional y exclusivo cuando se enfocan con la cámara determinados objetos que pueden ir desde carteles promocionales hasta productos reales. En la captura siguiente se muestra la detección de una botella de ketchup de una marca determinada y la adición de contenido virtual, en este caso, un libro de recetas: Figura 2.3: Captura de pantalla de BlippAR Ocio: la industria del ocio en la informática está creciendo muchísimo en los últimos años, generando más ingresos que industrias como el cine. Aparecen nuevas oportunidades para jugar y divertirse gracias a la realidad aumentada. Algunos ejemplos de juegos actuales son los siguientes: 11 Capítulo 2. Antecedentes • Ingress[5]: en este juego creado por Google se hace uso de la realidad aumentada mediante posicionamiento GPS. Los jugadores pueden elegir entre dos facciones y han de moverse a localizaciones reales donde se encuentran “portales” virtuales que deben controlar. A continuación puede observarse una captura de pantalla de este juego en la que se observa un portal en la plaza de toros de Córdoba: Figura 2.4: Captura de pantalla de Ingress Accesibilidad: gracias a la información adicional que ofrece la realidad aumentada se pueden crear utilidades que permitan mejorar la accesibilidad en los entornos en los que sea necesario. Ejemplos de este tipo de aplicación de la Realidad Aumentada son: • Word Lens[6]: esta aplicación permite traducir carteles de unos idiomas a otros. Es especialmente útil si se viaja a una zona sin conocer el idioma y cuenta con idiomas como inglés, italiano, alemán, portugués, ruso y español. Necesita buena iluminación y no detecta texto manuscrito o muy estilizado. A 12 Capítulo 2. Antecedentes continuación, puede observarse un ejemplo de su funcionamiento: Figura 2.5: Fotografía de un teléfono móvil ejecutando Word Lens 2.2. Visión Artificial: OpenCV Para poder añadir información virtual en un entorno real se ha de identificar de alguna manera la situación espacial del entorno. Aunque existen librerías y aplicaciones que utilizan el posicionamiento GPS en este proyecto se tiene interés por las aplicaciones de Realidad Aumentada que utilizan marcadores. Por esta razón, para poder añadir información virtual sobre los marcadores primero hay que detectarlos. El objetivo de usar una librería de Visión Artificial no es otro que el de poder detectar los elementos gráficos (en este caso marcadores y tableros) que nos interesan. Por esta razón, la librería ArUco utiliza funciones de OpenCV para adquirir y procesar las imágenes que recibe, con el objetivo de identificar los marcadores que estas imágenes contengan. OpenCV está liberada bajo licencia BSD por lo que es libre tanto para uso académico como comercial. Tiene interfaces en C++, C, Python y Java (lo cual es interesante para este proyecto) y está soportado en sistemas Windows, Linux, Mac OS, iOS y Android. Esta librería es ampliamente utilizada en el mundo de la Visión Artificial y cuenta con gran cantidad de documentación (libros, tutoriales, guías de uso, documentación de 13 Capítulo 2. Antecedentes la API, etc). Además de la Realidad Aumentada, esta y otras librerías de Realidad Aumentada tienen áreas de aplicación tales como las siguientes: Sistemas de reconocimiento facial Interacción hombre-máquina Robótica Identificación de objetos Segmentación y reconocimiento de imágenes Comprensión y seguimiento de movimiento A continuación, se muestra una imagen del vehículo Stanley1 que es un coche autónomo creado por la Universidad de Stanford que ganó la competición DARPA Grand Challenge de vehículos autónomos en 2005. Este coche utilizaba OpenCV para sus labores de Visión Artificial. Figura 2.6: Coche autónomo Stanley 2.3. Realidad Aumentada: librerías existentes Como ya se comentó en la introducción, la Realidad Aumentada permite combinar tanto la información real del entorno como información virtual mediante distintos sistemas tales como reconocimiento de patrones e imágenes o posicionamiento GPS. 1 “Stanley2”. Licensed under Public domain via Wikimedia Commons - http://commons. wikimedia.org/wiki/File:Stanley2.JPG\#mediaviewer/File:Stanley2.JPG 14 Capítulo 2. Antecedentes Para poder realizar aplicaciones que utilicen estas posibilidades con un objetivo concreto existen distintas librerías que los programadores pueden utilizar. Algunas de estas librerías se detallan a continuación: ARToolKit[10]: es una librería de Realidad Aumentada mediante patrones. Permite posicionar y orientar la cámara con respecto a los marcadores y posicionar objetos 3D sobre los mismos. El lenguaje que se ha de usar con esta libería es C, aunque también permite otros lenguajes como Java o Matlab. Aunque esta librería está disponible para Java, la mentablemente está algo desactualizada ya que su última versión es para Windows XP. Su licencia es GNU/GPL. ARLab[11]: es un SDK de Realidad Aumentada que ofrece opciones muy diversas tales como Realidad Aumentada mediante geolocalización, reconocimiento y seguimiento de imágenes, seguimiento de objetos, botones virtuales y un motor 3D. Es de pago y permite desarrollos tanto para Android como para iOS. Figura 2.7: Logotipo de la librería de Realidad Aumentada ARLab droidAR[12]: es un marco de trabajo para utilizar realidad aumentada en Android. Las posibilidades que ofrecen son tanto la detección de patrones como el uso de geolocalización. Inicialmente fue desarrollado por Simon Heinen en 2010 y ahora es desarrollado por la comunidad y está liberado bajo licencia GNU GPL v3. GRATF[13]: su objetivo es el de ofrecer una librería que haga reconocimiento y estimación de la posición de marcadores dentro de imágenes o vídeos. El lenguaje de programación de esta librería es C#. A continuación se muestra un marcador de esta librería. 15 Capítulo 2. Antecedentes Figura 2.8: Ejemplo de un marcador de la librería de Realidad Aumentada GRATF ArUco[7]: una librería de Realidad Aumentada minimalista basada en OpenCV y cuyas principales funcionalidades son la detección de marcadores y tableros de marcadores con una línea de código en C++. Cuenta con la posibilidad de utilizar hasta 1024 marcadores distintos. Lógicamente, también permite la creación de estos marcadores y tableros. El lenguaje de esta librería es C++. 2.4. ArUco: visión general Ya que la librería de este Proyecto Fin de Carrera accederá a la de ArUco vamos a comentar brevemente sus distintas clases. Clase Board (definida en board.h) Clase BoardConfiguration (definida en board.h) Clase BoardDetector (definida en boarddetector.h) Clase CameraParameters (definida en cameraparameters.h) Clase CvDrawingUtils (definida en cvdrawingutils.h) Clase FiducidalMarkers (definida en arucofidmarkers.h) Clase Marker (definida en markers.h) Clase MarkerCandidate (definida en markerdetector.h) Clase MarkerDetector (definida en markerdetector.h) Estructura MarkerInfo (definida en board.h) 16 Capítulo 2. Antecedentes 2.4.1. Clase Board La clase Board representa un tablero con varios marcadores. Un tablero contiene varios marcadores para que sean detectados de manera más robusta. Esta clase hereda de la clase vector cuyos elementos son de tipo Marker. A continuación, puede observarse un tablero de marcadores: Figura 2.9: Tablero de marcadores de ArUco 2.4.2. Clase BoardConfiguration Esta clase, que es amiga de la clase Board, hereda de la clase vector de elementos MarkerInfo. Guarda la configuración de un tablero. 2.4.3. Clase BoardDetector Esta clase sirve para detectar los tableros de ArUco. Cuenta con la función detect que puede detectar el tablero que se encuentre en la imagen que se le pasa como parámetro. 2.4.4. Clase CameraParameters Representa la clase en la que se guardan los parámetros de la cámara. Estos parámetros son la matriz de la cámara, el coeficiente de distorsión y el tamaño de la imagen. 2.4.5. Clase CvDrawingUtils Son un conjunto de funciones para dibujar sobre las imágenes mediante OpenCV. Permite dibujar ejes y cubos en tres dimensiones sobre los tableros y marcadores. 17 Capítulo 2. Antecedentes 2.4.6. Clase FiducidalMarkers En esta clase se encuentran diversos métodos para crear imágenes con marcadores y tableros. 2.4.7. Clase Marker Esta clase representa un marcador. Es un vector de las cuatro esquinas del marcador (puntos Point2f de OpenCV). 2.4.8. Clase MarkerCandidate Esta clase representa un candidato a ser marcador y hereda de Marker. 2.4.9. Clase MarkerDetector Es la clase principal para la detección de marcadores. Su método más importante es detect, que recibe entre sus parámetros una imagen de tipo Mat de OpenCV y devuelve un vector con los marcadores encontrados en ella. 2.4.10. Estructura MarkerInfo Esta estructura contiene la representación 3D de un marcador. Hereda de la clase vector de puntos tridimensionales de OpenCV Point3f. 2.5. Programas de prueba de la librería ArUco La librería ArUco cuenta con unos programas (cuyo código incluye) para poder observar y entender mejor el funcionamiento de la librería, además de comprobar la correcta instalación de todos sus elementos. Algunas de estas utilidades son las siguientes: arucoBoardPix2meters arucoCreateBoard arucoCreateMarker arucoSelectoptimalmarkers arucoSimple arucoSimpleBoard arucoTest 18 Capítulo 2. Antecedentes arucoTestBoard arucoTestBoardGl 2.5.1. arucoBoardPix2meters Este programa convierte un fichero que contenga una configuración de tablero (BoardConfiguration expresado en píxeles en otro expresado en metros. 2.5.2. arucoCreateBoard Esta utilidad crea una imagen con un tablero cuyo aspecto viene dado por los parámetros que se le indican por línea de comandos. 2.5.3. arucoCreateMarker Con esta aplicación se puede crear un marcador cuyo aspecto viene dado por los parámetros que se le indican por línea de comandos. 2.5.4. arucoSelectoptimalmarkers El objetivo de este programa es seleccionar los n mejores marcadores en base a la distancia Hamming entre sí. Posteriormente, los guarda en una imagen. 2.5.5. arucoSimple Este programa realiza la detección de marcadores en una imagen o en un vídeo que recibe como entrada. Para cada marcador, dibuja sus bordes y los muestra por pantalla. En caso de haber información de 3D (parámetros de la cámara) dibuja un cubo 3D en cada marcador. 2.5.6. arucoSimpleBoard Esta aplicación es similar a la anterior pero, además de detectar los marcadores, detecta el tablero que los contiene. 2.5.7. arucoTest Este programa detecta en tiempo real los marcadores que encuentra en un vídeo o a través de la cámara web del ordenador. 19 Capítulo 2. Antecedentes 2.5.8. arucoTestBoard Este programa detecta en tiempo real los marcadores y el tablero que encuentra en un vídeo o a través de la cámara web del ordenador. 2.5.9. arucoTestBoardGl Este programa detecta en tiempo real los marcadores y el tablero que encuentra en un vídeo o a través de la cámara web del ordenador. Utiliza OpenGL para dibujar. 2.6. Librerías basadas en ArUco 2.6.1. js-aruco Js-aruco[14] es una librería de Javascript para aplicaciones de realidad aumentada basada totalmente en la librería ArUco. Es de código abierto y realizado por Juan Mellado en el año 2011. Ofrece acceso a la webcam mediante Flash y detección de marcadores, además de estimación de la pose en tres dimensiones. No permite la creación y detección de tableros. 2.7. Otros proyectos fin de carrera 2.7.1. Simulador gráfico de operaciones sobre ficheros Autor: José Carlos Garrido Pérez Director: Dr. Nicolás Luis Fernández García Presentado en: diciembre de 2011 Descripción: al igual que el presente proyecto, utiliza el lenguaje Java dado que su objetivo era poder ser ejecutado tanto en Linux como en Windows. Se ha utilizado como guía para algunas partes de la documentación por ser del mismo autor que el presente proyecto. Es una aplicación con el objetivo de mostrar de manera gráfica y didáctica a los alumnos el funcionamiento de las distintas organizaciones de ficheros (apilo, indexada, árboles, hashing, etc.). La aplicación resultante de este proyecto se denomina GraFich y se puede observar la apariencia del tutorial de estructuras de ficheros a continuación: 20 Capítulo 2. Antecedentes Figura 2.10: Tutorial de GraFich 2.8. Justificación del proyecto Como se ha mostrado anteriormente, existen pocas alternativas en cuanto a librerías de Realidad Aumentada no privativas que puedan ser usadas en Java. Además, la librería ArUco ya cuenta con una cantidad de usuarios importante y se considera necesario intentar aumentar este número de usuarios ofreciendo la alternativa de programar en Java sus aplicaciones con ArUco. Además, se desea que esta librería funcione en sistemas tanto Linux como Windows (en versiones recientes como Windows 7), por lo que no se ha encontrado librería que cumpla con todos los requisitos. A continuación, se muestra un cuadro con las necesidades que se pretenden cubrir: Características Creación de marcadores Creación de tableros Detección de marcadores Detección de tableros Programación en Java Licencia libre Ejec. en Linux y Windows rec. ARToolKit X X Librería ARLab DroidAR X X X X X GRATF X X X X X Tabla 2.1: Comparativa entre los antecedentes y el proyecto 21 Proyecto X X X X X X X Capítulo 3 Restricciones y recursos Para desarrollar la aplicación planteada, se van a tener en cuenta una serie de restricciones que nos harán tomar decisiones para llegar a desarrollar un programa de la calidad esperada para un proyecto fin de carrera, acorde con las necesidades de los usuarios. Se consideran dos tipos de factores en las restricciones: Factores iniciales Factores estratégicos 3.1. Factores iniciales Para el desarrollo del proyecto se consierarán los siguientes factores dato: Utilización de la librería ArUco: la definición del proyecto hace referencia a la utilización de esta librería para la detección de marcadores y tableros. Utilización del lenguaje de programación Java: la definición del proyecto hace referencia a la utilización de este lenguaje de programación. El objetivo es poder ampliar el número de usuarios gracias a la popularidad de Java y también poder realizar una librería que funcione tanto en sistemas Linux como Windows. 3.2. Factores estratégicos A lo largo del proceso de desarrollo de este proyecto se han planteado diferentes alternativas que nos han hecho tomar decisiones con el objeto de seleccionar las opciones que mejor se adapten a los objetivos. Estas decisiones conforman los factores estratégicos del proyecto que se describen a continuación: Entorno de desarrollo: NetBeans Modo de acceso a las funciones de ArUco: JNI 23 Capítulo 3. Restricciones y recursos 3.2.1. Entorno de desarrollo: NetBeans Las dos alternativas más importantes que se han barajado en la elección para el entorno de desarrollo son las siguientes: NetBeans: es un entorno de desarrollo multiplataforma cuyo código ha sido escrito en Java. Es ampliamente utilizado para desarrollar programas en Java. Este proyecto de código abierto fue fundado por Sun Microsystems en el año 2000. Cuenta con diferentes módulos que pueden añadirse para programar en otros lenguajes o tener características especiales. Eclipse: al igual que NetBeans es un entorno de desarrollo multiplataforma y de código abierto. Eclipse contiene un editor de texto avanzado, su propio depurador de código y un intérprete propio de lenguaje Java. Ambos entornos de programación cuentan con las siguientes ventajas: Resaltado de sintaxis del lenguaje Java con diferentes colores según si el texto es un tipo de variable, clase, cadena de texto, etc., lo que es muy útil para evitar errores al escribir el código. Un mecanismo mediante el cual, al escribir el nombre de una clase, puedes ver una lista de los métodos que contiene, de manera que ayuda al programador a llamar a uno de ellos. Permite ejecutar applets sin necesidad de utilizar un navegador externo. Un mecanismo de gestión de variables que revisa el ámbito de cada una, su valor predeterminado, o las referencias a la misma antes y después de su declaración. Después de este análisis y algunas pruebas, se ha decidido que el entorno de desarrollo a utilizar sea NetBeans debido a que se ha encontrado más facilidad para utilizar JNI en este sistema. 3.2.2. Modo de acceso a las funciones de ArUco: JNI Para poder realizar llamadas al código nativo de la librería existían las siguientes alternativas: Volver a escribir la librería: esta opción alargaría la duración del proyecto y no se tendría garantía de poder conseguir un resultado mejor. Además, las funciones de ArUco en C++ ya han sido utilizadas, probadas y depuradas por muchos programadores por lo que usar el código nativo se ha considerado una opción interesante. 24 Capítulo 3. Restricciones y recursos JNA: esta opción es más simple que JNI y funciona bien para C. Sin embargo, según el estudio realizado se dificulta el uso de clases C++ con este sistema, cosa que sí que permite JNI. JNI: su manejo es algo más complejo que JNA pero ofrece buenas soluciones para mapear las clases y según los articulos consultados realiza una gestión de memoria más eficiente. 3.3. Recursos 3.3.1. Recursos de hardware 3.3.1.1. Recursos de hardware para el desarrollo Ordenador personal portátil Sony Vaio VCPEH2M0E: • Chipset de la placa base: Intel HM65 Express • CPU Intel Core i5-2430M • Memoria 4 GB DDR3 1333 MHz • Disco Duro 750GB SATA 5400 RPM • Pantalla VAIO Display 15,5"Panorámica (16:9) WXGA de 1366 x 768 • VGA NVIDIA GeForce 410M GPU con 1 GB VRAM Impresora láser Xerox Phaser 6000 3.3.1.2. Recursos de hardware para uso del programa Los requisitos que se establecen como mínimos y necesarios para que el programa funcione, son los requisitos mínimos declarados por Sun Microsystems para que la Máquina Virtual de Java pueda funcionar correctamente. 3.3.2. Recursos de software Los programas que se han elegido para llevar a cabo este proyecto son: Windows 7 Ultimate Ubuntu 14.04 Netbeans: entorno de desarrollo que se utilizará para programar la aplicación Máquina virtual de Java: versión de Java 1.7.0_55. Kile 2.1.3: entorno del lenguaje LATEXpara la realización de los manuales. 25 Capítulo 3. Restricciones y recursos 3.3.3. Recursos humanos Dirección del proyecto: don Rafael Muñoz Salinas Diseño y desarrollo: José Carlos Garrido Pérez 26 Capítulo 4 Especificación de requisitos 4.1. Requisitos funcionales Los requisitos funcionales representan todos los servicios que el sistema deberá proporcionar al usuario, además de cómo se comporta el sistema en cada una de las acciones llevadas a cabo para rendir esos servicios. Los requisitos funcionales están denotados por las letras RF, seguidas de un guión y un número de requisito que lo clasificará como único en el sistema. Para la librería que se desea desarrollar, son los siguientes: RF-1: Ha de poder ser utilizado en programas desarrollados en lenguaje Java. RF-2: Ha de poder ser utilizado tanto en Linux como en Windows. RF-3: La ejecución de las funciones ha de realizarse, siempre que sea posible, en la librería ArUco (código nativo). RF-4: Ha de permitir la creación de marcadores. RF-5: Ha de permitir la detección de marcadores. RF-6: Ha de permitir la creación de tableros. RF-7: Ha de permitir la detección de tableros. RF-8: Una vez detectados los marcadores ha de permitir que se dibujen sus bordes y se muestre su identificador en las imágenes. RF-9: Una vez detectados los marcadores ha de permitir dibujar los ejes de la escena sobre ellos y un cubo en 3 dimensiones si se cuenta con la información necesaria. RF-10: Una vez detectado el tablero ha de permitir dibujar los ejes de la escena sobre él y un cubo en 3 dimensiones si se cuenta con la información necesaria. 27 Capítulo 4. Especificación de requisitos RF-11: Ha de venir acompañado con algunas utilidades que demuestren el funcionamiento de la librería y tengan un objetivo didáctico. RF-12: Ha de venir acompañado de los archivos de ayuda necesarios para hacer accesible su instalación y utilización a los programadores que quieran usarlo. 28 Capítulo 5 Especificación del modelo de clases En este capítulo se va a especificar el modelo de clases de la librería en tres partes: Análisis de clases Java: que son las clases que los programadores van a tener que usar en su código y que están realizadas en el lenguaje de programación Java. Análisis de los ficheros C++ del “wrapper”: donde se encuentran las funciones de C++ que serán llamadas desde Java y que transformarán tanto los parámetros como los resultados a tipos que el lenguaje que corresponda pueda procesar. Análisis de los programas Java de ejemplo: con la librería se muestran algunos programas de ejemplo para poder entender mejor cómo funciona. También son analizados en este capítulo. 5.1. Análisis de clases Java Como se ha comentado se va a mostrar toda la información necsaria para poder utilizar las clases Java de la librería que es objeto del presente proyecto fin de carrera. Estas clases son listadas a continuación: Clase Board Clase BoardConfiguration Clase BoardDetector Clase CameraParameters Clase CvDrawingUtils Clase FiducidalMarkers Clase Marker 29 Capítulo 5. Especificación del modelo de clases Clase MarkerDetector Clase MarkerInfo 5.1.1. Clase Board La clase Board representa un tablero con varios marcadores. Un tablero contiene varios marcadores para que sean detectados de manera más robusta. Esta clase cuenta con el siguiente atributo privado: nativeHandle: es un atributo de tipo long donde se guardará la dirección de memoria del objeto Board ArUco que se cree y se asocie con este objeto Java. Además, cuenta con la declaración de las siguientes funciones nativas privadas que tendrán que definirse en el fichero JBoard.cpp del wrapper de C++: JBoard: llamará al constructor vacío de la clase Board de Aruco. JBoard: llamará al constructor de copia de la clase Board de Aruco. JsaveToFile: que llamará a la función saveToFile de la clase Board de Aruco. JreadFromFile: que llamará a la función readFromFile de la clase Board de Aruco. JglGetModelViewMatrix: que llamará a la función glGetModelViewMatrix de la clase Board de Aruco. Eleva una excepción si los parámetros extrínsecos de la cámara no son válidos. JOgreGetPoseParameters: que llamará a la función OgreGetPoseParameters de la clase Board de Aruco. Eleva una excepción si los parámetros extrínsecos de la cámara no son válidos. JgetRvec: que sirve para devolver una referencia al atributo Rvec de la clase Board de Aruco. Recibe la referencia de un objeto de tipo Mat creado en Java para guardar ahí la referencia al objeto Rvec de ArUco. JgetTvec: que sirve para devolver una referencia al atributo Tvec de la clase Board de Aruco. Recibe la referencia de un objeto de tipo Mat creado en Java para guardar ahí la referencia al objeto Tvec de ArUco. JgetBoardConf: que sirve para devolver el objeto conf de tipo BoardConfiguration que es un atributo de la clase Board de Aruco. Jget: que sirve para devolver un objeto Marker que se encuentra en el vector de marcadores que es Board en la posición que se le pasa como parámetro. 30 Capítulo 5. Especificación del modelo de clases Jsize: que sirve para devolver un entero con el tamaño del vector de marcadores que es Board haciendo uso de la función Board.size() heredada de la clase vector. Los métodos públicos de la clase Board son los siguientes: Board: constructor vacío de la clase que, crea el objeto Board de Java, así como el de ArUco y los enlaza escribiendo la dirección del segundo en nativeHandle. Todo esto se hace mediante la llamada a la función de código nativo JBoard. Board: constructor de copia de la clase. Recibe como parámetro un objeto de tipo Board y crea otra instancia que es copia de la anterior. Hace uso de la función nativa de constructor de copia JBoard. glGetModelViewMatrix: esta función recibe como parámetro un vector cuyos elementos son de tipo double. Este vector, denominado modelview_matrix será pasado como parámetro a la función nativa JglGetModelViewMatrix que se encargará de rellenarlo. En caso de que haya algún problema en la obtención de estos datos se elevará una excepción. OgreGetPoseParameters: esta función recibe y rellena (haciendo uso de la función nativa JOgreGetPoseParameters los siguientes parámetros: • position: vector de elementos de tipo double. • orientation: vector de elementos de tipo double. En caso de que haya algún problema en la obtención de estos datos se elevará una excepción. saveToFile: esta función recibe un parámetro de tipo String de Java llamado filePath y lo pasa como parámetro a la función nativa JsaveToFile. El resultado de esta llamada es que se grabará en el fichero indicado por filePath los datos correspondientes al tablero desde el que se llame. En caso de que haya algún problema con la creación del fichero, lanzará una excepción informando de este hecho. readFromFile: esta función recibe un parámetro de tipo String de Java llamado filePath y lo pasa como parámetro a la función nativa JreadFromFile. El resultado de esta llamada es que se leerá el fichero indicado por filePath cargando los datos correspondientes en el tablero desde el que se llame. En caso de que haya algún problema con la lectura del fichero, lanzará una excepción informando de este hecho. getRvec: esta función devuelve un objeto de tipo Mat de OpenCV que contiene el vector de rotación Rvec del tablero desde el que se llame. Para ello, hace uso de la 31 Capítulo 5. Especificación del modelo de clases función nativa JgetRvec a la que, mediante la función de Mat getNativeObjAddr se le pasa la dirección nativa del objeto Mat en la que se enlazará con Rvec. getTvec: esta función devuelve un objeto de tipo Mat de OpenCV que contiene el vector de traslación Tvec del tablero desde el que se llame. Para ello, hace uso de la función nativa JgetRvec a la que, mediante la función de Mat getNativeObjAddr se le pasa la dirección nativa del objeto Mat en la que se enlazará con Tvec. getBoardConf: esta función devuelve un objeto del tipo BoardConfiguration que se recibe haciendo una llamada a la función nativa JgetBoardConf. Este objeto conf es un atributo de los objetos Board de ArUco. get: esta función devuelve un objeto de tipo Marker que se encuentra en la posición dada por el parámetro pos que es de tipo entero dentro del vector de marcadores que es la clase de ArUco Board. Para obtener este objeto realiza una llamada a la función nativa Jget. size: esta función devuelve un entero con el tamaño del vector de marcadores que es la clase Board de ArUco. Para obtener este dato realiza una llamada a la función nativa Jsize. El diagrama de esta clase es el siguiente: Figura 5.1: Diagrama de clases de Board 5.1.2. Clase BoardConfiguration Esta clase, cuya clase equivalente en Aruco es amiga de la clase Board y hereda de la clase vector de elementos MarkerInfo, contiene la configuración del tablero. Cuenta con el siguiente atributo privado: 32 Capítulo 5. Especificación del modelo de clases nativeHandle: es un atributo de tipo long donde se guardará la dirección de memoria del objeto BoardConfiguration ArUco que se cree y se asocie con este objeto Java. Además, cuenta con la declaración de las siguientes funciones nativas privadas que tendrán que definirse en el fichero JBoardConfiguration.cpp del wrapper de C++: JBoardConfiguration: llamará al constructor vacío de la clase BoardConfiguration de Aruco. JBoardConfiguration: llamará al constructor de copia de la clase BoardConfiguration de Aruco. JsaveToFile: que llamará a la función saveToFile de la clase BoardConfiguration de Aruco. JreadFromFile: que llamará a la función readFromFile de la clase BoardConfiguration de Aruco. JisExpressedInMeters: que llamará a la función isExpressedInMeters de la clase BoardConfiguration de Aruco. JisExpressedInPixels: que llamará a la función isExpressedInPixels de la clase BoardConfiguration de Aruco. JgetIndexOfMarkerId: que llamará a la función getIndexOfMarkerId de la clase BoardConfiguration de Aruco. JgetIdList: que llamará a la función getIdList de la clase BoardConfiguration de Aruco. JgetMarkerInfo: que llamará a la función getMarkerInfo de la clase BoardConfiguration de Aruco. JgetMInfoType: que sirve para devolver un entero con el valor del atributo público mInfoType del objeto BoardConfiguration de Aruco. Jget: que sirve para devolver un objeto MarkerInfo que se encuentra en el vector de objetos MarkerInfo que es BoardConfiguration en la posición que se le pasa como parámetro. Jsize: que sirve para devolver un entero con el tamaño del vector de objetos MarkerInfo que es BoardConfiguration. Sus métodos públicos son los siguientes: 33 Capítulo 5. Especificación del modelo de clases BoardConfiguration: constructor vacío de la clase que, crea el objeto BoardConfiguration de Java, así como el de ArUco y los enlaza escribiendo la dirección del segundo en nativeHandle. Todo esto se hace mediante la llamada a la función de código nativo JBoard. BoardConfiguration: constructor de copia de la clase. Recibe como parámetro un objeto de tipo BoardConfiguration y crea otra instancia que es copia de la anterior. Hace uso de la función nativa de constructor de copia JBoardConfiguration. saveToFile: esta función recibe un parámetro de tipo String de Java llamado filePath y lo pasa como parámetro a la función nativa JsaveToFile. El resultado de esta llamada es que se grabará en el fichero indicado por filePath los datos correspondientes al tablero desde el que se llame. En caso de que haya algún problema con la creación del fichero, lanzará una excepción informando de este hecho. readFromFile: esta función recibe un parámetro de tipo String de Java llamado filePath y lo pasa como parámetro a la función nativa JreadFromFile. El resultado de esta llamada es que se leerá el fichero indicado por filePath cargando los datos correspondientes en el tablero desde el que se llame. En caso de que haya algún problema con la lectura del fichero, lanzará una excepción informando de este hecho. isExpressedInMeters: es una función booleana que devolverá True si el tablero está expresado en metros. isExpressedInPixels: es una función booleana que devolverá True si el tablero está expresado en píxeles. getIndexOfMarkerId: recibe como parámetro el entero que indica el id de un marcador y devuelve un entero que indica el índice de ese marcador en el tablero (si está en él). getIdList: recibe como parámetro un ArrayList de enteros en el que se guardarán los identificadores de los marcadores del tablero. Como parámetro opcional puede recibir una variable booleana denominada “append” que, en caso de ser verdadera hace que los identificadores se añadan al final del ArrayList. getMarkerInfo: devuelve la información de el marcador cuyo id se le pasa como parámetro. Esta información se encuentra en un objeto del tipo MarkerInfo. En caso de que el identificador que se le pasa como parámetro no se encuentre en el vector, se elevará una excepción. 34 Capítulo 5. Especificación del modelo de clases getMInfoType: devuelve un entero con el valor del atributo público mInfoType del objeto de Aruco. Si los datos extán en píxeles, devolverá un 0, si están en meetros, un 1 y, si no está definido un -1. get: devuelve el objeto de tipo MarkerInfo que se encuentre en la posición que indica el entero que se le pasa como parámetro. size: devuelve el número de objetos MarkerInfo que contiene. El diagrama de esta clase es el siguiente: Figura 5.2: Diagrama de clases de BoardConfiguration 5.1.3. Clase BoardDetector Esta clase sirve para detectar tableros de ArUco y cuenta con el siguiente atributo privado: nativeHandle: es un atributo de tipo long donde se guardará la dirección de memoria del objeto BoardDetector ArUco que se cree y se asocie con este objeto Java. Además, cuenta con la declaración de las siguientes funciones nativas privadas que tendrán que definirse en el fichero JBoardDetector.cpp del wrapper de C++: JBoardDetector: llamará al constructor vacío de la clase BoardDetector de Aruco. Jdetect: que llamará a la función detect de la clase BoardDetector de Aruco. JgetDetectedBoard: que llamará a la función getDetectedBoard de la clase BoardDetector de Aruco. 35 Capítulo 5. Especificación del modelo de clases JgetMarkerDetector: que llamará a la función getMarkerDetector de la clase BoardDetector de Aruco. JgetDetectedMarkers: que llamará a la función getDetectedMarkers de la clase BoardDetector de Aruco. JsetYPerpendicular: que llamará a la función setYPerpendicular de la clase BoardDetector de Aruco. Jdetect: que llamará a la función detect de la clase BoardDetector de Aruco en la versión que solamente recibe una imagen Mat como parámetro. Jdetect: que llamará a la función detect de la clase BoardDetector de Aruco en la versión alternativa basada en marcadores que ya han sido detectados. JsetParams: que llamará a la función setParams de la clase BoardDetector de Aruco en la versión que solamente recibe un objeto del tipo BoardConfiguration como parámetro. JsetParams: que llamará a la función setParams de la clase BoardDetector de Aruco en la versión que recibe, además de un objeto del tipo BoardConfiguration, los parámetros de la cámara y el tamaño del lado de los marcadores como parámetros. Los métodos públicos de la clase BoardDetector son: BoardDetector: constructor vacío de la clase que, crea el objeto BoardDetector de Java, así como el de ArUco y los enlaza escribiendo la dirección del segundo en nativeHandle. Todo esto se hace mediante la llamada a la función de código nativo JBoardDetector. detect: recibe una imagen y detecta los marcadores existentes en ella para después buscar el tablero indicado cuando se usó setParams. El valor devuelto es un flotante que indica la probabilidad de haber encontrado el marcador. Su parámetro es: • im: imagen con el formato Mat de OpenCV. En caso de que se produzca algún error, eleva una excepción. getDetectedBoard: devuelve un objeto de tipo Board con el tablero detectado. getMarkerDetector: devuelve un objeto de tipo MarkerDetector con el detector de marcadores interno. getDetectedMarkers: devuelve un ArrayList con los objetos Marker detectados. 36 Capítulo 5. Especificación del modelo de clases setYPerpendicular: por defecto, el eje Y está configurado para apuntar hacia arriba. Si se marca como falso será el Z el que apunte hacia arriba. detect (método alternativo): basado en marcadores que ya ha sido detectados. Recibe los siguientes parámetros: • detectedMarkers: que es el resultado que provee MarkerDetector. • BConf: el tablero cuya presencia queremos comprobar. • Bdetected: la información de salida del tablero detectado. • camMatrix: matriz de la cámara con los datos intrínsecos. Es opcional, si no se indica se usará una matriz nueva. • distCoeff: coeficientes de distorsión de la cámara. Es opcional, si no se indica se usará una matriz nueva. • markerSizeMeters: tamaño de los lados del marcador expresado en metros. Es opcional, si no se indica se usará el valor -1. En caso de que se produzca algún error, eleva una excepción. detect: mismo método que el anterior pero que utiliza el objeto de tipo CameraParameters en lugar de las matrices de distorsión y cámara por separado. • detectedMarkers: que es el resultado que provee MarkerDetector. • BConf: el tablero cuya presencia queremos comprobar. • Bdetected: la información de salida del tablero detectado. • CP: de tipo CameraParameters. • markerSizeMeters: tamaño de los lados del marcador expresado en metros. Es opcional, si no se indica se usará el valor -1. setParams: esta función ha de usarse si se planea realizar también detección de marcadores con esta clase. Su parámetro es: • bc: referencia a un objeto del tipo BoardConfiguration. setParams: esta función ha de usarse si se planea realizar también detección de marcadores con esta clase. • bc: referencia a un objeto de tipo BoardConfiguration. • cp: referencia a un objeto de tipo CameraParameters. • markerSizeMeters: variable real (que por defecto tiene su valor a -1). 37 Capítulo 5. Especificación del modelo de clases El diagrama de esta clase es el siguiente: Figura 5.3: Diagrama de clases de BoardDetector 5.1.4. Clase CameraParameters Representa la clase en la que se guardan los parámetros de la cámara. Esta clase cuenta con el siguiente atributo privado: nativeHandle: es un atributo de tipo long donde se guardará la dirección de memoria del objeto CameraParameters ArUco que se cree y se asocie con este objeto Java. Además, cuenta con la declaración de las siguientes funciones nativas privadas que tendrán que definirse en el fichero JCameraParameters.cpp del wrapper de C++: JCameraParameters: llamará al constructor vacío de la clase CameraParameters de Aruco. JCameraParameters: llamará al constructor de copia de la clase CameraParameters de Aruco. JCameraParameters: llamará al constructor parametrizado de la clase CameraParameters de Aruco. JreadFromFile: que llamará a la función readFromFile de la clase CameraParameters de Aruco. JisValid: que llamará a la función isValid de la clase CameraParameters de Aruco. JsetParams: que llamará a la función setParams de la clase CameraParameters de Aruco. JsaveToFile: que llamará a la función saveToFile de la clase CameraParameters de Aruco. 38 Capítulo 5. Especificación del modelo de clases JreadFromXMLFile: que llamará a la función readFromXMLFile de la clase CameraParameters de Aruco. Jresize: que llamará a la función isValid de la clase CameraParameters de Aruco. JglGetProjectionMatrix: que llamará a la función glGetProjectionMatrix de la clase CameraParameters de Aruco. JOgreGetProjectionMatrix: que llamará a la función OgreGetProjectionMatrix de la clase CameraParameters de Aruco. JgetCameraLocation: que llamará a la función getCameraLocation de la clase CameraParameters de Aruco. JgetCameraMatrix: que sirve para devolver un objeto de tipo Mat con el valor del atributo público CameraMatrix del objeto CameraParameters de Aruco. JgetDistorsion: que sirve para devolver un objeto de tipo Mat con el valor del atributo público Distorsion del objeto CameraParameters de Aruco. JgetCamSizeWidth: que obtendrá la anchura de la imagen proveniente del objeto de tipo Size con el valor del atributo público CamSize.width del objeto CameraParameters de Aruco. JgetCamSizeHeight: que obtendrá la altura de la imagen proveniente del objeto de tipo Size con el valor del atributo público CamSize.height del objeto CameraParameters de Aruco. Los métodos públicos de la clase CameraParameters son: CameraParameters: constructor vacío de la clase que, crea el objeto CameraParameters de Java, así como el de ArUco y los enlaza escribiendo la dirección del segundo en nativeHandle. Todo esto se hace mediante la llamada a la función de código nativo JCameraParameters. CameraParameters: constructor de copia de la clase. Recibe como parámetro un objeto de tipo CameraParameters y crea otra instancia que es copia de la anterior. Hace uso de la función nativa de constructor de copia JCameraParameters. CameraParameters: constructor parametrizado de la clase. Recibe como parámetros dos objetos de tipo Mat con la matriz de la cámara y el coeficiente de distorsión, además de un objeto de tipo Size con el tamaño de la imagen. getCameraLocation: devuelve la posición de la cámara en el sistema referencial dado por los vectores de rotación y translación pasados como parámetros. Los dos 39 Capítulo 5. Especificación del modelo de clases vectores de entrada son del tipo Mat de OpenCV y el punto de salida es del tipo Point3 también de OpenCV. setParams: función que fija los parámetros de la cámara. Recibe como parámetros dos objetos de tipo Mat con la matriz de la cámara y el coeficiente de distorsión, además de un objeto de tipo Size con el tamaño de la imagen. isValid: función de tipo booleano que devuelve verdadero si el objeto es válido y falso en caso contrario. readFromFile: lee los parámetros de la cámara desde un fichero. Recibe como parámetro la ruta hasta el fichero. En caso de no poder leer el fichero, lanza una excepción. saveToFile: guarda los parámetros de la cámara en un fichero. Recibe como parámetro la ruta hasta el fichero (String) y un booleano opcional (por defecto con valor verdadero) que indica si el fichero se desea guardar como XML o no. En caso de no poder escribir en el fichero, lanza una excepción. readFromXMLFile: lee los parámetros de la cámara desde un fichero YAML generado con la utilidad método de calibrado de OpenCV 2.2. En caso de no poder leer el fichero, lanza una excepción. resize: recibe una variable de tipo Size de OpenCV para ajustar el parámetro de tamaño de la imagen indicado. Si los parámetros de la cámara no son válidos a la hora de redimensionar, elevará una excepción. glGetProjectionMatrix: dados los parámetros intrínsecos de la cámara, devuelve la matriz GL_MODELVIEW para OpenGL. Los parámetros que recibe son los siguientes: • orgImgSize: tamaño de la imagen original. Es un objeto de tipo Size de OpenCV. • size: tamaño de la imagen o ventana donde se va a renderizar (que puede ser diferente de la imagen real de la cámara). Es un objeto de tipo Size de OpenCV. • proj_matrix: matriz de 16 reales la proyección de salida para OpenGL. Es un vector de variables de tipo double. • gnear,gfar: rango de renderización visible. Son variables de tipo double. • invert: indica si la matriz de proyección de salida tiene que producir una imagen horizontalmente invertida porque los datos de la imagen no han sido almacenados en el orden de glDrawPixels de abajo hacia arriba. Es de tipo booleano. 40 Capítulo 5. Especificación del modelo de clases En caso de que los parámetros extrínsecos no sean correctos, lanza una excepción. OgreGetProjectionMatrix: configura la cámara para un proyecto de Ogre. Tiene los mismos argumentos que glGetProjectionMatrix. En caso de que los parámetros extrínsecos no sean correctos, lanza una excepción. getCameraMatrix: función que devuelve un objeto de tipo Mat de OpenCV con la matriz de la cámara. getDistorsion: función que devuelve un objeto de tipo Mat de OpenCV con la matriz de distorsión. getCamSize: que devuelve un objeto de tipo Size con el tamaño de la imagen. El diagrama de esta clase es el siguiente: Figura 5.4: Diagrama de clases de CameraParameters 5.1.5. Clase CvDrawingUtils Es un conjunto de funciones para dibujar imágenes que utiliza OpenCV. Esta clase cuenta con el siguiente atributo privado: nativeHandle: es un atributo de tipo long donde se guardará la dirección de memoria del objeto CvDrawingUtils ArUco que se cree y se asocie con este objeto Java. Además, cuenta con la declaración de las siguientes funciones nativas privadas que tendrán que definirse en el fichero JCvDrawingUtils.cpp del wrapper de C++: JCvDrawingUtils: llamará al constructor vacío de la clase CvDrawingUtils de Aruco. Jdraw3dAxis: que llamará a la función draw3dAxis de la clase CvDrawingUtils de Aruco en la versión que recibe un marcador. 41 Capítulo 5. Especificación del modelo de clases Jdraw3dCube: que llamará a la función draw3dCube de la clase CvDrawingUtils de Aruco en la versión que recibe un marcador. Jdraw3dAxis: que llamará a la función draw3dAxis de la clase CvDrawingUtils de Aruco en la versión que recibe un tablero. Jdraw3dCube: que llamará a la función draw3dCube de la clase CvDrawingUtils de Aruco en la versión que recibe un tablero. Los métodos públicos de la clase CvDrawingUtils son: CvDrawingUtils: constructor vacío de la clase que, crea el objeto CvDrawingUtils de Java, así como el de ArUco y los enlaza escribiendo la dirección del segundo en nativeHandle. Todo esto se hace mediante la llamada a la función de código nativo JCvDrawingUtils. draw3dAxis: esta función recibe como parámetros una imagen de tipo Mat, un objeto de tipo Marker y otro de tipo CameraParameters. Su cometido es el de dibujar en la imagen los ejes tridimensionales de la escena sobre el marcador que recibe. draw3dCube: esta función recibe como parámetros una imagen de tipo Mat, un objeto de tipo Marker y otro de tipo CameraParameters. Su cometido es el de dibujar en la imagen un cubo tridimensional sobre el marcador que recibe. draw3dAxis: esta función recibe como parámetros una imagen de tipo Mat, un objeto de tipo Board y otro de tipo CameraParameters. Su cometido es el de dibujar en la imagen los ejes tridimensionales de la escena sobre el tablero que recibe. draw3dCube: esta función recibe como parámetros una imagen de tipo Mat, un objeto de tipo Board y otro de tipo CameraParameters. Su cometido es el de dibujar en la imagen un cubo tridimensional sobre el tablero que recibe. El diagrama de esta clase es el siguiente: Figura 5.5: Diagrama de clases de CvDrawingUtils 42 Capítulo 5. Especificación del modelo de clases 5.1.6. Clase FiducidalMarkers En esta clase se encuentran diversos métodos para crear imágenes con marcadores y tableros. Posee la declaración de las siguientes funciones nativas privadas que tendrán que definirse en el fichero JFiducidalMarkers.cpp del wrapper de C++: JcreateMarkerImage: que llamará a la función createMarkerImage de la clase FiducidalMarkers de Aruco. Jdetect: que llamará a la función Jdetect de la clase FiducidalMarkers de Aruco. JgetMarkerMat: que llamará a la función JgetMarkerMat de la clase FiducidalMarkers de Aruco. JcreateBoardImage: que llamará a la función JcreateBoardImage de la clase FiducidalMarkers de Aruco. JcreateBoardImage_ChessBoard: que llamará a la función JcreateBoardImage_ChessBoard de la clase FiducidalMarkers de Aruco. JcreateBoardImage_Frame: que llamará a la función JcreateBoardImage_Frame de la clase FiducidalMarkers de Aruco. Los métodos públicos de la clase FiducidalMarkers son: createMarkerImage: crea un marcador con el identificador especificado utilizando una versión modificada del código hamming. Hay dos tipos de marcadores: • Marcadores de 10 bits: sus identificadores están en el intervalo 0-1023. • Marcadores de 3 bits: utilizados en aplicaciones que necesitan menos marcadores que han de ser pequeños. Sus identificadores se encuentran en el intervalo 2000-2006. Los parámetros que recibe son: • id: de tipo entero, indica el identificador del marcador que estamos creando. • size: de tipo entero, indica el tamaño del lado del marcador en píxeles. En caso de que el id indicado no sea válido, elevará una excepción. detect: detecta marcadores de ArUco de 10 bits. Recibe la imagen en la que se encuentran los marcadores (tipo Mat de OpenCV) y un entero con el número de rotaciones de 90 grados necesarias para situar el marcador en la posición correcta. Devolverá un -1 si la imagen que se le pasa no es un marcador válido y su identificador en caso de que sí lo sea (valor entero). 43 Capítulo 5. Especificación del modelo de clases getMarkerMat: es similar a createMarkerImage pero en lugar de devolver una imagen visible, devuelve una matriz binaria 8UC1 con la información del marcador cuyo identificador se le pasa como parámetro. En caso de que este identificador contenga un valor incorrecto, elevará una excepción. createBoardImage: crea una imagen imprimible del tablero. Recibe los siguientes parámetros: • gridSize: de tipo Size de OpenCV que indica el número de marcadores que habrá en el eje X y en el eje Y. • MarkerSize: de tipo un entero que contiene el tamaño del lado de los marcadores en píxeles. • MarkerDistance: de tipo entero que contiene la distancia entre marcadores en píxeles. • TInfo: que es un objeto de tipo BoardConfiguration en el que se escribirá la configuración del tablero creado. • excludedIds: que es un ArrayList de enteros con los identificadores que no se desea que aparezcan en el tablero. Este parámetro es opcional. createBoardImage_ChessBoard: crea una imagen imprimible del tablero de forma similar al aspecto de un tablero de ajedrez. Sus parámetros son: • gridSize: de tipo Size de OpenCV que indica el número de marcadores que habrá en el eje X y en el eje Y. • MarkerSize: de tipo un entero que contiene el tamaño del lado de los marcadores en píxeles. • TInfo: que es un objeto de tipo BoardConfiguration en el que se escribirá la configuración del tablero creado. • setDataCentered: es un parámetro de tipo booleano que indica si el centro se sitúa en el centro del tablero. En caso de que se ponga como falso el centro se sitúa en la esquina superior izquierda. Es un parámetro opcional que por defecto está a valor verdadero. • excludedIds: que es un ArrayList de enteros con los identificadores que no se desea que aparezcan en el tablero. Este parámetro es opcional. createBoardImage_Frame: crea una imagen imprimible del tablero con marco. Sus parámetros son: • gridSize: de tipo Size de OpenCV que indica el número de marcadores que habrá en el eje X y en el eje Y. 44 Capítulo 5. Especificación del modelo de clases • MarkerSize: de tipo un entero que contiene el tamaño del lado de los marcadores en píxeles. • TInfo: que es un objeto de tipo BoardConfiguration en el que se escribirá la configuración del tablero creado. • setDataCentered: es un parámetro de tipo booleano que indica si el centro se sitúa en el centro del tablero. En caso de que se ponga como falso el centro se sitúa en la esquina superior izquierda. Es un parámetro opcional que por defecto está a valor verdadero. • excludedIds: que es un ArrayList de enteros con los identificadores que no se desea que aparezcan en el tablero. Este parámetro es opcional. 5.1.7. Clase Marker Esta clase representa un marcador. En la clase C++ subyacente hereda de un vector con las cuatro esquinas del marcador (puntos Point2f ). Cuenta con el siguiente atributo privado: nativeHandle: es un atributo de tipo long donde se guardará la dirección de memoria del objeto Marker ArUco que se cree y se asocie con este objeto Java. Además, cuenta con la declaración de las siguientes funciones nativas privadas que tendrán que definirse en el fichero JMarker.cpp del wrapper de C++: JMarker: llamará al constructor vacío de la clase Marker de Aruco. JMarker: llamará al constructor de copia de la clase Marker de Aruco. JMarker: llamará al constructor parametrizado de la clase Marker de Aruco. JisValid: que llamará a la función isValid de la clase Marker de Aruco. Jdraw: que llamará a la función draw de la clase Marker de Aruco. JglGetModelViewMatrix: que llamará a la función glGetModelViewMatrix de la clase Marker de Aruco. JgetArea: que llamará a la función getArea de la clase Marker de Aruco. JgetPerimeter: que llamará a la función getPerimeter de la clase Marker de Aruco. JgetCenter: que llamará a la función getCenter de la clase Marker de Aruco. JcalculateExtrinsics: que llamará a la función calculateExtrinsics de la clase Marker de Aruco recibiendo un flotante (“markerSize”), un objeto de tipo CameraParameters (“CP”) y el booleano (“setYPerpendicular”). 45 Capítulo 5. Especificación del modelo de clases JcalculateExtrinsics: que llamará a la función calculateExtrinsics de la clase Marker de Aruco recibiendo un flotante (“markerSize”), la dirección de memoria de un objeto de tipo Mat (“cameraMatrix”), la dirección de memoria de otro objeto de tipo Mat (“distCoeff”) y el booleano (“setYPerpendicular”). JOgreGetPoseParameters: que llamará a la función OgreGetPoseParameters de la clase Marker de Aruco. JgetRvec: devuelve, en el objeto que se le pasa como parámetro, la matriz de rotación Rvec que es un atributo público del objeto Marker de Aruco. JgetTvec: devuelve, en el objeto que se le pasa como parámetro, la matriz de traslación Tvec que es un atributo público del objeto Marker de Aruco. JgetId: devuelve un entero con el valor del atributo público id del objeto Marker de Aruco. JgetSSize: devuelve un flotante con el valor del atributo público ssize del objeto Marker de Aruco. JlessThan: que llamará al operador < de la clase Marker de Aruco. JtoString: que llamará al operador << de la clase Marker de Aruco. Jsize: dado que dentro de Aruco Marker hereda de vector cuyos elementos son Point2f de OpenCV se estima interesante ofrecer la función size() de la clase vector) que es llamada mediante Jsize(). Jget: dado que dentro de Aruco Marker hereda de vector cuyos elementos son Point2f de OpenCV se estima interesante ofrecer la posibilidad de obtener los elementos del vector usando esta función. Los métodos públicos de la clase Marker son: Marker: constructor vacío de la clase que, crea el objeto Marker de Java, así como el de ArUco y los enlaza escribiendo la dirección del segundo en nativeHandle. Todo esto se hace mediante la llamada a la función de código nativo JMarker. Marker: constructor de copia de la clase. Recibe como parámetro un objeto de tipo Marker y crea otra instancia que es copia de la anterior. Hace uso de la función nativa de constructor de copia JMarker. Marker: constructor parametrizado que recibe un ArrayList que ha de contener cuatro objetos de tipo Point de OpenCV con las coordnadas de los puntos de las esquinas del marcador. Además, puede recibir un entero que contiene el identificador del marcador (en caso de que no se pase el entero, se utilizará el valor -1). 46 Capítulo 5. Especificación del modelo de clases isValid: es un método de tipo boleano que devuelve verdadero si el marcador es válido y falso en caso contrario. draw: recibe una imagen en la que dibuja líneas que delimitan al marcador detectado. Sus parámetros son: • img: es la imagen en la que se desea dibujar el marcador. Es del tipo Mat de OpenCV. • color: es una variable del tipo Scalar de OpenCV que contiene el valor del color de las líneas que delimitarán el marcador. • lineWidth: es un entero que indica anchura de la línea en píxeles. En caso de no seleccionarse ningún valor se utilizará el valor 1 por defecto. • writeId: variable booleana que, en caso de estar a valor verdadero, indica que se escriba el id del marcador detectado. Su valor por defecto es verdadero. calculateExtrinsics: calcula los valores de Rvec y Tvec con respecto de la cámara. Los parámetros de esta función son: • markerSize: tamaño del lado del marcador expresado en metros. Es una variable de tipo flotante. • CP: parámetros de la cámara. Es un objeto del tipo CameraParameters. • setYPerperdicular: en caso de ser verdadero, el eje Y será perpendicular a la superficie. En caso contrario lo será el eje Z. Este parámetro es opcional y su valor por defecto es verdadero. Si los parámetros de la cámara, el marcador o su tamaño no son válidos elevará una excepción. calculateExtrinsics: calcula los valores de Rvec y Tvec con respecto de la cámara. Los parámetros de esta función son: • markerSize: tamaño del lado del marcador expresado en metros. Es una variable de tipo flotante. • CameraMatrix: matriz con los parámetros de la cámara (fx,fy,cx,cy). Es del tipo Mat de OpenCV. • Distorsion: matriz con los parámetros de distorsión (k1,k2,p1,p2). Es del tipo Mat de OpenCV. Este parámetro es opcional, en caso de no indicarse se utilizará una matriz vacía. • setYPerperdicular: en caso de ser verdadero, el eje Y será perpendicular a la superficie. En caso contrario lo será el eje Z. Este parámetro es opcional y su valor por defecto es verdadero. 47 Capítulo 5. Especificación del modelo de clases Si los parámetros de la cámara, el marcador o su tamaño no son válidos elevará una excepción. glGetModelViewMatrix: dados los parámetros de la cámara extrínsecos devuelve la matriz GL_MODELVIEW para OpenGL. Recibe como parámetro: • modelview_matrix: matriz de doble precisión en la que se guardará la matriz GL_MODELVIEW. En caso de que los parámetros extrínsecos no sean correctos, lanza una excepción. OgreGetPoseParameters: devuelve el vector de posición y el cuaternión de orientación para un nodo de escena Ogre o para una entidad. Recibe los siguientes parámetros: • position: vector de tres reales. • orientation: vector de cuatro reales. En caso de que los parámetros extrínsecos no sean correctos, lanza una excepción. getCenter: devuelve el centroide del marcador. El tipo dvuelto es Point de OpenCV. getPerimeter: devuelve un flotante que contiene el perímetro del marcador. getArea: devuelve un flotante que contiene el área del marcador. getId: devuelve un entero con el valor del identificador del marcador en cuestión. getSSize: devuelve un valor real que contiene el tamaño de los lados del marcador en metros. getTvec: devuelve la matriz de traslación con respecto de la cámara. Utiliza el tipo de dato Mat de OpenCV. getRvec: devuelve la matriz de rotación con respecto de la cámara. Utiliza el tipo de dato Mat de OpenCV. lessThan: devuelve un booleano que será verdadero si el identificador del marcador desde el que se llama es menor que el del que se le pasa como parámetro y falso en caso contrario. toString: devuelve una cadena con el identificador del marcador, las coordenadas de sus esquinas y los valores de Rvec y Tvec. size: devuelve un entero con la cantidad de puntos (deben ser 4) que tiene guardados este marcador. 48 Capítulo 5. Especificación del modelo de clases get: devuelve la esquina del marcador (un punto Point de OpenCV) que ocupa la posición que se le indique en el entero que se le pasa como parámetro. El diagrama de esta clase es el siguiente: Figura 5.6: Diagrama de clases de Marker 5.1.8. Clase MarkerDetector Es la clase principal para realizar las acciones de detección de marcadores. Esta clase cuenta con el siguiente atributo privado: nativeHandle: es un atributo de tipo long donde se guardará la dirección de memoria del objeto MarkerDetector ArUco que se cree y se asocie con este objeto Java. Además, cuenta con la declaración de las siguientes funciones nativas privadas que tendrán que definirse en el fichero JMarkerDetector.cpp del wrapper de C++: JMarkerDetector: llamará al constructor vacío de la clase MarkerDetector de Aruco. JMarkerDetector: llamará al constructor de copia de la clase MarkerDetector de Aruco. Jdetect: que llamará a la función detect de la clase MarkerDetector de Aruco. 49 Capítulo 5. Especificación del modelo de clases Jdetect: que llamará a la función detect de la clase MarkerDetector de Aruco. JgetDesiredSpeed: que llamará a la función getDesiredSpeed de la clase MarkerDetector de Aruco. JsetDesiredSpeed: que llamará a la función setDesiredSpeed de la clase MarkerDetector de Aruco. JsetThresholdMethod: que llamará a la función setThresholdMethod de la clase MarkerDetector de Aruco. JgetThresholdMethod: que llamará a la función getThresholdMethod de la clase MarkerDetector de Aruco. JsetThresholdParams: que llamará a la función setThresholdParams de la clase MarkerDetector de Aruco. JgetThresholdParam1: que llamará a la función getThresholdParams de la clase MarkerDetector de Aruco y obtendra el primero de los dos parámetros. JgetThresholdParam2: que llamará a la función getThresholdParams de la clase MarkerDetector de Aruco y obtendra el segundo de los dos parámetros. JgetThresholdedImage: que llamará a la función getThresholdedImage de la clase MarkerDetector de Aruco. JsetCornerRefinementMethod: que llamará a la función setCornerRefinementMethod de la clase MarkerDetector de Aruco. JgetCornerRefinementMethod: que llamará a la función getCornerRefinementMethod de la clase MarkerDetector de Aruco. JsetMinMaxSize: que llamará a la función setMinMaxSize de la clase MarkerDetector de Aruco. JgetMinSize: que llamará a la función getMinMaxSize de la clase MarkerDetector de Aruco y obtendrá el valor del mínimo. JgetMaxSize: que llamará a la función getMinMaxSize de la clase MarkerDetector de Aruco y obtendrá el valor del máximo. JenableErosion: que llamará a la función enableErosion de la clase MarkerDetector de Aruco. JpyrDown: que llamará a la función pyrDown de la clase MarkerDetector de Aruco. 50 Capítulo 5. Especificación del modelo de clases Esta clase cuenta con dos enumeraciones públicas (“enum” en Java), son las siguientes: ThresholdMethods: en la que se encuentran los siguientes tipos de umbral: • FIXED_THRES • ADPT_THRES • CANNY CornerRefinementMethod: en la que se encuentran los siguientes tipos de refinado de las esquinas: • NONE • HARRIS • SUBPIX • LINES Los métodos públicos de la clase MarkerDetector son: MarkerDetector: constructor vacío de la clase que, crea el objeto MarkerDetector de Java, así como el de ArUco y los enlaza escribiendo la dirección del segundo en nativeHandle. Todo esto se hace mediante la llamada a la función de código nativo JMarkerDetector. MarkerDetector: constructor de copia de la clase. Recibe como parámetro un objeto de tipo MarkerDetector y crea otra instancia que es copia de la anterior. Hace uso de la función nativa de constructor de copia JMarkerDetector. detect: este método detecta marcadores en la imagen que recibe como parámetro. Si se le pasa información sobre los parámetros de la cámara y el tamaño del marcador, entonces, se detectarán los valores extrínsecos de los marcadores. Sus parámetros son: • input: imagen de entrada en color. Es del tipo Mat de OpenCV. • detectedMarkers: ArrayList de objetos de tipo Marker que dará como salida los marcadores detectados. • camMatrix: información intrínseca de la cámara. Es del tipo Mat de OpenCV. • distCoeff: coeficiente de distorsión de la cámara. Si no se incluye se asume que no hay distorsión en la cámara. Es del tipo Mat de OpenCV. • markerSizeMeters: flotante que contiene tamaño del lado del marcador expresado en metros. 51 Capítulo 5. Especificación del modelo de clases • setYPerperdicular: booleano que si tiene valor verdadero el eje Y será perpendicular a la superficie. De otro modo, lo será el eje Z. Si se produce algún error en la detección, elevará una excepción. detect: es el mismo método anterior pero con los parámetros que pueden leerse a continuación: • img: imagen de entrada en color. Es del tipo Mat de OpenCV. • detectedMarkers: ArrayList de objetos de tipo Marker que dará como salida los marcadores detectados. • camParam: parámetros de la cámara. Es un objeto del tipo CameraParameters. • markerSizeMeters: flotante que contiene tamaño del lado del marcador expresado en metros. • setYPerperdicular: booleano que si tiene valor verdadero el eje Y será perpendicular a la superficie. De otro modo, lo será el eje Z. Si se produce algún error en la detección, elevará una excepción. setCornerRefinementMethod: que recibe como parámetro un valor del tipo CornerRefinementMethod y lo fija los como método de refinado de las esquinas. getCornerRefinementMethod: que devuelve el valor de tipo CornerRefinementMethod que esté fijado como método de refinado de las esquinas. setThresholdMethod: que recibe como parámetro un valor del tipo ThresholdMethods y lo fija como método de umbral. getThresholdMethod: que devuelve el valor de tipo ThresholdMethods que esté fijado como método de umbral. setThresholdParams: fija los parámetros del método de umbral. Actualmente se está utilizando el método “adaptiveThreshold” de OpenCV. Los parámetros son: • param1: “blockSize” (tamaño de bloque) del vecindario de píxeles que es usado para calcular un valor umbral para el pixel. Es de tipo double. • param2: la constante extraída de la media o la media ponderada. Es de tipo double. getThresholdParam1: devuelve el primero de los dos parámetros comentados anteriormente. getThresholdParam2: devuelve el segundo de los dos parámetros comentados anteriormente. 52 Capítulo 5. Especificación del modelo de clases getThresholdedImage: devuelve una referencia a la imagen interna a la que se le ha realizado el umbral. Utilizado para visualización y para ajustar manualmente los parámetros. El valor devuelto es de tipo Mat de OpenCV. setDesiredSpeed: recibe un parámetro de tipo entero que especifica la velocidad requerida para los procesos internos. Si se necesita una velocidad máxima (con el coste de un porcentaje de detección menor) se ha de usar el valor 3. Si, por el contrario, se necesita un mayor porcentaje de detección, se ha de usar el valor cero. getDesiredSpeed: devuelve un entero especificando la velocidad requerida para los procesos internos. setMinMaxSize: especifica el tamaño mínimo y máximo de los marcadores en forma de fracción del tamaño de imagen. Por tamaño se entiende el máximo de filas o columnas. Los dos parámetros de tipo real que recibe son: • min: es el mínimo tamaño de los marcadores. Si no se indica su valor será 0.03. • max: es el máximo tamaño de los marcadores. Si no se indica su valor será 0.5. Si alguno de los dos valores no se encuentra entre 0 y 1 elevará una excepción. getMinSize: devuelve el tamaño mínimo de los marcadores en forma de fracción del tamaño de imagen. getMaxSize: devuelve el tamaño máximo de los marcadores en forma de fracción del tamaño de imagen. enableErosion: es una función que recibe una variable booleana que permite activar o desactivar el proceso de erosión que es necesario para los tableros de tipo “chessboard”. Por defecto la erosión está activada. pyrDown: usa una versión más pequeña de la imagen de entrada para detección de marcadores. Si el marcador es lo suficientemente pequeño, se puede utilizar una imagen más pequeña para realizar la detección sin una reducción en la precisión apreciable. Internamente, se está realizando una operación de tipo pyrdown. Como parámetro recibe un entero sin signo que indica el número de veces que el tamaño de imagen es dividido por dos. El diagrama de esta clase es el siguiente: 53 Capítulo 5. Especificación del modelo de clases Figura 5.7: Diagrama de clases de MarkerDetector 5.1.9. Clase MarkerInfo Esta clase contiene la representación 3D de un marcador. En su estructura C++ subyacente hereda de la clase vector de puntos tridimensionales de OpenCV Point3f. Cuenta con las siguientes declaraciones de funciones nativas que se definirán en el fichero JMarkerInfo.cpp del wrapper de C++. JMarkerInfo: llamará al constructor vacío de la estructura MarkerInfo de Aruco. JMarkerInfo: llamará al constructor de copia de la estructura MarkerInfo de Aruco. JMarkerInfo: llamará al constructor parametrizado de la estructura MarkerInfo de Aruco. JgetId: devuelve un entero con el valor del atributo público id de la estructura MarkerInfo de Aruco. Jsize: llamará a la función size() del vector de puntos de OpenCV. Jget: que sirve para devolver un objeto Point3 que se encuentra en el vector de marcadores que es MarkerInfo en la posición que se le pasa como parámetro. Los métodos públicos de la clase MarkerInfo son: MarkerInfo: constructor vacío de la clase que, crea el objeto MarkerInfo de Java, así como la estructura de ArUco y los enlaza escribiendo la dirección del segundo en nativeHandle. Todo esto se hace mediante la llamada a la función de código nativo JMarkerInfo. 54 Capítulo 5. Especificación del modelo de clases MarkerInfo: constructor de copia de la clase. Recibe como parámetro un objeto de tipo MarkerInfo y crea otra instancia que es copia de la anterior. Hace uso de la función nativa de constructor de copia JMarkerInfo. MarkerInfo: constructor parametrizado de la clase. Recibe como parámetro un entero con el identificador del marcador. getId: devuelve un entero con el valor de la variable id de la estructura MarkerInfo de Aruco. size: este método devuelve un entero con el tamaño del vector de puntos que es la estructura MarkerInfo de ArUco. Para obtener este dato realiza una llamada a la función nativa Jsize. get: este método devuelve el objeto del tipo Point3 de OpenCV que se encuentre en la posición del vector que se le indica como parámetro. El diagrama de esta clase es el siguiente: Figura 5.8: Diagrama de clases de MarkerInfo 5.2. Análisis de los ficheros C++ del “wrapper” Las clases Java llamarán a funciones de la librería ArUco mediante JNI. Para poder transformar los parámetros tanto de entrada como de salida se han creado los ficheros del “wrapper”. Estos ficheros son los siguientes: Handle.h JBoard.cpp y JBoard.h 55 Capítulo 5. Especificación del modelo de clases JBoardConfiguration.cpp y JBoardConfiguration.h JBoardDetector.cpp y JBoardDetector.h JCameraParameters.cpp y JCameraParameters.h JCvDrawingUtils.cpp y JCvDrawingUtils.h JFiducidalMarkers.cpp y JFiducidalMarkers.h JMarker.cpp y JMarker.h JMarkerDetector.cpp y JMarkerDetector.h JMarkerInfo.cpp y JMarkerInfo.h Todos los nombres de funciones que no están en “Handle.h” comienzan con el prefijo Java_wrapper_NombreClase debido a que JNI genera estos nombres de manera automática. Por claridad, se van a omitir estos prefijos. 5.2.1. Handle.h Este fichero es de suma importancia para poder enlazar los objetos Java con sus objetos de la librería ArUco subyacentes. Como ya se ha explicado previamente, cada instancia en la parte Java tiene una variable privada de tipo long llamada nativeHandle. Dentro de este fichero se encuentran las funciones responsables de obtener y modificar esos valores para que cada instancia de la parte Java pueda hacer llamadas a su correspondiente objeto en la parte de C++. Sus funciones son las siguientes: getHandleField: sirve para obtener el identificador del atributo nativeHandle del objeto jobject que se le pasa como parámetro. Esto servirá para poder leer o modificar este atributo en el objeto Java desde la parte nativa como veremos en las dos siguientes funciones. getHandle: obtiene el valor del campo nativeHandle del objeto jobject que se le pasa como parámetro y lo convierte a un puntero del tipo C++ que sea necesario. Mediante este puntero, se podrá acceder a los atributos y métodos del objeto de ArUco. setHandle: realiza la función inversa a getHandle. Recibe un puntero C++ como parámetro y guarda su dirección de memoria en la variable nativeHandle del objeto Java. De este modo, quedarán los dos objetos enlazados para cuando se quiera acceder al objeto de ArUco. 56 Capítulo 5. Especificación del modelo de clases newJavaException: esta función sirve para elevar una excepción desde ArUco hasta el código Java. Recibe la referencia a la clase Java de la excepción (por ejemplo “java/lang/Exception”) y el texto que se desea mostrar en la excepción. Esta función lanza la excepción en Java con esos valores. 5.2.2. JBoard.cpp y JBoard.h Estos ficheros son los responsables de llamar a las funciones de la clase Board. Transforman los parámetros de entrada de manera adecuada para que puedan ser manejados en C++ y los de salida para que puedan ser utilizados en Java. El contenido de JBoard.h es generado automáticamente por la utilidad javah. Este fichero contiene las cabeceras de todas las funciones que se indicaron como native en la clase Board de Java. En el fichero JBoard.cpp se desarrollan estas funciones, que son las siguientes: JBoard__: llamada desde el constructor vacío de la clase Board de Java. Crea un objeto de tipo Board en ArUco y guarda su dirección de memoria en el atributo nativeHandle del correspondiente objeto Board de la parte Java. JBoard__Lwrapper_Board_2: constructor de copia de la clase Board. Recibe como parámetro un objeto Board de Java del que extrae su objeto ArUco y crea un nuevo objeto Board usando el constructor de copia de ArUco. Después, guarda la referencia del nuevo objeto en el de Java utilizando su atributo nativeHandle. JsaveToFile: función que sirve para llamar al método saveToFile de la clase Board de ArUco. Recibe una cadena de Java como parámetro y la convierte en una cadena legible para el objeto de C++. En caso de que haya algún problema con la ruta en la que se desea crear el archivo, elevará una excepción a Java. JreadFromFile: función que sirve para llamar al método readFromFile de la clase Board de ArUco. Recibe una cadena de Java como parámetro y la convierte en una cadena legible para el objeto de C++. En caso de que haya algún problema con la ruta en la que se ha de encontrar el archivo, elevará una excepción a Java. JglGetModelViewMatrix: recibe un vector de 16 elementos llamado modelview_matrix y lo rellena llamando a la función glGetModelViewMatrix de la clase Board. JOgreGetPoseParameters: recibe dos vectores (position de 3 elementos de tipo double y orientation de 4 elementos de tipo double) y los rellena llamando a la función OgreGetPoseParameters de la clase Board. JgetRvec: como el atributo Rvec de tipo Mat de OpenCV es público, mediante esta función se devuelve a Java si se desea acceder a él. El objeto de tipo Mat es creado 57 Capítulo 5. Especificación del modelo de clases por la función de Java y se le pasa a esta función su dirección de memoria, en la que se escribe la dirección de memoria de Rvec. JgetTvec: como el atributo Tvec de tipo Mat de OpenCV es público, mediante esta función se devuelve a Java si se desea acceder a él. El objeto de tipo Mat es creado por la función de Java y se le pasa a esta función su dirección de memoria, en la que se escribe la dirección de memoria de Tvec. JgetBoardConf: como el atributo conf de tipo BoardConfiguration es público, mediante esta función se devuelve a Java. Creando un objeto Java de tipo BoardConfiguration y enlazándolo con el correspondiente objeto conf. Jget: ya que Board hereda de vector <Marker>, esta función permite usar el operador [] del vector. Recibe un entero con la posición del vector que se desea devolver y retorna el objeto Java de la clase Marker que corresponde a ese marcador. Jsize: dado que Board hereda de vector <Marker>, esta función permite llamar a la función size() del vector. Devuelve un entero con el tamaño del vector de marcadores. 5.2.3. JBoardConfiguration.cpp y JBoardConfiguration.h JBoardConfiguration__: llamada desde el constructor vacío de la clase BoardConfiguration de Java. Crea un objeto de tipo BoardConfiguration en ArUco y guarda su dirección de memoria en el atributo nativeHandle del correspondiente objeto BoardConfiguration de la parte Java. JBoardConfiguration__Lwrapper_BoardConfiguration_2: constructor de copia de la clase BoardConfiguration. Recibe como parámetro un objeto BoardConfiguration de Java del que extrae su objeto ArUco y crea un nuevo objeto BoardConfiguration usando el constructor de copia de ArUco. Después, guarda la referencia del nuevo objeto en el de Java utilizando su atributo nativeHandle. JsaveToFile: función que sirve para llamar al método saveToFile de la clase BoardConfiguration de ArUco. Recibe una cadena de Java como parámetro y la convierte en una cadena legible para el objeto de C++. En caso de que haya algún problema con la ruta en la que se desea crear el archivo, elevará una excepción a Java. JreadFromFile: función que sirve para llamar al método readFromFile de la clase BoardConfiguration de ArUco. Recibe una cadena de Java como parámetro y la convierte en una cadena legible para el objeto de C++. En caso de que haya algún problema con la ruta en la que se ha de encontrar el archivo, elevará una excepción a Java. 58 Capítulo 5. Especificación del modelo de clases JisExpressedInMeters: función que sirve para llamar al método isExpressedInMeters de la clase BoardConfiguration de ArUco. Devuelve un valor booleano a Java. JisExpressedInPixels: función que sirve para llamar al método isExpressedInPixels de la clase BoardConfiguration de ArUco. Devuelve un valor booleano a Java. JgetIndexOfMarkerId: función que sirve para llamar al método getIndexOfMarkerId de la clase BoardConfiguration de ArUco. Recibe un entero con el identificador del marcador en cuestión y devuelve otro entero con su índice. JgetIdList: función que sirve para llamar al método getIdList de la clase BoardConfiguration de ArUco. Recibe un objeto que contiene un ArrayList de Java en el que se van a incluir los identificadores de dotos los marcadores contenidos en el tablero. Además contiene una variable booleana que indica si los valores se añadirán al final o no. Se ocupa de introducir los identificadores en el ArrayList. Jsize: dado que BoardConfiguration hereda de vector <MarkerInfo>, esta función permite llamar a la función size() del vector. Devuelve un entero con el tamaño del vector de objetos de tipo MarkerInfo. JgetMarkerInfo: función que sirve para llamar al método getMarkerInfo de la clase BoardConfiguration de ArUco. Recibe un entero con el identificador del objeto MarkerInfo en cuestión y devuelve otro entero con su índice. JgetMInfoType: como el atributo mInfoType de tipo entero es público, mediante esta función se devuelve a Java. Jget: dado que BoardConfiguration hereda de vector <MarkerInfo>, esta función permite llamar a la función get() del vector. Devuelve el objeto de tipo MarkerInfo que ocupe la posición que se le pasa como parámetro. 5.2.4. JBoardDetector.cpp y JBoardDetector.h JBoardDetector: llamada desde el constructor vacío de la clase BoardDetector de Java. Crea un objeto de tipo BoardDetector en ArUco y guarda su dirección de memoria en el atributo nativeHandle del correspondiente objeto BoardDetector de la parte Java. Jdetect__J: función que sirve para llamar al método detect de la clase BoardDetector de ArUco. Recibe como parámetro la dirección de un objeto Mat que contiene la imagen en la que se ha de dtectar el tablero. 59 Capítulo 5. Especificación del modelo de clases JgetDetectedBoard: función que sirve para llamar al método getDetectedBoard de la clase BoardDetector de ArUco y devuelve un objeto Java de tipo Board con el tablero detectado. JgetMarkerDetector: función que sirve para llamar al método getMarkerDetector de la clase BoardDetector de ArUco y devuelve un objeto Java de tipo M con el tablero detectado. JgetDetectedMarkers: función que sirve para llamar al método getDetectedMarkers de la clase BoardDetector de ArUco. Recibe un ArrayList de marcadores de Java y lo rellena con los resultados de la llamada a getDetectedMarkers. JsetYPerpendicular: función que sirve para llamar al método setYPerpendicular de la clase BoardDetector de ArUco. Recibe un parámetro booleano que utiliza en la llamada a la función. Jdetect__Ljava_util_ArrayList_2Lwrapper_BoardConfiguration_2Lwrapper_Board_2Lwrapper_CameraParameters_2F: función que sirve para llamar al método detect de la clase BoardDetector de ArUco. Jdetect__Ljava_util_ArrayList_2Lwrapper_BoardConfiguration_2Lwrapper_Board_2JJF: función que sirve para llamar al método detect de la clase BoardDetector de ArUco. JsetParams__Lwrapper_BoardConfiguration_2Lwrapper_CameraParameters_2F: función que sirve para llamar al método setParams de la clase BoardDetector de ArUco. Recibe un objeto de Java del tipo BoardConfiguration, otro del tipo CameraParameters y un flotante que utiliza en la llamada a la función correspondiente. JsetParams__Lwrapper_BoardConfiguration_2: función que sirve para llamar al método setParams de la clase BoardDetector de ArUco. Recibe un objeto de Java del tipo BoardConfiguration que utiliza en la llamada a la función correspondiente. 5.2.5. JCameraParameters.cpp y JCameraParameters.h JCameraParameters__: llamada desde el constructor vacío de la clase CameraParameters de Java. Crea un objeto de tipo CameraParameters en ArUco y guarda su dirección de memoria en el atributo nativeHandle del correspondiente objeto CameraParameters de la parte Java. JCameraParameters__Lwrapper_CameraParameters_2: constructor de copia de la clase CameraParameters. Recibe como parámetro un objeto CameraParameters de 60 Capítulo 5. Especificación del modelo de clases Java del que extrae su objeto ArUco y crea un nuevo objeto CameraParameters usando el constructor de copia de ArUco. Después, guarda la referencia del nuevo objeto en el de Java utilizando su atributo nativeHandle. JCameraParameters__JJDD: constructor parametrizado de CameraParameters que recibe las matrices de la cámara y de distorsión, además del ancho y el alto de la imagen. JreadFromFile: función que sirve para llamar al método readFromFile de la clase CameraParameters de ArUco. Recibe una cadena de Java como parámetro y la convierte en una cadena legible para el objeto de C++. En caso de que haya algún problema con la ruta en la que se ha de encontrar el archivo, elevará una excepción a Java. JisValid: función que sirve para llamar al método isValid de la clase CameraParameters de ArUco. Devuelve una variable booleana con el resultado de la llamada a la función. JsetParams: función que sirve para llamar al método setParams de la clase CameraParameters de ArUco. JsaveToFile: función que sirve para llamar al método saveToFile de la clase CameraParameters de ArUco. Recibe una cadena de Java como parámetro y la convierte en una cadena legible para el objeto de C++. En caso de que haya algún problema con la ruta en la que se desea crear el archivo, elevará una excepción a Java. JreadFromXMLFile: función que sirve para llamar al método readFromXMLFile de la clase CameraParameters de ArUco. Recibe una cadena de Java como parámetro y la convierte en una cadena legible para el objeto de C++. En caso de que haya algún problema con la ruta en la que se desea crear el archivo, elevará una excepción a Java. Jresize: función que sirve para llamar al método resize de la clase CameraParameters de ArUco. Recibe dos parámetros de tipo double con la altura y la anchura. JglGetProjectionMatrix: función que sirve para llamar al método glGetProjectionMatrix de la clase CameraParameters de ArUco. JOgreGetProjectionMatrix: función que sirve para llamar al método OgreGetProjectionMatrix de la clase CameraParameters de ArUco. JgetCameraLocation: función que sirve para llamar al método getCameraLocation de la clase CameraParameters de ArUco. Recibe las direcciones de los dos objetos 61 Capítulo 5. Especificación del modelo de clases Mat en los que se encuentran los vectores de rotación y traslación y devuelve un objeto de la clase Point de OpenCV en Java con la posición de la cámara. JgetCameraMatrix: como el atributo CameraMatrix de tipo Mat de OpenCV es público, mediante esta función se devuelve a Java si se desea acceder a él. El objeto de tipo Mat es creado por la función de Java y se le pasa a esta función su dirección de memoria, en la que se escribe la dirección de memoria de CameraMatrix. JgetDistorsion: como el atributo Distorsion de tipo Mat de OpenCV es público, mediante esta función se devuelve a Java si se desea acceder a él. El objeto de tipo Mat es creado por la función de Java y se le pasa a esta función su dirección de memoria, en la que se escribe la dirección de memoria de Distorsion. JgetCamSizeWidth: como el atributo CamSize de tipo Size de OpenCV es público, mediante esta función se devuelve a Java el valor de la anchura (de tipo double) si se desea acceder a él. JgetCamSizeHeight: como el atributo CamSize de tipo Size de OpenCV es público, mediante esta función se devuelve a Java el valor de la altura (de tipo double) si se desea acceder a él. 5.2.6. JCvDrawingUtils.cpp y JCvDrawingUtils.h JCvDrawingUtils: llamada desde el constructor vacío de la clase CvDrawingUtils de Java. Crea un objeto de tipo CvDrawingUtils en ArUco y guarda su dirección de memoria en el atributo nativeHandle del correspondiente objeto CvDrawingUtils de la parte Java. Jdraw3dAxis__JLwrapper_Marker_2Lwrapper_CameraParameters_2: función que sirve para llamar al método draw3dAxis de la clase CvDrawingUtils de ArUco. Recibe un parámetro con la dirección del objeto Mat que contiene la imagen a modificar, el objeto Java con el marcador y el objeto de tipo CameraParameters que serán utilizados en la llamada a la función ArUco. Jdraw3dCube__JLwrapper_Marker_2Lwrapper_CameraParameters_2: función que sirve para llamar al método draw3dCube de la clase CvDrawingUtils de ArUco. Recibe un parámetro con la dirección del objeto Mat que contiene la imagen a modificar, el objeto Java con el marcador y el objeto de tipo CameraParameters que serán utilizados en la llamada a la función ArUco. Jdraw3dAxis__JLwrapper_Board_2Lwrapper_CameraParameters_2: función que sirve para llamar al método draw3dAxis de la clase CvDrawingUtils de ArUco. Recibe un parámetro con la dirección del objeto Mat que contiene la imagen a modificar, 62 Capítulo 5. Especificación del modelo de clases el objeto Java con el tablero y el objeto de tipo CameraParameters que serán utilizados en la llamada a la función ArUco. Jdraw3dCube__JLwrapper_Board_2Lwrapper_CameraParameters_2: función que sirve para llamar al método draw3dAxis de la clase CvDrawingUtils de ArUco. Recibe un parámetro con la dirección del objeto Mat que contiene la imagen a modificar, el objeto Java con el tablero y el objeto de tipo CameraParameters que serán utilizados en la llamada a la función ArUco. 5.2.7. JFiducidalMarkers.cpp y JFiducidalMarkers.h JcreateMarkerImage: función que sirve para llamar al método createMarkerImage de la clase FiducidalMarkers de ArUco. Jdetect: función que sirve para llamar al método detect de la clase FiducidalMarkers de ArUco. JgetMarkerMat: función que sirve para llamar al método getMarkerMat de la clase FiducidalMarkers de ArUco. JcreateBoardImage: función que sirve para llamar al método createBoardImage de la clase FiducidalMarkers de ArUco. JcreateBoardImage_1ChessBoard: función que sirve para llamar al método createBoardImage_ChessBoard de la clase FiducidalMarkers de ArUco. JcreateBoardImage_1Frame: función que sirve para llamar al método createBoardImage_Frame de la clase FiducidalMarkers de ArUco. 5.2.8. JMarker.cpp y JMarker.h JMarker__: llamada desde el constructor vacío de la clase Marker de Java. Crea un objeto de tipo Marker en ArUco y guarda su dirección de memoria en el atributo nativeHandle del correspondiente objeto Marker de la parte Java. JMarker__Lwrapper_Marker_2: constructor de copia de la clase Marker. Recibe como parámetro un objeto Marker de Java del que extrae su objeto ArUco y crea un nuevo objeto Marker usando el constructor de copia de ArUco. Después, guarda la referencia del nuevo objeto en el de Java utilizando su atributo nativeHandle. JMarker__Ljava_util_ArrayList_2I: constructor parametrizado de la clase que recibe el ArrayList que contiene las coordenadas de los puntos de las esquinas del marcador 63 Capítulo 5. Especificación del modelo de clases JisValid: función que sirve para llamar al método isValid de la clase Marker de ArUco. Devuelve una variable booleana con el resultado de la llamada a la función. Jdraw: función que sirve para llamar al método draw de la clase Marker de ArUco. JglGetModelViewMatrix: función que sirve para llamar al método glGetModelViewMatrix de la clase Marker de ArUco. JgetArea: función que sirve para llamar al método getArea de la clase Marker de ArUco. JgetPerimeter: función que sirve para llamar al método getPerimeter de la clase Marker de ArUco. JgetCenter: función que sirve para llamar al método getCenter de la clase Marker de ArUco. JcalculateExtrinsics__FLwrapper_CameraParameters_2Z: función que sirve para llamar al método calculateExtrinsics de la clase Marker de ArUco. JcalculateExtrinsics__FJJZ: función que sirve para llamar al método calculateExtrinsics de la clase Marker de ArUco. JOgreGetPoseParameters: función que sirve para llamar al método OgreGetPoseParameters de la clase Marker de ArUco. JgetRvec: como el atributo Rvec de tipo Mat de OpenCV es público, mediante esta función se devuelve a Java si se desea acceder a él. El objeto de tipo Mat es creado por la función de Java y se le pasa a esta función su dirección de memoria, en la que se escribe la dirección de memoria de Rvec. JgetTvec: como el atributo Tvec de tipo Mat de OpenCV es público, mediante esta función se devuelve a Java si se desea acceder a él. El objeto de tipo Mat es creado por la función de Java y se le pasa a esta función su dirección de memoria, en la que se escribe la dirección de memoria de Tvec. JgetId: como el atributo id de tipo entero es público, mediante esta función se devuelve a Java si se desea acceder a él. JgetSSize: como el atributo ssize de tipo float es público, mediante esta función se devuelve a Java si se desea acceder a él. JlessThan: recibe un objeto de tipo Marker como parámetro y lo compara con el objeto desde el que se llama utilizando el operador “menor que”. Si el objeto desde el que se llama es menor, devuelve verdadero, en caso contrario, devuelve falso. 64 Capítulo 5. Especificación del modelo de clases JtoString: devuelve una cadena de Java con el texto que retornaría el objeto ArUco al utilizar el operador << de la clase Marker de Aruco. Jsize: dado que Marker hereda de vector <Point2f>, esta función permite llamar a la función size() del vector. Devuelve un entero con el tamaño del vector. Jget: dado que Marker hereda de vector <Point2f>, esta función permite llamar a la función get() del vector. Devuelve el objeto de tipo Point de OpenCV de Java que ocupe la posición que se le pasa como parámetro. 5.2.9. JMarkerDetector.cpp y JMarkerDetector.h JMarkerDetector__: llamada desde el constructor vacío de la clase MarkerDetector de Java. Crea un objeto de tipo MarkerDetector en ArUco y guarda su dirección de memoria en el atributo nativeHandle del correspondiente objeto MarkerDetector de la parte Java. JMarkerDetector__Lwrapper_MarkerDetector_2: constructor de copia de la clase MarkerDetector. Recibe como parámetro un objeto MarkerDetector de Java del que extrae su objeto ArUco y crea un nuevo objeto MarkerDetector usando el constructor de copia de ArUco. Después, guarda la referencia del nuevo objeto en el de Java utilizando su atributo nativeHandle. Jdetect__JLjava_util_ArrayList_2JJFZ: función que sirve para llamar al método detect de la clase MarkerDetectorde ArUco. Jdetect__JLjava_util_ArrayList_2Lwrapper_CameraParameters_2FZ: JgetDesiredSpeed: función que sirve para llamar al método OgreGetPoseParameters de la clase MarkerDetector de ArUco. JsetDesiredSpeed: función que sirve para llamar al método setDesiredSpeed de la clase MarkerDetector de ArUco. JsetThresholdMethod: función que sirve para llamar al método setThresholdMethod de la clase MarkerDetector de ArUco. JgetThresholdMethod: función que sirve para llamar al método getThresholdMethod de la clase MarkerDetector de ArUco. JsetThresholdParams: función que sirve para llamar al método setThresholdParams de la clase MarkerDetector de ArUco. JgetThresholdParam1: función que sirve para llamar al método getThresholdParam1 de la clase MarkerDetector de ArUco. 65 Capítulo 5. Especificación del modelo de clases JgetThresholdParam2: función que sirve para llamar al método getThresholdParam2 de la clase MarkerDetector de ArUco. JgetThresholdedImage: función que sirve para llamar al método getThresholdedImage de la clase MarkerDetector de ArUco. JsetCornerRefinementMethod: función que sirve para llamar al método setCornerRefinementMethod de la clase MarkerDetector de ArUco. JgetCornerRefinementMethod: función que sirve para llamar al método getCornerRefinementMethod de la clase MarkerDetector de ArUco. JsetMinMaxSize: función que sirve para llamar al método setMinMaxSize de la clase MarkerDetector de ArUco. JgetMinSize: función que sirve para llamar al método getMinSize de la clase MarkerDetector de ArUco. JgetMaxSize: función que sirve para llamar al método getMaxSize de la clase MarkerDetector de ArUco. JenableErosion: función que sirve para llamar al método enableErosion de la clase MarkerDetector de ArUco. JpyrDown: función que sirve para llamar al método pyrDown de la clase MarkerDetector de ArUco. 5.2.10. JMarkerInfo.cpp y JMarkerInfo.h JMarkerInfo__: llamada desde el constructor vacío de la clase MarkerInfo de Java. Crea un objeto de tipo MarkerInfo en ArUco y guarda su dirección de memoria en el atributo nativeHandle del correspondiente objeto MarkerInfo de la parte Java. JMarkerInfo__I: constructor parametrizado de MarkerInfo que recibe un entero con el identificador del marcador que se desea crear. JMarkerInfo__Lwrapper_MarkerInfo_2: constructor de copia de la clase MarkerInfo. Recibe como parámetro un objeto MarkerInfo de Java del que extrae su objeto ArUco y crea un nuevo objeto MarkerInfo usando el constructor de copia de ArUco. Después, guarda la referencia del nuevo objeto en el de Java utilizando su atributo nativeHandle. JgetId: función que sirve para llamar al método getId de la clase MarkerInfo de ArUco. Devuelve el identificador del marcador en cuestión. 66 Capítulo 5. Especificación del modelo de clases Jsize: dado que MarkerInfo hereda de vector <Point2f>, esta función permite llamar a la función size() del vector. Devuelve un entero con el tamaño del vector. Jget: dado que MarkerInfo hereda de vector <Point2f>, esta función permite llamar a la función get() del vector. Devuelve el objeto de tipo Point de OpenCV de Java que ocupe la posición que se le pasa como parámetro. 5.3. Análisis de los programas Java de ejemplo Con un objetivo tanto didáctico como demostrativo del buen funcionamiento de la librería se han incluido ciertas aplicaciones de ejemplo que intentan ofrecer las mismas funcionalidades que las aplicaciones de ejemplo de la librería ArUco original. Estas utilidades son: arucoCreateBoard arucoCreateMarker arucoSimple arucoSimpleBoard 5.3.1. arucoCreateBoard En esta utilidad se hace uso de las clases BoardConfiguration y FiducidalMarkers. La aplicación crea un tablero con el tamaño que se le pasa como parámetro y lo guarda en un archivo de imagen. Además, guarda la configuración del tablero en un fichero que también se le pasa como parámetro. Además, los tableros pueden crearse de tipo panel, chessboard o frame; según se indique en el quinto parámetro. Utilizando el siguiente comando llamamdo a arucoCreateBoard: 1 2 arucoCreateBoard 5 : 6 boardImage . png boardConfiguration . yml 200 0 Se obtiene la siguiente imagen denominada boardImage.png como se ha indicado en la llamada por línea de comandos: 67 Capítulo 5. Especificación del modelo de clases Figura 5.9: Imagen con el tablero resultado de arucoCreateBoard Además, se obtiene el archivo boardConfiguration.yml con la información de configuración del tablero. En él se incluye el número de marcadores, el tipo de tablero que es y los identificadores y posición de las esquinas de los marcadores en la imagen. No se incluirá por completo debido a su tamaño, pero el comienzo del fichero es el siguiente: 1 2 3 4 5 6 7 8 9 10 %YAML : 1 . 0 aruco_bc_nmarkers : 30 aruco_bc_mInfoType : 0 aruco_bc_markers : − { id : 9 3 9 , corners : [ [ −580. , −700. , 0 . [ −380. , −700. , 0 . ] , [ − 3 8 0 . , −500. , 0 . [ −580. , −500. , 0 . ] ] } − { id : 1 1 1 , corners : [ [ −340. , −700. , 0 . [ −140. , −700. , 0 . ] , [ − 1 4 0 . , −500. , 0 . [ −340. , −500. , 0 . ] ] } ], ], ], ], El fichero sigue con los identificadores y la posición de las esquinas de todos y cada uno de los marcadores que aparecen en el tablero. 5.3.2. arucoCreateMarker Esta aplicación utiliza la clase FiducidalMarkers para crear la imagen de un marcador cuyo identificador se le pasa como parámetro de la línea de comandos al invocar al programa. Además, también se le pasa el nombre del fichero que queremos tener como salida 68 Capítulo 5. Especificación del modelo de clases y el tamaño en píxeles del lado del marcador. En caso de que se llame a esta utilidad del siguiente modo en la línea de comandos: 1 arucoCreateMarker 23 marker23 . jpg 50 Se obtendrá como salida el fichero denominado marker23.jpg que contiene la imagen siguiente: Figura 5.10: Marcador con el identificador 23 creado por arucoCreateMarker 5.3.3. arucoSimple Esta aplicación recibe como parámetro una imagen con un conjunto de marcadores y haciendo uso de las clases CameraParameters, CvDrawingUtils, MarkerDetector y Marker, detecta los marcadores que contiene la imagen. Además, dibuja un cuadrado en los bordes de cada marcador, muestra su identificador, y un cubo sobre los mismos. El comando utilizado para ejecutar esta aplicación es el siguiente: 1 2 arucoSimple . / RESULTS / TESTDATA / image−test . png . / RESULTS / TESTDATA / intrinsics . yml 5 arucoSimple . png Como resultado, se muestran en pantalla tanto la imagen con los marcadores detectados como la imagen umbralizada y se guarda la imagen de salida que se muestra a continuación: 69 Capítulo 5. Especificación del modelo de clases Figura 5.11: Imagen resultado de ejecutar arucoSimple 5.3.4. arucoSimpleBoard Esta utilidad utiliza las clases CameraParameters, MarkerDetector, Marker, BoardConfiguration, BoardDetector, CvDrawingUtils y Board. Recibe una imagen que contiene un tablero, además de la configuración del mismo. También puede incluir los parámetros de la cámara, el tamaño del lado de los marcadores y un nombre base de fichero para la salida. La aplicación detecta los marcadores y el tablero en la imagen, dibuja el borde de los marcadores y su identificador, y un eje con la posición 3D del tablero. 1 2 3 4 . / RESULTS / TESTDATA / image−test . png . / RESULTS / TESTDATA / board_pix . yml . / RESULTS / TESTDATA / intrinsics . yml 5 SimpleBoard . png Como resultado, se muestran en pantalla tanto la imagen con el tablero y los marcadores detectados. Aparecen los ejes tanto del tablero como de los marcadores y se guarda la imagen de salida que se muestra a continuación: 70 Capítulo 5. Especificación del modelo de clases Figura 5.12: Imagen resultado de ejecutar arucoSimpleBoard 71 Capítulo 6 Diagramas de secuencia Con estos diagramas de secuencia va a explicarse la forma de proceder de la librería con respecto a algunas acciones relevantes tales como: Creación de un objeto Llamada a un método con modificación de parámetros Llamada a un método que escribe en disco Llamada a un método que lee desde el disco 6.1. Creación de un objeto En esta sección se va a mostrar cómo se llama al constructor de una clase, en concreto MarkerDetector y cómo esta llamada es transmitida por las distintas capas de la librería hasta el código nativo de ArUco. Los pasos son los siguientes: 1. El programador llama desde el programa principal al constructor vacío de MarkerDetector. 2. Este constructor, dentro de la clase Java llama a la función del “wrapper” de C++ JMarkerDetector 3. Dentro de esa función se llama al constructor de MarkerDetector de ArUco. 4. Este constructor crea y devuelve un objeto MarkerDetector de Aruco. 5. La función del “wrapper” de C++ se ocupa de cambiar el atributo de la clase Java nativeHandle para que contenga la dirección de memoria del objeto ArUco creado. Esto garantiza que se pueda volver a acceder a ese objeto en concreto desde Java. 6. La función del constructor de Java devuelve el objeto MarkerDetector de Java (que ya referencia al objeto ArUco) al programa principal 73 Capítulo 6. Diagramas de secuencia Figura 6.1: Diagrama de secuencia del constructor de MarkerDetector 6.2. Llamada a un método con modificación de parámetros A continuación se mostrará cómo, usando el objeto que se ha creado anteriormente, se realiza una llamada a función. En este caso se va a llamar a la función detect de MarkerDetector que, en la parte Java, recibe un objeto Mat de OpenCV con una imagen en la que se han de detectar marcadores y un ArrayList de objetos de tipo Marker que se rellenará con la información de los marcadores detectados. Los pasos que se siguen son: 1. El programador llama desde el programa principal al método detect del objeto MarkerDetector anteriormente creado. Como primer parámeetro le pasa una imagen de tipo Mat de OpenCV y como segundo parámetro un ArrayList <Marker> en el que se desean guardar los marcadores que se encuentren. 2. Dentro del método detect de Java se llama a la función nativa del “wrapper” de C++ JDetect que se encuentra en el fichero JMarkerDetector.cpp. Se le pasa la dirección del objeto tipo Mat mediante la función getObjectNativeAddr de esa clase y el objeto ArrayList de Java. 3. En la función del “wrapper” se crea un puntero a Mat para poder acceder a la imagen y un vector de marcadores de ArUco. De este modo se le pasa el contenido del puntero a Mat y el vector de marcadores al método detect del objeto MarkerDetector asociado al objeto Java que llamó a esta función. Esto se realiza mediante un acceso a la variable nativeHandle. 74 Capítulo 6. Diagramas de secuencia 4. Dentro de ArUco se ejecuta el método detect y se devuelve el vector de objetos Marker al “wrapper”. 5. La función del “wrapper” se ocupa de recorrer el vector de objetos Marker y añadirlos al ArrayList de Java. 6. En el programa principal se puede contar, tras la ejecución de este método con el ArrayList que contiene los objetos Marker de Java. Figura 6.2: Diagrama de secuencia de la función detect de MarkerDetector 6.3. Llamada a un método que escribe en disco En esta sección se podrá observar cómo realiza la escritura en disco desde la librería. Se va a utilizar como ejemplo la función saveToFile de la clase Board. Los pasos que se siguen son: 1. Una vez se tiene un objeto de tipo Board creado, el programador llama a su método saveToFile y le pasa como parámetro una cadena de tipo String de Java con la ruta donde quiere que se guarde la información del tablero. 2. El método correspondiente de Java llama a la función nativa JsaveToFile del “wrapper” de C++ y le pasa el String. 3. Dentro del “wrapper”, se obtienen los caracteres de la cadena mediante la función de JNI GetStringUTFChars y se guardan en un char * que ArUco puede procesar. 75 Capítulo 6. Diagramas de secuencia 4. Se realiza la llamada al método saveToFile del objeto Board de ArUco enlazado con el de Java por medio de nativeHandle. Se le pasa como parámetro el char * ya citado. 5. El método de ArUco realiza la escritura en disco y, en caso de haber algún problema de apertura del archivo elevará una excepción que llega a Java. Figura 6.3: Diagrama de secuencia de la función saveToFile de Board 6.4. Llamada a un método que lee desde el disco En esta sección se muestra cómo se realiza la lectura desde disco en la librería. Se va a utilizar como ejemplo la función readFromFile de la clase Board. Los pasos que se siguen son: 1. Una vez se tiene un objeto de tipo Board creado, el programador llama a su método readFromFile y le pasa como parámetro una cadena de tipo String de Java con la ruta donde se encuentra el fichero que se desea leer. 2. El método correspondiente de Java llama a la función nativa JreadFromFile del “wrapper” de C++ y le pasa el String. 3. Dentro del “wrapper”, se obtienen los caracteres de la cadena mediante la función de JNI GetStringUTFChars y se guardan en un char * que ArUco puede procesar. 76 Capítulo 6. Diagramas de secuencia 4. Se realiza la llamada al método readFromFile del objeto Board de ArUco enlazado con el de Java por medio de nativeHandle. Se le pasa como parámetro el char * ya citado. 5. El método de ArUco realiza la lectura de disco y, en caso de haber algún problema de apertura del archivo elevará una excepción que llega a Java. En caso de que todo vaya bien, el objeto Board de ArUco habrá cargado la nueva información que será accesible desde Java gracias a la variable nativeHandle. Figura 6.4: Diagrama de secuencia de la función readFromFile de Board 77 Capítulo 7 Diagrama de paquetes Debido al tamaño de la librería se va a dividir conceptualmente en los siguientes paquetes: Clases Java: • Wrapper: estas son las clases que utilizarán los programadores que hagan uso de la librería. Tienen los mismos nombres que las clases de ArUco en C++ y serán cargadas en sus programas. Dentro se encuentran los ficheros: ◦ Board.java ◦ BoardConfiguration.java ◦ BoardDetector.java ◦ CameraParameters.java ◦ CvDrawingUtils.java ◦ FiducidalMarkers.java ◦ Marker.java ◦ MarkerDetector.java ◦ MarkerInfo.java • Ejemplos: son un conjunto de programas de prueba que permitirán a los desarrolladores hacerse una idea del funcionamiento de la librería. Dentro se encuentran los ficheros: • arucoCreateBoard.java • arucoCreateMarker.java • arucoSimple.java • arucoSimpleBoard.java Wrapper C++: 79 Capítulo 7. Diagrama de paquetes • Handle.h: es el fichero encargado de ofrecer las funciones para mantener la relación entre los objetos Java y sus objetos ArUco subyacentes. • Ficheros “wrapper”: son los que se encargan de realizar las transformaciones de los parámetros de entrada, las llamadas a las funcione ArUco y las transformaciones y devoluciones de los resultados. Dentro se encuentran los ficheros: ◦ JBoard.cpp y JBoard.h ◦ JBoardConfiguration.cpp y JBoardConfiguration.h ◦ JBoardDetector.cpp y JBoardDetector.h ◦ JCameraParameters.cpp y JCameraParameters.h ◦ JCvDrawingUtils.cpp y JCvDrawingUtils.h ◦ JFiducidalMarkers.cpp y JFiducidalMarkers.h ◦ JMarker.cpp y JMarker.h ◦ JMarkerDetector.cpp y JMarkerDetector.h ◦ JMarkerInfo.cpp y JMarkerInfo.h 80 Capítulo 7. Diagrama de paquetes 7.1. Diagrama general de paquetes El diagrama de paquetes es el siguiente: Figura 7.1: Diagrama de paquetes de la librería 81 Capítulo 8 Pruebas En este capítulo se van a describir las pruebas realizadas a la librería fruto de este proyecto, para cerciorarse de que su funcionamiento es el correcto, y de que se trata de una librería robusta y amigable. Debido a que es imposible comprobar un software por completo, las pruebas han de hacerse de manera que se comprueben las líneas de programa con más probabilidad de contener o producir errores, como son: leer variables sin inicializar, controlar la condición de parada de bucles, comprobar el último y penúltimo valor de una variable que se incrementa o decrementa en un bucle, probar todas las combinaciones de valores de variables usadas en una condición, etcétera. Durante el proceso de codificación de la librería, se han ido realizando pruebas “sobre la marcha” para comprobar que cada parte de la misma estaba bien codificada. No obstante, este método de trabajo no es suficiente para eliminar todos los errores, ya que algunos han podido ser pasados por alto y otros pueden deberse a la interacción entre unos módulos y otros. Por tanto, la división modular para la realización de las pruebas es: Módulo de “Clases de Java”: donde las funciones a las que se ha prestado mayor atención han sido a las de creación y detección de marcadores Módulo de “Wrapper en C++”: donde se ha prestado especial atención a la transformación de los datos desde Java a C++ y viceversa. Módulo de “Programas de prueba”: donde se ha tenido especial cuidado en la carga y guardado de ficheros. Pruebas de funcionamiento en varios sistemas operativos: se han realizado pruebas en Windows 7, Kubuntu 14.10. 83 Capítulo 8. Pruebas 8.1. Clases de Java Se han realizado pruebas sobre cada clase y cada método, comprobando que se reciben los parámetros correctamente y que se devuelven los valores esperados. Además, se han realizado los siguientes cambios: En un principio se utilizaron para los métodos Java parámetros lo más parecidos posibles a los que recibirían las funciones de C++. Sin embargo, tras realizar las pruebas se ha estimado conveniente propiciar algunos cambios para obtener como resultado unos parámetros que sigan más la filosofía de funcionamiento de Java. Un ejemplo de este hecho es la utilización de la clase “ArrayList” de Java, ya que se comenzó utilizando vectores de objetos pero se ha estimado más conveniente utilizar “ArrayList” porque puede ser más cómodo de manejar para los usuarios que utilizan Java normalmente. Como ejemplo puede observarse la llamada a la función detect de la clase MarkerDetector en la que se utiliza ArrayList en lugar de vector: 1 p u b l i c v o i d detect ( Mat input , ArrayList<Marker> ←detectedMarkers ) ; Posteriormente, en la parte de C++ hace falta crear un vector de marcadores (línea 18) para mandarlo a la función detect (línea 21), después, se utilizan las funciones de JNI para poder llamar al método add de ArrayList y copiar en él los datos obtenidos de la función (línea 34). El código es el siguiente: 1 2 3 4 JNIEXPORT v o i d JNICALL ←Java_wrapper_MarkerDetector_Jdetect__JLjava_util_ArrayList_2JJFZ←( JNIEnv * env , jobject obj , jlong inputAddr , jobject ←detectedMarkers , jlong camMatrixAddr , jlong distCoeffAddr , ←jfloat markerSizeMeters , jboolean setYPerpendicular ) { MarkerDetector * inst = getHandle<MarkerDetector > ( env , obj ) ; / / use th e Array l i s t jclass ArrayList_class = env−>FindClass ( " j a v a / u t i l / ←ArrayList " ) ; 5 6 jclass Marker_class = env−>FindClass ( " w r a p p e r / Marker " ) ; 7 8 jmethodID Add_method = env−>GetMethodID ( ArrayList_class , " add←" , " ( L j a v a / l a n g / O b j e c t ; ) Z" ) ; 9 10 jmethodID Marker_Constructor_method = env−>GetMethodID ( ←Marker_class , " < i n i t > " , " ( ) V" ) ; 84 Capítulo 8. Pruebas jmethodID Marker_Copy_Constructor_method = env−>GetMethodID ( ←Marker_class , " < i n i t > " , " ( L w r a p p e r / Marker ; ) V" ) ; 11 12 13 cv : : Mat * input = ( cv : : Mat * ) inputAddr ; cv : : Mat * camMatrix = ( cv : : Mat * ) camMatrixAddr ; cv : : Mat * distCoeff = ( cv : : Mat * ) distCoeffAddr ; 14 15 16 17 std : : vector <Marker> vMarkers ; 18 19 try { 20 inst−>detect ( * input , vMarkers , * camMatrix , * distCoeff , ←markerSizeMeters , setYPerpendicular ) ; } c a t c h ( cv : : Exception e ) { newJavaException ( env , " j a v a / l a n g / E x c e p t i o n " , e . what ( ) ) ; } jobject newMarkers [ vMarkers . size ( ) ] ; 21 22 23 24 25 26 27 f o r ( i n t i = 0 ; i< vMarkers . size ( ) ; i++) { Marker * markPtr = &vMarkers [ i ] ; jobject dummy = env−>NewObject ( Marker_class , ←Marker_Constructor_method ) ; setHandle ( env , dummy , markPtr ) ; 28 29 30 31 32 newMarkers [ i ] = env−>NewObject ( Marker_class , ←Marker_Copy_Constructor_method , dummy ) ; env−>CallBooleanMethod ( detectedMarkers , Add_method , ←newMarkers [ i ] ) ; env−>DeleteLocalRef ( newMarkers [ i ] ) ; env−>DeleteLocalRef ( dummy ) ; } 33 34 35 36 37 38 env−>DeleteLocalRef ( ArrayList_class ) ; env−>DeleteLocalRef ( Marker_class ) ; 39 40 41 42 } Algunas funciones de ArUco reciben dos parámetros por referencia para ser modificados, se ha estimado oportuno separar esto en dos funciones de Java que no reciben parámetros pero devuelven el valor. Por ejemplo, en la clase MarkerDetector de ArUco existe la función getMinMaxSize que recibe por referencia dos variables de tipo flotante para modificarlas. Se ha estimado más acorde con la filosofía de Java dividir esta función en las dos funciones siguientes: 85 Capítulo 8. Pruebas 1 2 p u b l i c f l o a t getMinSize ( ) ; p u b l i c f l o a t getMaxSize ( ) ; De este modo, el valor será devuelto (y puede ser guardado en una variable) pero se utiliza un estilo de programación más acorde con la forma de proceder en Java. Parámetros por defecto: algunas funciones de la librería ArUco permiten usar algunos parámetros con valores por defecto y Java no permite esta funcionalidad. Al hacer las pruebas y percibirse este hecho se ha intentado “simular” este tipo de llamadas creando diferentes funciones que incluyen esos valores por defecto. Por ejemplo, en la clase Marker de ArUco la definición de la función draw es la siguiente: v o i d draw ( cv : : Mat &in , cv : : Scalar color , i n t lineWidth=1 ,←b o o l writeId= t r u e ) ; 1 Se ha optado por crear las siguientes tres funciones en Java: p u b l i c v o i d draw ( Mat img , Scalar color , i n t lineWidth , ←boolean writeId ) { Jdraw ( img . getNativeObjAddr ( ) , color . val , lineWidth , ←writeId ) ; 1 2 3 } 4 5 6 7 p u b l i c v o i d draw ( Mat img , Scalar color , i n t lineWidth ) { Jdraw ( img . getNativeObjAddr ( ) , color . val , lineWidth , t r u e ←); } 8 9 10 11 p u b l i c v o i d draw ( Mat img , Scalar color ) { Jdraw ( img . getNativeObjAddr ( ) , color . val , 1 , t r u e ) ; } De este modo, aunque no se simula totalmente el uso de los parámetros con valores por defecto, sí que pueden realizarse algunas llamadas a función más cómodas al programador y que respeten los valores por defecto que se usan en ArUco. Acceso a los atributos de objetos de ArUco: se han creado funciones “get” y “set” para poder acceder a los atributos públicos de los objetos de ArUco. Acceso a las funciones de los vectores: dado que varias clases de ArUco heredan de la clase vector de C++ se han implementado los métodos “get” y “size” en las clases Java que las representan. 86 Capítulo 8. Pruebas 8.2. “Wrapper” en C++ Dentro de esta parte de la librería se han realizado pruebas de funcionamiento sobre todos sus ficheros y se ha prestado especial atención a los siguientes hechos: Mantenimiento de la relación entre objetos Java y C++: este punto fue crítico a la hora de comenzar el proyecto. Se ha utilizado una variable de tipo “long” en los objetos Java denominada “nativeHandle” en la que se ha guardado la dirección de memoria de los objetos de C++. De este modo se mantiene una relación entre los objetos de los dos lenguajes y se evita que los objetos de C++ se “pierdan”. Esta variable es modificada por las funciones que se encuentran en el fichero “Handle.h”. Transformación de los parámetros recibidos desde Java a los parámetros que han de recibir las funciones de ArUco: se ha puesto especial cuidado en que los parámetros no se modifiquen al cambiar los tipos por aquellos necesarios para ArUco. Algunos ejemplos son los siguientes: • Tipos básicos: el paso de tipos básicos es muy simple como enteros, booleanos, flotantes, etc. es bastante sencillo con JNI ya que pueden utilizarse directamente. • Tipo Mat de OpenCV: para pasar las imágenes desde Java al “wrapper” de C++ sin que se tenga que realizar una copia de las mismas en memoria se investigó el funcionamiento de la clase Mat y se descubrió la existencia de la función getNativeObjAddr que devuelve la dirección de memoria del objeto Mat de OpenCV en C++. De este modo, se pasa al lado de C++ solamente la dirección de memoria. Se puede tomar como ejemplo el método draw de la clase Marker. En la clase de Java se recibe el objeto de tipo Mat pero, como se puede observar, a la función JDraw de C++ se le pasa la dirección del objeto nativo de forma transparente al usuario que llama a draw: 1 2 3 p u b l i c v o i d draw ( Mat img , Scalar color ) { Jdraw ( img . getNativeObjAddr ( ) , color . val , 1 , t r u e ) ; } Posteriormente, cuando la dirección llega a C++ se crea un puntero al objeto Mat y se le hace apuntar a la dirección que ha sido pasada como parámetro (línea 3). Después, puede pasarse el contenido de ese puntero a la función draw de ArUco (línea 10) como se observa a continuación: 87 Capítulo 8. Pruebas JNIEXPORT v o i d JNICALL Java_wrapper_Marker_Jdraw ( JNIEnv * ←env , jobject obj , jlong img , jdoubleArray color , ←jint lineWidth , jboolean writeId ) { Marker * m = getHandle<Marker > ( env , obj ) ; cv : : Mat * inMat = ( cv : : Mat * ) img ; 1 2 3 4 d o u b l e buf [ 4 ] ; env−>GetDoubleArrayRegion ( color , 0 , 4 , buf ) ; 5 6 7 Scalar scColor = Scalar ( buf [ 0 ] , buf [ 1 ] , buf [ 2 ] , buf [ 3 ] ) ; 8 9 m−>draw ( * inMat , scColor , lineWidth , writeId ) ; 10 11 } Transformación y devolución de los resultados hacia Java: los valores y tipos devueltos han sido comprobados desde la parte Java de la librería. En el caso de algunos tipos devueltos ha sido más sencillo crear los objetos Java y devolverlos gracias a las herramientas que ofrece JNI. Algunos ejemplos son los siguientes: • Tipo Point de OpenCV: para mostrar cómo se ha resuelto la devolución de datos desde OpenCV se va a mostrar cómo se devuelve un punto en la función getCenter de la clase Marker. La llamada es la siguiente: 1 2 3 p u b l i c Point getCenter ( ) { r e t u r n JgetCenter ( ) ; } Una vez más, para el usuario de la librería es transparente y solamente sabe que se devuelve un objeto de tipo Point de OpenCV de Java. Sin embargo, en el lado de C++ la cosa se complica ya que no se puede devolver un objeto Point de C++ y esperar que Java lo reconozca como tal. De hecho, la solución por la que se ha optado es la de crear un objeto Point de C++ para recibir el resultado de la llamada a ArUco (línea 3), crear el objeto de Java (líneas 5 a 7) y devolverlo (línea 11). Esto puede hacerse gracias a las funciones que ofrece JNI. El código es el siguiente: 1 2 3 JNIEXPORT jobject JNICALL Java_wrapper_Marker_JgetCenter ( ←JNIEnv * env , jobject obj ) { Marker * m = getHandle<Marker > ( env , obj ) ; Point a = m−>getCenter ( ) ; 4 5 jclass Point_class = env−>FindClass ( " o r g / o p e n c v / c o r e / ←Point " ) ; 88 Capítulo 8. Pruebas jmethodID constructor = env−>GetMethodID ( Point_class , " <←i n i t > " , " (DD)V" ) ; jobject returnedPoint = env−>NewObject ( Point_class , ←constructor , ( jdouble ) a . x , ( jdouble ) a . y ) ; 6 7 8 env−>DeleteLocalRef ( Point_class ) ; 9 10 r e t u r n returnedPoint ; 11 12 } Creación y modificación de objetos Java desde C++: JNI provee de herramientas que permiten hacer referencia a clases y objetos Java. Dentro de la parte de C++ se ha puesto especial cuidado a la hora de utilizar estas herramientas. 8.3. Programas de prueba Se intentaron realizar programas parecidos a los que se ofrecen con la librería ArUco. De este modo, podía probarse el correcto funcionamiento de la librería, intentando que los resultados fueran lo más parecidos posible a los de la librería en código nativo. Se observaron algunos problemas dependientes de OpenCV debido a que la gestión de vídeo no está tan depurada en Java como en C++. Por ejemplo, se tuvo que renunciar a la utilización de VideoCapture porque no permite cargar vídeos. Además, las pruebas con estos programas fueron de gran utilidad para encontrar errores en las clases Java y en el código C++. 8.4. Pruebas de funcionamiento en varios sistemas operativos Se han realizado pruebas en los siguientes sistemas operativos: Windows 7 Kubuntu 14.10 Se ha comprobado el correcto funcionamiento de la librería en ambas plataformas. 89 Capítulo 9 Conclusiones y futuras mejoras 9.1. Conclusiones La principal conclusión del presente proyecto de fin de carrera es la siguiente: se ha realizado un wrapper mediante JNI que permite utilizar las funciones de la librería ArUco desde Java. Los siguientes objetivos planteados al comienzo del proyecto se han cumplido: La librería ofrece la posibilidad de ser utilizada en Java. La librería ofrece las mismas funcionalidades que ArUco, siendo las más importantes: • Creación de marcadores • Creación de tableros • Detección de marcadores • Detección de tableros • Funciones que permitan dibujar señales que demuestren el reconocimiento de los marcadores y tableros, tales como sus bordes, identificadores, ejes tridimensionales, etc. La ejecución del código se realiza en lenguaje nativo, directamente en la propia ArUco. La librería viene acompañada con varios ejemplos que muestran su utilidad y funcionamiento, con el objetivo de que puedan ser utilizados con una función didáctica. Se ha obtenido tanto una librería para sistemas Linux como para sistemas Windows 91 Capítulo 9. Conclusiones y futuras mejoras 9.2. Futuras mejoras Aunque, como se ha visto en el capítulo anterior, la aplicación cumple tanto el objetivo principal como los secundarios, se estima que hay varias mejoras que podrían ser de utilidad de cara a mejorar este programa en el futuro. Las mejoras que se podrían hacer son: Programar la función setMarkerDetectorFunction: dentro de MarkerDetector, se encuentra la función setMarkerDetectorFunction que recibe una función como parámetro. Debido a la dificultad para hacer esta llamada desde Java se ha dejado como futura mejora. Probablemente, el mejor modo de hacer esto sería enviar un objeto encargado de realizar el proceso. Ampliar el número de Sistemas Operativos en los que funciona la librería: debido a la importancia de los dispositivos móviles sería realmente interesante poder conseguir que la librería funcione tanto para Android como para iOS, además de Mac OS. Traducción de la documentación a otros idiomas: sería interesante traducir la documentación y ayuda, además del Manual de Usuario a otros idiomas como por ejemplo el inglés. De este modo se podría llegar a más usuarios y que tuvieran menos dificultad a la hora de iniciarse con esta librería. Ampliar el número de Sistemas Operativos en los que funciona la librería: debido a la importancia de los dispositivos móviles sería realmente interesante poder conseguir que la librería funcione tanto para Android como para iOS, además de Mac OS. Programa de ejemplo con interfaz gráfica: para acercar la Realidad Aumentada a las personas con menos capacidades de programación podría ser interesante realizar una aplicación con interfaz gráfica que permitiera crear e imprimir tanto marcadores como tableros y, posteriormente, detectarlos y mostrar sobre ellos alguna información. 92 Bibliografía [1] Oliver Bimber, Ramesh Raskar. “Spatial Augmented Reality”. Ed AK Peters, 2005. [2] Página web de learnAR http://www.learnar.org/. Fecha de última consulta: 11 de agosto de 2014. [3] Página web de Google Sky Map http://www.google.com/mobile/ skymap/. Fecha de última consulta: 11 de agosto de 2014. [4] Página web de blippAR http://blippar.com. Fecha de última consulta: 11 de agosto de 2014. [5] Página web de Ingress https://www.ingress.com/. Fecha de última consulta: 11 de agosto de 2014. [6] Página web de Quest Visual, creadores de Word Lens http://questvisual. com/. Fecha de última consulta: 11 de agosto de 2014. [7] Página web de ArUco http://www.uco.es/investiga/grupos/ava/ node/26. Fecha de última consulta: 11 de agosto de 2014. [8] Página web de OpenCV http://opencv.org/. Fecha de última consulta: 11 de agosto de 2014. [9] Página web de Java http://java.com/es/. Fecha de última consulta: 11 de agosto de 2014. [10] Página web de ARToolKit en SourceForge http://artoolkit. sourceforge.net/. Fecha de última consulta: 11 de agosto de 2014. [11] Página web de ARLab http://www.arlab.com/. Fecha de última consulta: 11 de agosto de 2014. [12] Página web de droidAR https://github.com/bitstars/droidar. Fecha de última consulta: 11 de agosto de 2014. [13] Página web de GRATF http://www.aforgenet.com/projects/ gratf/. Fecha de última consulta: 11 de agosto de 2014. 93 BIBLIOGRAFÍA [14] Página web de js-aruco https://code.google.com/p/js-aruco/. Fecha de última consulta: 16 de agosto de 2014. 94