ESCUELA TÉCNICA SUPERIOR DE INGENIERIA (ICAI) INGENIERO ELECTROMECÁNICO Desarrollo de un control visual basado en un sensor Kinect para la navegación autónoma en un entorno forestal Autor: Aldo García Regueira Directora: Ángela Ribeiro Seijas Madrid Julio 2015 AUTORIZACIÓN PARA LA DIGITALIZACIÓN, DEPÓSITO Y DIVULGACIÓN EN ACCESO ABIERTO (RESTRINGIDO) DE DOCUMENTACIÓN 1º. Declaración de la autoría y acreditación de la misma. El autor D. ALDO GARCÍA REGUEIRA, como ESTUDIANTE de la UNIVERSIDAD PONTIFICIA COMILLAS (COMILLAS), DECLARA que es el titular de los derechos de propiedad intelectual, objeto de la presente cesión, en relación con la obra DESARROLLO DE UN CONTROL VISUAL BASADO EN UN SENSOR KINECT PARA LA NAVEGACIÓN AUTÓNOMA EN UN ENTORNO FORESTAL1, que ésta es una obra original, y que ostenta la condición de autor en el sentido que otorga la Ley de Propiedad Intelectual como titular único o cotitular de la obra. En caso de ser cotitular, el autor (firmante) declara asimismo que cuenta con el consentimiento de los restantes titulares para hacer la presente cesión. En caso de previa cesión a terceros de derechos de explotación de la obra, el autor declara que tiene la oportuna autorización de dichos titulares de derechos a los fines de esta cesión o bien que retiene la facultad de ceder estos derechos en la forma prevista en la presente cesión y así lo acredita. 2º. Objeto y fines de la cesión. Con el fin de dar la máxima difusión a la obra citada a través del Repositorio institucional de la Universidad y hacer posible su utilización de forma libre y gratuita ( con las limitaciones que más adelante se detallan) por todos los usuarios del repositorio y del portal e-ciencia, el autor CEDE a la Universidad Pontificia Comillas de forma gratuita y no exclusiva, por el máximo plazo legal y con ámbito universal, los derechos de digitalización, de archivo, de reproducción, de distribución, de comunicación pública, incluido el derecho de puesta a disposición electrónica, tal y como se describen en la Ley de Propiedad Intelectual. El derecho de transformación se cede a los únicos efectos de lo dispuesto en la letra (a) del apartado siguiente. 3º. Condiciones de la cesión. Sin perjuicio de la titularidad de la obra, que sigue correspondiendo a su autor, la cesión de derechos contemplada en esta licencia, el repositorio institucional podrá: 1 Especificar si es una tesis doctoral, proyecto fin de carrera, proyecto fin de Máster o cualquier otro trabajo que deba ser objeto de evaluación académica 1 (a) Transformarla para adaptarla a cualquier tecnología susceptible de incorporarla a internet; realizar adaptaciones para hacer posible la utilización de la obra en formatos electrónicos, así como incorporar metadatos para realizar el registro de la obra e incorporar “marcas de agua” o cualquier otro sistema de seguridad o de protección. (b) Reproducirla en un soporte digital para su incorporación a una base de datos electrónica, incluyendo el derecho de reproducir y almacenar la obra en servidores, a los efectos de garantizar su seguridad, conservación y preservar el formato. . (c) Comunicarla y ponerla a disposición del público a través de un archivo abierto institucional, accesible de modo libre y gratuito a través de internet.2 (d) Distribuir copias electrónicas de la obra a los usuarios en un soporte digital. 3 4º. Derechos del autor. El autor, en tanto que titular de una obra que cede con carácter no exclusivo a la Universidad por medio de su registro en el Repositorio Institucional tiene derecho a: a) A que la Universidad identifique claramente su nombre como el autor o propietario de los derechos del documento. b) Comunicar y dar publicidad a la obra en la versión que ceda y en otras posteriores a través de cualquier medio. c) Solicitar la retirada de la obra del repositorio por causa justificada. A tal fin deberá ponerse en contacto con el vicerrector/a de investigación ([email protected]). d) Autorizar expresamente a COMILLAS para, en su caso, realizar los trámites necesarios para la obtención del ISBN. 2 En el supuesto de que el autor opte por el acceso restringido, este apartado quedaría redactado en los siguientes términos: (c) Comunicarla y ponerla a disposición del público a través de un archivo institucional, accesible de modo restringido, en los términos previstos en el Reglamento del Repositorio Institucional 3 En el supuesto de que el autor opte por el acceso restringido, este apartado quedaría eliminado. 2 d) Recibir notificación fehaciente de cualquier reclamación que puedan formular terceras personas en relación con la obra y, en particular, de reclamaciones relativas a los derechos de propiedad intelectual sobre ella. 5º. Deberes del autor. El autor se compromete a: a) Garantizar que el compromiso que adquiere mediante el presente escrito no infringe ningún derecho de terceros, ya sean de propiedad industrial, intelectual o cualquier otro. b) Garantizar que el contenido de las obras no atenta contra los derechos al honor, a la intimidad y a la imagen de terceros. c) Asumir toda reclamación o responsabilidad, incluyendo las indemnizaciones por daños, que pudieran ejercitarse contra la Universidad por terceros que vieran infringidos sus derechos e intereses a causa de la cesión. d) Asumir la responsabilidad en el caso de que las instituciones fueran condenadas por infracción de derechos derivada de las obras objeto de la cesión. 6º. Fines y funcionamiento del Repositorio Institucional. La obra se pondrá a disposición de los usuarios para que hagan de ella un uso justo y respetuoso con los derechos del autor, según lo permitido por la legislación aplicable, y con fines de estudio, investigación, o cualquier otro fin lícito. Con dicha finalidad, la Universidad asume los siguientes deberes y se reserva las siguientes facultades: a) Deberes del repositorio Institucional: - La Universidad informará a los usuarios del archivo sobre los usos permitidos, y no garantiza ni asume responsabilidad alguna por otras formas en que los usuarios hagan un uso posterior de las obras no conforme con la legislación vigente. El uso posterior, más allá de la copia privada, requerirá que se cite la fuente y se reconozca la autoría, que no se obtenga beneficio comercial, y que no se realicen obras derivadas. - La Universidad no revisará el contenido de las obras, que en todo caso permanecerá bajo la responsabilidad exclusiva del autor y no estará obligada a ejercitar acciones legales en nombre del autor en el supuesto de infracciones a derechos de propiedad intelectual derivados del depósito y archivo de las obras. El autor renuncia a cualquier reclamación frente a la Universidad por las formas no ajustadas a la legislación vigente en que los usuarios hagan uso de las obras. - La Universidad adoptará las medidas necesarias para la preservación de la obra en un futuro. 3 ESCUELA TÉCNICA SUPERIOR DE INGENIERIA (ICAI) INGENIERO ELECTROMECÁNICO Desarrollo de un control visual basado en un sensor Kinect para la navegación autónoma en un entorno forestal Autor: Aldo García Regueira Directora: Ángela Ribeiro Seijas Madrid Julio 2015 Desarrollo de un control visual basado en un sensor Kinect para la navegación autónoma en un entorno forestal Autor: García Regueira, Aldo Directora: Ribeiro Seijas, Ángela Entidad colaboradora: Centro de Automática y Robótica - (CSIC-UPM) RESUMEN DEL PROYECTO Introducción La robótica es una rama de la tecnología, se ocupa del estudio y funcionamiento de los robots, mediante la manufactura y las aplicaciones de éstos. Los robots son un grandes aliados en la realización de tareas repetitivas, que requieran una alta precisión o se lleven a cabo en entornos adversos para el ser humano; estando presentes en una amplia variedad de campos, desde robots quirúrgicos, brazos mecánicos en líneas de montaje, robots enviados al espacio para la exploración, aviones no tripulados etc. Estos avances podrán producir en un futuro una mayor industrialización en ciertas áreas como la agrícola, al asistir al operario a gestionar el cultivo, ya sea con tareas de inspección o de tratamiento de cultivos, simplificando sus tareas, reduciendo costes y aumentando la productividad De la misma forma que el salto de animales de tiro a tractores, produjo grandes desarrollos en la agricultura, por medio de la robótica se busca optimizar el guiado y la gestión de las máquinas empleadas. [1] Por tanto, el objetivo de este proyecto es hacer posible la navegación autónoma y segura de un vehículo en entornos con árboles, como son los forestales o las zonas dedicadas al cultivo de árboles con o sin frutos. El vehículo deberá recorrer una zona o un cultivo con cobertura total, realizando tareas de inspección, para ello, será esencial disponer de un sistema sensorial adecuado que le permita construir una imagen aproximada de su entorno; se propone el uso de una cámara de profundidad, y en concreto del sensor Kinect como base del desarrollo del sistema de guiado del vehículo, ya que dispone de una aceptable velocidad de reacción para un rango medio (un orden de magnitud de varios metros) a un precio bastante asequible en comparación con otros dispositivos. Metodología Tomando como base del control visual el sensor Kinect para obtener los datos del entorno en tiempo real y elaborar un mapeado tridimensional, necesario para sentar las bases de la navegación autónoma y poder generar las órdenes para guiar al vehículo. El proceso seguido (tal y como es mostrado en la figura 1) consiste en procesar las imágenes de color y profundidad del entorno, obtenidas por medio del sensor Kinect, para recrear el entorno en tres dimensiones. Figura 1. Proceso seguido [2][3][4] Esta información del entorno, será tratada en el compilador de Visual Studio, aquí se necesita recrear la escena a partir de los fotogramas que se vayan capturando en tiempo real; por lo que es necesario emplear herramientas como OpenCV, más especializadas en el campo de visión por ordenador, que permiten relacionar dos fotogramas distintos mediante los puntos de interés (keypoints), cuanto más fuerte sea la relación de los puntos de interés entre los dos fotogramas, mejor definida estará la escena, esto se consigue creando una imagen panorámica formada por los fotogramas base proporcionados por el sensor, y localizando dichos fotogramas en la panorámica, obteniendo así las relaciones espaciales ente ellos, tal y como muestra la figura 2. Figura 2, Panorámica y localización Estas relaciones espaciales entre los distintos fotogramas, se van guardando, junto con los valores de color y profundidad, en una matriz global, que almacena la nube de puntos de todo el entorno, partiendo del fotograma base y añadiendo los siguientes fotogramas, con las traslaciones calculadas previamente. Con la escena ya totalmente determinada, es necesario representar la nube de puntos, para ello se dispondrá de la herramienta de OpenGL, especializada en aplicaciones de vectores gráficos. Para una mejor representación, se aplica una rotación a la escena tridimensional mostrada y también la posibilidad de cambiar de escala, ya que el rango inicial del display puede no ser suficiente para escenas de gran tamaño. Resultados Para determinar la viabilidad del proyecto, se han realizado a lo largo del trabajo varios experimentos que se detallan a continuación. En primer lugar se realizaron pruebas al sensor Kinect para comprobar su funcionamiento en entornos exteriores con árboles, sin embargo, la imagen de profundidad proporcionada en condiciones de alta intensidad luminosa, no era suficientemente fiable para desarrollar un control visual; por otra parte, se llevaron a cabo experimentos para comparar la última versión del Kinect (Kinect One) con el Kinect 360, que es el usado en este proyecto, aprobando el uso del nuevo Kinect para entornos exteriores. Debido a ciertos problemas con la velocidad de proceso, se realizaron experimentos sobre la creación de la escena, mostrando poca fiabilidad a la hora de detectar los fotogramas base en la panorámica y largos tiempos de procesamiento, que impiden que el programa pueda trabajar a tiempo real. Finalmente se logra representar con éxito la escena tridimensional (figura 3), sin embargo, los cálculos para la localización espacial de los fotogramas, no son suficientemente precisos para formar un mapeado detallado. Figura 3 Escena tridimensional [5][6] Conclusiones Se ha desarrollado un programa basado en el sensor Kinect, que intenta responder a la necesidad de la navegación autónoma de un vehículo, el programa procesa los datos proporcionados por el sensor y localiza espacialmente los fotogramas, recreando con éxito el mapeado tridimensional del entorno, sin embargo, problemas como la precisión de cálculos o el largo tiempo de proceso, impiden que el programa funcione en tiempo real, impidiendo satisfacer la necesidad pedida. Referencias [1] Revista ambienta, Agricultura de precisión http://www.revistaambienta.es/WebAmbienta/marm/Dinamicas/secciones/articul os/AP.htm [2] Logo de Microsoft Visual Studio https://www.visualstudio.com/ [3] Logo de OpenCV http://opencv.org/ [4] Logo de OpenGL https://www.opengl.org/ [5] Vídeo de la escena, disponible en YouTube https://youtu.be/TM1jzjGJ7K0 [6] Vídeo de la escena, disponible en YouTube https://youtu.be/eR4S4tpCaIE Development of a visual control based on a Kinect sensor for the autonomous navigation through a forest environment Author: García Regueira, Aldo Director: Ribeiro Seijas, Ángela Collaborating organization: Centro de Automática y Robótica - (CSICUPM) ABSTRACT Introduction Robotics is a branch of technology which deals with the study and functioning of robots, along with their design, construction and use. Robots are great allies in the performance of repetitive tasks which require high accuracy or which take place in hostile environments for human beings. They can be found in a wide variety of fields, going from surgical robots and mechanical arms in assembly lines to space explorer robots or unmanned aircrafts. These developments will produce a greater industrialization in some fields such as agriculture, by assisting the operator on managing the crops, both in terms of crops inspection or crops treatment tasks. It will simplify these tasks, decrease the costs and increase the productivity. Just like what happened with the leap from draft animals to tractors, which caused great developments in agriculture, the main aim of robotics is to optimize the guiding and management of the machines which are utilized. [1] Therefore, the objective of this project is making the autonomous and safe navigation of a vehicle possible in environments with trees, such as forests or tree cropping areas, whether with or without fruit. The vehicle must go across an area or a crop with total coverture, carrying out inspection tasks; for this, it will be essential to have a suitable sensorial system which allows to construct an approximate image of its surroundings; the Kinect sensor is proposed as the key element for the development of the guiding system of the vehicle, because it provides an acceptable reaction speed for a medium range (several meters) at a reasonable price if compared to other devices. Methodology Taking the Kinect sensor as the base of the visual control for getting the data of the surroundings in real time and creating a tridimensional mapping, which is necessary for setting the bases of the autonomous navigation and for being able to produce the instructions for guiding the vehicle The proceedings (as showed in figure 1) consist in the treatment of the Depth and Color Image of the environment, which are obtained through the Kinect sensor, for the recreation of an spatial representation of the surroundings. Figure 1. Proceedings [2][3][4] This information of the surroundings will be treated in the Microsoft Visual Studio compiler, recreating the scene from the frames captured in real time; so, the use of tools such as OpenCV is mandatory, due to the need of more specialized programs in the field of computer vision, allowing to relate two different frames through the points of interest (keypoints). The stronger the relation between two frames is, the better the scene is recreated; this is achieved by creating a panoramic view composed by the two base frames provided by the sensor and by locating those frames in the panoramic scene, getting through this proceeding, then, the spatial relations between the two base frames, as shown in figure 2. Figure 2, Panoramic and location These spatial relations between two frames are saved along with the values of color and depth of each image in a global matrix which stores the point cloud of the entire environment, starting from the initial frame and adding the next frames with the movements previously computed. With the surroundings finally defined, it is necessary to represent the point cloud; for this, the OpenGL tool is used. This tool is specialized in graphic vectors applications. For a better display, a rotation is applied to the tridimensional scene and there is also the possibility to change the scale in case that the initial size of the displayed figures may not be adequate for bigger scenes. Results For determining the viability of the project, several experiments have been carried out The details of these experiments are the ones which go next: First of all, some tests were carried out for verifying the functioning of the Kinect sensor outdoors, in spaces without trees; however, the depth image given by the sensor in cases of high light intensity conditions was not reliable enough for developing a visual control. Besides, certain tests were also carried out to compare the newest version of Kinect (Kinect One) with Kinect 360, which is the one utilized in this project; the results where favorable for the new one, showing its reliability outdoors. Due to certain problems with processing speed, experiments involving the creation of the panoramic image were executed, showing low reliability when it came to the detection of the base frames in the panoramic image and also showing long processing times, which make functioning in real time impossible for the program. Finally the tridimensional scene is successfully displayed (figure 3); however, the calculations required for the spatial location of the frames are not accurate enough to create a detailed mapping. Figure 3 Tridimensional scene [5][6] Conclusions A program which intends to solve the need for the autonomous navigation of a vehicle has been developed using the Kinect sensor. The program processes the data provided by the sensor and spatially locates the frames; this way, it successfully recreates the tridimensional mapping of the surroundings. However, certain problems related to the accuracy of calculations or to the long time of processing make it impossible for the program to work in real time, and this does not allow the program to fulfill the requested need. References [1] Ambienta Magazine, mechanization on agriculture http://www.revistaambienta.es/WebAmbienta/marm/Dinamicas/secciones/articul os/AP.htm [2] Microsoft Visual Studio https://www.visualstudio.com/ [3] OpenCV Logo http://opencv.org/ [4] OpenGL Logo https://www.opengl.org/ [5] Scene video, available on YouTube https://youtu.be/TM1jzjGJ7K0 [6] Scene video, available on YouTube https://youtu.be/eR4S4tpCaIE UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL ÍNDICE DE LA MEMORIA Índice de la memoria Parte I Memoria descriptiva ....................................................................... 7 Capítulo 1 Introducción ................................................................................... 9 1.1 Estudio de las tecnologías existentes .................................................................. 10 1.2 Motivación del proyecto...................................................................................... 12 1.3 Objetivos .............................................................................................................. 12 1.4 Metodología desarrollada ................................................................................... 13 1.5 Recursos / herramientas empleadas .................................................................. 14 Capítulo 2 Desarrollo ..................................................................................... 15 2.1 Esquema general.................................................................................................. 15 2.2 Extracción de datos del entorno ......................................................................... 17 2.2.1 Qué es y cómo funciona el Kinect ................................................................... 17 2.2.2 Software del Kinect ......................................................................................... 20 2.2.3 Extracción de datos ......................................................................................... 26 2.3 Tratamiento de imagen y obtención de panorámica ........................................ 30 2.3.1 OpenCV y el formato Mat ............................................................................... 30 2.3.2 RGB y RGBA .................................................................................................. 32 2.3.3 Stitching........................................................................................................... 34 2.3.4 Localización en la escena ................................................................................ 37 2.3.5 Posicionamiento en la escena .......................................................................... 39 2.3.6 Mat_Omega, la matriz de almacenamiento ..................................................... 42 2.3.7 Giro, rotación y traslación en los nuevos fotogramas ..................................... 44 2.4 Tratamiento del mapa de bits en 3D .................................................................. 50 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL ÍNDICE DE LA MEMORIA 2.4.1 Las bases de la representación 3D, OpenGL ................................................... 50 2.4.2 Creando la nube de puntos .............................................................................. 52 Capítulo 3 Resultados/Experimentos ............................................................. 61 3.1 Experimentos de extracción de datos ................................................................ 61 3.1.1 El Kinect en exteriores, para diversos grados de iluminación ......................... 61 3.1.2 El Kinect One frente al Kinect 360 ................................................................. 63 3.1.3 Detección de cristales ...................................................................................... 65 3.2 Experimentos en la creación de la escena ......................................................... 67 3.2.1 Funcionamiento de Homography .................................................................... 67 3.2.2 Funcionamiento de Stitching ........................................................................... 70 3.2.3 Stitching en escenas luminosas ....................................................................... 82 3.2.4 Stitching en escenas no estáticas ..................................................................... 82 3.3 Experimentos y resultado en el espacio tridimensional ................................... 85 3.3.1 Análisis de las herramientas disponibles en el espacio tridimensional ........... 85 3.3.2 Representación de un fotograma ..................................................................... 90 3.3.3 Recreación de la escena tridimensional ........................................................... 92 Capítulo 4 Conclusiones ................................................................................. 97 Capítulo 5 Futuros desarrollos ...................................................................... 99 Bibliografía................................................................................................................ 103 Parte II Estudio económico...................................................................... 107 Parte III Presupuesto ................................................................................. 111 Capítulo 1 Mediciones .................................................................................. 113 1.1 Componentes de Hardware y herramientas .................................................. 113 1.2 Componentes de Software ............................................................................... 114 1.3 Mano de obra directa ....................................................................................... 114 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL ÍNDICE DE LA MEMORIA Capítulo 2 Precios unitarios ......................................................................... 115 2.1 Componentes de Hardware y herramientas ................................................. 115 2.2 Componentes de Software .............................................................................. 115 2.3 Mano de obra directa ...................................................................................... 116 Capítulo 3 Sumas Parciales.......................................................................... 117 3.1 Componentes de Hardware y herramientas ................................................. 117 3.2 Componentes de Software .............................................................................. 118 3.3 Mano de obra directa ...................................................................................... 118 Capítulo 4 Presupuesto General .................................................................. 119 Parte IV Código fuente .............................................................................. 121 Parte V Manual del usuario .................................................................... 157 Capítulo 1 Instalación de librerías ............................................................... 159 1.1 Instalación de OpenCV .................................................................................... 159 1.1 Instalación de OpenGL .................................................................................... 163 Capítulo 2 Funcionamiento del programa .................................................. 165 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Memoria Índice de tablas Tabla 1 Tiempo Homography ............................................................................... 69 Tabla 2 Tiempo Stitching ...................................................................................... 81 Tabla 3 Medición de Hardware ........................................................................... 113 Tabla 4 Medición de Software ............................................................................ 114 Tabla 5 Medición de mano de obra ..................................................................... 114 Tabla 6 Precio de Hardware ................................................................................ 115 Tabla 7 Precio de Software ................................................................................. 116 Tabla 8 Precio de mano de obra .......................................................................... 116 Tabla 9 Suma de Hardware ................................................................................. 117 Tabla 10 Suma de Software................................................................................. 118 Tabla 11 Suma de mano de obra ......................................................................... 118 Tabla 12 Presupuesto general .............................................................................. 119 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL ÍNDICE DE FIGURAS Índice de figuras Figura 1. Sensores para la conduccion autónoma (Nissan) ................................... 10 http://www.coches.net/noticias/coches-sin-conductor Figura 2. Sistema sensorial del robot de la universidad de Almería ..................... 11 http://www.ual.es/personal/rgonzalez/papers/279.pdf Figura 3. Mars Rover............................................................................................. 11 http://mars.nasa.gov/msl/mission/instruments/ Figura 4. Esquema de metodología ...................................................................... 14 Figura 5. Esquema del desarrollo. ......................................................................... 16 Figura 6. Visión Binocular .................................................................................... 17 http://blog.panasonic.es/tecnologia/tecnologia-3d-a-fondo/ Figura 7. Sensor Kinect ......................................................................................... 19 http://support.xbox.com/es-ES/xbox-360/kinect/kinect-sensor-components Figura 8. Escáner de luz 3D .................................................................................. 20 https://en.wikipedia.org/wiki/Structured-light_3D_scanner Figura 9. Kinect SDK V 1.8 .................................................................................. 21 https://www.microsoft.com/en-us/download/details.aspx?id=40278 Figura 10. Kinect Studio, Panel Central ................................................................ 21 Figura 11. Kinect Studio, Color ............................................................................ 22 Figura 12. Kinect Studio, Profundidad .................................................................. 22 Figura 13. Kinect Studio, Color y Profundidad..................................................... 23 Figura 14. Depth D2D-Origen ............................................................................... 24 Figura 15. Kinect Explorer D2D-Origen ............................................................... 25 Figura 16. Imagen Reflejo ..................................................................................... 25 http://www.disfrutalasmatematicas.com/geometria/images/reflect-labels.gif UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL ÍNDICE DE FIGURAS Figura 17. Entorno Visual Studio .......................................................................... 26 https://www.visualstudio.com/en-us/products/visual-studio-ultimate-with-msdn-vs.aspx Figura 18. Paleta RGB........................................................................................... 31 http://www.informatikzentrale.de/rgb-farbmodell.html Figura 19. RGB formato Mat ................................................................................ 31 http://docs.opencv.org/doc/tutorials/core/mat_the_basic_image_container Figura 20. Color RGBA error................................................................................ 33 Figura 21. RGB omitiendo capa alpha .................................................................. 34 Figura 22. Imágenes 30º Stitching......................................................................... 35 Figura 23. Stitiching image, escena....................................................................... 36 Figura 24. Las aristas y el origen .......................................................................... 40 Figura 25. Esquema proceso ................................................................................. 43 Figura 26. Intersección de fotogramas .................................................................. 44 Figura 27. Dentro o fuera de un polígono ............................................................. 45 Figura 28. Determinacion de ángulos .................................................................... 48 Figura 29. Representacion tridimensional estática ................................................ 53 Figura 30. Imagen de la representación estática.................................................... 53 Figura 31. Localizacion del sensor en la escena 3D.............................................. 54 Figura 32. Rotacion de la figura 3D ...................................................................... 55 Figura 33. Representación tridimensional, errores marcados ............................... 56 Figura 34. Recreacion de la escena ....................................................................... 57 Figura 35. . Diferencias de reescalado, imagen pequeña y base ........................... 59 Figura 36. Diferencias de reescalado, imagen grande ........................................... 59 Figura 37. Exterior diurno ..................................................................................... 62 Figura 38. Exterior anocheciendo ......................................................................... 62 Figura 39. Exterior noche cerrada ......................................................................... 63 Figura 40. Kinect One, exterior ............................................................................. 64 Figura 41. Kinect 360, exterior ............................................................................. 65 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL ÍNDICE DE FIGURAS Figura 42. Detección del cristal 1 .......................................................................... 66 Figura 43. Detección del cristal 2 .......................................................................... 66 Figura 44. Fotogramas Homography..................................................................... 67 Figura 45. Homography errónea ........................................................................... 68 Figura 46. Homography correcta........................................................................... 68 Figura 47. Stitching frente a Homography ............................................................ 69 Figura 48. Imágenes base-ligero desplazamiento.................................................. 70 Figura 49. Stitching-ligero desplazamiento........................................................... 71 Figura 50. Localización-ligero desplazamiento .................................................... 71 Figura 51. Imágenes base-avance .......................................................................... 72 Figura 52. Imágenes base-arriba 1 ........................................................................ 72 Figura 53. Imágenes base-arriba 2 ........................................................................ 73 Figura 54. Stitching-arriba 2 ................................................................................. 73 Figura 55. Localización-arriba 2 ........................................................................... 74 Figura 56. Imágenes base-abajo ............................................................................ 75 Figura 57. Stitching-abajo ..................................................................................... 75 Figura 58. Localización-abajo ............................................................................... 76 Figura 59. Imágenes base-derecha ........................................................................ 77 Figura 60. Stitching-derecha ................................................................................. 77 Figura 61. Localización-derecha ........................................................................... 78 Figura 62. Imágenes base-izquierda ...................................................................... 78 Figura 63. Stitching-izquierda ............................................................................... 79 Figura 64. Localización-izquierda ......................................................................... 80 Figura 65. Stitching-luz ......................................................................................... 82 Figura 66. Imágenes base-imágenes no estáticas .................................................. 83 Figura 67. Stitching-imágenes no estáticas ........................................................... 83 Figura 68. Localización-imágenes no estáticas ..................................................... 84 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL ÍNDICE DE FIGURAS Figura 69. Traslación y ejes .................................................................................. 86 Figura 70. Rotación eje X...................................................................................... 86 Figura 71. Rotación eje Y...................................................................................... 87 Figura 72. Normal vs grande ................................................................................. 88 Figura 73. Normal vs pequeño vs más pequeño .................................................... 88 Figura 74. Imagen límite ....................................................................................... 89 Figura 75. Representación tridimensional-Resultado 1 ........................................ 90 Figura 76. Representación tridimensional-Resultado 2 ........................................ 90 Figura 77. Escena formada con tres fotogramas ................................................... 92 Figura 78. Segunda escena formada por tres fotogramas ...................................... 93 Figura 79. Escena formada por dos fotogramas .................................................... 94 Figura 80. Escena formada por dos fotogramas, vista perpendicular ................... 95 Figura 81. Escena formada por dos fotogramas, vista posterior ........................... 96 Figura 82. Distición de objetos en el mapeado tridimensional ........................... 101 http://www.pointclouds.org/blog/gsoc12/cpotthast/index.php Figura 83. Configuración Avanzada del Sistema ................................................ 159 Figura 84. Variables de entorno .......................................................................... 160 Figura 85. Editando las variables de entorno ...................................................... 161 Figura 86. Propiedades ........................................................................................ 162 Figura 87. C/C++ General ................................................................................... 162 Figura 88. Directorio de Bibliotecas Adicionales ............................................... 163 Figura 89. C\C++ Editar ...................................................................................... 164 Figura 90. Vinculador General ............................................................................ 164 Figura 91. Visual Studio Compilar...................................................................... 165 Figura 92. Kinect Explorer D2D ......................................................................... 166 Figura 93. Representación 3D ............................................................................. 167 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Parte I MEMORIA 7 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL 8 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Introducción Capítulo 1 INTRODUCCIÓN La robótica es una rama de la tecnología, se ocupa del estudio y funcionamiento de los robots, mediante la manufactura y las aplicaciones de éstos. El concepto de crear maquinas que puedan operar de forma autónoma parte de hace tiempo (China Siglo III a.C.), pero está en constante evolución, además está presente en muchos aspectos de la vida cotidiana, investigación e industria. Entre los ejemplos podemos encontrar los robots enviados al espacio para la exploración, brazos mecánicos en las cadenas de montaje, robots quirúrgicos, aspiradoras etc. Los robots son un gran aliado en la realización de tareas monótonas y repetitivas o en aquellas que requieran de una alta precisión o se lleven a cabo en entornos adversos como zonas contaminadas o con explosivos, que en resumen, serian inseguras para un ser humano. Es tal el crecimiento, que día a día nos llegan nuevas noticias de avances en el campo de la navegación automática, ya sea en aviones no tripulados, popularmente conocidos por drones, o en coches que pueden circular sin ayuda humana por carreteras o incluso en la conducción sin piloto de un tractor agrícola. [1] Estos desarrollos podrán producir en un futuro una mayor industrialización en ciertas áreas como la agrícola lo que mejorará su competitividad frente a otros sectores. El objetivo de este proyecto es hacer posible la navegación autónoma y segura de un vehículo en entornos con árboles, como son los forestales o las zonas dedicadas al cultivo de arboles con o sin frutos. El vehículo deberá recorrer una zona o un cultivo con cobertura total, realizando 9 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Introducción tareas de inspección, para ello, será esencial disponer de un sistema sensorial adecuado que le permita construir una imagen aproximada de su entorno. 1.1 ESTUDIO DE LAS TECNOLOGÍAS EXISTENTES La mayoría de robots de navegación autónoma emplean cámaras para analizar sus alrededores y ser capaces de evitar obstáculos. Algunos usan un GPS para determinar la localización y evitan obstáculos con sensores láser [2] otros emplean radares (con un alcance de orden de magnitud de cientos de metros, como el coche de la figura 1) o cámaras mucho más potentes. Figura 1 Sensores para la conducción autónoma (Nissan) Otros usan sonares en conjunto con una cámara simple (webcam), como forma de evitar obstáculos [3] [4], como ejemplo, el robot de la figura 2. 10 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Introducción Figura 2 Sistema sensorial del robot de apoyo en invernaderos de la universidad de Almería Estas cámaras (digitales) pueden tener muy baja resolución o muy alta, pero para una navegación más efectiva, es mejor conocer la profundidad, y para ello, no sirve con una imagen sin más, que es todo lo que proporciona una cámara, para tener visión estereoscópica, se requiere conocer la profundidad, y esto se realiza por medio de más cámaras o sensores adicionales (infrarrojos por ejemplo), como ejemplo, el Mars Rover de la figura 3. Figura 3 Mars Rover 11 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Introducción El tipo de cámaras y elementos (sensores) que se usen, estarán determinados por la actividad a la que se quiera aplicar el vehículo, dependiendo de la precisión, velocidad de proceso, cantidad de datos, rango etc. 1.2 MOTIVACIÓN DEL PROYECTO Como la percepción de la profundidad de la escena es un elemento fundamental para navegar evitando obstáculos y las imágenes RGB son un buen punto de partida en la detección de las estructuras naturales que pueden guiar la navegación del vehículo (control visual), se propone el uso de una cámara de profundidad, y en concreto del sensor Microsoft KinectTM como base del desarrollo del sistema de guiado del vehículo. En definitiva, además de la imagen RGB (640×480 32-bit @ 30 fps), el sensor Kinect suministra una nube de puntos capturados por el sensor de infrarrojos (320×240 16-bit @ 30 fps) dando lugar a una imagen 3D de la zona. [4] [5] El sensor Kinect dispone de una aceptable velocidad de reacción para un rango medio (un orden de magnitud de varios metros), a un precio bastante asequible en comparación con otros dispositivos. 1.3 OBJETIVOS El objetivo se basa en la futura integración de un sensor Kinect en un quad para la navegación autónoma en entornos de árboles y viñedos. 12 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Introducción Por tanto el objetivo es construir, con la ayuda del Kinect, una imagen en tiempo real de la que se puedan extraer características de la escena que nos permitan en un futuro generar las órdenes para que el vehículo navegue en el entorno siguiendo la información proveniente de dicho sensor. En definitiva el proyecto tiene por objetivo sentar las bases de un control visual empleando el sensor Kinect como el elemento de percepción. 1.4 METODOLOGÍA DESARROLLADA En el mapeado tridimensional, es necesario conocer la relación espacial entre los distintos fotogramas que componen la escena, para ello es necesaria la representación visual y formar una visión panorámica además, también se requiere la información de profundidad de dichos fotogramas para llevar a cabo la representación del entorno en tres dimensiones, como se muestra en la figura 4. Para llevar a cabo una navegación autónoma, es necesario un análisis del entorno por parte del sensor, que proporciona los datos, de imagen (colores) y profundidad (milímetros). Una vez concluida y verificada la extracción de datos, se procede a elaborar la visión del entorno, para ello es necesario conocer la localización relativa de las imágenes entre sí, consiguiendo una imagen panorámica del entorno. Una vez creada la panorámica del entorno y las variaciones de posición del sensor, se procede a generar un mapa tridimensional de la escena a partir de los fotogramas con información recibida, además de las imágenes de profundidad. 13 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Introducción Figura 4 Esquema metodología 1.5 RECURSOS / HERRAMIENTAS EMPLEADAS Para este proyecto se ha usado: Hardware: - Ordenador portátil Toshiba Satellite A 500-1EJ Procesador Intel Core (TM) i3 CPU M330 @2.13GHz 2.13GHz 4GB de RAM Sistema operativo de 64 bits - Sensor Kinect - Cable de conexión USB (Kinect-Ordenador) Software: - Microsoft Visual Studio Ultimate 2013 - Librería OpenCV - Librería OpenGL 14 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Capítulo 2 DESARROLLO 2.1 ESQUEMA GENERAL En esta parte se van a explicar las distintas partes en los que se organiza el trabajo y la relación entre ellas, siguiendo la figura 5. El proyecto consiste en el tratamiento de las imágenes proporcionadas por el sensor Kinect, tanto imagen RGB como de profundidad, para integrar la capa de percepción en un vehículo de modo que sea capaz de navegar de forma autónoma, detectando estructuras de interés en su entorno. El programa consta de varias partes diferenciadas, la obtención de datos recibidos por las cámaras del Kinect, profundidad y color (o RGB de Red Green y Blue), el tratamiento de las imágenes de color por medio del comando Stitching de OpenCV, para obtener una escena panorámica del entorno, y su posterior representación tridimensional con OpenGL. El esquema de colores en la figura 5 sirve para identificar la jerarquía de los bloques, en azul se encuentra el sensor Kinect, el cual procesa la información recibida del entorno y la convierte en datos que podemos aprovechar. Estos datos pasan al bloque violeta, donde está el programa base, en el que asimismo existen bloques correspondientes a las tareas que se tienen que realizar, rojo para la obtención de la escena panorámica y verde para su representación en tres dimensiones. 15 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Figura 5 Esquema desarrollo Todo ello es necesario para conseguir una visión estereoscópica, que es la que permite recoger información tridimensional, dotando al usuario de una percepción de la profundidad, esto es, detectando las distancias y los volúmenes del entorno. Para ello se requieren al menos dos lentes, situadas en posiciones distintas, pero que puedan ver la figura objetivo (figura 6), la visión de cada lente será ligeramente distinta, estas pequeñas diferencias serán procesadas por el cerebro para determinar la profundidad de la figura en cuestión. [6][7] 16 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Figura 6 Visión binocular 2.2 EXTRACCIÓN DE DATOS DEL ENTORNO Como ya se ha comentado anteriormente, para la creación del mapeado tridimensional, se requiere como base la imagen de color y profundidad, la información necesaria se obtiene por medio del sensor Kinect de Microsoft. 2.2.1 QUÉ ES Y CÓMO FUNCIONA EL KINECT Este sensor (originalmente llamado Project Natal) [8], fue diseñado por Alex Kipman, y Microsoft lo desarrolló durante veinte años, como un conjunto de sensores detectores de presencia (para responder a los movimientos físicos), su objetivo inicial era permitir a los usuarios 17 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo interactuar y controlar la consola Xbox sin tener contacto físico con ella, mediante un interfaz de usuario, por medio de gestos y comandos de voz. Anunciado por primera vez el 2 de junio de 2009 en la E3 (Electronic Entertainment Expo) y lanzado al público en 2010 para la consola Xbox 360, no sería hasta el 16 de Junio de 2011, cuando el Kinect podría ser utilizado desde un ordenador gracias al lanzamiento del Software Development Kit (SDK) para Windows 7, que permitiría desarrollar aplicaciones para el Kinect en C++, C# o Visual Basic. [9] Este SDK ha sufrido varias actualizaciones, incluida una segunda generación de Kinect, que salió para Xbox One en 2014 (versión 2.0). En este proyecto se utiliza la primera generación de Kinect, con su software más desarrollado, la versiona 1.8, (2013). [10] Las características técnicas que posee son las siguientes: Kinect 1.8 Campo de visión horizontal 57º Campo de visión vertical: 43º Rango de inclinación física: +-27º (el soporte) Rango de profundidad de 0.8 a 4 metros Flujo de datos 320x240 a 16 bits de profundidad a 30fps 640*480 bits de color a 30fps Audio de 16 bit a 16kHz Sensores: Lentes de color y sensación de profundidad Micrófono (con sistema de cancelación de eco) Ajuste del sensor con su motor de inclinación 18 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Las cámaras identificadas con el 1 en la figura 7 son las encargadas de suministrar la imagen de profundidad, con una resolución de 640x480 pixeles y una frecuencia de 30Hz. La cámara identificada con 2 en la figura 7, es la que proporciona la imagen RGB, también con una resolución de 640x480 pixeles, 32 bits de color (y una frecuencia de 30Hz). Además, en la figura 7, se han identificado los micrófonos (3) y una unidad motorizada de la base (4). Figura 7 Sensor Kinect El Kinect es un tipo de escáner de luz estructurada, que se basa en la proyección de un patrón de luz (Infrarroja) hacia un objeto y desde otro punto de vista, se capturan esos mismos patrones deformados por las formas del objeto (figura 8); con esa deformación, se obtiene la distancia desde cada punto (el emisor y el receptor).Estos focos emisor y receptor son las cámaras marcadas con un 1 en la figura 7. 19 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Figura 8 Escáner de luz 3D 2.2.2 SOFTWARE DEL KINECT Para poder trabajar con el sensor, se necesitan los programas adecuados, en este caso el Software Development Kit de Microsoft (SDK). La figura 9 muestra el Kinect SDK 1.8 (es la versión que corresponde al modelo de sensor utilizado en el presente trabajo), que es el software necesario para empezar a usar el sensor, ya que permite el uso y descarga de los programas base para trabajar con el sensor Kinect. [11] 20 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Figura 9 Kinect SDK Los programas empleados en este trabajo fueron: Kinect Studio, Kinect Depth Basics-D2D C++ y Kinect Explorer-D2D C++. Kinect Studio: Este programa, actúa paralelamente al programa principal, y proporciona información al usuario (únicamente) sobre el flujo de datos del sensor. Este es el panel central (figura 10), permite hacer grabaciones, y analizar cada frame de datos que transmite un programa ya activo (es decir, no puede funcionar por su cuenta). Figura 10 Kinect Studio-Panel Central 21 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Además del panel central, tiene ventanas secundarias que informan del color (figura 11) y la profundidad (figura 12), donde las zonas más claras indican una mayor profundidad Figura 11 Kinect Studio-Color Figura 12 Kinect Studio-Profundidad Como ayuda al usuario, en estas ventanas es posible obtener para un píxel la información de color (RGB) o de profundidad tan solo situando el cursor encima de dicho pixel. 22 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Otra ventana secundaria de interés (figura 13) suministra un visor en tres dimensiones, que puede ser rotado, permitiendo ver la escena desde distintos ángulos (rotación). Figura 13 Kinect Studio-Color y Profundidad Otro programa disponible es el que permite trabajar únicamente con los valores de profundidad, Kinect Depth Basics-D2D C++. Aunque en un inicio se considero utilizarlo por su simplicidad, fue desechado al no permitir la manipulación de datos de color. Este programa (véase figura 14) devuelve los valores de profundidad en números de 8 bits, sin embargo, no lo representa de manera uniforme, debido a que la escala de grises empleada en la imagen no corresponde al valor de profundidad. 23 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Figura 14 Depth D2D-origen Finalmente Kinect Explorer-D2D C++: este programa cuyo interfaz se muestra en la figura 15 es el que se ha utilizado como base en este proyecto, ya que proporciona información de profundidad y color, además de indicar la frecuencia de trabajo (frame rate). Asimismo, proporciona otras funcionalidades que podrán ser útiles en futuros desarrollos, como la detección de audio y el ángulo de origen de la fuente del ruido, la detección de personas y un acelerómetro. 24 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Figura 15 Kinect Explorer D2D Origen Es importante destacar que las imágenes proporcionadas por el sensor, son imágenes reflejadas, no la imagen de la realidad (véase la figura 16, A’B’C’ son las imágenes reflejadas frente al triangulo real ABC), no a la imagen real, por lo que habrá que tenerlo en cuenta, modificando los datos para poder obtener la imagen real cuando se quiera trabajar con ella. Figura 16 imagen reflejo Asimismo es necesario un compilador para poder trabajar con los programas anteriores. Se ha elegido como entorno integrado de desarrollo Microsoft Visual Studio Ultimate 2013, por su alta compatibilidad y versatilidad de librerías. 25 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo La figura 17 ilustra alguna de las ventanas que proporciona este entorno de desarrollo. Figura 17 Entorno Visual Studio En el Kinect SDK (o Kinect Toolkit Browser 1.8) se pueden encontrar los programas para distintos lenguajes de programación, fundamentalmente C++ y C#. En este trabajo se han escogido los programas escritos en C++ que aunque presenten una interfaz más simple (menos vistosa) a cambio tienen mayor compatibilidad con otras librerías de tratamiento de imagen, por ejemplo OpenCV. 2.2.3 EXTRACCIÓN DE DATOS El software que se ha tomado de partida está compuesto por un gran número de archivos .cpp y .h, de los cuales inicialmente los más interesantes son los que trabajan con la imagen RGB y de profundidad. 26 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Así, KinectWindow.cpp actúa casi como el principal, NuiImageBuffer.cpp contiene el trozo de código donde se procesan los datos de color y profundidad (este es el trozo de código que sufrirá mas modificaciones). Por último, NuiColorStream.cpp y NuiDepthStream.cpp son los ficheros que contienen el código para adquirir los datos del sensor Kinect. Estos módulos también habrá que cambiarlos para adaptarlos al nuevo uso. El programa Kinect Explorer-D2D C++ es un bucle constante, en el los datos se van actualizando por medio de las siguientes funciones: /// <summary> /// Process color, depth and skeleton streams /// </summary> void KinectWindow::UpdateStreams() { //OutputDebugStringA("\n alpha \n"); m_pColorStream->ProcessStreamFrame(); m_pDepthStream->ProcessStreamFrame(); m_pSkeletonStream->ProcessStreamFrame(); } De cada uno de los ProcessStreamFrame, partimos a las diversas funciones, donde el propio programa extrae los valores de color y profundidad, para representarlos en pantalla (figura 13) por ejemplo los valores de profundidad (en milímetros) vienen de la función NuiImageBuffer::CopyDepth del archivo NuiImageBuffer.cpp, cuyo código es el siguiente: void NuiImageBuffer::CopyDepth(const BYTE* pImage, UINT size, BOOL nearMode, DEPTH_TREATMENT treatment) { // Allocate buffer for color image. If required buffer size hasn't changed, the previously allocated buffer is returned UINT* rgbrun = (UINT*)ResetBuffer(m_width * m_height * BYTES_PER_PIXEL_RGB); // Initialize pixel pointers to start and end of image buffer NUI_DEPTH_IMAGE_PIXEL* pPixelRun = (NUI_DEPTH_IMAGE_PIXEL*)pImage; NUI_DEPTH_IMAGE_PIXEL* pPixelEnd = pPixelRun + m_srcWidth;// *m_srcHeight; Partiendo de esta función, y analizando el programa, se logran extraer los valores de profundidad asignados al comando depth (la función devuelve 27 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo NUI_DEPTH_IMAGE_PIXEL* pPixelRun de la que por medio de dicho comando se puede extraer los valores). USHORT depth = pPixelRun->depth; /// este es el valor USHORT index = pPixelRun->playerIndex; Una vez obtenidos dichos valores, se procede a almacenarlos en una matriz de 640x480, asignando un valor de profundidad a cada pixel. El almacenamiento de las matrices de profundidad se realiza de la siguiente manera: int p = 0; int int int for l; inv_d[640]; inv_I[640]; (int k = 0; k <= 479; k++){ l = 0; for (l = 0; l <= 639; l++){ USHORT depth = pPixelRun->depth; USHORT index = pPixelRun->playerIndex; inv_d[l] = depth; inv_I[l]=index; pPixelRun++; } for (int h = 0; h <= 639; h++){ LUT_depth[frame_complete] = inv_d[639 - h]; LUT_index[frame_complete] = inv_I[639 - h]; ++rgbrun; *rgbrun = m_depthColorTable[LUT_index[frame_complete]][LUT_depth[frame_complete]]; if (n_image == 1){ LUT_depth_matrixIMPAR[h][k] = LUT_depth[p] * 255 / 1250; }else{ LUT_depth_matrixPAR[h][k] = LUT_depth[p] * 255 / 1250; } p++; frame_complete++; } } 28 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Para obtener las componentes RGB se utiliza la función NuiImageBuffer::CopyRGB, que sigue una estructura similar al caso de la información de profundidad: void NuiImageBuffer::CopyRGB(const BYTE* pImage, UINT size){ // Allocate buffer for image UINT* pBuffer = (UINT*)ResetBuffer(m_width * m_height * BYTES_PER_PIXEL_RGB); A partir del código anterior los valores se guardan en una variable llamada m_pBuffer[] que se usara para almacenar en las matrices RGB a partir del código siguiente: for (LONG y = 0; y < m_height; ++y){ for (LONG x = 0; x < m_width; ++x){ if (n_image == 1){ LUT_B_matrixIMPAR[x][y] = m_pBuffer[p]; LUT_B_matrix[x][y] = m_pBuffer[p]; p++; LUT_G_matrixIMPAR[x][y] = m_pBuffer[p]; LUT_G_matrix[x][y] = m_pBuffer[p]; p++; LUT_R_matrixIMPAR[x][y] = m_pBuffer[p]; LUT_R_matrix[x][y] = m_pBuffer[p]; p++; p++; }else{ LUT_B_matrixPAR[x][y] = m_pBuffer[p]; LUT_B_matrix[x][y] = m_pBuffer[p]; p++; LUT_G_matrixPAR[x][y] = m_pBuffer[p]; LUT_G_matrix[x][y] = m_pBuffer[p]; p++; LUT_R_matrixPAR[x][y] = m_pBuffer[p]; LUT_R_matrix[x][y] = m_pBuffer[p]; p++; p++; } } } En definitiva se utilizan un total de 4 matrices, 3 para almacenar los tres planos de color y una cuarta para almacenar los valores de profundidad, (LUT_R_matrix, LUT_G_matrix y LUT_B_matrix, son las matrices de color). 29 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Los motivos de duplicación (PAR-IMPAR) de las matrices de color y también las de profundidad; así como el uso de la variable n_image, se explicarán más adelante, con la creación de la matriz global de datos (apartado 2.3.6, Mat_Omega, la matriz de almacenamiento). 2.3 TRATAMIENTO DE IMAGEN Y OBTENCIÓN DE PANORÁMICA Una vez obtenidos y almacenados los valores de profundidad y color, es necesario recrear la escena a partir de los fotogramas, para ello necesitaremos herramientas más especializadas al campo de la visión por ordenador, la que se ha usado en el presente desarrollo ha sido la librería OpenCV. 2.3.1 OPENCV Y EL FORMATO MAT En lo que sigue veremos los fundamentos de OpenCV (Open Source Computer Vision), que es una librería de programación libre, orientada al desarrollo de aplicaciones de visión por computador, Esta librería que está disponible desde hace más de 15 años, está desarrollada por Intel y se emplea en el desarrollo de software para multitud de campos, desde sistemas de seguridad hasta sistemas de reconocimiento de objetos [12] Por otra parte, el formato más común de representación de una imagen en color es el espacio de color RGB, donde cada píxel tiene asociados tres valores de intensidad correspondientes a tres planos de color R (Red), G (Green) y B (Blue), tal como se muestra en la figura18. [13] 30 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Es decir, que cada imagen tiene tres valores en cada pixel, correspondientes a la intensidad luminosa de cada color base. Figura 18 Paleta RGB Una vez tenemos los valores de las imágenes RGB, tenemos que pasarlos al formato adecuado para trabajar con ellos, en este caso el formato Mat, que no es más que una versión alterada de una matriz normal, en la que cada componente de la matriz almacena tres valores, estos valores pueden ir de 0 a 255 como en la figura 18 o como números decimales entre el 0 y el 1, tal como se muestra en la figura 19. [14] Figura 19 RGB formato Mat Para obtener las imágenes, se desarrolla el siguiente código para gestionar los valores dados por las matrices de color: void Mat_Image(Mat pImage,int unoo2) { for (int rw = 0; rw <479; rw++){ for (int cl = 0; cl < 639; cl++){ 31 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Mat paso_punto(1, 1, CV_8UC3, Scalar(LUT_B_matrix[639-cl][rw], LUT_G_matrix[639-cl][rw], LUT_R_matrix[639-cl][rw])); paso_punto.copyTo(pImage.row(rw).col(cl)); } } } Dada la imposibilidad de copiar todos los valores directamente, tengo que copiar los valores agrupados de tres en tres (Blue, Green y Red), almacenándose en la matriz imagen, por medio de la función .copyTo(). Es importante resaltar que la imagen obtenida es aquí invertida para que corresponda con la imagen real. 2.3.2 RGB Y RGBA El flujo de datos de imagen proporcionado por el Kinect, no es RGB puro, si no que incluye información sobre la opacidad (capa alpha o de transparencia) Ahora bien, en nuestro caso, esta información no es relevante para la construcción de la escena, y además ralentiza significativamente los cálculos, por lo que se omite del proceso. [15] En caso de no eliminar la componente de opacidad, el fotograma creado, quedara algo similar a la figura 20: 32 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Figura 20 Color RGBA error Una vez corregido ese error, es decir, omitiendo correctamente la capa alpha, la imagen quedaría como la figura 21. 33 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Figura 21 RGB omitiendo capa alpha 2.3.3 STITCHING Una vez obtenidos los fotogramas en el formato adecuado, el siguiente objetivo es localizar la zona de solapamiento de dos imágenes adquiridas de forma consecutiva, de modo que se pueda recrear la escena entera a partir de la unión de los dos fotogramas, esto puede realizarse de varias formas, empleando herramientas como Homography o Stitching. El método Homography [16] fue el inicialmente usado, debido a su gran velocidad, sin embargo, a pesar de ser más rápido que el Stitching, el Homography daba lugar a un mayor número de errores, como la imposibilidad de unir la segunda imagen en el lado que no fuese el predeterminado, es decir, en el propio código tenía que ir indicado a qué lado iba cada imagen, por esto y debido también a algunos problemas 34 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo respecto a la creación de panorámicas que aparecían cuando el nuevo fotograma estaba ya incluido en la imagen de partida, fue necesario reconsiderar este método y finalmente aplicar Stitching. La figura 22 muestra un ejemplo de imágenes de entrada tomadas mediante el sensor Kinect y convertidas al formato Mat (también fueron invertidas para coincidir con la realidad). Estas imágenes fueron tomadas una a continuación de la otra girando ligeramente el sensor. En la figura 23 se muestra la escena resultante de la función Stitching, formada por las imágenes de la figura 22, las nuevas dimensiones van a ser totalmente distintas, y dependerán de posición de cada imagen. Figura 22 Imágenes 30º Stitching 35 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Figura 23 Stitching image, escena Es interesante saber que se puede juntar más de dos imágenes en la imagen final, pero para simplificar las cosas y acortar el tiempo entre fotogramas, se hacen de dos en dos (esto ralentizaría mucho el proceso de creación de la escena). La función desarrollada para conseguir la unión de dos imágenes es la siguiente: void StichMix(Mat image1, Mat image2){ vector< Mat > vImg; vImg.push_back(image1); vImg.push_back(image2); Stitcher stitcher = Stitcher::createDefault(); Stitcher::Status status = stitcher.stitch(vImg, Stitch); } 36 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo 2.3.4 LOCALIZACIÓN EN LA ESCENA Ahora que se ha creado la escena a partir de las imágenes provenientes del sensor, es necesario saber localizarlas entre sí, es decir, hallar la localización espacial de la imagen nueva respecto a la previa. Para esto se hace un recorte de ambas imágenes, estos recortes se han de localizar en la escena creada con la herramienta Stitching. La localización de dichos recortes se basa en la detección de los puntos de interés (“keypoints”) por medio del algoritmo Sift en ambas imágenes (la escena y el recorte). Estos puntos de interés son lugares de la imagen que están bien definidos, tanto matemáticamente como gráficamente en la imagen, y permanecen estables frente a perturbaciones (filtrado de imagen, cambios de color, su estructura sigue destacando frente a los demás), aquí se comparan estos puntos de interés en dos imágenes, y se detecta la correlación ambas. [16][17] Para detectar la relación entre los puntos de interés y más adelante localizar y acotar la región de la escena ocupada por el recorte, se desarrolla la función llamada fcn_Square. El inicio de la función, y donde se detectan todos los puntos de interés de ambas imágenes es el siguiente: void fcn_Square(Mat queryImg_N, Mat trainImg_N, int BaseorNew){ SiftFeatureDetector detector(400); vector<KeyPoint> queryKeypoints_N, trainKeypoints_N; detector.detect(queryImg_N, queryKeypoints_N); detector.detect(trainImg_N, trainKeypoints_N); Mat queryDescriptors_N, trainDescriptors_N; extractor.compute(queryImg_N, queryKeypoints_N, queryDescriptors_N); extractor.compute(trainImg_N, trainKeypoints_N, trainDescriptors_N); Size size = queryDescriptors_N.size(); size = trainDescriptors_N.size(); 37 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Una vez tengo los puntos de interés detectados, para evitar tiempo de trabajo y posibles errores debidos a pequeñas deformaciones, se hace un filtrado de puntos de interés, para obtener solo los más relevantes, y que puedan determinar con certeza la localización del recorte en la escena, por medio del siguiente código: BFMatcher matcher(NORM_L2); vector<DMatch> matches_N; matcher.match(queryDescriptors_N, trainDescriptors_N, matches_N); Mat img_matches_N; drawMatches(queryImg_N, queryKeypoints_N, trainImg_N, trainKeypoints_N, matches_N, img_matches_N); vector< DMatch > good_matches; double min_dist = 100; for (int i = 0; i < queryDescriptors_N.rows; i++) { if (matches_N[i].distance < 3 * min_dist) { good_matches.push_back(matches_N[i]); } } Una vez detectados los puntos de interés más importantes, se pasa a detectar los bordes de la imagen recortada en la escena, para ello se utiliza el código siguiente: vector<Point2f> scene_corners(4); perspectiveTransform(obj_corners, scene_corners, H); vector<Point2f> obj_N; vector<Point2f> scene_N; for (int i = 0; i < good_matches.size(); i++){ obj_N.push_back(queryKeypoints_N[good_matches[i].queryIdx].pt); scene_N.push_back(trainKeypoints_N[good_matches[i].trainIdx].pt); } Mat H = findHomography(obj_N, scene_N, CV_RANSAC); vector<Point2f> obj_corners(4); obj_corners[0] = cvPoint(0, 0); obj_corners[1] = cvPoint(queryImg_N.cols, 0); obj_corners[2] = cvPoint(queryImg_N.cols, queryImg_N.rows); obj_corners[3] = cvPoint(0, queryImg_N.rows); perspectiveTransform(obj_corners, scene_corners, H); 38 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Tras obtener las esquinas de la posición del recorte a buscar dentro de la escena, se procede a almacenar las coordenadas de la siguiente forma: int int int int int int int int X_coordinate0 Y_coordinate0 X_coordinate1 Y_coordinate1 X_coordinate2 Y_coordinate2 X_coordinate3 Y_coordinate3 = = = = = = = = scene_corners[0].x; scene_corners[0].y; scene_corners[1].x; scene_corners[1].y; scene_corners[2].x; scene_corners[2].y; scene_corners[3].x; scene_corners[3].y; Finalmente, se guardan estas coordenadas en un vector externo a la función para utilizarlas más adelante. Es importante notar que la imagen que se va a buscar en la escena formada mediante Stitching, es un recorte de la imagen original con la que se formo dicha escena; el tamaño del recorte debe ser ligeramente inferior al de la foto base (no puede ser superior evidentemente) para poder localizarla correctamente en la imagen conjunta (escena) pero tampoco puede ser muy pequeña ya que esto podría conducir a la aparición de varias coincidencias y en consecuencia, a la perdida (o debilitamiento) de puntos de interés, resultando en una localización errónea o inexistente. 2.3.5 POSICIONAMIENTO EN LA ESCENA Tras obtener la localización de las imágenes que conforman la escena (los dos recortes) en la misma, es necesario conocer la relación entre esas dos imágenes, de forma que pueda localizar espacialmente la nueva imagen respecto a la anterior. De esta forma, es necesario localizar el punto central de ambos recortes, y así obtener la diferencia de posiciones ente las dos imágenes. A partir de las aristas que delimitan el área perteneciente a los recortes en la escena (obtenidas anteriormente, con la función fcn_Square), se puede determinar el punto central tal y como muestra la figura 24: 39 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Figura 24 Las aristas y el origen Una vez despejadas las variables , , y de las ecuaciones de la recta, se procede a calcular el origen de la siguiente manera: void intersect_central(int vecX[], int vecY[], int select){ long float alfaB_up = (vecY[0] - vecY[2]); long float alfaB_dw = (vecX[0] - vecX[2]); long float alfaB = alfaB_up / alfaB_dw; long float betaB = vecY[0] - alfaB*vecX[0]; ///////// ecuacion de los otros puntos long float gamaB_up = (vecY[1] - vecY[3]); long float gamaB_dw = (vecX[1] - vecX[3]); long float gamaB = gamaB_up / gamaB_dw; long float sigmB = vecY[1] - gamaB*vecX[1]; /// juto los dos long float x_up = -(betaB - sigmB); long float x_dw = alfaB - gamaB; long float x_central = x_up / x_dw; long float y_central = alfaB*x_central + betaB; if (select == 0){ X_Base = x_central; Y_Base = y_central; }else{ X_New = x_central; Y_New = y_central; } } La diferencia de puntos centrales, es decir el vector que une el punto central de la antigua imagen con la siguiente, se calcula mediante la diferencia de puntos centrales, es decir la diferencia ente el punto central 40 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo de la nueva imagen y el de la imagen previa. Aunque para ello debemos conocer cuál es la imagen nueva y cuál es la previa. 2.3.6 MAT_OMEGA, LA MATRIZ DE ALMACENAMIENTO Tras obtener la localización de una segunda imagen, surge el problema de almacenamiento de los valores, se requiere una variable para almacenar todos los puntos que deben ser representados, es decir, una matriz global donde almacenar la nube de puntos. Esta Matriz global, llamada Mat_Omega, guarda los valores en un punto por fila, con los datos agrupados de tal forma que en la primera columna se tiene el numero de punto, en la segunda la coordenada X, seguida por la coordenada Y y por la coordenada Z; finalmente, se almacenan también los valores RGB. El origen de las coordenadas XYZ será el origen de la primera imagen, es decir, la localización del sensor Kinect en el primer fotograma registrado. La diferencia en el valor de la coordenada Z frente al valor de profundidad se considera nula, tanto por la poca diferencia como por el incremento de tiempo que produciría. El almacenamiento en una matriz global (llamada Mat_Omega), del fotograma inicial se hace desarrollando el siguiente código: for ( int k = 0; k < 480; k++){ for (int l = 0; l <= 640; l++, numero++){ Mat_Omega[numero][0] = numero; Mat_Omega[numero][1] Mat_Omega[numero][2] Mat_Omega[numero][3] Mat_Omega[numero][4] Mat_Omega[numero][5] Mat_Omega[numero][6] } vueltas++; = = = = = = -l + 640 / 2; -k + 480 / 2; LUT_depth_matrixPAR[l][k]; LUT_1RGB[640 - l][k][0]; LUT_1RGB[640 - l][k][1]; LUT_1RGB[640 - l][k][2]; 41 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo } Hasta ahora, no había importado el orden de las imágenes que conformaban la escena, ya que lo único que se ha hecho ha sido adaptarlas al formato Mat, conformar una escena y localizar las imágenes en dicha escena, pero ahora es necesario saber cuál es la imagen nueva y cual la antigua, para ello, nos ayudaremos de unas variables llamadas n_image y mezcla, ambas con valor inicial 0. Además llamaremos al fotograma base LUT_1 y al siguiente fotograma LUT_2 (atención, LUT_2 no será necesariamente el fotograma nuevo, simplemente es el siguiente al fotograma base y el previo al tercer fotograma que se almacenará en LUT_1, como se muestra en la figura 25). En el momento en el que empieza el proceso de reconstrucción n_image aumenta su valor en 1; se escanea una primera imagen, el fotograma base (en LUT_1) y se continua con el proceso. En el siguiente fotograma, n_image se vuelve a incrementar en uno, esta vez con un valor de 2, se escanea la imagen y se almacena en LUT_2, acto seguido se procede a cambiar el valor de mezcla a la unidad, esto permite la unión de las dos imágenes, siendo la imagen antigua LUT_1 y la nueva LUT_2, cuando n_image es 0 (su valor fue reiniciado justo después de escanear LUT_2). En el tercer frame, se aumenta n_image en una unidad, y se procede a escanear la imagen LUT_1 (sobrescribiendo la LUT_1 anterior), se permite la reconstrucción con la variable mezcla y formando la imagen conjunta; como n_image aquí vale 0, se procede analizar la escena sabiendo que LUT_1 es la nueva y LUT_2 es la antigua, de forma opuesta al anterior. La figura 25 muestra un esquema detallado del proceso: 42 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Fig25 Esquema proceso 2.3.7 GIRO, ROTACIÓN Y TRASLACIÓN EN LOS NUEVOS FOTOGRAMAS Para recrear la escena, es necesario determinar donde se encuentran los nuevos fotogramas respecto a los antiguos, por tanto, además de conocer la diferencia de centros entre fotogramas (calculada en el apartado 2.3.5), es necesario conocer el ángulo de giro y la variación de profundidad de los fotogramas respecto al fotograma inicial. Esto se logra calculando los puntos que delimitan el área de intersección de las dos imágenes que conforman la escena, más concretamente los puntos 2 y 0´de la imagen 26, que muestra un ejemplo de intersección. 43 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Figura 26 Intersección de fotogramas Para determinar los puntos 2 y 0´de la figura 26, tengo que determinar cuáles son los puntos del recuadro gris que se encuentran dentro del rojo, para ello tengo que determinar con cuantos lados del polígono interseccionan avanzando en la misma dirección (y paralelamente a un eje), esto se puede apreciar en la figura 27, donde se muestran varios puntos R.V y N y se aprecia el numero de intersecciones; la prolongación de los puntos que están dentro del polígono, interseccionan una sola vez con los lados, mientras que los que no pertenecen al área interna del polígono, interseccionan dos veces o ninguna. 44 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Figura 27, Dentro o fuera de un polígono Mediante la siguiente función, se determina si la prolongación del punto dado intersecciona con la recta hacia el lado derecho (como mostraba la figura 27). int inter_recta(int r1, int r2, int w_X, int w_Y){ int rtr = 0; if (w_Y >= vecY_Base[r2]){ ///la de abajo if (w_Y <= vecY_Base[r1]){ ///la de arriba ///recta float C_X = 0; float alpha1 = 0; if (vecX_Base[r1] != vecX_Base[r2]){ alpha1 = (vecY_Base[r1] - vecY_Base[r2]) / (vecX_Base[r1] - vecX_Base[r2]); } char szb[255]; sprintf_s(szb, "\n alpha: %f ", alpha1); float Beta1 = vecY_Base[r1] - alpha1*vecX_Base[r1]; sprintf_s(szb, "\n beta: %f ", Beta1); if (alpha1!=0){ C_X = (w_Y - Beta1) / alpha1; } else{ C_X = vecX_Base[r1]; 45 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo } sprintf_s(szb, "\n CX: %f ", C_X); if (w_X <= C_X){ rtr = 1; ///intersecciona } } } if (w_Y <= vecY_Base[r2]){ ///la de abajo if (w_Y >= vecY_Base[r1]){ ///la de arriba ///recta float C_X = 0; float alpha1 = 0; if (vecX_Base[r1] != vecX_Base[r2]){ alpha1 = (vecY_Base[r1] - vecY_Base[r2]) / (vecX_Base[r1] - vecX_Base[r2]); } char szb[255]; sprintf_s(szb, "\n X2: %f ", vecX_Base[r1]); sprintf_s(szb, "\n X3: %f ", vecX_Base[r2]); sprintf_s(szb, "\n alphaDIF: %f ", alpha1); float Beta1 = vecY_Base[r1] - alpha1*vecX_Base[r1]; sprintf_s(szb, "\n betaDIF: %f ", Beta1); if (alpha1 != 0){ C_X = (w_Y - Beta1) / alpha1; } else{ C_X = vecX_Base[r1]; } if (w_X <= C_X){ rtr = 1; ///intersecciona } } } return rtr; } El funcionamiento es similar al proceso realizado en el apartado 2.3.5; ahora que se si la prolongación de un punto intersecciona con una recta, por lo que ahora tengo que verificar la prolongación de ese mismo punto. Una vez que se conocen las intersecciones de dicho punto con las rectas 46 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo que forman el polígono, se determina si el punto está o no dentro de dicho polígono de la siguiente forma: int intersec_Punto(float w_X1, float w_Y1){ int rr = 0; /// wx, primera recta con 0-3 int primera = inter_recta(0,3,w_X1,w_Y1); OutputDebugStringA("priemro escaneado\n"); /// segunda 1-2 int segunda = inter_recta(1, 2, w_X1, w_Y1); OutputDebugStringA("segundo escaneado\n"); ///tercera 01 int tercera = inter_recta(0, 1, w_X1, w_Y1); OutputDebugStringA("tercero escaneado\n"); ///cuarta 32 int cuarta = inter_recta(3, 2, w_X1, w_Y1); char sza[255]; sprintf_s(sza, "\n p1: %d ,p2: %d,p3: %d,p4: %d \n ", primera,segunda, tercera,cuarta); OutputDebugStringA(sza); if (primera+segunda+tercera+cuarta==1){ rr = 1; ///pertenece } return rr; } Tras repetir el para el otro polígono y para todos los puntos, finalmente obtengo al menos dos puntos que se encuentran en el área de intersección. Comparando las diferencias en los valores de profundidad de los puntos (estos puntos pertenecen a los dos fotogramas al mismo tiempo) determino el ángulo de giro respecto al eje X y al eje Y, determinándolo a partir de las diferencias de valores de profundidad y las coordenadas en X, como se explica en la figura 28, cabe resaltar que ciertas operaciones trigonométricas, necesarias para realizar los cálculos, ralentizan o incluso pueden detener el proceso. 47 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Figura 28, determinación de ángulos A partir de estos ángulos, determino la diferencia de profundidad entre los puntos centrales, de tal forma que la diferencia que exista entre los puntos más alejados (la arista de la nueva imagen, Bn-B0 de la figura 28) sea nula en esos puntos, es la línea amarilla que se aprecia en la figura 28. Una vez tengo el ángulo de giro, el de rotación, la translación y la variación de profundidad, voy almacenando estos valores en la matriz para proceder a su construcción en 3D: for (int k = 0; k < 480; k++){ for (int l = 0; l <= 640; l++, numero++){ Mat_Omega[numero][0] = numero; Mat_Omega[numero][1] = -l + 640 / 2 + X_New - X_Base + centros[cuenta2][0] - (-l + 640 / 2)*(1 - cos(giro));///x Mat_Omega[numero][2] = -k + 480 / 2 + Y_New - Y_Base + centros[cuenta2][1] - (-k + 480 / 2)*(1-cos(giro2));///y Mat_Omega[numero][3] = centros[cuenta2][2] + Z_prof - (-l + 640 / 2)*sin(giro) - (-k + 480 / 2)*sin(giro2);+ LUT_depth_matrixIMPAR[l][k];///z Mat_Omega[numero][4] = LUT_R_matrixPAR[640 - l][k];///R 48 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Mat_Omega[numero][5] = LUT_G_matrixPAR[640 - l][k];///G Mat_Omega[numero][6] = LUT_B_matrixPAR[640 - l][k];///B } vueltas++; } Es importante resaltar que las diferencias, ya sean de traslación, de profundidad o los ángulos se van acumulando, es decir, si en el primer fotograma adicional, el giro es de 25º, y en el siguiente es de -15º, el ángulo de este nuevo fotograma será de 10º (25-15) respecto el fotograma inicial, que es el valor aplicado en Mat_Omega. El proceso se repite sobre todas las imágenes que se van adquiriendo, almacenando las variaciones con respecto a la imagen anterior, y buscando así la reconstrucción continua de la escena. 49 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo 2.4 TRATAMIENTO DEL MAPA DE BITS EN 3D Ahora que ya se tiene la escena totalmente determinada y la nube de puntos almacenada hay representar dicha nube de puntos, para ello, hay multitud de librerías relacionadas, en este proyecto se escoge la herramienta OpenGL por su versatilidad y su buena compatibilidad con la representación de nubes de puntos bit a bit. 2.4.1 LAS BASES DE LA REPRESENTACIÓN 3D, OPENGL OpenGL (Open Graphics Library) es una Aplicación de Interfaz de Programación (API) multilenguaje para aplicaciones de vectores gráficos de dos o tres dimensiones, desarrollada por Silicon Graphics en los 90, es una de las plataformas graficas más utilizadas. Se emplea en desarrollo de videojuegos, donde compite con DirectX de Microsoft. [18][19] Con OpenGL queremos disponer de herramientas que nos permitan trabajar con la componente de profundidad de la imagen. Así, para cada fotograma, tenemos los valores de profundidad para cada bit en concreto una imagen de 640x480 pixeles. Para representarlos en un espacio tridimensional, necesitamos convertir los valores de profundidad, (originalmente milímetros) para cada pixel a vectores XYZ. Con las funciones de OpenGL es posible construir un programa que interpreta una serie de coordenadas y las representa en el espacio añadiendo color al punto (es decir, los valores de intensidad asociados a cada uno de los planos en el espacio de color RGB). De cada fotograma de imagen tenemos los valores de profundidad, bit a bit (640x480) Para representarlos necesitamos convertir los valores de 50 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo profundidad (originalmente milímetros) a vectores XYZ para poder ser representados. La función desarrollada en este proyecto es la siguiente: void OpenGL_3D(int argc){ glutInitDisplayMode(GLUT_DOUBLE); glutInitWindowSize(1280, 960); glutInitWindowPosition(50, 50); gluPerspective(45, width / (GLdouble)height, 0.1, 1000); glutCreateWindow(title); glutDisplayFunc(display); glutReshapeFunc(reshape); initGL(); glutTimerFunc(0, timer, 0); glutMainLoop(); } Donde GLUT_DOUBLE indica que se usa la técnica del doble buffer, necesaria para no tener un grafico estático, y que permitirá en un futuro trabajar con mayor facilidad con el mapeado tridimensional, ya sea para girar la figura o para reescalarla; emplea un buffer para pintar y el otro para visualizar la pantalla. Los comandos glutDisplayFunc y glutReshapeFunc, son dos callbacks, que pasan una función cada uno (display y reshape respectivamente) y que la función llamará cuando considere oportuno. El primero se encarga de pintar la imagen y el segundo de su escala. InitGL es el comando que activa la representación, sin él no se mostraría la ventana del display 3D. Por otra parte, glutMainLoop es un bucle infinito, (el que mantiene la ventana del display abierta y actualizada), este bucle genera un grave problema, ya que interrumpe el programa principal, sin embargo, existen formas de evitar esto, como rompiendo el bucle editando la librería o convirtiendo el bucle en el bucle general del programa. Se ha escogido la 51 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo primera opción, ya que es más simple e interfiere menos con el programa principal. [20] 2.4.2 CREANDO LA NUBE DE PUNTOS Ahora que se conoce la estructura base de la herramienta OpenGL, procedo a representar la nube de puntos, dicha nube de puntos esta almacenada en la matriz global llamada Mat_Omega (apartado 2.3.6). Como ya se comentó anteriormente, el comando display sirve para pintar la imagen a partir de los datos que le proporcionemos, en este caso de la matriz Mat_Omega, que almacena los valores de las escenas, el código desarrollado es el siguiente: void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0, 0, -1 * Mat_Omega[3][153600]); glRotatef(angleCube, 0, 1, 0); glPointSize(1); glBegin(GL_POINTS); for (int i = 0; i < numero;i++){ glColor3f(Mat_Omega[i][4]/255.f, Mat_Omega[i][5]/255.f, Mat_Omega[i][6]/255.f); glVertex3f(Mat_Omega[i][1]/3, Mat_Omega[i][2]/3, Mat_Omega[i][3]/3); } glEnd(); glutSwapBuffers(); angleCube -= 0.2; } Dada la representación tridimensional obtenida (figura 29), es necesario el giro de la imagen para una mejor apreciación de la misma, por lo que es necesario rotar la escena. 52 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Figura 29, representación tridimensional estática Además, tal y como se ve en la figura 30, la imagen tridimensional, aparece vista desde atrás, es decir, al otro lado de la figura 3D, se encuentra el sensor. Figura 30 Imagen de la representación estática Para añadir la rotación, se emplean los comandos glTranslate y glRotate, éstos realizan la translación y la rotación de la matriz. En translación, se hace retroceder el punto central de la primera imagen un cierto número de 53 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo unidades para que el centro absoluto de rotación no esté en la imagen, si no el punto correspondiente a la posición del sensor, tal y como muestra la figura 31, en la que la estrella amarilla representa el punto de rotación y el sensor. Figura 31, localización del sensor en la escena 3D En la rotación se requiere de otro callback, glutTimerFunc, cuya tarea es llamar a otra función (un timer en este caso) y se encarga de la frecuencia de llamada de los otros callbacks, en otras palabras, regula la actualización del la representación tridimensional. Para indicar el ángulo de rotación, se crea la variable angleCube, que varia al final de la función (display), e indica cuanto se va a rotar la imagen cada vez que el callback del timer vuelve a llamar al display, creando una rotación de la imagen tridimensional, basada en el incremento de ese ángulo cada vez que el glutTimerFunc lo actualice, tal y como se ve en la figura 32, en la que la imagen rota sobre el eje rojo, en la imagen de la derecha, ha pasado cierto número de ciclos y el rectángulo blanco y el punto que le acompaña, ha rotado. 54 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Figura 32, Rotación de la figura 3D Ahora que se ha resuelto el problema de la visualización, procedo a representarlo nuevamente, esta vez, se puede apreciar mejor la escena, gracias al movimiento de rotación de esta, que permite ver el mapeado desde todos los ángulos. Una vez representado, queda como en la figura 33, es importante resaltar las nubes de puntos resaltadas, estas, son las medidas que el sensor Kinect toma como erróneas, es decir, les da un valor de profundidad nulo. 55 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Figura 33, Representación tridimensional, errores marcados Una vez se han eliminando los valores residuales (medidas con valor de profundidad nulo) detectados antes, se procede a juntar varios fotogramas y representarlos, como muestra la figura 34; en esta representación, se puede apreciar un círculo rojo, éste señala áreas que quedan inaccesibles a la vista (el display tiene un rango limite en la distancia de los puntos), que se mostraran en el display conforme se vallan acercando. 56 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Figura 34, recreación de la escena [21] El problema del rango de representación se irá agravando al tener mapeados más grandes, por lo que se necesita alejar el punto de vista para tener una mejor visión de la escena, es decir, es necesario reescalar la escena. Esto se resuelve añadiendo comandos a la representación tridimensional, por medio de otro callback, llamado glutKeyboardFuncn, que llama a una función (de igual manera que la función display en el apartado 2.4.1) que permite al usuario introducir comandos, estos varian el valor de una variable (llamada reescalado) de 57 forma que afecte de manera UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo inversamente proporcional a las dimensiones espaciales de la nube de puntos, es decir, cuando el valor del reescalado sea mayor, la escala de la figura representada será menor; el código creado para solucionar el problema del reescalado es el siguiente: void keyboard(unsigned char key, int x, int y){ switch (key) { case 'p': case 'P': reescalado++; break; case 'o': case 'O': reescalado = 1; break; case 'i': case 'I': reescalado = reescalado/2; break; case 27: // escape exit(0); break; } Tal y como se aprecia, al pulsar la tecla “p” la variable reescalado aumentara, disminuyendo de tamaño la figura, al contrario que al pulsar la tecla “i”; además al pulsar la tecla “o”, la variable reescalado vuelve a su valor original y finalmente, si se pulsa la tecla Esc, se termina la representación. En la figura 35, se puede apreciar el efecto del reescalado al aumentar la variable. 58 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Desarrollo Figura 35 diferencias de reescalado, imagen pequeña y base Mientras que en la siguiente imagen (figura) se ha disminuido su valor, resultando una escena mucho más detallada (tanto la figura 35 como la 36 están a la misma escala). Figura 36 diferencias de reescalado, imagen grande 59 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL 60 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos Capítulo 3 RESULTADOS/EXPERIMENTOS En este capítulo, se llevaran a cabo experimentos para determinar la viabilidad del trabajo, ya que a lo largo del desarrollo han surgido problemas que dificultaban continuar, es necesario conocer los motivos de dichos problemas, y como pueden afectar al desarrollo del control visual. Debido a su naturaleza, se han clasificado según al bloque al que pertenecen, es decir, a extracción de datos, recreación de la escena o representación tridimensional. 3.1 EXPERIMENTOS DE EXTRACCIÓN DE DATOS Aquí se llevaran a cabo los experimentos relacionados con la obtención de datos del entorno por medio del sensor Kinect. 3.1.1 EL KINECT EN EXTERIORES, PARA DIVERSOS GRADOS DE ILUMINACIÓN El quad al que se le quiere añadir el sistema de control visual, va a recorrer exteriores, por tanto, es necesario realizar pruebas para verificar que el sensor es capaz de funcionar en estas condiciones, para ello se realizan las siguientes mediciones(en exteriores) a distintos grados de iluminación: En la figura 37, la captura se toma en condiciones de máxima intensidad lumínica, se puede observar que las barras y ciertas partes de la estructura, aparecen en color verde oscuro (indica 0 milímetros o error), algunas, 61 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos pertenecen a zonas donde se está reflejando la luz del sol, por lo que se están produciendo pequeños deslumbramientos. Figura 37 Exterior diurno En la siguiente figura, tomada con poca intensidad de luz, se aprecian mejor los contornos de las estructuras, y menos zonas en color verde oscuro y por supuesto, ninguna zona de deslumbramiento(las superficies tienen colores más uniformes). Figura 38 Exterior anocheciendo 62 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos Por último, la figura 39 fue tomada durante la noche, sin fuentes de luz, tal y como se aprecia en la imagen a color, sin embargo, la imagen de profundidad aparece bien definida, no existen zonas de deslumbramiento y se perciben perfectamente los contornos de las figuras. Figura 39 Exterior noche cerrada A la vista de estos resultados, se concluye que el Kinect que se usa en este proyecto, es poco apto para entornos con alta intensidad luminosa, sin embargo, es perfectamente capaz de trabajar en entornos exteriores con baja intensidad, también funciona de noche, sin embargo, solamente con la imagen de profundidad, por lo que el programa desarrollado no es capaz de recrear la escena al no poseer los fotogramas de color. 3.1.2 EL KINECT ONE FRENTE AL KINECT 360 En este apartado se compararán las dos versiones del sensor Kinect, por un lado, la más reciente, y perteneciente a la segunda generación, el Kinect One, y por el otro el Kinect 360 que es la versión previa, y el que se usa en este proyecto. 63 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos El objetivo de este experimento es comparar ambos sensores y determinar cuál es el más apto para la tarea. Para ello, se van a tomar capturas en entornos con alta luminosidad (que como se vio en el apartado 3.1.1, son las que inhabilitan al Kinect 360 para su uso en exteriores). La figura 40 muestra la captura del Kinect One, tanto imagen a color como de profundidad, el Kinect One es capaz de captar la imagen de profundidad con pocos errores, además de tener una calidad superior. Figura 40 Kinect One, exterior Por otra parte, la figura 41, muestra la captura que el sensor Kinect 360 es capaz de realizar en entornos luminosos, en este caso, el sensor no percibe imagen de profundidad alguna. 64 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos Figura 41 Kinect 360, exterior Como conclusión, se observa en la comparación que el sensor Kinect One es superior, ya que es capaz de trabajar en entornos muy luminosos, en los que el Kinect 360 no puede. 3.1.3 DETECCIÓN DE CRISTALES Aunque en el entorno forestal al que está destinado el quad, no abundan los objetos cristalinos o transparentes, resulta conveniente estar preparado para esta eventualidad. Por tanto, se realizará el siguiente experimento con el objetivo de determinar si el sensor es capaz de detectar objetos transparentes. Se han realizado dos capturas; en la figura 42, se muestra la detección del cristal aunque con ciertos errores, ya que la imagen de profundidad en la región del cristal no es estable. 65 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos Figura 42 Detección del cristal 1 Mientras que en la figura 43, el sensor no es capaz de detectarlo. Figura 43 Detección del cristal 2 Por tanto, se observa que el Kinect no es capaz de detectar obstáculos transparentes con fiabilidad, esto es importante a tener en cuenta para saber en qué entornos puede o no funcionar. 66 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos 3.2 EXPERIMENTOS EN LA CREACIÓN DE LA ESCENA En esta parte se llevaran a cabo experimentos relacionados con la unión de fotogramas y la creación de la escena panorámica que ayudará a crear el mapeado. 3.2.1 FUNCIONAMIENTO DE HOMOGRAPHY La herramienta Homography fue considerada inicialmente en vez del Stitching como se cuenta en el apartado 2.3.3, aquí se pretende demostrar las cualidades y desventajas de dicha herramienta. Para ello, se capturan las imágenes mostradas en la figura 44, que serán las que conformen la escena mediante ambas herramientas, para comparar su efectividad. Figura 44 Fotogramas Homography Primeramente se crea la panorámica con Homography, sin embargo, esta corresponde con la segunda imagen (figura 44 a la derecha), siendo incapaz de recrear la escena. 67 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos Figura 45 Homography errónea Ahora se han invertido las posiciones de los fotogramas base en el programa, realizando otra vez la operación de Homography, esta vez se obtiene la escena con éxito (figura 46). Figura 46 Homography correcta Para comparar esta herramienta con la que se usa en este proyecto, se toma la recreación de la escena por medio de Stitching (figura 47). 68 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos Figura 47 Stitching frente a Homography Además, se ha cronometrado el tiempo que tarda en hacer estas pruebas, mostrado en la Tabla 1, se han realizado varias pruebas con cada herramienta, el tiempo mostrado es la media. Herramienta Tiempo(s) Homography derecha-izquierda 6.2 Homography izquierda- derecha 6.3 Stitching 11.5 Tabla 1 Tiempo Homography Se observa que la herramienta Homography es más rápida, sin embargo, requiere conocer la disposición espacial antes de recrear la escena, esto es un problema ya que el proceso de recreación de la escena se emplea para conocer la posición relativa de los fotogramas. 69 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos 3.2.2 FUNCIONAMIENTO DE STITCHING La herramienta seleccionada para recrear la escena es el Stitching, que une dos fotrogramas y conforma una escena, en esta escena se buscarán fragmentos de dichos fotogramas para localizarlos y así determinar su localización relativa, tal y como se explica en los apartados 2.3.3 y 2.3.4. Entonces es necesario saber cuáles son las capacidades de esta herramienta, cuando falla y el tiempo que tarda en realizar el proceso, para ello, se han realizado varios fotogramas con el objetivo de mostrar las diferentes uniones que se realizarán en el programa. En la primera prueba, se llevara a cabo la unión de dos imágenes similares, con muy pequeñas variaciones, las imágenes base empleadas son las mostradas en la figura 48. Figura 48 Imágenes base-ligero desplazamiento Una vez realizado el proceso de Stitching, se obtiene la escena de la figura 49, que no se corresponde demasiado con la realidad. 70 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos Figura 49 Stitching-ligero desplazamiento Sin embargo, a la hora de buscar la localización de los frames base (figura 50), se tienen puntos centrales bastante próximos, por lo que se podrían obtener resultados precisos. Figura 50 Localización-ligero desplazamiento 71 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos En la siguiente prueba, se va a simular un avance frontal del sensor, es decir un acercamiento al objetivo, para ello se emplean las imagens de la figura 51. Figura 51 Imágenes base-avance El resultado de esta prueba es negativo, ya que la función Stitching no es capaz de devolver la escena, resultado en error y un cierre del programa; esto mismo sucede con las imágenes de la figura 52, en las que por la poca correlación entre los puntos de interés de las imágenes, no se puede lograr la construcción de la panorámica. Figura 52 Imágenes base-arriba 1 Realizo ahora un experimento similar al anterior, con cuidado de que las imágenes compartan más zonas en común, este experimento simula la unión de un fotograma y el que está justo encima de él (Figura 53). 72 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos Figura 53 Imágenes base-arriba 2 Ahora que los fotogramas están más próximos, el Stitching es capaz de crear la escena (Figura 54). Figura 54 Stitching-arriba 2 Ahora, la escena sale de nuevo deformada, y en la localización de los fotogramas que la conforman (figura 55), el programa no es capaz de detectar correctamente el segundo fotograma, proporcionando resultados erróneos. 73 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos Figura 55 Localización-arriba 2 El siguiente experimento se similar al anterior, ya que en vez de jugar con una imagen superior, se utiliza una inferior (siendo el desplazamiento relativo entre ambos fotogramas lo que marque el resultado), las imágenes empleadas en esta prueba, son las de la figura 56. 74 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos Figura 56 Imágenes base-abajo Esta vez la escena conformada, se aproxima a la realidad, tal y como se muestra en la figura 57. Figura 57 Stitching-abajo Sin embargo ahora el programa no es capaz de detectar correctamente ningún fotograma base, (figura 58), esto es debido a la poca relación que guardan los puntos de interés localizados. 75 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos Figura 58 Localización-abajo En la siguiente prueba, se analiza el caso del desplazamiento lateral de dos imágenes, siendo éstas las de la figura 59. 76 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos Figura 59 Imágenes base-derecha El Stitching devuelve esta vez una escena no uniforme, tal y como aparece en la figura 60, sin embargo, se corresponde bastante bien con la realidad. Figura 60 Stitching-derecha A la hora de localizar los fotogramas que conforman la panorámica, el programa resulta bastante preciso, obteniendo un buen resultado, tal y como se aprecia en la figura 61. 77 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos Figura 61 Localización-derecha Para terminar, en el último experimento, se proceden a unir dos imágenes con desplazamiento lateral entre ellas (figura 62). Figura 62 Imágenes base-izquierda 78 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos Al igual que en el experimento anterior, se obtiene una escena bastante próxima a la realidad (figura 63). Figura 63 Stitching-izquierda De igual forma, se obtienen unas localizaciones muy precisas de los fotogramas base, como se muestra en la figura 64. 79 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos Figura 64 Localización-izquierda Adicionalmente, se han recogido los tiempos de proceso de los experimentos (Tabla 2), siendo el tiempo de creación el tiempo que tarda en crear la escena y el tiempo total, el que tarda en crearla y localizar los fotogramas base; los tiempos son el valor medio estimado de varias mediciones. 80 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos Escena Tiempo Creación(s) Tiempo Total(s) 12.3 19.5 Avance (figura 51) x x Centro-Arriba (figura 52) x x Centro-Arriba 2 (figuras 53, 54, 55) 17.2 32.7 Centro-Abajo(figuras 56, 57, 58) 12.4 23.1 Centro-Derecha(figuras 59, 60, 61) 13.2 23.8 Centro-Izquierda(figuras 62, 63, 64) 12.2 23.3 Ligero desplazamiento (figuras 48, 49, 50) Tabla 2 Tiempo Stitching Para concluir, se observa que la herramienta Stitching no es del todo fiable, ya que requiere que el área que comparten las dos imágenes base sea bastante grande, y aun así, puede producir errores en la escena en situaciones con imágenes cuya área compartida sea demasiado grande (como en las figuras 50 y 51), esto es un problema, ya que en caso de que se tengan que procesar imágenes como éstas, el programa fallará, terminando la compilación. Otro de los problemas que genera, es el largo tiempo de procesamiento que tarda en realizar las tareas (tanto la generación de la escena, como la búsqueda de los fotogramas base en la misma), que unido al tiempo que tarda el programa en procesar los datos extraídos por el sensor Kinect, resultaría en una tasa de FPS (Frames Per Second) de 0.04 (1/30s), algo inviable para una aplicación a tiempo real. 81 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos 3.2.3 STITCHING EN ESCENAS LUMINOSAS A la hora de recrear una escena, puede haber momentos en los que el sensor reciba deslumbramientos (que según el apartado 3.1.1, podría fallar al percibir la profundidad), por lo tanto se requiere conocer como afectarán dichas condiciones a la creación de la escena. Para ello se han tomado dos fotogramas (figura 65), que difieren en la luminosidad y se intentará formar una escena con ellos. Figura 65 Stitching-luz El intento de formar una escena con las imágenes variando la luminosidad, devuelve un error, esto es debido a que la herramienta Stitching reconoce las imágenes como imágenes distintas. Esto indica que es necesario hacer un experimento con imágenes no estáticas, es decir, que contengan elementos en movimiento, como se verá en el apartado siguiente. 3.2.4 STITCHING EN ESCENAS NO ESTÁTICAS Ya que en algunos momentos durante la navegación, puede pasar que el quad se encuentre con objetos móviles (animales por ejemplo), o por ejemplo deslumbramientos momentáneos (apartado 3.2.3), se requiere 82 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos saber cómo actuará la herramienta de Stitching a la hora de recrear la escena cuando elementos presentes desaparezcan. Para ello, se han tomado dos imágenes en las que un objeto es omitido (la lámpara) tal y como se muestra en la figura 66. Figura 66 Imágenes base-imágenes no estáticas Con estas imágenes, se recrea la escena, mostrada en la figura 67, en la cual se puede apreciar la inclusión del objeto omitido, incluso en las zonas comunes a ambos fotogramas base. Figura 67 Stitching-imágenes no estáticas 83 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos Tras recrear con éxito la escena, se continúa el proceso y se buscan los fotogramas base en la panorámica, con el resultado mostrado en la figura 68. Figura 68 Localización-imágenes no estáticas Teniendo en cuenta los resultados de este experimento y el anterior (apartado 3.2.3) se concluye en que el programa puede trabajar en las condiciones de un entorno no estático, es decir, que contenga objetos móviles, sin embargo, tienen que ser objetos secundarios en la escena, que no definan completamente los fotogramas. 84 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos 3.3 EXPERIMENTOS Y RESULTADOS EN EL ESPACIO TRIDIMENSIONAL En esta última parte se llevaran a cabo los experimentos necesarios para garantizar el buen funcionamiento del display tridimensional empleando la herramienta de OpenGL, además se mostrarán los resultados obtenidos de la representación espacial de la escena. 3.3.1 ANÁLISIS DE LAS HERRAMIENTAS DISPONIBLES EN EL ESPACIO TRIDIMENSIONAL En este apartado se analizarán las distintas implementaciones realizadas para una representación óptima del mapeado tridimensional. A la hora de introducir los datos en la matriz global Mat_Omega (apartado 2.3.6), se requiere introducir las diferencias espaciales entre los fotogramas, es decir los giros y las traslaciones (estas fueron calculadas en el apartado 2.3.7). Para ello, se van a ir comprobando las distintas modificaciones, una por una, para escenas simples, es decir, planos de un solo color, fácilmente distinguibles unos de otros, para poder apreciar las diferencias. Empezando con la traslación espacial, se selecciona una traslación de (350, 200, 300) en coordenadas XYZ, y el resultado que se obtiene, se puede apreciar en la figura 69, en la que el plano blanco actúa de fotograma base, mientras que el rojo, simula al nuevo fotograma, al que se le aplica la traslación. Se ha añadido también la localización inicial del sensor Kinect (en el origen de coordenadas, marcado por la estrella) y también los sentidos positivos de los ejes de coordenadas. 85 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos Figura 69 Traslación y ejes Ahora vamos a comprobar el giro sobre el eje X, tal y como muestra la figura 70, el plano rojo, aparece girado 45 grados respecto al fotograma base (el plano blanco). Figura 70 Rotación eje X Mientras que en el giro sobre el eje Y (figura 71), se aprecia que el plano rojo aparece girado 45 grados también respecto el plano blanco, el eje de 86 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos giro, es el eje central del plano rojo, que en este caso, coincide con el centro del plano blanco. Figura 71 Rotación eje Y Se comprueba el correcto funcionamiento de las diferencias entre fotogramas al ser introducidos en la matriz global. Uno de los errores de esta herramienta es la superposición de superficies en la representación, esto puede causar problemas a la hora de visualizar la escena. Además, tal y como fue mencionado en el apartado 2.4.2, el usuario necesita poder cambiar la escala, para momentos en los que la escena sea tan grande que no se pueda apreciar en el display, o por el lado contrario, que se necesite más detalle para observarla, tal y como se muestra en la comparativa de la figura 73. 87 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos Figura 72 Normal vs grande Por otra parte, a la hora de tener escenas muy grandes, es necesario una visión general de las mismas (figura 72), para ello, se aplica el reescalado inverso, además en caso de no ser suficiente, el reescalado es acumulativo, es decir, al activar el comando que disminuye la escala, se puede volver a activar para disminuir aun más la escala. Figura 73 Normal vs pequeño vs más pequeño Como conclusión, se puede apreciar que el reescalado funciona perfectamente, sin embargo, estaría bien que para desarrollos futuros, fueran introducidos más comandos que permitiesen una visión más interactiva de la escena. 88 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos Finalmente es necesario comprobar cuál es el límite de puntos que pueden ser representados para ello, por medio de figuras simples, se va a ir llenando el espacio, hasta que éste no sea capaz de representar la rotación. En la figura 74, se han ido añadiendo figuras una tras otra hasta llegar al límite. Figura 74 Imagen límite El límite de fotogramas que se pueden representar, esta acotado por el procesador del ordenador, y por el tamaño que se le quiera dar a la matriz global, en el caso de este proyecto (con un procesador Intel® Core™ i3 CPU M330 @2.13GHz) el límite donde la fluidez se empieza a resentir se ha encontrado en 9 fotogramas. 3.3.2 REPRESENTACIÓN DE UN FOTOGRAMA El objetivo de este experimento es lograr la representación tridimensional de un único fotograma. Para ello, se han recogido los datos de dicho fotograma (color y profundidad) mediante el sensor Kinect, que tras ser almacenados en la matriz global, se procede a representarla. 89 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos Las figuras 75 y 76 muestran algunas de las capturas de la representación tridimensional obtenida. Figura 75 Representación tridimensional-Resultado 1 Figura 76 representación tridimensional-Resultado 2 90 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos La valoración de la representación es positiva, sin embargo, se observan ciertos errores, que son precisos de eliminar para continuar, tales como los puntos correspondientes a la imagen residual (puntos con valor de profundidad nulo, detectados en el apartado 2.4.2); este problema se puede resolver fácilmente añadiendo una condición en la función de representación, que impide añadir valores con profundidad igual a cero. El otro problema es la falta de uniformidad en las superficies, que habría que arreglar para desarrollos futuros en los que se desee detectar los objetos y las superficies y reconocerlos, avisando al usuario o al vehículo de la existencia de los mismos, la distancia a la que se encuentran y su forma. 3.3.3 RECREACIÓN DE LA ESCENA TRIDIMENSIONAL En este apartado se llevara a cabo la representación de una serie de escenas tridimensionales, el objetivo es demostrar que se ha logrado recrear la escena con éxito. Este es el paso que culmina todos los anteriores, la recreación de la escena por medio de OpenCV fue para posicionar los distintos fotogramas adecuadamente en el espacio, y aquí van a ser representados, se han realizado grabaciones de cada representación, referidas en el pie de las imágenes. En esta primera escena (figura 77), el mapeado está formado por la unión de tres fotogramas, cabe resaltar, que hay zonas en las que se superponen los fotogramas (por ejemplo solo se ve la mitad del televisor), esto es un problema de la herramienta de representación, que en caso de superposición, representa el más cercano al observador. 91 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos Figura 77 Escena formada con tres fotogramas [22] En la siguiente escena, también formada con tres fotogramas, no logra la precisión suficiente, ya que el primer y el tercer fotograma acaban casi unidos, bloqueando el segundo (que aparece resaltado con un círculo amarillo en la figura 78). 92 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos Figura 78 Segunda escena formada por tres fotogramas [23] En el último mapeado tridimensional (figura 79), se juntan dos fotogramas únicamente, estos poseen un área compartida superior, permitiendo así apreciar algunos de los errores de cálculo del proceso (figura 80), marcados en rojo. 93 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos Figura 79 Escena formada por dos fotogramas [24] Se aprecia que hay zonas donde se superponen las imágenes y resulta difícil distinguir los objetos, esto es debido a la superposición de puntos desde la perspectiva del observador del display, como se explica al principio de este apartado. 94 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos Figura 80 Escena formada por dos fotogramas, vista perpendicular La figura 81 muestra el mapeado visto desde atrás de la pared física, donde ahora sí se pueden apreciar los objetos que en las anteriores figuras habían salido cortados. 95 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Resultados/Experimentos Figura 81 escena formada por dos fotogramas, vista posterior Como conclusión, la representación se ha llevado a cabo adecuadamente [25], los puntos residuales (nombrados en el apartado 3.3.2) han sido eliminados y se puede distinguir la escena con cierta claridad, sin embargo, los cálculos realizados a la hora de reconstruir la escena, no son demasiado precisos, y además el proceso de reconstrucción no es nada fiable, ya que la unión de ciertos fotogramas acaban en errores que terminan el proceso (apartado 3.2.2). Con respecto al experimento 3.3.1 la fluidez cuando existen zonas que se superponen, se ve resentida a partir de 3 fotogramas, en los que el display ha de seleccionar que puntos son visibles y cuáles no. 96 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Conclusiones Capítulo 4 CONCLUSIONES Se ha desarrollado un programa basado en el sensor Kinect, que intenta responder a la necesidad de la navegación autónoma de un vehículo, el programa analiza el entorno en tiempo real, permitiendo la generación de órdenes de navegación únicamente a partir de la información proporcionada por el sensor Kinect. Los objetivos principales del proyecto se han cubierto mayormente; la recreación del entorno se ha completado con relativo éxito, aunque debido a ciertos problemas expuestos a continuación (y mostrados en el Capitulo 3 de experimentos y resultados), no se ha podido continuar al siguiente paso, distinción de obstáculos. En primer lugar se han extraído con éxito los datos proporcionados por el sensor Kinect, tanto de color como de profundidad, y esos datos han sido invertidos para coincidir con la imagen real. Empleando la herramienta OpenCV, se ha construido la escena con éxito, posicionando los fotogramas en el espacio hasta conformar el mapeado tridimensional de la escena, detectando las variaciones relativas entre los fotogramas: la translación en las tres coordenadas y también los giros respecto los ejes X e Y. Además se han almacenando los valores de la nube de puntos en la matriz: Mat_Omega, para ser posteriormente analizados y representados con OpenGL, se han solucionado los problemas de visualización tridimensional aplicando la rotación de la escena y el reescalado. A partir de esta representación, en un futuro se crearan las órdenes que necesite el vehículo seleccionado para la navegación autónoma. 97 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Conclusiones Sin embargo, también han surgido ciertos problemas que impiden un desarrollo adecuado del proyecto, siendo el mayor de ellos la poca velocidad del proceso, que dificulta enormemente que pueda ser aplicado en tiempo real (una unión de dos frames cada 30 segundos, en el mejor de los casos). Esta lentitud de proceso, aumenta los fallos en la construcción de la escena en aspectos como la fiabilidad de los puntos de interés en los fotogramas consecutivos, produciendo irregularidades en la escena, o una terminación del programa en el peor de los casos; este problema se agrava debido a la falta de uniformidad del programa base a la hora de escanear imágenes del entorno, y a la imposibilidad de introducir un temporizador (real o controlado por el propio usuario) que de un ritmo interno mas estructurado al programa. Por otra parte la viabilidad del Kinect (versión 1.8) como sensor no es del todo adecuada, debido a la alta posibilidad de deslumbramiento en exteriores, que “cegarían” la cámara y podría transmitir errores (dan los valores erróneos de cero milímetros de distancia en las áreas deslumbradas, inhibiendo su capacidad para detectar obstáculos próximos en esas zonas, ver apartados 3.1.1 y 3.1.2) En cualquier caso, los medios para llegar a los objetivos propuestos funcionan correctamente, solo que estos medios no resultan ser del todo óptimos. En conclusión, los objetivos conseguidos: el flujo de datos proporcionado por el Kinect, la representación tridimensional del entorno y la recreación de la escena; funcionan adecuadamente por separado, sin embargo en conjunto, no son adecuados para responder a la necesidad pedida, y requieren otro planteamiento, otra selección de herramientas y nuevas versiones de hardware. 98 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Futuros desarrollos Capítulo 5 FUTUROS DESARROLLOS Se ha logrado recrear el mapeado tridimensional del entorno a partir de los datos proporcionados por el sensor Kinect 360, objetivo indispensable para guiar al vehículo a través del entorno, sin embargo ciertos problemas impiden que la presente solución sea completamente viable. Dada la lentitud del procesamiento de datos, la construcción de la escena no es lo suficientemente rápida para aplicarse en tiempo real. Las herramientas empleadas para la recreación de la escena (Stitching de OpenCV) no son del todo eficientes, y presentan cierta posibilidad de error a la hora de crear la escena (error que impide continuar con el programa), por lo que un uso continuado supondría un cierre inevitable del programa (apartado 3.2.2). Además, el sensor Kinect 360 que se usa en este proyecto, no es apto para condiciones en el exterior (apartado 3.1.1) ya que en situaciones de alta luminosidad no funciona correctamente. Por lo tanto en futuros desarrollos, sería necesario arreglar los problemas que impiden el óptimo funcionamiento de este futuro control visual. Para mejorar la velocidad de proceso, un buen comienzo seria cambiar el ordenador por otro más potente (es un Toshiba con un procesador Intel(R) Core(TM) i3 CPU 2.13GHz), con mayor velocidad de procesamiento; de esta forma, el programa podría ser optimizado y llegar a ser una aplicación real, en el caso de lograr una tasa de FPS (Frames Per Second) aceptable. Sería recomendable que el programa guardase una cierta jerarquía, agregando timers o esperando inputs del usuario, para optimizar el procesamiento de la escena; sin embargo, el programa no admite los 99 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Futuros desarrollos inputs más comunes del usuario como getchar() o waitKey(); otra forma seria ajustando el bucle de OpenGL y haciendo que este regule el programa, pero es poco recomendable (apartado 2.4.1) Ciertos fallos en la recreación de la escena mediante Stitching deben ser arreglados, tales como la detección previa de la compatibilidad de las imágenes que se van a juntar, evitando errores en el programa (aunque esto demorase el proceso). En cualquier caso, quizás las soluciones ofrecidas por estas librerías no sean las más adecuadas, existen varias opciones para implementar el sistema, una de las opciones más razonables y que fuertemente se recomienda en futuros desarrollos es la de PCL, por estar orientada al trabajo con nubes de puntos, y contar con varios tipos de filtrado para los mismos. [26] Esta opción no fue considerada en su momento por el desconocimiento de la misma (únicamente sirve para trabajar con nubes de puntos) y el grado de avance que existía en aquel momento con la solución escogida, sin embargo, esta librería, se podría decir que engloba las funciones requeridas de las librerías que se usan (OpenCV y OpenGL). Una vez se han resuelto los problemas existentes, el paso siguiente es la detección de obstáculos y el reconocimiento de objetos. Para el reconocimiento de obstáculos, es necesaria la aplicación de filtros, ya sea manualmente (seleccionando un rango de valores de profundidad) o filtros ya programados que eliminen los valores residuales (apartado 2.4.2 figura 33)o que sean capaces de reconstruir las superfices a partir de los datos imperfectos del sensor; en cualquier caso hay que reconstruir la nube de puntos en figuras compactas, para ello detectando las superficies (vectores normales) y después reconocer los objetos, en otras palabras, se busca transformar la escena tridimensional actual en un conjunto de cubos, superficies. [27] 100 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Futuros desarrollos Una vez categorizados los objetos de la escena en formas reconocibles, se obtendría algo similar a esto: Figura 82 Distinción de objetos en el mapeado tridimensional Partiendo de este mapeado mas estructurado, se informaría al vehículo de la forma de los obstáculos y la distancia a la que se encuentran, esto es reconociendo los obstáculos y pudiendo distinguirlos entre sí. 101 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL 102 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Bibliografía BIBLIOGRAFÍA [1] Coches. Net, Los fabricantes avanzan en la conducción automatizada http://www.coches.net/noticias/coches-sin-conductor [2] Universidad Carlos III, Un nuevo modelo para la navegación autónoma de vehículos http://portal.uc3m.es/portal/page/portal/actualidad_cientifica/noticias/navegacion _autonoma_vehiculos [3] Universidad Carlos III, Sistema de apoyo a la navegación de un robot móvil en invernaderos http://www.ual.es/personal/rgonzalez/papers/279.pdf [4] Kinect for Windows SDK https://msdn.microsoft.com/en-us/library/hh855347.aspx [5] Kinect for Windows Human Interface Guidelines v 1.8.0 https://msdn.microsoft.com/en-us/library/jj663791.aspx [6] Explicación visión estereoscópica http://www.adpsystems.net/Pdf/Visi%C3%B3n%20%20estereosc%C3%B3pica.pdf [7] Blog Panasonic, Explicación de la visión http://blog.panasonic.es/tecnologia/tecnologia-3d-a-fondo/ [8] Seattle Pi E3 2009 http://blog.seattlepi.com/digitaljoystick/2009/06/01/e3-2009-microsoft-at-e3several-metric-tons-of-press-releaseapalloza/ [9] Kinect for Windows SDK beta launches, wants PC users to get a move on http://www.engadget.com/2011/06/16/microsoft-launches-kinect-for-windows-sdk-betawants-pc-users-t/ [10] Wikipedia Kinect Microsoft http://es.wikipedia.org/wiki/Kinect [11] Microsoft Download Center Kinect for Windows SDK v 1.8 https://www.microsoft.com/en-us/download/details.aspx?id=40278 103 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Bibliografía [12] Wikipedia, OpenCV https://en.wikipedia.org/wiki/OpenCV [13] InformatikZentrale RGB Farbmodell http://www.informatikzentrale.de/rgb-farbmodell.html [14] OpenCV Docs Mat The basic Image Container http://docs.opencv.org/doc/tutorials/core/mat_the_basic_image_container/mat_th e_basic_image_container.html#matthebasicimagecontainer [15] Wikipedia RGBA https://en.wikipedia.org/wiki/RGBA_color_space [16] OpenCV Docs Features 2D Homography http://docs.opencv.org/doc/tutorials/features2d/feature_homography/feature_hom ography.html#feature-homography [17] OpenCV Docs Common Interfaces of Feature Detectors http://docs.opencv.org/modules/features2d/doc/common_interfaces_of_feature_d etectors.html [18] Nvidia GameWorks https://developer.nvidia.com/opengl [19] Wikipedia OpenGL https://es.wikipedia.org/wiki/OpenGL [20] Software GLUT Hack http://www.sjbaker.org/steve/software/glut_hack.html [21] Vídeo de la escena, disponible en Youtube https://youtu.be/eR4S4tpCaIE [22] Vídeo de la escena, disponible en Youtube https://youtu.be/HtoT2piXwHA [23] Vídeo de la escena, disponible en Youtube https://youtu.be/TM1jzjGJ7K0 [24] Vídeo de la escena, disponible en Youtube https://youtu.be/gs4oMp2ewV4 [25] Lista de reproducción de los mapeados tridimensionales, Youtube 104 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Bibliografía https://www.youtube.com/watch?v=V-SSGvK6sA&list=PLITBzsKx59J3E5b9FjKyEqoPBxli_1gjI [26] Point Cloud Library http://pointclouds.org/about/ [27] Wikibooks OpenGL https://en.wikibooks.org/wiki/OpenGL_Programming [28] OpenCV Download http://opencv.org/downloads.html [29] OpenGL Download https://www.opengl.org/wiki/Getting_Started 105 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Bibliografía 106 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Bibliografía Parte II ESTUDIO ECONÓMICO 107 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL 108 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Estudio económico Capítulo 1 ESTUDIO ECONÓMICO La mayoría de sistemas de navegación autónoma emplean cámaras o sensores para analizar los alrededores y ser capaces de detectar obstáculos (recordar que para obtener una visión estereoscópica se requieren al menos dos cámaras), con el sensor Kinect, se tienen las cámaras necesarias para ello, además de ciertos sensores adicionales (como detector de audio), esto puede ser de utilidad para futuros proyectos. Con el Kinect, se tiene una mayor fiabilidad en los datos, ya que ha sido programado y testeado; cosa que no ocurre en otros sistemas, como si se implementase con cámaras normales para después calcular los valores de profundidad y color (la escena); esto hace que el precio del desarrollo disminuya enormemente, ya que los datos proporcionados por los sensores no requieren ser calculados a partir de la diferencia de las imágenes proporcionadas por las cámaras (que son más baratas) además el hecho de incluir el sensor Kinect, implica un coste menor, ya que este está orientado a mas campos que al de navegación autónoma, lo que le proporciona mayor número de clientes y disminuye su precio. Una de las principales características a tener en cuenta, es el rango de detección de obstáculos, que en el Kinect, este es de 4 metros, teniendo gran precisión, aunque poca velocidad de refresco, esto lo hace apto para vehículos lentos y que recorran terrenos muy accidentados o con multitud de obstáculos, no siendo viable para vehículos rápidos por recorridos más simples (como coches en una autopista). El coste inicial de desarrollo es de 12823.7€. El coste del material necesario para construir modelos (para una flota de vehículos, por ejemplo) seria de aproximadamente 3500€, que se podría reducir a 100€ si se busca un 109 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Estudio económico compilador libre que funcione con ambas librerías y se logra omitir la presencia del ordenador en el proceso. El coste de la mano de obra para testear cada una de las unidades, no es muy elevado (en torno a 20€ como máximo, por si surgen complicaciones), ya que el programa es único, y no requiere calibración. Suponiendo que no haya problemas de distribución del producto por que el sensor Kinect pertenezca a Microsoft, el coste del producto para la empresa seria de 120€ aproximadamente. A falta de un estudio de mercado, se estima que la demanda potencial del producto a lo largo de su producción será de 150 unidades (en España). Siendo así, a cada unidad habrá que repercutirle 12823.7/150€ (85.5€) para cubrir los costes de desarrollo, ascendiendo el precio a 235.5€. Considerando un beneficio empresarial del 20%, el precio de venta queda en 385€, y al sumar el IVA (21%), el precio final de venta al público queda en 341.9€. A la vista de las demás alternativas del mercado (orientado a los clientes que necesitan un sensor de precisión donde la velocidad de proceso sea algo secundario) este precio es bastante competitivo. Una cosa a tener en cuenta es el relativo poco mercado que existe, ya que si hubiera más demanda, hacer un sistema de navegación con cámaras normales, aunque poseyera un desarrollo del prototipo mucho más costoso, el coste de construcción por unidad es más barato (los componentes son más baratos), lo que rebajaría mucho su precio (cubriría más fácilmente los costes de desarrollo). Por lo que utilizar un sensor Kinect para la navegación autónoma es una buena idea para abrir nuevos mercados, que más adelante se encaminaran al uso de cámaras normales, más económicas, aunque de mayor coste de desarrollo. 110 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Estudio económico Parte III PRESUPUESTO 111 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL 112 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Mediciones Capítulo 1 MEDICIONES En este capítulo se recogen las unidades de los equipos y componentes empleados, además de las horas imputables al proyecto, clasificadas según la tarea en cuestión. 1.1 COMPONENTES DE HARDWARE Y HERRAMIENTAS En esta tabla aparecen los componentes de hardware y las herramientas empleadas para llevar a cabo el proyecto. Equipo y hardware Cantidad Horas Horas de uso al año 1 300 1000 Kinect 360 1 280 280 Cable USB Kinect 1 280 280 Ordenador Toshiba Satellite i3 Tabla 3Medición de Hardware 113 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Mediciones 1.2 COMPONENTES DE SOFTWARE En la siguiente tabla se muestran los precios de los distintos programas empleados en la elaboración del dispositivo de visión Elemento Cantidad Horas Horas de uso al año 1 300 380 Kinect SDK 1 280 280 Librería OpenCV 1 100 100 Librería OpenGL 1 70 70 Microsoft Word 1 50 700 Microsoft Visual Studio Ultimate 2013 Tabla 4 Medición de Software 1.3 MANO DE OBRA DIRECTA Por último, en esta tabla aparecen las distintas actividades con su correspondiente tiempo aplicado a cada una Actividad Horas Programación 65 Documentación 85 Pruebas y testeo 70 Solución de errores 90 Tabla 5 Medición de mano de obra 114 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Precios unitarios Capítulo 2 PRECIOS UNITARIOS En este capítulo, se calculan los costes de cada elemento por separado, en función del precio unitario de cada uno 2.1 COMPONENTES DE HARDWARE Y HERRAMIENTAS Equipo y hardware Precio[€/ud] Ordenador Toshiba Satellite i3 800 Kinect 360 70 Cable USB Kinect 15 Tabla 6 Precio de hardware 2.2 COMPONENTES DE SOFTWARE Elemento Precio[€/ud] Microsoft Visual Studio 3328 Ultimate 2013 Kinect SDK Gratuito(con el Kinect) Librería OpenCV Gratuito Librería OpenGL Versión Libre Microsoft Word 119 Tabla 7 Precio de software 115 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Precios unitarios 2.3 MANO DE OBRA DIRECTA Actividad Precio[€/ud] Programación 25 Documentación 30 Pruebas y testeo 30 Solución de errores 40 Tabla 8 Precio de mano de obra 116 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Sumas parciales Capítulo 3 SUMAS PARCIALES En los siguientes apartados se indica el precio unitario de cada elemento utilizado en el proyecto, así como el precio de la hora de la mano de obra directa 3.1 COMPONENTES DE HARDWARE Y HERRAMIENTAS Equipo y hardware Horas Horas de de uso proyecto al año 1 300 1000 800 25% 200 1 280 280 70 15% 49 1 280 280 15 10% 13.5 Cantidad Precio[€/ud] Amortización anual Coste total(€) Ordenador Toshiba Satellite i3 Kinect 360 Cable USB Kinect Total 262.5 Tabla 9 Suma de hardware 117 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Sumas parciales 3.2 COMPONENTES DE SOFTWARE Elemento Cantidad Horas Horas de de uso proyecto Precio[€/ud] al año Amortización Anual Coste total Microsoft Visual Studio Ultimate 1 300 500 3328 20% 2662.4 Kinect SDK 1 280 280 Gratuito 20% 0 Librería OpenCV 1 100 100 Gratuito 20% 0 Librería OpenGL 1 70 70 Versión Libre 20% 0 Microsoft Word 1 50 700 119 20% 23.8 2013 Total 2686.2 Tabla 10 Suma de software 3.3 MANO DE OBRA DIRECTA Actividad Horas Precio[€/ud] Coste total(€) Programación 65 25 1625 Documentación 85 30 2550 Pruebas y testeo 70 30 2100 Solución de errores 90 40 3600 Total 9875 Tabla 11Suma de mano de obra 118 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Presupuesto general Capítulo 4 PRESUPUESTO GENERAL Sumando la contribución de todos los elementos anteriores, se concluye que el coste del proyecto, impuestos incluidos, asciende a: Concepto Coste(€) Componentes de Hardware y herramientas 262.5 Componentes de Software 2686.2 Mano de obra directa 9875 Total 12823.7 Tabla 12 Presupuesto general 119 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Presupuesto general 120 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Presupuesto general Parte IV CÓDIGO FUENTE 121 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL 122 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente Capítulo 1 CÓDIGO FUENTE Se adjunta a continuación el archivo NuiImageBuffer.cpp. /////Verificar que se tienen las librerias instaladas!!!!! #define GLUT_DISABLE_ATEXIT_HACK #include <strsafe.h> #include "resource.h" #include <stdio.h> #include #include #include #include < opencv2\stitching\stitcher.hpp > ////uno nuevo "opencv2/highgui/highgui.hpp" <iostream> "opencv2/core/core.hpp" #include #include #include #include #include #include #include #include "opencv2/flann/miniflann.hpp" "opencv2/imgproc/imgproc.hpp" "opencv2/photo/photo.hpp" "opencv2/video/video.hpp" "opencv2/features2d/features2d.hpp" "opencv2/objdetect/objdetect.hpp" "opencv2/calib3d/calib3d.hpp" "opencv2/ml/ml.hpp" #include #include #include #include "opencv2/contrib/contrib.hpp" "opencv2/core/core_c.h" "opencv2/highgui/highgui_c.h" "opencv2/imgproc/imgproc_c.h" using namespace cv; using namespace std; #include "opencv2/nonfree/nonfree.hpp" #include #include #include #include #include <vector> "stdafx.h" <cmath> "NuiImageBuffer.h" "Utility.h" #include #include #include #include #include #include <stdlib.h> <stdio.h> <string.h> <math.h> <time.h> <glut.h> ///dio problemas tiene que estar por debajo de stdlib ////// 123 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente long int cont = 0; int frame_complete = 0; int LUT_depth[307200]; //640*480 int LUT_index[307200]; int variableauxiliarDep = 0; int variableauxiliarRGB = 0; int LUT_depth_matrixPAR[640][480]; int LUT_depth_matrixIMPAR[640][480]; int LUT_depth_matrix2[640][480]; int LUT_R_matrixPAR[640][480]; int LUT_G_matrixPAR[640][480]; int LUT_B_matrixPAR[640][480]; int LUT_R_matrixIMPAR[640][480]; int LUT_G_matrixIMPAR[640][480]; int LUT_B_matrixIMPAR[640][480]; int LUT_R_matrix[640][480]; int LUT_G_matrix[640][480]; int LUT_B_matrix[640][480]; int LUT_R_matrix2[640][480]; int LUT_G_matrix2[640][480]; int LUT_B_matrix2[640][480]; //string Sto_bitmap; int colors[921600]; int Height = 639; int Width = 479; int T_timer = 0; int GO = 10; int mezcla = 0; int n_image = 0; int n_image2 = 0; int cuenta1,cuenta2,cuenta3 = 0; vector<KeyPoint> queryKeypoints_B, trainKeypoints_B; char title[] = "3D Shapes"; //GLfloat anglePyramid = 0.0f; // Rotational angle for pyramid [NEW] GLfloat angleCube = 0.0f; // Rotational angle for cube [NEW] int refreshMills = 15; // refresh interval in milliseconds [NEW] int width = 640; int height = 480; int depth_M[640][480]; int Inicio_SCAN = 0; float reescalado = 1; Mat LUT_RGB(480, 640, CV_8UC3, Scalar(0, 0, 0)); Mat LUT_1(480, 640, CV_8UC3, Scalar(0, 0, 0)); Mat LUT_2(480, 640, CV_8UC3, Scalar(0, 0, 0)); Mat Stitch(480, 640, CV_8UC3, Scalar(0, 0, 0)); int LUT_1RGB[640][480][3]; int LUT_2RGB[640][480][3]; float vecX_Base[] = { 0, 0, 0, 0 }; float vecY_Base[] = { 0, 0, 0, 0 }; float vecX_New[] = { 0, 0, 0, 0 }; 124 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente float vecY_New[] = { 0, 0, 0, 0 }; float X_Base, Y_Base, X_New, Y_New = 0; float vectorXY_desp[50][3];///50 por ejemplo, hay que cambiarlo es Y 2 es Z float vec[3][10]; float centros[50][3]; ///0 es X, 1 es Y, 2 es Z 0 es X 1 int numero_puntos = 1536000; float Mat_Omega[3072000][7]; /// numero,X,Y,Z, R,G,B int giro,rot, Z_prof = 0; int numero = 0; /////////// #define #define #define #define BYTES_PER_PIXEL_RGB BYTES_PER_PIXEL_INFRARED BYTES_PER_PIXEL_BAYER BYTES_PER_PIXEL_DEPTH 4 2 1 sizeof(NUI_DEPTH_IMAGE_PIXEL) #define #define #define #define COLOR_INDEX_BLUE COLOR_INDEX_GREEN COLOR_INDEX_RED COLOR_INDEX_ALPHA 0 1 2 3 #define #define #define #define #define #define #define MIN_DEPTH MAX_DEPTH UNKNOWN_DEPTH UNKNOWN_DEPTH_COLOR TOO_NEAR_COLOR TOO_FAR_COLOR NEAREST_COLOR 400 16383 0 0x003F3F07 0x001F7FFF 0x007F0F3F 0x00FFFFFF // intensity shift table to generate different tracked players const BYTE NuiImageBuffer::m_intensityShiftR[] const BYTE NuiImageBuffer::m_intensityShiftG[] const BYTE NuiImageBuffer::m_intensityShiftB[] render colors for different = {0, 2, 0, 2, 0, 0, 2}; = {0, 2, 2, 0, 2, 0, 0}; = {0, 0, 2, 2, 0, 2, 0}; /// <summary> /// Constructor /// </summary> NuiImageBuffer::NuiImageBuffer() : m_nearMode(false) , m_depthTreatment(CLAMP_UNRELIABLE_DEPTHS) , m_nSizeInBytes(0) , m_width(0) , m_height(0) , m_srcWidth(0) , m_srcHeight(0) , m_pBuffer(nullptr) { ////aqui inicializo la matriz en la que podre los vectores Mat_Omega[numero_puntos][7]; /// numero,X,Y,Z, R,G,B for (int p = 0; p < numero_puntos; p++) { Mat_Omega[p][0]=p; 125 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente Mat_Omega[p][3]=0; ///si Z es 0, no sera representado } InitDepthColorTable(); } /// <summary> /// Destructor /// </summary> NuiImageBuffer::~NuiImageBuffer() { SafeDelete(m_pBuffer); } /// <summary> /// Set image size according to image resolution /// </summary> /// <param name="resolution">Image resolution</param> void NuiImageBuffer::SetImageSize(NUI_IMAGE_RESOLUTION resolution) { GetImageSize(resolution, m_srcWidth, m_srcHeight); } /// <summary> /// Calculate image width and height according to image resolution enumeration value. /// If resolution enumeration is invalid, width and height will be set to zero. /// </summary> /// <param name="resolution">Enumeration value which indicates the image resolution format</param> /// <param name="width">Calculated image width</param> /// <param name="height">Calculated image height</param> void NuiImageBuffer::GetImageSize(NUI_IMAGE_RESOLUTION resolution, DWORD& width, DWORD& height) { NuiImageResolutionToSize(resolution, width, height); } /// <summary> /// Get width of image. /// </sumamry> /// <returns>Width of image.</returns> DWORD NuiImageBuffer::GetWidth() const { return m_width; } /// <summary> /// Get height of image. /// </sumamry> /// <returns>Width of height.</returns> DWORD NuiImageBuffer::GetHeight() const { return m_height; } 126 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente /// <suumary> /// Get size of buffer. /// <summary> /// <returns>Size of buffer.</returns> DWORD NuiImageBuffer::GetBufferSize() const { return m_nSizeInBytes; } /// <summary> /// Return allocated buffer. /// </summary> /// <returns> /// The pointer to the allocated buffer /// Return value could be nullptr if the buffer is not allocated /// </returns> BYTE* NuiImageBuffer::GetBuffer() const { return m_pBuffer; } /// <summary> /// Allocate a buffer of size and return it /// </summary> /// <param name="size">Size of buffer to allocate</param> /// <returns>The pointer to the allocated buffer. If size hasn't changed, the previously allocated buffer is returned</returns> BYTE* NuiImageBuffer::ResetBuffer(UINT size) { if (!m_pBuffer || m_nSizeInBytes != size) { SafeDeleteArray(m_pBuffer); if (0 != size) { m_pBuffer = new BYTE[size]; } m_nSizeInBytes = size; } printf("\n\nalphaBuffer \n\n"); return m_pBuffer; } /// <summary> /// Clear buffer /// </summary> void NuiImageBuffer::Clear() { m_width = 0; m_height = 0; ResetBuffer(0); } /// <summary> /// Initialize the depth-color mapping table. /// </summary> 127 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente void NuiImageBuffer::InitDepthColorTable() { // Get the min and max reliable depth USHORT minReliableDepth = (m_nearMode ? NUI_IMAGE_DEPTH_MINIMUM_NEAR_MODE : NUI_IMAGE_DEPTH_MINIMUM) >> NUI_IMAGE_PLAYER_INDEX_SHIFT; USHORT maxReliableDepth = (m_nearMode ? NUI_IMAGE_DEPTH_MAXIMUM_NEAR_MODE : NUI_IMAGE_DEPTH_MAXIMUM) >> NUI_IMAGE_PLAYER_INDEX_SHIFT; ZeroMemory(m_depthColorTable, sizeof(m_depthColorTable)); // Set color for unknown depth m_depthColorTable[0][UNKNOWN_DEPTH] = UNKNOWN_DEPTH_COLOR; switch (m_depthTreatment) { case CLAMP_UNRELIABLE_DEPTHS: // Fill in the "near" portion of the table with solid color for (int depth = UNKNOWN_DEPTH + 1; depth < minReliableDepth; depth++) { m_depthColorTable[0][depth] = TOO_NEAR_COLOR; } // Fill in the "far" portion of the table with solid color for (int depth = maxReliableDepth + 1; depth <= USHRT_MAX; depth++) { m_depthColorTable[0][depth] = TOO_FAR_COLOR; } break; case TINT_UNRELIABLE_DEPTHS: { // Fill in the "near" portion of the table with a tinted gradient for (int depth = UNKNOWN_DEPTH + 1; depth < minReliableDepth; depth++) { BYTE intensity = GetIntensity(depth); BYTE r = intensity >> 3; BYTE g = intensity >> 1; BYTE b = intensity; SetColor(&m_depthColorTable[0][depth], r, g, b); } // Fill in the "far" portion of the table with a tinted gradient for (int depth = maxReliableDepth + 1; depth <= USHRT_MAX; depth++) { BYTE BYTE BYTE BYTE intensity = GetIntensity(depth); r = intensity; g = intensity >> 3; b = intensity >> 1; 128 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente SetColor(&m_depthColorTable[0][depth], r, g, b); } } break; case DISPLAY_ALL_DEPTHS: minReliableDepth = MIN_DEPTH; maxReliableDepth = MAX_DEPTH; for (int depth = UNKNOWN_DEPTH + 1; depth < minReliableDepth; depth++) { m_depthColorTable[0][depth] = NEAREST_COLOR; } break; default: break; } for (USHORT depth = minReliableDepth; depth <= maxReliableDepth; depth++) { BYTE intensity = GetIntensity(depth); for (int index = 0; index <= MAX_PLAYER_INDEX; index++) { BYTE r = intensity >> m_intensityShiftR[index]; BYTE g = intensity >> m_intensityShiftG[index]; BYTE b = intensity >> m_intensityShiftB[index]; SetColor(&m_depthColorTable[index][depth], r, g, b); } } } /// <summary> /// Set color value /// </summary> /// <param name="pColor">The pointer to the variable to be set with color</param> /// <param name="red">Red component of the color</param> /// <param name="green">Green component of the color</parma> /// <param name="blue">Blue component of the color</param> /// <param name="alpha">Alpha component of the color</param> void NuiImageBuffer::SetColor(UINT* pColor, BYTE red, BYTE green, BYTE blue, BYTE alpha) { if (!pColor) return; BYTE* c = (BYTE*)pColor; c[COLOR_INDEX_RED] = red; c[COLOR_INDEX_GREEN] = green; c[COLOR_INDEX_BLUE] = blue; c[COLOR_INDEX_ALPHA] = alpha; } 129 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente /// <summary> /// Calculate intensity of a certain depth /// </summary> /// <param name="depth">A certain depth</param> /// <returns>Intensity calculated from a certain depth</returns> BYTE NuiImageBuffer::GetIntensity(int depth) { // Validate arguments if (depth < MIN_DEPTH || depth > MAX_DEPTH) { return UCHAR_MAX; } // Use a logarithmic scale that shows more detail for nearer depths. // The constants in this formula were chosen such that values between // MIN_DEPTH and MAX_DEPTH will map to the full range of possible // byte values. const float depthRangeScale = 500.0f; const int intensityRangeScale = 74; return (BYTE)(~(BYTE)min( UCHAR_MAX, log((double)(depth - MIN_DEPTH) / depthRangeScale + 1) * intensityRangeScale)); } /// <summary> ///Stacks an image in the Mat format /// </summary> void Mat_Image(Mat pImage,int unoo2) { for (int rw = 0; rw <479; rw++){ for (int cl = 0; cl < 639; cl++){ Mat paso_punto(1, 1, CV_8UC3, Scalar(LUT_B_matrix[639cl][rw], LUT_G_matrix[639-cl][rw], LUT_R_matrix[639-cl][rw])); paso_punto.copyTo(pImage.row(rw).col(cl));//para que funcione if (unoo2==1){ LUT_1RGB[cl][rw][0] = LUT_R_matrix[cl][rw]; LUT_1RGB[cl][rw][1] = LUT_G_matrix[cl][rw]; LUT_1RGB[cl][rw][2] = LUT_B_matrix[cl][rw]; } else{ LUT_2RGB[cl][rw][0] = LUT_R_matrix[cl][rw]; LUT_2RGB[cl][rw][1] = LUT_G_matrix[cl][rw]; LUT_2RGB[cl][rw][2] = LUT_B_matrix[cl][rw]; } } } } void initGL() { //////OPENGL //printf("alpha \n"); 130 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente // OpenGL setup glClearColor(0, 0, 0, 1);// Set background color to black and opaque glClearDepth(1); // Set background depth to farthest glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// aparece en display ///////// // Global variables: GLuint vboId; // Vertex buffer ID GLuint cboId; // Color buffer ID glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45, width / (GLdouble)height, 0.1, 1000); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0, 0, 0, 0, 0, 1, 0, 1, 0); } void display() { ////OPENGL ///matrix creation //int D = depth_M[314][239]; ///std::cout << D; ///La primera coorodenada els la del eje de coordenadas (horizontal) la 2 es ordenadas (altura) y la ultima es profundidad int D = 800; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear color and depth buffers glMatrixMode(GL_MODELVIEW); // To operate on model-view matrix glLoadIdentity(); glTranslatef(0, 0, -1 * D); //lo muevo a dicho punto como centro, va a girar respecto ahi glRotatef(angleCube, 0, 1, 0); glPointSize(1);// le he puesto mucho tamaño D = 500;//ajustar esto LUT_depth_matrixPAR[314][239]; glBegin(GL_POINTS); for (int i = 0; i < numero;i++){ if (Mat_Omega[i][3] != 0){ glColor3f(Mat_Omega[i][4] / 255.f, Mat_Omega[i][5] / 255.f, Mat_Omega[i][6] / 255.f); 131 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente glVertex3f(Mat_Omega[i][1] / 3 / reescalado, Mat_Omega[i][2] / 3 / reescalado, Mat_Omega[i][3] / 3 / reescalado); } } int int int int int Dii = sqrt(50); Diii = sqrt(51); Di = sqrt(51); Dv = sqrt(66); Div = sqrt(49); int w = 640 / 3; //parece que no entra tan grande int h = 480 / 3; /* // I glColor3f(1, 0, 1); // Purple glVertex3f(-w / 2, h / 2, w);// D - 7); // II glColor3f(1, glVertex3f(w */ // III glColor3f(0, glVertex3f(w 0, 0); // Red / 2, h / 2, w); 255, 255); // blue / 2, -h / 2, -w); // IV glColor3f(0, 1, 0); // Green glVertex3f(-w / 2, -h / 2, -w); // V glColor3f(255, 51, 0); glVertex3f(0, 0, D - D); // White glEnd(); // End of drawing /* glBegin(GL_QUADS); glColor3f(255, 255, 255); // White glVertex3f(w / 2, h / 2, -w); glVertex3f(-w / 2, h / 2, -w); glVertex3f(-w / 2, h / 2, w); glVertex3f(w / 2, h / 2, w); glEnd(); glLineWidth(4); glColor3f(1.0, 0.0, 0.0); glBegin(GL_LINES); for (int i = 0; i < cuenta2; i++){ 132 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente glVertex3f(centros[cuenta2][0], centros[cuenta2][0], centros[cuenta2][0]); glVertex3f(30+5*i, 5*i, 30); } glEnd(); */ glutSwapBuffers(); angleCube -= 0.2; } void timer(int value) { glutPostRedisplay(); glutTimerFunc(refreshMills, timer, 0); } void reshape(GLsizei width, GLsizei height) { if (height == 0) height = 1; GLfloat aspect = (GLfloat)width / (GLfloat)height; glViewport(0, 0, width, height); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // reset gluPerspective(45.0f, aspect, 0.1f, 100.0f); } void keyboard(unsigned char key, int x, int y) { switch (key) { case 'p': case 'P': reescalado++; break; case 'o': case 'O': reescalado = 1; break; case 'i': case 'I': reescalado = reescalado/2; break; case 27: // escape exit(0); 133 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente break; } } void OpenGL_3D(int argc){ glutInitDisplayMode(GLUT_DOUBLE); glutInitWindowSize(1280, 960); glutInitWindowPosition(50, 50); gluPerspective(45, width / (GLdouble)height, 0.1, 1000); glutCreateWindow(title); glutDisplayFunc(display); glutReshapeFunc(reshape); initGL(); glutTimerFunc(0, timer, 0); glutKeyboardFunc(keyboard); glutMainLoop(); } void StichMix(Mat image1, Mat image2){ vector< Mat > vImg; vImg.push_back(image1); vImg.push_back(image2);//si añado mas, se pueden juntar mas Stitcher stitcher = Stitcher::createDefault(); Stitcher::Status status = stitcher.stitch(vImg, Stitch); //imshow("Stitching Result", result); // int nRows = result.rows; // int nCols = result.cols; // printf("Found %d Cols.\n", nCols); } ///// void fcn_Square(Mat queryImg_N, Mat trainImg_N, int BaseorNew){ SiftFeatureDetector detector(400); vector<KeyPoint> queryKeypoints_N, trainKeypoints_N; detector.detect(queryImg_N, queryKeypoints_N); detector.detect(trainImg_N, trainKeypoints_N); 134 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente SiftDescriptorExtractor extractor; Mat queryDescriptors_N, trainDescriptors_N; extractor.compute(queryImg_N, queryKeypoints_N, queryDescriptors_N); extractor.compute(trainImg_N, trainKeypoints_N, trainDescriptors_N); Size size = queryDescriptors_N.size(); size = trainDescriptors_N.size(); BFMatcher matcher(NORM_L2); vector<DMatch> matches_N; matcher.match(queryDescriptors_N, trainDescriptors_N, matches_N); printf("Found %d matches.\n", matches_N.size()); //namedWindow("matches", 1); Mat img_matches_N; drawMatches(queryImg_N, queryKeypoints_N, trainImg_N, trainKeypoints_N, matches_N, img_matches_N); // imshow("matches", trainImg); ////// vector< DMatch > good_matches; double min_dist = 100; for (int i = 0; i < queryDescriptors_N.rows; i++) { if (matches_N[i].distance < 3 * min_dist) { good_matches.push_back(matches_N[i]); } } //Localiza vector<Point2f> obj_N; vector<Point2f> scene_N; for (int i = 0; i < good_matches.size(); i++) { //obtiene keypoints obj_N.push_back(queryKeypoints_N[good_matches[i].queryIdx].pt); scene_N.push_back(trainKeypoints_N[good_matches[i].trainIdx].pt); } Mat H = findHomography(obj_N, scene_N, CV_RANSAC); 135 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente //obtiene esqunas vector<Point2f> obj_corners(4); obj_corners[0] = cvPoint(0, 0); obj_corners[1] = cvPoint(queryImg_N.cols, 0); obj_corners[2] = cvPoint(queryImg_N.cols, queryImg_N.rows); obj_corners[3] = cvPoint(0, queryImg_N.rows); vector<Point2f> scene_corners(4); perspectiveTransform(obj_corners, scene_corners, H); //dibujar lineas line(img_matches_N, scene_corners[0] scene_corners[1] + Point2f(queryImg_N.cols, line(img_matches_N, scene_corners[1] scene_corners[2] + Point2f(queryImg_N.cols, line(img_matches_N, scene_corners[2] scene_corners[3] + Point2f(queryImg_N.cols, line(img_matches_N, scene_corners[3] scene_corners[0] + Point2f(queryImg_N.cols, int int int int int int int int X_coordinate0 Y_coordinate0 X_coordinate1 Y_coordinate1 X_coordinate2 Y_coordinate2 X_coordinate3 Y_coordinate3 = = = = = = = = + Point2f(queryImg_N.cols, 0), Scalar(0, 255, 0), 4); + Point2f(queryImg_N.cols, 0), Scalar(0, 255, 0), 4); + Point2f(queryImg_N.cols, 0), Scalar(0, 255, 0), 4); + Point2f(queryImg_N.cols, 0), Scalar(0, 255, 0), 4); scene_corners[0].x; scene_corners[0].y; scene_corners[1].x; scene_corners[1].y; scene_corners[2].x; scene_corners[2].y; scene_corners[3].x; scene_corners[3].y; if (BaseorNew==1){ vecX_New[0] = X_coordinate0; vecY_New[0] = Y_coordinate0; vecX_New[1] = X_coordinate1; vecY_New[1] = Y_coordinate1; vecX_New[2] = X_coordinate2; vecY_New[2] = Y_coordinate2; vecX_New[3] = X_coordinate3; vecY_New[3] = Y_coordinate3; //OutputDebugStringA("gammaaaAAAA \n\n\n\n"); }else{ vecX_Base[0] = X_coordinate0; vecY_Base[0] = Y_coordinate0; vecX_Base[1] = X_coordinate1; vecY_Base[1] = Y_coordinate1; vecX_Base[2] = X_coordinate2; vecY_Base[2] = Y_coordinate2; vecX_Base[3] = X_coordinate3; vecY_Base[3] = Y_coordinate3; } 136 0), 0), 0), 0), UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente //namedWindow("MyWindow", 1);// CV_WINDOW_AUTOSIZE); //create a window with the name "MyWindow" //imshow("MyWindow", queryImg); //display the image which is stored in the 'img' in the "MyWindow" window //imshow("matches_N", img_matches_N); // imshow("matches22_N", trainImg_N); } void intersect_central(float vecX[], float vecY[], float select){ long float alfaB_up = (vecY[0] - vecY[2]);///(29- 410) / (108 - 495); long float alfaB_dw = (vecX[0] - vecX[2]);//(vecY_Base[0] vecY_Base[2]) / (vecX_Base[0] - vecX_Base[2]); long float alfaB = alfaB_up / alfaB_dw; long float betaB = vecY[0] - alfaB*vecX[0]; ///////// ecuacion de los otros puntos long float gamaB_up = (vecY[1] - vecY[3]); long float gamaB_dw = (vecX[1] - vecX[3]); long float gamaB = gamaB_up / gamaB_dw; long float sigmB = vecY[1] - gamaB*vecX[1]; /// juto los dos long float x_up = -(betaB - sigmB); long float x_dw = alfaB - gamaB; long float x_central = x_up / x_dw; long float y_central = alfaB*x_central + betaB; if (select == 0){ ///es base X_Base Y_Base X_Base Y_Base = = = = 0; 0; x_central; y_central; } else{ X_New Y_New X_New Y_New = = = = 0; 0; x_central; y_central; } } /// <summary> /// Copy color frame image to image buffer /// </summary> /// <param name="pImage">The pointer to the frame image to copy</param> /// <param name="size">Size in bytes to copy</param> void NuiImageBuffer::CopyRGB(const BYTE* pImage, UINT size){ //OutputDebugStringA("\nprueba RGB\n"); // Check source buffer size //OutputDebugStringA("\n alpha \n"); if (size != m_srcWidth * m_srcHeight * BYTES_PER_PIXEL_RGB){ 137 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente return; } // Set image size to source image size m_width = m_srcWidth; m_height = m_srcHeight; // Allocate buffer for image UINT* pBuffer = (UINT*)ResetBuffer(m_width * m_height * BYTES_PER_PIXEL_RGB); //////aatatata int p = 0; int dpt = 0; int p2 = 0; // loop over each row and column of the color for (LONG y = 0; y < m_height; ++y){ for (LONG x = 0; x < m_width; ++x){ if (n_image == 1){/// porque se actualiza despues, va cambiado LUT_B_matrixIMPAR[x][y] = m_pBuffer[p]; LUT_B_matrix[x][y] = m_pBuffer[p]; p++; LUT_G_matrixIMPAR[x][y] = m_pBuffer[p]; LUT_G_matrix[x][y] = m_pBuffer[p]; p++; LUT_R_matrixIMPAR[x][y] = m_pBuffer[p]; LUT_R_matrix[x][y] = m_pBuffer[p]; p++; p++; } else{ //// creacion de matriz LUT_B_matrixPAR[x][y] = m_pBuffer[p]; LUT_B_matrix[x][y] = m_pBuffer[p]; p++; LUT_G_matrixPAR[x][y] = m_pBuffer[p]; LUT_G_matrix[x][y] = m_pBuffer[p]; p++; LUT_R_matrixPAR[x][y] = m_pBuffer[p]; LUT_R_matrix[x][y] = m_pBuffer[p]; p++; p++; } // Mat paso_punto(1, 1, CV_8UC3, Scalar(RGB_array[p], RGB_array[p+1], RGB_array[p+2])); // paso_punto.copyTo(LUT_RGB.row(639-x).col(y)); } } ///// Copy source image to buffer memcpy_s(m_pBuffer, m_nSizeInBytes, pImage, size); 138 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente /* char sz15[255]; sprintf_s(sz15, " R: %d ", LUT_R_matrix[300][240]); // m_depthColorTable[300][240]); OutputDebugStringA(sz15); sprintf_s(sz15, " G: %d ", LUT_G_matrix[300][240]); // m_depthColorTable[300][240]); OutputDebugStringA(sz15); sprintf_s(sz15, " B: %d ", LUT_B_matrix[300][240]); // m_depthColorTable[300][240]); OutputDebugStringA(sz15); */ /////// ///////////// char sz152[255]; variableauxiliarRGB = variableauxiliarRGB + 10; sprintf_s(sz152, " RGB=VarAuxDepth: %d ", variableauxiliarDep); // m_depthColorTable[300][240]); OutputDebugStringA(sz152); sprintf_s(sz152, " ((RGB: %d)) \n", variableauxiliarRGB); // m_depthColorTable[300][240]); OutputDebugStringA(sz152); } /// <summary> /// Copy raw bayer data and convert to RGB image /// </summary> /// <param name="pImage">The pointer to the frame image to copy</param> /// <param name="size">Size in bytes to copy</param> void NuiImageBuffer::CopyBayer(const BYTE* pImage, UINT size) { // Check source buffer size if (size != m_srcWidth * m_srcHeight * BYTES_PER_PIXEL_BAYER) { return; } // Check if source image size can mod by 2 if (0 != m_srcWidth % 2 || 0 != m_srcHeight % 2) { return; } // Converted image size is equal to source image size m_width = m_srcWidth; m_height = m_srcHeight; // Allocate buffer for image UINT* pBuffer = (UINT*)ResetBuffer(m_width * m_height * BYTES_PER_PIXEL_RGB); ///OutputDebugStringA("\nprueba Bayer\nBayer\n Bayer\n QUE SI, BAYER\n"); // Run through pixels for (DWORD y = 0; y < m_srcHeight; y += 2) 139 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente { for (DWORD x = 0; x < m_srcWidth; x += 2) { int firstRowOffset = (y * m_srcWidth) + x; int secondRowOffset = firstRowOffset + m_srcWidth; // _____ // Get bayer colors from source image // | | | BYTE r = pImage[firstRowOffset + 1]; // |g1|r | BYTE g1 = pImage[firstRowOffset]; // |--|--| BYTE g2 = pImage[secondRowOffset + 1]; // |b |g2| BYTE b = pImage[secondRowOffset]; // |__|__| // Set color to buffered pixel SetColor(pBuffer + firstRowOffset, r, g1, SetColor(pBuffer + firstRowOffset + 1, r, g1, SetColor(pBuffer + secondRowOffset, r, g2, SetColor(pBuffer + secondRowOffset + 1, r, g2, } } } b); b); b); b); /// <summary> /// Copy and convert infrared frame image to image buffer /// </summary> /// <param name="pImage">The pointer to the frame image to copy</param> /// <param name="size">Size in bytes to copy</param> void NuiImageBuffer::CopyInfrared(const BYTE* pImage, UINT size) { // Check source buffer size if (size != m_srcWidth * m_srcHeight * BYTES_PER_PIXEL_INFRARED) { return; } //OutputDebugStringA("\nprueba Infarred\n"); // Converted image size is equal to source image size m_width = m_srcWidth; m_height = m_srcHeight; // Allocate buffer for image UINT* pBuffer = (UINT*)ResetBuffer(m_width * m_height * BYTES_PER_PIXEL_RGB); // Initialize pixel pointers USHORT* pPixelRun = (USHORT*)pImage; USHORT* pPixelEnd = pPixelRun + size / BYTES_PER_PIXEL_INFRARED; // Run through pixels while (pPixelRun < pPixelEnd) { // Convert pixel from 16-bit to 8-bit intensity BYTE intensity = (*pPixelRun) >> 8; // Set pixel color with R, G and B components all equal to intensity SetColor(pBuffer, intensity, intensity, intensity); 140 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente // Move to next pixel ++pPixelRun; ++pBuffer; } } int inter_recta(int r1, int r2, int w_X, int w_Y){ int rtr = 0; if (w_Y >= vecY_Base[r2]){ ///la de abajo if (w_Y <= vecY_Base[r1]){ ///la de arriba ///recta float C_X = 0; float alpha1 = 0; if (vecX_Base[r1] != vecX_Base[r2]){ alpha1 = (vecY_Base[r1] - vecY_Base[r2]) / (vecX_Base[r1] - vecX_Base[r2]); } char szb[255]; sprintf_s(szb, "\n alpha: %f ", alpha1); //OutputDebugStringA(szb); float Beta1 = vecY_Base[r1] - alpha1*vecX_Base[r1]; sprintf_s(szb, "\n betab: %f ", Beta1); //OutputDebugStringA(szb); if (alpha1!=0){ C_X = (w_Y - Beta1) / alpha1; } else{ C_X = vecX_Base[r1]; } sprintf_s(szb, "\n CX: %f ", C_X); //OutputDebugStringA(szb); if (w_X <= C_X){ rtr = 1; ///intersecciona } } } if (w_Y <= vecY_Base[r2]){ ///la de abajo if (w_Y >= vecY_Base[r1]){ ///la de arriba ///recta float C_X = 0; float alpha1 = 0; if (vecX_Base[r1] != vecX_Base[r2]){ alpha1 = (vecY_Base[r1] - vecY_Base[r2]) / (vecX_Base[r1] - vecX_Base[r2]); } char szb[255]; sprintf_s(szb, "\n X2: %f ", vecX_Base[r1]); //OutputDebugStringA(szb); 141 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente sprintf_s(szb, "\n X3: %f ", vecX_Base[r2]); //OutputDebugStringA(szb); sprintf_s(szb, "\n alphaDIF: %f ", alpha1); //OutputDebugStringA(szb); float Beta1 = vecY_Base[r1] - alpha1*vecX_Base[r1]; sprintf_s(szb, "\n betaDIF: %f ", Beta1); //OutputDebugStringA(szb); if (alpha1 != 0){ C_X = (w_Y - Beta1) / alpha1; } else{ C_X = vecX_Base[r1]; } if (w_X <= C_X){ rtr = 1; ///intersecciona } } } return rtr; } int inter_rectaDos(int r1, int r2, int w_X, int w_Y){ int rtr = 0; if (w_Y >= vecY_New[r2]){ ///la de abajo if (w_Y <= vecY_New[r1]){ ///la de arriba ///recta float C_X = 0; float alpha1 = 0; if (vecX_New[r1] != vecX_New[r2]){ alpha1 = (vecY_New[r1] - vecY_New[r2]) / (vecX_New[r1] - vecX_New[r2]); } float Beta1 = vecY_New[r1] - alpha1*vecX_New[r1]; if (alpha1 != 0){ C_X = (w_Y - Beta1) / alpha1; } else{ C_X = vecX_Base[r1]; } if (w_X <= C_X){ rtr = 1; ///intersecciona } } } if (w_Y <= vecY_New[r2]){ ///la de abajo if (w_Y >= vecY_New[r1]){ ///la de arriba ///recta float C_X = 0; float alpha1 = 0; 142 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente if (vecX_New[r1] != vecX_New[r2]){ alpha1 = (vecX_New[r1] - vecX_New[r2]); } (vecY_New[r1] - vecY_New[r2]) / float Beta1 = vecY_New[r1] - alpha1*vecX_New[r1]; if (alpha1 != 0){ C_X = (w_Y - Beta1) / alpha1; } else{ C_X = vecX_New[r1]; } if (w_X <= C_X){ rtr = 1; ///intersecciona } } } return rtr; } int intersec_Punto(float w_X1, float w_Y1){ int rr = 0; /// wx, primera recta con 0-3 int primera = inter_recta(0,3,w_X1,w_Y1);///primero la de arriba, despues abajo, y las coordenadas del punto //OutputDebugStringA("priemro escanado\n"); /// segunda 1-2 int segunda = inter_recta(1, 2, w_X1, w_Y1); //OutputDebugStringA("segundo escanado\n"); ///tercera 01 int tercera = inter_recta(0, 1, w_X1, w_Y1); //OutputDebugStringA("tercero escanado\n"); // ///cuarta 32 int cuarta = inter_recta(3, 2, w_X1, w_Y1); // char sza[255]; // sprintf_s(sza, "\n awi p1: %d ,p2: %d,p3: %d,p4: %d \n ", primera,segunda, tercera,cuarta); // OutputDebugStringA(sza); if (primera+segunda+tercera+cuarta==1){ rr = 1; } return rr; //// si dentro o si fuera } int intersec_PuntoDos(float w_X1, float w_Y1){ int rr = 0; /// wx, primera recta con 0-3 int primera = inter_rectaDos(0, 3, w_X1, w_Y1);///primero la de arriba, despues abajo, y las coordenadas del punto 143 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente /// segunda 1-2 int segunda = inter_rectaDos(1, 2, w_X1, w_Y1); ///tercera 01 int tercera = inter_rectaDos(0, 1, w_X1, w_Y1); // ///cuarta 32 int cuarta = inter_rectaDos(3, 2, w_X1, w_Y1); if (primera + segunda + tercera + cuarta == 1){ rr = 1; } return rr; } void Cuatro_Puntos(int x_1, int x_2, int x_3, int x_4 ){ ///////////////// tengo las 4 aristas en vecX_Base, vecY_Base y vecX_New , vecY_New ///esta el priemr punto dentro? int punto_1 = intersec_Punto(vecX_New[0],vecY_New[0]); ///esta el segundo? int punto_2 = intersec_Punto(vecX_New[1], vecY_New[1]); /// y el tercero int punto_3 = intersec_Punto(vecX_New[2], vecY_New[2]); ///y el 4? int punto_4 = intersec_Punto(vecX_New[3], vecY_New[3]); ///balance if (punto_1==1){ x_1 = 1; }else{ x_1 = 0; } if (punto_2 == 1){ x_2 = 1; }else{ x_2 = 0; } if (punto_3 == 1){ x_3 = 1; }else{ x_3 = 0; } if (punto_4 == 1){ x_4 = 1; }else{ x_4 = 0; } ///////////////// ////se continua con los demas puntos } void Cuatro_PuntosDos(int x_1, int x_2, int x_3, int x_4){ ///////////////// tengo las 4 aristas en vecX_Base, vecY_Base y vecX_New , vecY_New 144 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente int punto_1 = intersec_PuntoDos(vecX_Base[0], vecY_Base[0]); ///esta el segundo? int punto_2 = intersec_PuntoDos(vecX_Base[1], vecY_Base[1]); /// y el tercero int punto_3 = intersec_PuntoDos(vecX_New[2], vecY_Base[2]); ///y el 4? int punto_4 = intersec_PuntoDos(vecX_Base[3], vecY_Base[3]); ///balance if (punto_1 == 1){ x_1 = 1; } else{ x_1 = 0; } if (punto_2 == 1){ x_2 = 1; }else{ x_2 = 0; } if (punto_3 == 1){ x_3 = 1; }else{ x_3 = 0; } if (punto_4 == 1){ x_4 = 1; }else{ x_4 = 0; } ///////////////// ////se continua con los demas puntos } ////obtener giro, rotacion y diferencia de profunidad void Cambio_Imagenes(float giroY,float giroX, int Z){ ///////////////// tengo las 4 aristas en vecX_Base, vecY_Base y vecX_New , vecY_New ////los puntos centrales X_Base Y_Base X_New Y_New ////int giro, int giro 2, int Z_prof int Cuatro[4]; int CuatroDos[4]; ///obtener los 4 puntos Cuatro_Puntos(Cuatro[0], Cuatro[1], Cuatro[2], Cuatro[3]); //Busca puntos exixtentes en el fotograma nuevo Cuatro_PuntosDos(CuatroDos[0], CuatroDos[1], CuatroDos[2],CuatroDos[3]);//busca en el fotograma Base ///ahora ya se cuales son los puntos comunes //obtener giros y prof int punto_FotNew[2]; int punto_FotBase[2];///como generlamente solo suele haber un punto, cojo el primero de cada int Prof_New_Base[2];///priemro New, despues Base for (int i = 0; i < 3; i++){ 145 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente if (CuatroDos[i] == 1){ punto_FotBase[0] = vecX_Base[i]; punto_FotBase[1] = vecY_Base[i]; } if (CuatroDos[i]==1){ punto_FotNew[0] = vecX_New[i]; punto_FotNew[1] = vecY_New[i]; } } ///localizacion punto //punto base if (n_image==1){ int x = punto_FotNew[0] - X_Base;///calculo las diferencias int y = punto_FotNew[1] - Y_Base; Prof_New_Base[0] = LUT_depth_matrixPAR[x][y];///tengo la profundidad int x2 = punto_FotBase[0] - X_New;///calculo las diferencias int y2 = punto_FotBase[1] - Y_New; Prof_New_Base[1] = LUT_depth_matrixPAR[x2][y2];///tengo la profundidad } else{ int x = punto_FotNew[0] - X_Base;///calculo las diferencias int y = punto_FotNew[1] - Y_Base; Prof_New_Base[0] = LUT_depth_matrixIMPAR[x][y]; int x2 = punto_FotBase[0] - X_New;///calculo las diferencias int y2 = punto_FotBase[1] - Y_New; Prof_New_Base[1] = LUT_depth_matrixIMPAR[x2][y2];///tengo la profundidad } giroY = atan((Prof_New_Base[0] - Prof_New_Base[0]) / (X_Base X_New));///dan muchos problemas giroX = atan((Prof_New_Base[0] - Prof_New_Base[0]) / (Y_Base Y_New));///dan muchos problemas Z = Prof_New_Base[1] - Prof_New_Base[0]; } /// <summary> /// Copy and convert depth frame image to image buffer /// </summary> /// <param name="pImage">The pointer to the frame image to copy</param> /// <param name="size">Size in bytes to copy</param> /// <param name="nearMode">Depth stream range mode</param> /// <param name="treatment">Depth treatment mode</param> void NuiImageBuffer::CopyDepth(const BYTE* pImage, UINT size, BOOL nearMode, DEPTH_TREATMENT treatment) { frame_complete = 0; // Check source buffer size if (size != m_srcWidth * m_srcHeight * BYTES_PER_PIXEL_DEPTH) { return; } 146 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente // Check if range mode and depth treatment have been changed. Reinitlialize depth-color table with changed parameters if (m_nearMode != (FALSE != nearMode) || m_depthTreatment != treatment) { m_nearMode = (FALSE != nearMode); m_depthTreatment = treatment; InitDepthColorTable(); } // Converted image size is equal to source image size m_width = m_srcWidth; m_height = m_srcHeight; // Allocate buffer for color image. If required buffer size hasn't changed, the previously allocated buffer is returned UINT* rgbrun = (UINT*)ResetBuffer(m_width * m_height * BYTES_PER_PIXEL_RGB); // Initialize pixel pointers to start and end of image buffer NUI_DEPTH_IMAGE_PIXEL* pPixelRun = (NUI_DEPTH_IMAGE_PIXEL*)pImage; NUI_DEPTH_IMAGE_PIXEL* pPixelEnd = pPixelRun + m_srcWidth;// *m_srcHeight; ////relleno la matriz int p = 0; int l; int inv_d[640]; int inv_I[640]; for (int k = 0; k <= 479; k++){ l = 0; for (l = 0; l <= 639; l++){ USHORT depth = pPixelRun->depth; /// este es el valor USHORT index = pPixelRun->playerIndex; //int depth = pBufferRun->depth; //saca la profundidad del sitio inv_d[l] = depth; inv_I[l]=index; pPixelRun++; } for (int h = 0; h <= 639; h++){ LUT_depth[frame_complete] = inv_d[639 - h]; LUT_index[frame_complete] = inv_I[639 - h]; ++rgbrun; *rgbrun = m_depthColorTable[LUT_index[frame_complete]][LUT_depth[frame_complete]]; //LUT_depth_matrix[h][k] = LUT_depth[p]; if (n_image == 1){ ///porque se actualiza despues, va cambiado LUT_depth_matrixIMPAR[h][k] = LUT_depth[p] * 255 / 1250; }else{ 147 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente LUT_depth_matrixPAR[h][k] = LUT_depth[p] * 255 / 1250; } if (n_image2 == 1){ LUT_depth_matrix2[h][k] = LUT_depth[p] * 255 / 1250; } p++; frame_complete++; } } variableauxiliarDep = variableauxiliarDep + 25; /// //// char sz15[255]; sprintf_s(sz15, "(( Depth: %d ))", variableauxiliarDep); // m_depthColorTable[300][240]); //OutputDebugStringA(sz15); sprintf_s(sz15, " Depth=VarAuxRGB: %d \n", variableauxiliarRGB); // m_depthColorTable[300][240]); //OutputDebugStringA(sz15); ///////////////////// ///////////////////////////////////////////////////////////////////// ////////////////////// ////toca sacar imagenes ///////////////////////////////////////////////////////////////////// ////////////////////// if (Inicio_SCAN == 0){ if (T_timer >= GO){ ///cuando el contador llega al momento fijado de inicio (GO es el numero de bucles) /* Son las matrices que voy a usar, fuera, porque si no se reinician Mat LUT_RGB(480, 640, CV_8UC3, Scalar(0, 0, 0)); Mat LUT_1(480, 640, CV_8UC3, Scalar(0, 0, 0)); Mat LUT_2(480, 640, CV_8UC3, Scalar(0, 0, 0)); //namedWindow("MyWindow", CV_WINDOW_AUTOSIZE); //create a window with the name "MyWindow" //imshow("MyWindow", LUT_1); //display the image which is stored in the 'img' in the "MyWindow" window //OutputDebugStringA("\n SCAN: ON\n"); */ n_image++;/// su valor inical es 0 if (n_image == 1){ OutputDebugStringA("PAR \n"); 148 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente ////PAR ////Imagen Base if (cuenta1 == 0){ OutputDebugStringA("\n Primer fotograma, el BASE \n"); Mat_Image(LUT_1,1); /// va a la funcion dada, que carga la imagen cuenta1 = 1; /// para no volver a pasar por aqui namedWindow("L1 first", CV_WINDOW_AUTOSIZE); imshow("L1 first", LUT_1); /////////// ///copio la primera imagen al vector requerido int vueltas = 0; for ( int k = 0; k < 480; k++){ for (int l = 0; l <= 640; l++, numero++){ Mat_Omega[numero][0] = numero; Mat_Omega[numero][1] = -l + 640 / 2;///x Mat_Omega[numero][2] = -k + 480 / 2;///y Mat_Omega[numero][3] = LUT_depth_matrixPAR[l][k];///z Mat_Omega[numero][4] = LUT_1RGB[640 - l][k][0];// LUT_R_matrixPAR[640 - l][k];///R Mat_Omega[numero][5] = LUT_1RGB[640 - l][k][1];//LUT_G_matrixPAR[640 - l][k];///G Mat_Omega[numero][6] = LUT_1RGB[640 - l][k][2];// LUT_B_matrixPAR[640 - l][k];///B No puedo poner PAR, porque se escaneo antes de eso } vueltas++; } numero=numero centros[0][1] centros[0][0] centros[0][2] - 480;///307200 = 0; = 0; = LUT_depth_matrixPAR[320][240]; //aqui se carga L1, pero no se autoriza OpenGL_3D(0); ///para saltar, se quita }else{ ////////// //cargo L1 y autorizo Mat_Image(LUT_1,1); mezcla = 1; } 149 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente } if (n_image == 2){ n_image = 0; OutputDebugStringA("IMPAR \n"); ////IMPAR ///Cargare L2 Mat_Image(LUT_2,2); mezcla = 1; } /////////////////////////////////////////////// ////////////////////////////////////////////// if (mezcla == 1){ ////juntar L1 y L2, en Pares, la Base es L2, en impares, L1 (el inicial no salta), a Stitch /* vecX_Base[] = { 0, 0, 0, 0 }; vecY_Base[] = { 0, 0, 0, 0 }; vecX_New[] = { 0, 0, 0, 0 }; vecY_New[] = { 0, 0, 0, 0 }; */ //// detectar si son la misma ///////////////////////////////////////////////////////////////////// //////// ////es necesario un evento que diga que si son la misma o han avanzado /////////////////////si no son la misma, comeinzo el proceso normal, StichMix(LUT_1, LUT_2); OutputDebugStringA("Stitch -> Complete \n"); if (cuenta2 == 0){ namedWindow("L2 first", CV_WINDOW_AUTOSIZE); //create a window with the name "MyWindow" imshow("L2 first", LUT_2); //display the image which is stored in the 'img' in the "MyWindow" window namedWindow("MyWindow1", CV_WINDOW_AUTOSIZE); //create a window with the name "MyWindow" imshow("MyWindow1", Stitch); //display the image which is stored in the 'img' in the "MyWindow" window } if (cuenta2 == 1){ //namedWindow("MyWindow3", CV_WINDOW_AUTOSIZE); //create a window with the name "MyWindow" //imshow("MyWindow3", Stitch); //display the image which is stored in the 'img' in the "MyWindow" window //OpenGL_3D(0); 150 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente } if (cuenta2 == 2){ //namedWindow("MyWindow2", CV_WINDOW_AUTOSIZE); //create a window with the name "MyWindow" //imshow("MyWindow2", Stitch); //display the image which is stored in the 'img' in the "MyWindow" window //namedWindow("L1 2", CV_WINDOW_AUTOSIZE); //create a window with the name "MyWindow" //imshow("L1 2", LUT_1); //display the image which is stored in the 'img' in the "MyWindow" window } OutputDebugStringA("JUNTAR \n"); if (n_image == 1){ //BASE L2 //es PAR //////// /// hago los recortes Mat Centro_Origen(LUT_2, Rect(140, 40, 400, 400)); ////chequear si los recortes son apropiados Mat Centro_Nueva(LUT_1, Rect(140, 40, 400, 400)); /////// //// los paso a escala de grises //cvtColor(Centro_Nueva, LUT_1, CV_RGB2GRAY); //cvtColor(Centro_Origen, LUT_2, CV_RGB2GRAY); //////// fcn_Square(Centro_Origen, Stitch, 0); ///nuevo es 1 /// OutputDebugStringA("test 12 PAR\n"); OutputDebugStringA("Base Complete \n"); fcn_Square(Centro_Nueva, Stitch,1); OutputDebugStringA("New Complete \n"); }else{ ///es IMPAR //////// /// hago los recortes Mat Centro_Origen(LUT_1, Rect(140, 40, 400, 400)); ////chequear si los recortes son apropiados Mat Centro_Nueva(LUT_2, Rect(140, 40, 400, 400)); /////// //// los paso a escala de grises //cvtColor(Centro_Nueva, LUT_2, CV_RGB2GRAY); 151 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente //cvtColor(Centro_Origen, LUT_1, CV_RGB2GRAY); //////// /// fcn_Square(Centro_Origen, Stitch, 0); /// OutputDebugStringA("test 12 IMPAR\n"); OutputDebugStringA("Base Complete \n"); fcn_Square(Centro_Nueva, Stitch, 1); OutputDebugStringA("New Complete \n"); } ////ahora saco los valores del punto central char sz152[255]; /// \n", vecX_Base[0], /// \n", vecX_Base[1], /// \n", vecX_Base[2], /// \n", vecX_Base[3], ///// sprintf_s(sz152, "0_Found Base vecY_Base[0]); OutputDebugStringA(sz152); sprintf_s(sz152, "1_Found Base vecY_Base[1]); OutputDebugStringA(sz152); sprintf_s(sz152, "2_Found Base vecY_Base[2]); OutputDebugStringA(sz152); sprintf_s(sz152, "3_Found Base vecY_Base[3]); OutputDebugStringA(sz152); X: %f and Y: %f X: %f and Y: %f X: %f and Y: %f X: %f and Y: %f ///////////////////// ///aqui saco los valores del punto central intersect_central(vecX_Base, vecY_Base, 0);/// 0 para Base intersect_central(vecX_New, vecY_New, 1); sprintf_s(sz152, "\n Centro x Base: %f Centro y Base: %f\n ", X_Base, Y_Base); OutputDebugStringA(sz152); sprintf_s(sz152, "\n Centro x New: %f Centro y New: %f \n ", X_New,Y_New); OutputDebugStringA(sz152); ///// las transformaciones van aqui ///////////////// tengo las 4 aristas en vecX_Base, vecY_Base y vecX_New , vecY_New ////los puntos centrales X_Base Y_Base X_New Y_New ///// float giro = 0;///sobre el eje vertical 152 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente float giro2 = 0;///sobre el eje horizontal int Z_prof = 0;////POSITIVO ES ALEJARLO DEL SENSOR Cambio_Imagenes(giro,giro2, Z_prof);////cuidado con las operaciones trigonometricas, da muchos errores float variacion = X_New - X_Base; centros[cuenta2 + 1][0] = centros[cuenta2][0] + X_New - X_Base; centros[cuenta2 + 1][1] = centros[cuenta2][1] + Y_New - Y_Base; centros[cuenta2 + 1][2] = centros[cuenta2][2] + Z_prof; /////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////// /// if (n_image == 1){ ////PAR, añado LUT_1 char sz152[255]; int vueltas = 0; for (int k = 0; k < 480; k++){ for (int l = 0; l <= 640; l++, numero++){ Mat_Omega[numero][0] = numero; Mat_Omega[numero][1] = -l + 640 / 2 + X_New - X_Base + centros[cuenta2][0] - (-l + 640 / 2)*(1 cos(giro));///x Mat_Omega[numero][2] = -k + 480 / 2 + Y_New - Y_Base + centros[cuenta2][1] - (-k + 480 / 2)*(1cos(giro2));///y Mat_Omega[numero][3] = centros[cuenta2][2] + Z_prof - (-l + 640 / 2)*sin(giro) - (-k + 480 / 2)*sin(giro2)+ LUT_depth_matrixPAR[l][k];///z if (LUT_depth_matrixPAR[l][k]==0){ Mat_Omega[numero][3] = 0; } 153 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente Mat_Omega[numero][4] = LUT_1RGB[640 - l][k][0];// LUT_R_matrixPAR[640 - l][k];///R Mat_Omega[numero][5] = LUT_1RGB[640 - l][k][1];//LUT_G_matrixPAR[640 - l][k];///G Mat_Omega[numero][6] = LUT_1RGB[640 - l][k][2];// LUT_B_matrixPAR[640 - l][k];///B No puedo poner PAR, porque se escaneo antes de eso } vueltas++; } numero = numero - 480;///307200 OpenGL_3D(0);///poner como comentario para saltar }else{ ///IMPAR, añado LUT_2 int vueltas = 0; for (int k = 0; k < 480; k++){ for (int l = 0; l <= 640; l++, numero++){ Mat_Omega[numero][0] = numero; Mat_Omega[numero][1] = -l + 640 / 2 + X_New - X_Base + centros[cuenta2][0] - (-l + 640 / 2)*(1 cos(giro));///x Mat_Omega[numero][2] = -k + 480 / 2 + Y_New - Y_Base + centros[cuenta2][1] - (-k + 480 / 2)*(1 cos(giro2));///y Mat_Omega[numero][3] = centros[cuenta2][2] + Z_prof - (-l + 640 / 2)*sin(giro) - (-k + 480 / 2)*sin(giro2) +LUT_depth_matrixPAR[l][k];///z if (LUT_depth_matrixPAR[l][k] == 0){ Mat_Omega[numero][3] = 0; } Mat_Omega[numero][4] = LUT_2RGB[640 - l][k][0];// LUT_R_matrixPAR[640 - l][k];///R Mat_Omega[numero][5] = LUT_2RGB[640 - l][k][1];//LUT_G_matrixPAR[640 - l][k];///G Mat_Omega[numero][6] = LUT_2RGB[640 - l][k][2];// LUT_B_matrixPAR[640 - l][k];///B No puedo poner PAR, porque se escaneo antes de eso } vueltas++; } numero = numero - 480;///307200 154 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente OpenGL_3D(0);///poner como comentario para saltar } cuenta2++;//indica el numero de mezcla por el que voy if (cuenta2 == 2){ OpenGL_3D(0); } } // destroyWindow("MyWindow"); } } if(T_timer < GO + 5){ T_timer++; } //////////////////////////////// //char sz15[255]; //sprintf_s(sz15, " pequeña: %d ", LUT_depth_matrix[320][240]); // m_depthColorTable[300][240]); //OutputDebugStringA(sz15); } 155 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente 156 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Código fuente Parte V MANUAL DEL USUARIO 157 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL 158 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Instalación de librerías Capítulo 1 INSTALACIÓN DE LIBRERÍAS En este capítulo se explican los pasos a seguir para instalar las librerías necesarias para que el programa funcione correctamente. El compilador usado en este proyecto es Microsoft Visual Studio Ultimate 2013, y la arquitectura del sistema es de 64 bits, y las instrucciones dadas son para estas características 1.1 INSTALACIÓN DE OPENCV El primer paso es descargar la librería de OpenCV [28], requerida para formar la panorámica y localizar los fotogramas base. Una vez descargada se preparan las variables de entorno, para ello, se accede a Mi PC, y se después a Propiedades, aquí, se selecciona Configuración Avanzada del Sistema, tal y como muestra la figura 83. Figura 83, Configuración Avanzada del Sistema Tras esto, se selecciona la opción de Variables de Entorno, (figura 84) 159 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Instalación de librerías Figura 84 variables de entorno Y entonces se crea una nueva variable, llamada OPENCV_DIR (el círculo verde en la figura 85), a la que hay que añadir la localización de los archivos descargados, después de esto, se edita la variable ya existente, Path (círculo rojo), y se le añade al final %OPENCV_DIR%\x64\vc13\bin, donde el 64 representa a la arquitectura del sistema (64 bits) y vc13 representa el tipo de compilador usado. 160 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Instalación de librerías Figura 85 Editando las variables de entorno El paso siguiente, ya en el compilador, requiere ir a la ventana de Propiedades, a la que se accede haciendo clic izquierdo en el nombre del proyecto, en la parte del Explorador de Soluciones, tal y como se muestra en la figura 85. 161 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Instalación de librerías Figura 86 Propiedades El siguiente paso es seleccionar Todas las Configuraciones, C/C++ y General (figura 87) y es necesario añadir en $(OPENCV_DIR)\include en la pestaña de Directorios de Inclusión Adicionales, tal y como se muestra en la figura 87 Figura 87 C/C++ General 162 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Instalación de librerías El paso siguiente es añadir %OPENCV_DIR%\x64\vc13\bin en el apartado de Vinculador, General, en Directorios de Bibliotecas Adicionales, como aparece en la figura 88 Figura 88 Directorios de Bibliotecas Adicionales Por último se añadirá en dependencias adicionales (Vinculador->Entrada>Dependencias Adicionales) los nombres de todos los archivos de las librerías (.lib) descargadas (presentes en la carpeta lib de OpenCV), tras aceptar los cambios, la librería OpenCV está instalada. 1.2 INSTALACION DE OPENGL La librería OpenGL es la encargada de realizar la representación tridimensional de la escena. Tras descargar los archivos de OpenGL en la pagina correspondiente [29], se procede a abrir el compilador, en la Pestaña de Propiedades, vista en el anterior apartado (figura 86), y en la opción de C/C++ y General, como aparece en la figura 89, se hace clic en Editar 163 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Instalación de librerías Figura 89 C/C++ Editar Aquí deberemos añadir el directorio de la carpeta llamada include, de los archivos descargados. Tras esto, en Vinculador, General, en Directorios de Bibliotecas Adicionales, añadiremos el directorio de la carpeta lib de los archivos de OpenGL Figura 90 Vinculador General Finalmente, de igual forma que en apartado anterior, es necesario añadir todos los nombres de todas las librerías, que se pueden encontrar en la carpeta lib. 164 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Funcionamiento del programa Capítulo 2 FUNCIONAMIENTO DEL PROGRAMA En este capítulo se detalla el funcionamiento básico del programa desarrollado. Una vez se ha conectado el sensor Kinect, y se ha detectado su presencia en el equipo, se puede proceder a compilar el programa, iniciando la depuración, como muestra la figura 91 Figura 91 Visual Studio Compilar La señal que marca el inicio del programa es la aparición de la ventana menú de Kinect Explorer D2D (apartado 2.2.2) (figura 92) 165 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Funcionamiento del programa Figura 92 Kinect Explorer D2D Tras unos instantes, se puede observar como el frame rate en la esquina superior derecha decae hasta valores muy pequeños (entre 0 y 3), esto indica que el programa está procesando la información, de datos normales o formando una panorámica, que como se comentó en el apartado 3.2.2, esto ralentiza demasiado el proceso para que pueda funcionar en tiempo real. A partir de la aparición de la ventana-menú (figura 92) ya se puede empezar a mover el sensor Kinect, sin embargo, hay que tener cuidado, ya que debido al largo tiempo que puede estar procesando el programa, no es posible conocer con exactitud cuando se escanea cada imagen, con lo que se podrían escanear dos fotogramas que la herramienta de Stitching no sería capaz de unir, resultado en un error de programa. Tras un cierto tiempo, suponiendo que el Stitching no haya fallado en la reconstrucción de las escenas, el programa llegará a la función llamada OpenGL_3D y mostrará la ventana con la representación 3D tal y como muestra la figura 93. 166 UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) INGENIERO INDUSTRIAL Funcionamiento del programa Figura 93 Representación 3D El programa está configurado para que solo represente 3 panorámicas, esto es debido a que tras ciertos experimentos (apartado 3.3.3) se considera el limite debido a la superposición de puntos, que empeoran visibilidad de la escena, tal y como fue comentado en el apartado 3.3.3. Es importante saber, que debido a los problemas explicados anteriormente, al tratar de representar las 3 escenas, es muy probable que el programa falle, por los motivos anteriormente mencionados; una forma de ir adaptando las representaciones es ir activando antes o después la función de OpenGL_3D, siempre que se hayan almacenado previamente valores en la matriz global. 167