Full text
Equation Chapter 1 Section 1 Proyecto Fin de Carrera Ingeniería de las Tecnologías de Telecomunicación Percepción de la acción en una imagen mediante algoritmos de aprendizaje automático Autor: Alejandro Navarro De La Cruz Tutor: Rubén Martín Clemente Dpto. Teoría de la Señal y Comunicaciones Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2022
iii Proyecto Fin de Carrera Ingeniería de Telecomunicación Percepción de la acción en una imagen mediante algoritmos de aprendizaje automático Autor: Alejandro Navarro De La Cruz Tutor: Rubén Martín Clemente Profesor titular Dpto. de Teoría de la Señal y Comunicaciones Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2022
v Proyecto Fin de Carrera: Percepción de la acción en una imagen mediante algoritmos de aprendizaje automático Autor: Alejandro Navarro De La Cruz Tutor: Rubén Martín Clemente El tribunal nombrado para juzgar el Proyecto arriba indicado, compuesto por los siguientes miembros: Presidente: Vocales: Secretario: Acuerdan otorgarle la calificación de: Sevilla, 2022
El Secretario del Tribunal
vii A mi familia A mis maestros
ix Agradecimientos Me gustaría agradecer a muchas personas, tanto por estos años de carrera como por estos meses de desarrollo del proyecto. A esos profesores de carrera que disfrutan transmitiendo sus conocimientos con paciencia y exigiendo a los alumnos, sois los que me habéis hecho aprender realmente y los que me habéis motivado a estar aquí. A mi tutor por proponer este tema y ayudarme a ampliar mi visión y mis oportunidades en un campo que antes ni me planteaba investigar y del que ahora quiero enfocar mi carrera. A Javier y Francisco por apoyarme y ayudarme a deducir soluciones durante este trabajo. A mis compañeros y amigos de carrera que, sin mayor motivo que el de ayudar a otros, generan un ambiente proactivo en el que enseñar y aprender entre nosotros. A mis amigos que durante estos años me han hecho crecer como persona. Y finalmente a mi padres, Pedro y Dolores, y a mi hermana, Nazaret, por creer en mí incluso más que yo mismo y brindarme todas las oportunidades, el apoyo y el cariño que me han sabido dar sin esperar nada a cambio. Alejandro Navarro De La Cruz Sevilla, 2022
ÍNDICE DE FIGURAS Figura 2-1. MATLAB [2] 4 Figura 2-3. C [4]. 5 Figura 2-4. Python [6]. 5 Figura 2-5. Visual Studio Code [8]. 6 Figura 2-6. OpenCV [9]. 6 Figura 2-7. Davinci Resolve [12]. 7 Figura 2-8. Gráfica de lenguajes de programación más demandados [13]. 7 Figura 2-9. Imagen de partido de tenis [14]. 8 Figura 2-10. Imagen de halterofilia [14]. 9 Figura 2-11. Imagen de tiro con arco [14]. 9 Figura 2-12. Diferencias de movimiento seguido [15]. 10 Figura 2-13. Representación 3D y 2D del flujo óptico [16]. 10 Figura 2-14. Ejemplo flujo de óptico obtenido con OpenCV [17]. 11 Figura 2-15. Representación de soluciones según los valores de lambda por el método de Lucas-Kanade [18]. 15 Figura 2-16. Representación de soluciones según los valores de lambda por el método de Shi-Tomasi [20]. 16 Figura 2-17. Imagen de partido de tenis emitido por RTVE [14] 17 Figura 2-18. Imagen diferencia obtenida tras procesarla. 17 Figura 2-19. Vista general de la interfaz desarrollada con Tkinter. 19 Figura 2-20. Vista del Slider en la interfaz desarrollada con Tkinter. 21 Figura 2-21. Vista de los Spinboxs en la interfaz desarrollada con Tkinter. 21 Figura 2-22. Vista de visualizar de imágenes en la interfaz desarrollada con Tkinter. 23 Figura 2-23. Vista de botón de abrir archivos en la interfaz desarrollada con Tkinter. 23 Figura 2-24. Vista de la sección dedicada a los parámetros del cálculo de esquinas en la interfaz desarrollada con
xvii Tkinter. 25 Figura 2-25. Vista del que calcula la diferencia de imágenes en la interfaz desarrollada con Tkinter. 27 Figura 2-26. Vista de la sección dedicada a los parámetros del flujo óptico en la interfaz desarrollada con Tkinter. 31 Figura 2-27. Resultado de flujo óptico. 31 Figura 3-1. Jupyter [27]. 34 Figura 3-3. Visual Studio Code [8]. 34 Figura 3-4. TensorFlow [29]. 35 Figura 3-5. Keras [30]. 35 Figura 3-6. Inteligencia artificial, aprendizaje automático y aprendizaje profundo [31]. 36 Figura 3-7. Estructura de red básica [25]. 37 Figura 3-8. Ejemplo de flujo óptico de tiro con arco. 40 Figura 3-9. Tipos de resultados [25]. 45 Figura 3-10. Matriz de confusión y resultados del modelo propio. 46 Figura 3-11. Matriz de confusión y resultados del modelo con MobileNetV2. 46 Figura 3-12. Matriz de confusión y resultados del modelo con VGG16. 46 Figura 3-13. Matriz de confusión y resultados del modelo con modelo basado en MNIST. 46 Figura 3-14. Resultados de scorings en el test de prueba con modelo propio. 47 Figura 3-15. Resultados de scorings en el test de prueba con MobileNetV2. 47 Figura 3-16. Resultados de scorings en el test de prueba con VGG16 48 Figura 3-17. Resultados de scorings en el test de prueba con modelo basado en MNIST. 48
xix
1 1 INSTRUCCIONES PARA CUBIERTA Y PORTADA l objetivo de este trabajo es crear algún método con el que plasmar en una imagen un movimiento o una acción. Con estas imágenes etiquetadas según lo que representaran sería con lo que luego entrenaríamos a la IA (Inteligencia Artificial) para que clasifique estos movimientos según la acción que represente. Normalmente se suelen separar estas partes Por un lado, existen empresas, departamentos o entidades que se dedican a la recogida de datos y por otro encontramos a aquellos que usan estos datos para crear redes neuronales. En el caso de este trabajo, se pretende realizar el proceso completo y obtener una visión general de ambas partes. Por lo tanto, se plantea varias cosas: • ¿Qué acciones o movimientos quiero plasmar? • ¿Cómo reflejar ese movimiento en una sola imagen? • ¿Qué herramientas usar? • ¿Cuál es el resultado que quiero obtener? • ¿Qué es el Deep Learning? • ¿Cómo consigo que una inteligencia artificial aprenda a diferenciar las acciones? Estas son preguntas que iré respondiendo poco a poco. E
Instrucciones para Cubierta y Portada 2
3 2 CREACIÓN DE DATASETS DE IMÁGENES ntes de introducirnos en el campo de Machine Learning debemos plantearnos: qué va a predecir la máquina, poner en foco cuál va a ser el estudio por realizar; qué datos nos harán falta, los cuáles serán la fuente con la que entrenar a un modelo; y cómo obtener estos datos, crear un dataset para pasar a la segunda parte. Sabemos que el objetivo final es determinar qué acción se está llevando a cabo, pero de alguna forma tendremos que caracterizar estas acciones. Los datos con los que entrenar a una inteligencia artificial son de vital importancia, sin ellos no hay nada que aprender y debemos seleccionar bien cuál será la información que tomará de entrada. Para los problemas de clasificación, en general, suelen existir datasets, bien creados por empresas o bien creados por la comunidad que gira entorno a desarrollos de investigación de estos ámbitos. No todos los bancos son de libre uso y gratuitos, al fin y al cabo, estamos hablando de información, un comercio muy explotado en nuestros días. Muchos de los recursos que podemos encontrar son de pago o difíciles de canalizar para una tarea tan concreta como la de este trabajo. De alguna forma, queremos capturar una acción, la que sea, y determinarla con rasgos que la hagan única o distintas a otras. 2.1. Análisis Empezaré realizando un análisis general. Lo primero será planificar las tareas e hitos desde una visión muy temprana del proyecto y sin profundizar en cada punto. Seguido a esto, comienzo a ejecutar el plan propuesto para alcanzar este primer objetivo: caracterizar acciones y crear datos con los que entrenar una inteligencia artificial. 2.1.1 Planificación Es buena práctica establecer las fases generales y el ciclo que llevaré para realizar esta parte. Seguir un hilo en el que ir avanzando. Lo primero es ver el estado al inicio, qué herramientas tengo y de qué conocimientos parto, conocer cuál será el punto de partida desde el que comenzar. Luego, en función de lo que haya visto en la fase anterior, plantear las necesidades suficientes como base para comenzar a desarrollar el proyecto: qué conocimientos adquirir para realizar un análisis en profundidad del problema y qué herramientas son necesarias en primera instancia. Antes de empezar con la fase de aprendizaje, se deben obtener las herramientas. Por ello, se hace un análisis y presentación de los instrumentos con los que iré trabajando. En cuanto al conocimiento necesario, pasaré a A
Creación de datasets de imágenes 4 recopilar fuentes fiables y a adquirir las habilidades mínimas necesarias. Tras esto, me encontraré en disposición de tratar el problema desde un punto de vista teórico y marcar los siguientes pasos a seguir en el desarrollo que será el paso posterior. Durante la resolución del problema se entiende que irán surgiendo casuísticas inesperadas o nuevos casos a tratar que no se tuvieron en cuenta, igual para la parte de análisis. Así, que realmente, estimo que durante todo el proyecto estaré en fase de aprendizaje asimilando y descubriendo funciones, métodos, herramientas, técnicas… Los desarrollos, de códigos, sobre todo, requieren de continuo feedback. Ver cuáles son los resultados, estimar si son los esperados o suficientemente válidos, y volver a implementar en caso de querer mejorarlos. Es una retroalimentación continua al igual que la parte de aprendizaje, se van descubriendo mejoras conforme se va avanzando. Por último, con una aplicación que funcione correctamente, solo queda usarlo para crear un dataset completo, verificar que hemos logrado el objetivo de esta primera parte y continuar con la segunda. 2.1.2 Conocimientos base Partía de conocimientos con lenguaje de programación en Python. Durante la carrera, en varias asignaturas, nos exigían saber de este lenguaje de programación por su importancia, su curva de aprendizaje y el uso extendido. Por tanto, manejaba este lenguaje de forma fluida y sabía recabar información sobre este bastante bien, sabía cómo instalarlo y configurarlo, aunque ya lo tenía en mi equipo personal. En mi caso, mi carrera me ha servido de una buena base en cuanto a imágenes se refiere, sobre todo en mi especialidad ya que estuve cursando asignaturas de la rama de Imagen y Sonido y tengo el conocimiento aprendido bastante reciente. Había visto tratamientos de imágenes en general en asignaturas como Visión Artificial, Tratamiento Digital de Señales Multimedia, Fundamentos de Procesamiento de Imágenes o Televisión. Desde varios tipos de funciones matemáticas, algoritmos y funciones que se suelen aplicar a las imágenes, hasta detección de esquinas o clasificación de imágenes. También he tratado con videos en otras como Producción Audiovisual o Equipos y Sistemas de Audio, Video y Televisión, aunque en estas no llegué a implementar código, no eran objetivos de las asignaturas. A pesar de ello, apenas había trabajado o visto librerías dedicas al procesamiento de imágenes. Algunas funciones específicas de MATLAB u otras muy básicas de Python. Lo que sí tenía era una buena base teórica de las técnicas que existían en estos ámbitos, ya sea para usarlas o para descubrir y profundizar en otras nuevas. 2.1.3 Análisis de herramientas 2.1.3.1 MATLAB Matlab es una plataforma de programación y de cálculo numérico utilizada por millones de científicos e ingenieros para analizar datos, desarrollo de algoritmos y realizar modelos. Cuenta con un entorno de desarrollo de trabajo muy robusto y una amplia gama de librerías, simuladores y herramientas para manejar, entre otras muchas, el tratamiento de imágenes, de matrices y la presentación de estos datos en gráficas. Tiene su propio lenguaje de programación y en algunos ámbitos, entre ellos los de ingeniería, tiene un gran prestigio por su versatilidad y por estar enfocado y optimizado para cálculos matemáticos[1]. Figura 2-1. MATLAB [2]
5 Percepción de la acción en una imagen mediante algoritmos de aprendizaje automático 2.1.3.2 Java Java es una plataforma informática de lenguaje de programación basada en programación orientada a objetos. Es, y sigue siendo, uno de los lenguajes más usados para múltiples aplicaciones, incluso para imágenes ya que incluye también la librería de OpenCV. Java es un lenguaje de programación que utiliza clases y que, como Python, está orientado a objetos. A pesar de ser un lenguaje multiplataforma y de carácter general, ha quedado para uso de desarrollo web. Esto se ve reflejado sobre todo en el enfoque que suelen tener los foros en los que se resuelven problemas de programación[3]. 2.1.3.3 C C es el lenguaje de programación a más bajo nivel que vamos a encontrar en este análisis, nivel medio. Por lo que, permite tener mayor control software y optimizar mejor el rendimiento del programa a desarrollar. También permite usar librerías para facilitar el uso en ciertas aplicaciones como para tratamiento de imágenes, con OpenCV. Se usa tanto para el desarrollo de sistemas operativos y aplicaciones como base para otros lenguajes. También, debido a tener menos nivel de abstracción, resulta menos amigable y más costoso para el usuario trabajar con este lenguaje.[4] Figura 2-2. C [4]. 2.1.3.4 Python Python es un lenguaje de programación de alto nivel muy extendido siendo uno de los más usado. Se trata de un lenguaje interpretado y orientado a objetos. Cabe resaltar su fácil legibilidad y manejabilidad, teniendo una curva de aprendizaje alta. Ha día de hoy es altamente usado en diferentes ámbitos, entre ellos el procesamiento de imágenes y matrices, y cuenta con un gran apoyo de los usuarios y de las empresas. Además, cuenta con una amplia gama de librerías de diversos ámbitos, tanto de empresas de renombre o pequeñas, como de usuarios. Una de las más usados en tratamiento de imágenes es la librería Numpy que sirve para trabajar con cadenas de arrays de forma óptima y con mayor rendimiento, sirve como base para la construcción de otras muchas librerías. [5] Figura 2-3. Python [6]. 2.1.3.5 Visual Studio Code Visual Studio Code es un editor de código ligero pero potente que viene de la mano de Microsoft. Es una aplicación de escritorio disponible en todas las plataformas y con el que editar en cualquier lenguaje de programación gracias a su rico ecosistema de extensiones. Viene preparado para trabajar con Git. No solo es editor de código, sino que también cuenta con terminales y herramientas para ejecutar archivos o para debuggearlo de forma interactiva. Mencionar su famosa función IntelliSense que resalta, autocompleta o sugiere partes del código. [7]
Creación de datasets de imágenes 12 𝐼1(𝑥,𝑦)=𝐼2(𝑥+𝑉𝑥∗∆𝑡,𝑦+𝑉𝑦) ( 2-5 ) Aunque, debido al ruido y/o a la discretización del espacio, no se cumple esta relación de forma estricta, por ello necesitamos de métodos más refinados. Tenemos que para pequeños cambios según Taylor: 𝐼(𝑥+∆𝑥,𝑦+∆𝑦,𝑡+∆𝑡)=𝐼(𝑥,𝑦,𝑡)+𝜕𝐼 𝜕𝑥∆𝑥+𝜕𝐼 𝜕𝑦∆𝑦+𝜕𝐼 𝜕𝑡∆𝑡+𝑇𝑂𝑆 ( 2-6 ) Donde TOS es la continuación de la ecuación y podemos despreciarla (𝑇𝑂𝑆=0), quedándonos finalmente con: 𝐼(𝑥,𝑦,𝑧)=𝐼(𝑥+∆𝑥,𝑦+∆𝑦,𝑡+∆𝑡) ( 2-7 ) Del cual se deduce que 𝜕𝐼 𝜕𝑥∆𝑥+𝜕𝐼 𝜕𝑦∆𝑦+𝜕𝐼 𝜕𝑡∆𝑡=0 ( 2-8 ) Y, por lo tanto: 𝜕𝐼 𝜕𝑥𝑉𝑥+𝜕𝐼 𝜕𝑦𝑉𝑦+𝜕𝐼 𝜕𝑡𝑉𝑡=0 ( 2-9 ) Esta última es llamada función fundamental y es escrita también como: 𝐼𝑥∗𝑉𝑥+𝐼𝑦∗𝑉𝑦=−𝐼𝑡 Aquí nos encontramos con un sistema subdeterminado ya que consta de una ecuación y dos incógnitas (𝑉𝑥,𝑉𝑦), es decir que tenemos infinitas soluciones [15]. De esta forma solo nos queda hacer hipótesis, y así nacen los dos métodos: de Lucas-Kanade y de Horn-Schunk El flujo óptico puede ser calculado mediante dos métodos diferenciales: • Lucas-Kanade (L-K): Parte de la suposición de que los puntos cercanos pertenecientes a la vecinidad de un píxel tienen igual velocidad. De esta forma, para un conjunto de puntos pertenecientes a la misma vecinidad, tenemos: 𝐼𝑥(𝑝)∗𝑉𝑥+𝐼𝑦(𝑝)∗𝑉𝑦=−𝐼𝑡(𝑝) 𝐼𝑥(𝑞)∗𝑉𝑥+𝐼𝑦(𝑞)∗𝑉𝑦=−𝐼𝑡(𝑞) 𝐼𝑥(𝑟)∗𝑉𝑥+𝐼𝑦(𝑟)∗𝑉𝑦=−𝐼𝑡(𝑟) . . . ( 2-10 ) Obteniendo así un sistema sobredeterminado. El sistema de ecuaciones puede resumirse en 𝐴∗𝑣=𝑏 con
13 Percepción de la acción en una imagen mediante algoritmos de aprendizaje automático 𝐴= ( 𝐼𝑥(𝑝) 𝐼𝑥(𝑞) 𝐼𝑥(𝑟) ... 𝐼𝑦(𝑝) 𝐼𝑦(𝑞) 𝐼𝑥(𝑟) ... ) , 𝑏= ( 𝐼𝑡(𝑝) 𝐼𝑡(𝑞) 𝐼𝑡(𝑟) ... ) ( 2-11 ) Para obtener una solución en este sistema aplicamos el método de mínimos cuadrados 𝐴∗𝑣=𝑏 ( 2-12 ) (𝑣𝑥 𝑣𝑦 )=𝑣= (𝐴𝑡𝐴)−1𝐴𝑡𝑏 ( 2-13 ) Y requiere que 𝐴 sea de rango completo para que 𝐴𝑡𝐴 tenga inversa. De esta forma las zonas que se mantienen homogéneas (𝐼𝑥=𝐼𝑦=𝐼𝑡) no se detecta movimiento, y en los bordes y esquinas, que es donde encontramos un gran salto (∇𝐼≠0), se detecta correctamente cualquier movimiento. Por lo tanto, nos interesa fijar los puntos de estudio en bordes o esquinas [15]. • Horn Shunck. Este método parte de la hipótesis de que el movimiento, al pasar el tiempo, es suave en toda la escena. Así, de las infinitas soluciones que existen, nos limitamos a aquellas que minimizan el funcional de la energía: 𝐸=∬((𝐼𝑥𝑉𝑥+𝐼𝑦𝑉𝑦+𝐼𝑡)2+∝2(‖∇𝑉𝑥‖2+‖∇𝑉𝑦‖2))𝑑𝑥 𝑑𝑦 ( 2-14 ) Que intenta penalizar los cambios bruscos. Si nos fijamos en los valores que puede tomar ∝, si se hace 0 es como si estuviéramos tratando con la misma función vista de Lucas-Kanade. Sin embargo, cuando Alpha tienda a infinito los gradientes de 𝑉𝑥 e 𝑉𝑦 tienden a disminuir, recordemos la hipótesis de la que parte, en la que intentamos minimizar la 𝐸. Por tanto, a mayor sea Alpha más se penalizarán las soluciones menos suaves. En comparación con el método de L-K, es más sensible al ruido, pero proporciona el flujo óptico en regiones donde L-K no funciona [15]. 2.1.4.3 Detección de esquinas El movimiento es más fácil de manejar si se siguen puntos característicos, las esquinas contienen dos bordes (lo que facilita su detección) y son buenos candidatos. Los detectores de esquinas proporcionan rasgos de la imagen para su seguimiento. Los puntos candidatos para ser esquinas son aquellos tiene mayor singularidad, es decir, que tiene una baja o menor autosimilitud. La autosimilitud se puede medir como la suma de diferencias al cuadrado (SDC) en una vecinidad [15]. 𝑆𝐷𝐶(∆𝑖,∆𝑗)=∑∑(𝑓(𝑖+∆𝑖,𝑗+∆𝑗)−𝑓(𝑖,𝑗))2 𝑗𝑖 ( 2-15 ) A mayor SDC menor autosimilitud lo que es equivalente a mayor singularidad, por lo tanto, buscamos los puntos con mayor autosimilitud. Si introducimos la definición de tensor: 𝑇=∑∑(𝐼𝑥2𝐼𝑥𝐼𝑦 𝐼𝑥𝐼𝑦𝐼𝑦 2) 𝑗𝑖 ( 2-16 )
Creación de datasets de imágenes 14 Obtenemos 𝑆𝐷𝐶(∆𝑖,∆𝑗)=(∆𝑖,∆𝑗)𝑇(∆𝑖,∆𝑗)𝑡 ( 2-17 ) Un punto singular tiene una alta SDC para cualquier dirección (∆𝑖,∆𝑗), esto implica que 𝑇 ha de ser significativo, es decir, los autovalores de 𝑇 (𝜆1 y 𝜆2) han de ser significativos. Se plantean entonces tres escenarios respecto a sus autovalores: 1. 𝜆1 y 𝜆2 no significativos. Lo que significa que el punto (𝑖,𝑗) no es singular. 2. 𝜆1 es significativo, pero 𝜆2 no (o viceversa). Sería indicio de que el punto (𝑖,𝑗) pertenece a un contorno. 3. 𝜆1 y 𝜆2 son significativos. En este caso el o punto (𝑖,𝑗) sí pertenece a una esquina. Encontramos dos métodos usados para la detección de esquinas que se basan en estas ideas, • Harris. Fue un primer acercamiento realizado por Chris Harris y Mike Stephens en el documento “A Combined Corner and Edge Detecter” en 1988. Toma la idea de buscar variaciones de intensidades en todas las direcciones y la plasma de forma matemática [18]. 𝐸(𝑢,𝑣)=∑𝑤(𝑥,𝑦)[𝐼(𝑥+𝑢,𝑦+𝑣)−𝐼(𝑥,𝑦)]2 𝑥,𝑦 ( 2-18 ) • 𝑤(𝑥,𝑦)→𝑊𝑖𝑛𝑑𝑜𝑤𝑓𝑢𝑛𝑐𝑡𝑖𝑜𝑛 • 𝐼(𝑥+𝑢,𝑦+𝑢)→𝑆ℎ𝑖𝑓𝑡𝑒𝑑 𝑖𝑛𝑡𝑒𝑛𝑠𝑖𝑡𝑦 • 𝐼(𝑥,𝑦)→𝐼𝑛𝑡𝑒𝑠𝑖𝑡𝑦 La ventana (𝑤) es una simple ventana rectangular o una ventana gaussiana con mayores pesos en los píxeles inferiores. De esta forma el objetivo se centra en maximizar 𝐸(𝑢,𝑣), es decir, maximizar el segundo termino. Aplicando las series de Taylor y realizando algunos pasos intermedios llegamos a la siguiente ecuación. 𝐸(𝑢,𝑣)≈[𝑢 𝑣] 𝑀[𝑢 𝑣] ( 2-19 ) Donde 𝑀=∑𝑤(𝑥,𝑦)[𝐼𝑥𝐼𝑥𝐼𝑥𝐼𝑦 𝐼𝑥𝐼𝑦𝐼𝑦𝐼𝑦] 𝑥,𝑦 ( 2-20 ) Aquí 𝐼𝑥 e 𝐼𝑦 son las intensidades derivadas en 𝑥 o en 𝑦. Luego viene la parte importante en la que decidimos si se trata de una esquina o no. 𝑅=det(𝑀)−𝑘(𝑡𝑟𝑎𝑐𝑒(𝑀))2 ( 2-21 ) Donde o det(𝑀)= 𝜆1𝜆2 o 𝑡𝑟𝑎𝑐𝑒(𝑀)=𝜆1+𝜆2 o 𝜆1 y 𝜆2 son los autovalores de 𝑀.
15 Percepción de la acción en una imagen mediante algoritmos de aprendizaje automático Basándonos en lo dicho anteriormente, aplicamos el siguiente criterio: o Cuando |𝑅| es un valor pequeño, que ocurre cuando 𝜆1 y 𝜆2 son pequeños, la región es plana, no hay esquinas. o Cuando 𝑅<0, cuando 𝜆1≫𝜆2 o viceversa, la región es un borde. o Cuando 𝑅 es un valor grande, 𝜆1 y 𝜆2 son grandes y del mismo grado (𝜆1~𝜆2) se trata de una esquina. Figura 2-14. Representación de soluciones según los valores de lambda por el método de Lucas-Kanade [18]. Al fijarse en las variaciones de intensidades y no realizar cálculos de autovalores se hace más rápido. • MinimunEigenvalue (Shi-Tomasi). J.Shi y C.Tomasi retomaron los cálculos del método de detección de esquinas de Harris en el documento “Good Feature to Track” [19] haciendo pequeñas modificaciones que mostraron mejores resultados [20]. Ellos proponían cambiar el cálculo de 𝑅: 𝑅=min {𝜆1,𝜆2} ( 2-22 ) Si el valor de R era mayor que un determinado umbral, esta región era considerada como esquina.
Creación de datasets de imágenes 16 Figura 2-15. Representación de soluciones según los valores de lambda por el método de Shi-Tomasi [20]. De esta forma, a partir de un determinada 𝜆 se considera esquina que queda representada en la zona gris. Es robusto pues usa 𝑚𝑖𝑛{𝜆1,𝜆2}. 2.1.4.4 Imágenes diferencia Durante el desarrollo me di cuenta de que gran parte del material que tenía era de eventos transmitidos los cuales tenían marcadores, logos, marcas de agua u otros elementos añadidos o elementos del propio escenario como carteles de publicidad o similares. Estos elementos suelen venir en tonos de colores que resaltan sobre el vídeo o con líneas muy definidas. Al aplicar los algoritmos me daba cuenta de que todas las esquinas las detectaba en estos objetos u otros propios del escenario en el que se grababa el evento. Me suponía un problema porque sin las esquinas bien colocadas no sería capaz de calcular el flujo óptico y no conseguía corregir esto a través de los parámetros de los que disponía con la función encargada de calcular las esquinas. Se me hacía necesario añadir pues una nueva fase en el preprocesado de la imagen antes de pasar a llamar a la función. Aproveché que la mayoría del material del que disponía eran videos cuyas cámaras se mantenían estáticas. Quitando algunos pequeños momentos, la mayor parte del tiempo el movimiento que se daba en la imagen eran de los deportistas u objetos que estos usaban, quitando eso apenas había movimiento en la escena.
17 Percepción de la acción en una imagen mediante algoritmos de aprendizaje automático Figura 2-16. Imagen de partido de tenis emitido por RTVE [14] Por ejemplo, en un partido de tenis si nos fijamos, podemos ver que los únicos que se mueven son los jugadores con sus raquetas y la pelota la ser golpeada por ellos. En alguna ocasión los recogepelotas se mueven o personas entre el público, pero eran pocas veces y solo me hacía falta detectar las esquinas en algún momento en el que me beneficiara. La solución que le di a esto fue aplicar la diferencia de imágenes. Este método consiste básicamente en restar dos imágenes entre sí. Dada una imagen 𝐼1 y otra 𝐼2 [15]: 𝐼2−𝐼1 ( 2-23 ) Tras esto habría que reescalar los valores adecuadamente Esta proporciona información sobre el movimiento real. Esta técnica es bastante limitada ya que es muy susceptible al ruido y los cambios de iluminación pueden ser pequeños. Figura 2-17. Imagen diferencia obtenida tras procesarla. Las zonas blancas o en color corresponden al movimiento que ha habido en la escena. Sin embargo, vemos como para estos casos tenemos unos resultados decentes sobre todo para obtener esquinas.
Creación de datasets de imágenes 18 OpenCV implementa un método que ya escala los valores adecuadamente. Se trata del método substract y calcula la diferencia por cada elemento entre cadenas [21]. 𝑑𝑠𝑡(𝐼)=𝑠𝑎𝑡𝑢𝑟𝑎𝑡𝑒(𝑠𝑟𝑐1(𝐼)−𝑠𝑟𝑐2(𝐼))→𝑠𝑖 𝑚𝑎𝑠𝑘(𝐼)≠0 ( 2-24 ) 2.1.5 Conclusiones Aprovechando que se van a implementar las imágenes diferencia, se estima que lo óptimo es crear vídeos a partir de estos y usarlos para el cálculo del flujo óptico, no solo de esquinas, ya que se entiende que al algoritmo le conlleva a menos errores seguir la esquina sobre un fondo negro. Por otro lado, el método que se usará para el cálculo de detección de esquinas será el de MinimunEigenvalue. Dado que se estima que el algoritmo Shi Tomasi ofrece mejores resultados tras hacer comparaciones con resultados reales. Y, por último, en el caso del flujo óptico, teniendo en cuenta que vamos a tratar con acciones de deportes para este trabajo, veo conveniente usar el método de Lucas-Kanade por los cambios bruscos que pueden ocurrir. 2.2 Desarrollo Teniendo una base teórica sobre la que partir, pasamos a la fase de resolución de los problemas planteados. Tal y como se ha organizado. Primero el uso de Tkinter para que se entiendan algunas líneas y funciones que van dedicadas a la interfaz, luego habría que preparar los archivos con los que vamos a trabajar, que son archivos de vídeo. Después, un factor cuya importancia se irá explicando conforme se vea el desarrollo, la estructura que ha de tener el directorio de trabajo. Dejando para el final, la explicación de la líneas y funciones de código más relevantes. 2.2.1 Tkinter Ejecutar todo el archivo Python e ir cambiando las variables de los parámetros manualmente en código me resultaba una tarea repetitiva y poco eficiente sobre todo teniendo en cuenta las veces que tendría que repetirlo y que el cálculo del flujo óptico no era inmediato ya que dependía de la duración del vídeo. Por tanto, la solución que se me ocurrió fue implementar una interfaz que me permitiera seleccionar los parámetros, hacer el cálculo de esquinas y ver los resultados de este, y luego, al ser el proceso con mayor carga computacional, aplicar el flujo óptico. A esto también se le añadió el botón para calcular la diferencia de imágenes. Con esto, el proceso de crear las imágenes para el dataset se agilizaba en gran medida.
19 Percepción de la acción en una imagen mediante algoritmos de aprendizaje automático Figura 2-18. Vista general de la interfaz desarrollada con Tkinter. Tkinter funciona a través de widgets. Contiene un nodo raíz que será la ventana, a partir de este se le van añadiendo los diferentes widgets con las propiedades u Objetos que se deseen. root = Toplevel() root.title("Settings") labelVideo=Label(root) labelVideo.grid(row=8, columnspan=8, pady=10) Primero se crean los widgets a través de Objetos de Tkinter en los que se les indica la ventana en la que se van a poner. Luego con la función grid() indicamos la posición que tendrá dentro de la ventana y cuánto ocupará. initFrame=IntVar() initFrame.set(min_frame) label_init_frame=Label(root, text="Frame inicial: ") label_init_frame.grid(row=0, column=0) spin_frame_init=Spinbox(root,from_=min_frame,to=max_frame,increment=1, textvariable=initFrame, command=frame_init_Spinbox) spin_frame_init.grid(row=0, column=1) Este es un ejemplo de cómo he ido creando los distintos widgets en la interfaz. Primero declaro la variable y establezco un valor por defecto para evitar errores o que no se le pase en primera instancia valores nulos. Luego, creo un widget, en este caso es un objeto Label que es una etiqueta que uso solamente para escribir texto y así entender a que va dirigida la entrada que va al lado desde la interfaz. 2.2.2 Preparación de archivos de vídeos Los vídeos vienen de diferentes fuentes (competiciones, videotutoriales, …). La fracción de vídeo a estudiar suele ser una pequeña parte para la longitud total de vídeo. Tratar con secuencias tan largas en la interfaz implementada con Tkinter puede llegar a ser muy tedioso. Veía necesario hacer un primer recorte o selección de tiempo que fuera menos extenso, para que pueda ser manejable en la interfaz estimo que el vídeo no exceda los 1000 frames, teniendo en cuenta que el estándar de transmisión es aproximadamente de 30 frames por segundo, esto equivale a no exceder los 33 segundo aproximadamente. Esta estimación la he establecido considerando que la pantalla con la que se trabaja es de 15’6” y una resolución de 1920x1080 píxeles, que es el tipo de pantalla con el que he estado trabajando. Este recorte lo realizo con la aplicación de DaVinci Resolve. En todos los casos, he estado exportando los archivos a mp4. 2.2.3 Estructura de archivos Para hacer un buen seguimiento y análisis de los resultados obtenidos me decidí por obtener varias imágenes en función de los resultados, que me ayudaran a conocer los puntos a mejorar. Es por ello que, para usar este código es necesario tener dentro del directorio del archivo .py las siguientes carpetas: o /in: Imagen del primer frame. o /out: Resultado final del flujo óptico. o /out_image: Resultado final del flujo óptico sobre la imagen del frame final. o /video_out: Video seleccionado con el flujo óptico añadido. o /video_out_diff: Video diferencia. 2.2.4 Funciones En esta subsección voy a ir entrando en detalle en las partes del código necesarias para que se entienda la solución
Creación de datasets de imágenes 20 en desarrollo. global labelVideo global corners Por lo general las funciones que he creado no devuelven nada a través de return, los resultados van pasándose a través de variables globales porque se usan otros muchos métodos y era necesario si quería implementar la interfaz. open_file() cap = cv.VideoCapture(videopath) Una de las primeras líneas que encontramos tras las funciones es la que abre el video. Primero se abre una ventana en la que seleccionamos el archivo entre las carpetas de nuestra computadora y luego lo abrimos a través de la función VideoCapture de OpenCV. Para alcanzar el objetivo final, que son imágenes que represente el movimiento mediante líneas, tenía dos hitos a conseguir: • Reconocer bien las esquinas • Obtener un flujo óptico que represente el movimiento. Primero empiezo con funciones secundarias y para el final están las funciones que aplican los métodos con los que cubrir los hitos. 2.2.4.1 validateFrame Asegura que el valor de frame introducido no sea negativo ni mayor que el máximo del vídeo. Parámetros: ▪ initFrame: Número del frame de inicio. def validateFrame(initFrame): if initFrame >= max_frame-1: initFrame = max_frame-1 elif initFrame < min_frame: initFrame = min_frame return initFrame 2.2.4.2 frame_init_Slider El Slider es una forma manual de escoger el frame inicial, si se producen cambios en este, se visualizará la imagen del frame que está seleccionando.
21 Percepción de la acción en una imagen mediante algoritmos de aprendizaje automático Figura 2-19. Vista del Slider en la interfaz desarrollada con Tkinter. Esta función valida el valor introducido en el Slider y cambia el valor de este. El Slider se sitúa en la parte superior, debajo de los Spinbox de frame inicial y final, como una barra. Parámetros: ▪ initFrame: Valor del frame inicial. def frame_init_Slider(initFrame): frame=validateFrame(int(initFrame)) get_image(frame) initFrame = frame 2.2.4.3 frame_init_Spinbox Los Spinbox son entradas de números. Constan de dos botones para subir o disminuir el número que se muestra dentro del texto. También puede seleccionarse el texto y modificarlo a través del teclado. Figura 2-20. Vista de los Spinboxs en la interfaz desarrollada con Tkinter. La función valida el valor inicial del frame introducido en el Spinbox y cambia el valor de este. Tanto el Spinbox de selección del frame inicial como el del frame final se sitúan al principio de la interfaz, en la parte superior.
Creación de datasets de imágenes 28 ▪ prevPts: Vector 2D de los puntos a los que aplicar el flujo óptico. ▪ nextPts: Salida en vector 2D con los puntos calculados. ▪ status: Vector que marca el estado de los puntos, si han sido calculados correctamente o no. ▪ err: Vector que marca errores de los puntos si se da el caso. ▪ winSize: Tamaño de la ventana para la búsqueda de cada nivel de la pirámide del algoritmo Lucas-Kanade. ▪ maxLevel: Número máximo de niveles de pirámides del algoritmo Lucas-Kanade. ▪ criteria: Especifica los parámetros de criterios para el algoritmo iterativo. ▪ flag: Presenta varias opciones: o OPTFLOW_USE_INITIAL_FLOW: Especifica si el método usa las estimaciones iniciales guardadas en nextPts o las toma del anterior guardadas en prevPts. o OPTFLOW_LK_GET_MIN_EIGENVALS: Especifica si usar una media del mínimo de los autovalores como error o no. ▪ minEigThreshold: Especifica el valor mínimo de los autovalores. La función creada prepara los datos para llamar al método encargado de calcular el flujo óptico, luego aplica el método a lo largo del vídeo. Parámetros: ▪ initFrame: Valor del frame inicial a tomar. ▪ endFrame: Valor del frame final a tomar. ▪ m_winSize: Número de filas de la ventana a usar para la búsqueda de cada nivel de la pirámide del algoritmo Lucas-Kanade. ▪ n_winSize: Número de columnas de la ventana a usar para la búsqueda de cada nivel de la pirámide del algoritmo Lucas-Kanade. ▪ maxLevel: Número máximo de niveles de pirámides del algoritmo Lucas-Kanade. ▪ criteria_type: Especifica el criterio a aplicar en el algoritmo de búsqueda iterativo: o COUNT (valor=0): El número máximo de iteraciones o elementos a computar. o MAX_ITER (valor=1): ditto (igual que COUNT). o EPS (valor=2): El número medio deseado o cambios en parámetros al cual el algoritmo iterativo parar. ▪ criteria_maxCount: Número máximo de iteraciones para el algoritmo de búsqueda. ▪ criteria_epsilon: Especifica la búsqueda mínima a la que puede proceder. El procedimiento parará si alcanza este valor. El valor medio deseado. ▪ corners: Esquinas detectadas. def optical_flow(initFrame, endFrame, m_winSize, n_winSize, maxLevel, criteria_type, criteria_maxCount, criteria_epsilon, corners): Valida el frame inicial y lo almacena en una variable auxiliar, igual para el frame final. Las entradas son tipos de objetos por lo que es necesario extraer los valores. initFrame_aux = validateFrame(initFrame.get()) endFrame_aux = endFrame.get() Se hace la conversión y se extraen los datos en variables auxiliares. En caso de no introducir parámetros o de
29 Percepción de la acción en una imagen mediante algoritmos de aprendizaje automático que estén mal por el tipo de formato que se necesita, se pone valores por defecto y se avisa en consola. if maxLevel.get()=='' or 0 >= int(maxLevel.get()): print("ERROR: Debe haber un número positivo de esquinas") maxLevel_aux = 2 else: maxLevel_aux = int(maxLevel.get()) if criteria_type.get()=='' or 0 >= int(criteria_type.get()): print("ERROR: Debe haber un número positivo de esquinas") criteria_type_aux = 1 else: criteria_type_aux = int(criteria_type.get()) if criteria_maxCount.get()=='' or 0 >= int(criteria_maxCount.get()): print("ERROR: Debe haber un número positivo de esquinas") criteria_maxCount_aux = 10 else: criteria_maxCount_aux = int(criteria_maxCount.get()) if criteria_epsilon.get()=='' or (float(criteria_epsilon.get()) > 1 or float(criteria_epsilon.get()) < 0): print("ERROR: qualityLevel debe estar entre 0 y 1") criteria_epsilon_aux=0.03 else: criteria_epsilon_aux = float(criteria_epsilon.get()) if n_winSize.get() != '' and m_winSize.get() != '': winSize = (int(n_winSize.get()) ,int(m_winSize.get())) else: winSize=(15,15) Aunamos los parámetros del algoritmo en una variable. lk_params = dict(winSize = winSize, maxLevel = maxLevel_aux, criteria = ( criteria_type_aux , criteria_maxCount_aux, criteria_epsilon_aux)) Creamos colores aleatorios para luego diferenciar las líneas en el resultado. Luego tomamos el primer frame y lo guardamos en una carpeta aparte (/in). color = np.random.randint(0, 255, (100, 3)) ret, old_frame = cap.read() old_gray = cv.cvtColor(old_frame, cv.COLOR_BGR2GRAY) cv.imwrite("in/" + filename + ".jpg", old_frame) Creamos una máscara para dibujar las líneas y ajustamos un contador con las iteraciones que vamos a hacer, es decir, las veces que aplicaremos el algoritmo del flujo óptico que es una vez por cada comparación entre dos frames del vídeo según la sección de video que hayamos marcado. mask = np.zeros_like(old_frame) count= endFrame_aux-initFrame_aux Tomamos al ancho y el alto de la imagen para crear un video con el resultado. frame_width = int(cap.get(3)) frame_height = int(cap.get(4)) size = (frame_width, frame_height) video = cv.VideoWriter("video_out/" + filename + ".avi", cv.VideoWriter_fourcc(*'MJPG'), FPS, size) Volvemos a apuntar al frame inicial y comenzamos el bucle. cap.set(CAP_PROP_POS_FRAMES, initFrame_aux)
Creación de datasets de imágenes 30 while(count>0): count-=1 ret, frame = cap.read() if not ret: print('No hay más frames') break frame_gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY) Aplicamos la función del flujo óptico por Lucas-Kanade y guardamos las esquinas nuevas y las viejas para luego unirlas e ir representando las líneas que definen al flujo óptico. optical_flow, st, err = cv.calcOpticalFlowPyrLK(old_gray, frame_gray, corners, None, **lk_params) if optical_flow is not None: good_new = optical_flow[st==1] good_old = corners[st==1] for i, (new, old) in enumerate(zip(good_new, good_old)): a, b = new.ravel() c, d = old.ravel() mask = cv.line(mask,(int(a),int(b)),(int(c),int(d)),color[i].tolist(),2) frame = cv.circle(frame, (int(a), int(b)), 5, color[i].tolist(), -1) Creamos distintas imágenes y las vamos guardando en carpetas diferentes. img = cv.add(frame, mask) cv.imwrite("out_image/" + filename + ".jpg", img) cv.imwrite("out/" + filename + ".jpg", mask) video.write(img) Actualizamos la imagen para la siguiente iteración. old_gray = frame_gray.copy() corners = good_new.reshape(-1, 1, 2) print("Terminado") video.release() Como resultado al usar esta función se obtiene: - Una imagen del primer frame. Dentro de la carpeta /in. - Una imagen del resultado del flujo óptico con la imagen del frame final. Dentro de la carpeta /out_image. - Una imagen del resultado del flujo óptico con el fondo en negro. Dentro de la carpeta /out. - Un vídeo del resultado del flujo óptico con el video original. Dentro de la carpeta video_out.
31 Percepción de la acción en una imagen mediante algoritmos de aprendizaje automático Figura 2-25. Vista de la sección dedicada a los parámetros del flujo óptico en la interfaz desarrollada con Tkinter. En la interfaz, los parámetros de esta función se modifican en la zona marcada y para ser llamada tendría que pulsarse el botón “Aplicar flujo óptico”. 2.3 Resultados La mejor forma de ver los resultados es viendo como ha quedado la aplicación en imágenes mostradas anteriormente y con muestras de imágenes obtenidas para el dataset. La aplicación tiene una estética sencilla, pero cumple su función de agilizar el proceso de creación de datos para la segunda parte de este trabajo. En cuanto a los resultados finales, lo mejor es verlo con un ejemplo: Figura 2-26. Resultado de flujo óptico. Esta imagen no es la que finalmente se le pasará a la máquina de aprendizaje automático, esta se ha sacado de la carpeta /video_out y es un buen ejemplo del resultado obtenido. Sería el flujo óptico con la imagen del vídeo del que se ha obtenido de fondo. Tomando como esquinas las pesas y parte del cuerpo, el algoritmo hace un buen seguimiento del movimiento realizado. 2.4 Posibilidad de mejora Tanto en la fase de desarrollo de código como en la fase de uso, han ido surgiendo problemas o posibilidades de mejoras. Algunos los he ido solucionando, pero otros me suponían una dedicación de tiempo que no tenía. • Al cambiar de video no se modifican los valores tomados por los widgets ya que esto se realiza solo la primera vez que se abre la ventana y esto puede conllevar a dos cosas: a) Que no tome el video completo porque el video que se abrió inicialmente era de menor duración que este. b) Que el video que se abrió inicialmente fuera de mayor duración que el nuevo que se abra, lo que daría errores mientras se ejecuta el método del cálculo de flujo óptico ya que no encuentra ninguna esquina ni muestras de imágenes. Aun así, el resultado final sigue siendo válido. • Podría haber puesto mensajes en la interfaz avisando de distintas cosas. Por ejemplo, que se ha terminado el proceso de imágenes diferencias o del cálculo de flujo óptico, o que se va a sobrescribir un archivo ya existente. Incluso podría haber mostrado errores u otros avisos. • Haber añadido capturas de excepciones en el código. También mostrar más información en el terminal. • Tras aplicar el método de diff, se podría abrir el archivo de vídeo que se ha generado y trabajar con este sin necesidad de cambiarlo manualmente.
Creación de datasets de imágenes 32 • Comprobar que se tienen las carpeteas y la estructura de directorios bien creada, y en caso de no ser así que las cree el propio programa. • Dar la opción de escoger entre la detección de esquinas de Harris o de Shi-Tomasi. • OpenCV permite el uso de GPU. En este caso, la aplicación no es exigente en cuanto a recurso de hardware, pero se podría haber optimizado en este aspecto. • Durante el desarrollo he ido descubriendo otras librerías para generar interfaces que podrían haber supuesto una mejor solución que Tkinter, pero cambiar esto a mitad del desarrollo supone un cambio bastante radical. Algunas de estas librerías son PySimpleGUI o Flet. • Existe otro método para el cálculo del flujo óptico que no he tenido en cuenta, desarrollado por Gunnar FarneBack en su publicación “Two-Frane Motion Estimation Base don Polynomial Expansion” [24]. Lo descubrí navegando a través de OpenCV con una función denominada calcOpticalFlowFarneback [23] y ya tenía implementado el método de Lucas-Kanade. Hubiera sido buena práctica reconsiderar y estudiar este nuevo método. 2.5 Conclusiones Todo el código está pensado para usar los métodos de calcOpticalFlowPyrLK y de goodFeatureToTrack de la forma más cómoda posible. La creación de la interfaz no entraba dentro de los planes al principio. Sin embargo, una vez creada, se agiliza mucho la tarea repetitiva de crear imágenes para el dataset. La dificultad del desarrollo aumentó cuando añadí el componente de Tkinter, sobre todo para ajustar los widgets y moldear las variables. Pero tras ver la aplicación y usarla para crear el dataset, puedo decir que he ahorrado tiempo. Crear la aplicación funcional ha sido una inversión de tiempo que ha sido amortizada. El código está preparado para poderle realizar casi todo tipo de modificaciones a las funciones principales de OpenCV que más interesan para este trabajo. El poder ver los resultados del cálculo de las esquinas al instante sin necesidad de buscar en carpetas es un punto a favor, se pueden valorar las esquinas y modificar los parámetros para volver a estimarlas, todo ello ejecutando el archivo de Python una sola vez. Considero que, tras ver los resultados finales esto son bastante válidos para la tarea que vamos a llevar a cabo y por lo tanto el objetivo de esta parte queda cumplido.
33 3 MÁQUINA DE APRENDIZAJE AUTOMÁTICO l ámbito de Machine Learning es un campo que ahora mismo está en alza. Cada vez más entidades se suman a la investigación de este campo y son muchos los que afirman que nos encontramos ante el inicio de una gran revolución y que esto cambiará nuestra forma de vivir y trabajar. Seguido a esto se pueden encontrar otras entidades educativas que ven las nuevas necesidades que se van a generar y cada vez se fomenta más el aprendizaje de estas tecnologías. En este segundo capítulo me centro en todo lo referente al Deep Learning. Una vez que ya tengo creado un banco con la información necesaria, queda aprender los conceptos necesarios y aplicarlos para entrenar un modelo que cumpla con el objetivo planteado, clasificar imágenes como acciones. 3.1 Análisis Siguiendo la misma estructura que puse en el capítulo anterior, aquí empezaré con una planificación de tareas y de organización a seguir. Luego procedo a seguir el plan para conseguir el segundo objetivo de este trabajo: entrenar a una máquina para que clasifique las acciones vistas en el capítulo anterior. 3.1.1 Planificación Teniendo en cuenta la base de la que comienzo, lo primero es empezar por aprender. En este aspecto mi tutor, me proporcionó un libro muy útil de Jordi Torres [25] en el que se van introduciendo conceptos a la par que se realizan ejercicios prácticos. La idea es ir asimilando y aprendiendo a medida que voy realizando las prácticas. El libro se divide en dos partes que completan este aprendizaje, ya que así lo ha establecido el autor. La primera parte es más teórica, aunque con algunos ejemplos prácticos, todo centrado en el aprendizaje automático, por lo que en primera instancia me centro en leer y asimilar. Tras eso, estimo que tendré capacidad para comenzar a desarrollar parte del código mientras a la par sigo con el aprendizaje del libro. Una vez terminado de leer el libro y haber realizado varias pruebas quedaría terminar de implementar el código e ir haciendo modificaciones hasta obtener resultados óptimos. Paralelamente a esto iré desarrollando la memoria que será la última tarea para llevar a cabo. No he marcado tiempos porque me resulta difícil predecirlos debido a mi desconocimiento en la materia. E
Máquina de aprendizaje automático 34 34 3.1.2 Conocimientos base En este campo mi conocimiento era muy escaso. Había oído hablar sobre inteligencias artificiales, aprendizaje automático o aprendizaje profundo, pero no sabía siquiera distinguir entre estos conceptos. Durante la carrera hicimos algunas prácticas sobre esto en la asignatura de Fundamentos de Procesamiento de Imagen para clasificar imágenes, pero era una práctica guiada y en tan poco tiempo no pudimos ahondar en estos campos. 3.1.3 Análisis de herramientas 3.1.3.1 Lenguajes de programación Los leguajes que se han considerados como opción han sido los mismos que en la primera parte: Python, C, Java y Matlab. La descripción de estos queda en el primer capítulo. 3.1.3.2 JupyterNotebook/JupyterLab Project Jupyter es un proyecto sin ánimo de lucro, de código abierto, creado en 2014 para el desarrollo interactivo de ciencia de datos y computación científica para todos los lenguajes de programación. Su producto insignia es JupyterNotebook que consiste en un entorno de desarrollo para notebooks, código y datos usando navegadores webs. Con un diseño modular para expandir y enriquecerlo de funcionalidades [26]. Figura 3-1. Jupyter [27]. Jupyter permite combinar notas de texto, generalmente en formato Markdown, con código que se puede ir descomponiendo y ejecutando en bloques. Parece simple pero esta idea facilita tanto el desarrollo del código como la documentación. 3.1.3.3 Google Colab También conocido como Colaboratory. Se trata de un proyecto de investigación de Google creado para ayudar y fomentar el aprendizaje y la investigación en el ámbito de Machine Learning. Es un entorno JupyterNotebook que no requiere de configuración ni tampoco muchos recursos de hardware ya que se ejecuta en Cloud, solo se necesita de una cuenta de Google y es de uso gratuito. Permite usar Keras, TensorFlow o Pytorch, junto con una GPU para agilizar las ejecuciones y entrenamientos [28]. 3.1.3.4 Visual Studio Code Este es el entorno que se ha usado para el desarrollo del primer capítulo, la descripción se encuentra en la sección 2.1.3.5 Visual Studio Code. Figura 3-2. Visual Studio Code [8].
35 Percepción de la acción en una imagen mediante algoritmos de aprendizaje automático Como añadido, Visual Studio Code entre su amplio repertorio de extensiones tiene una dedicada a Jupyter para poder ejecutar notebooks y código al igual que en la interfaz de JupyterNotebook. 3.1.3.5 TensorFlow TensorFlow es una librería de código abierto creado por Google para el desarrollo de Machine Learning. Es una plataforma de extremo a extremo que facilita tanto la creación como la implementación de redes neuronales. Puede configurarse para desarrollar y ejecutar modelos en múltiples plataformas y entornos. Además, está implementado para el uso de CPU y GPU por lo que ofrece un rendimiento mayor. Es muy versátil para la creación e implementación de modelos. [2] Figura 3-3. TensorFlow [29]. A TensorFlow lo acompaña una gran comunidad y cantidad de recursos que hacen de este una de las librerías más usadas y apoyadas. 3.1.3.6 Keras Es una biblioteca de código abierto desarrollada por François Chollet, ingeniero de Google. Escrita en Python que usa como backend TensorFlow, de hecho, se encuentra incluido en este mismo. Fue desarrollada con la intención de hacer más accesible y rápido el desarrollo de redes profundas, bien para facilitar el aprendizaje para los nuevos como para la investigación. Su curva de aprendizaje es suave en comparación con otras librerías. [1] Figura 3-4. Keras [30]. Es una librería que fomenta la simplicidad, se trata de un nivel de abstracción mayor que el que ofrece TensorFlow. Por esta misma razón, TensorFlow ofrece mayor flexibilidad, porque trabaja a niveles inferiores, y también mayor rendimiento. 3.1.3.7 Conclusiones Si bien es cierto que Java ofrece mejor rendimiento y rapidez, debido a que se trata de un tipado estático y compilado mientras que Python es dinámico e interpretado, la tendencia general y más extendida es usar Python para ciencia de datos e inteligencia artificial. Esto se debe a que Python es un lenguaje de alto nivel lo que lo hace más atractivo y amigable para los programadores. Además, Python ofrece muchas facilidades al desarrollar y una gran cantidad de librerías útiles tanto para construir redes como para interpretar los resultados como las ya mencionadas Numpy y Pandas. En la primera parte ya usamos Python por lo que es otro punto a favor seguir desarrollando en el mismo lenguaje. Todas las herramientas de edición de código propuestas son muy válidas para el propósito a seguir. Sin embargo, dado el entorno con el que se ha trabajado en la primera parte del trabajo, se escoge Visual Studio Code como editor aprovechando la extensión que ofrece de JupyterNotebook. Los recursos del sistema no suponen un problema ya que se estima que el equipo con el que he trabajado cumple las exigencias que se puedan plantear en este trabajo, contando con GPU propia. Por otro lado, teniendo en cuenta que me encuentro en fase de aprendizaje y que parto de no tener conocimientos
Máquina de aprendizaje automático 36 36 respecto a redes neuronales o inteligencia artificial, considero que la mejor opción es usar Keras. Además, Keras viene incluido con la librería de TensorFlow y esto lo simplifica todo. A pesar de que trabajar con TensorFlow puede llegar a ser mejor decisión por flexibilidad y rendimiento, no voy a trabajar con redes neuronales de excesiva complejidad. Otro punto a favor para usar Keras, es que es la librería que se usa en el libro de Jordi Torres. 3.1.4 Análisis teórico Dado que voy a trabajar con Deep Learning, conviene conocer qué es y el contexto del que procede. Qué relación existe entre Deep Learning, Machine Learning (ML) y Artificial Intelligence (AI). Así como algunas técnicas muy usadas y ciertas características que ayudan a comprender todos estos conceptos mejor. 3.1.4.1 Inteligencia Artificial “La inteligencia artificial nació en el año 1950 cuando un puñado de pioneros del naciente campo de la ciencia computacional empezaron a preguntarse si los computadores podrían ser creados para ‘pensar’, una pregunta cuyas ramificaciones nos seguimos preguntando hoy en día” [31]. “Por inteligencia artificial nos referimos a aquella inteligencia que muestran las máquinas, en contraste con la inteligencia natural de los humanos. En este sentido, una posible definición concisa y general de inteligencia artificial podría ser el esfuerzo para automatizar tareas intelectuales normalmente realizadas por humanos. Como tal, el área de inteligencia artificial es un campo muy amplio que abarca muchas áreas del conocimiento relacionadas con el aprendizaje automático; incluso se incluyen muchos más enfoques no siempre catalogados como aprendizaje automático… Además, a lo largo del tiempo, a medida que los computadores han sido cada vez más capaces de ‘hacer cosas’, se han ido cambiando las tareas o tecnologías consideradas como ‘inteligentes’” [25]. Figura 3-5. Inteligencia artificial, aprendizaje automático y aprendizaje profundo [31]. “La inteligencia artificial es un campo que abarca el aprendizaje automático y el aprendizaje profundo, pero que también incluye algunos otros campos no relacionados con el ‘aprendizaje’”. Estos suelen ser máquinas que siguen una serie de reglas que podríamos considerar con ‘inteligencia’” [31]. 3.1.4.2 Aprendizaje automático “Machine Learning se podría definir como el subcampo de la inteligencia artificial que proporciona a los ordenadores la capacidad de aprender sin ser explícitamente programados, es decir, sin que necesiten que el programador indique las reglas que debe seguir para lograr su tarea, sino que las hace automáticamente…. Estos algoritmos aprenden de los datos con el fin de encontrar patrones o tendencias para comprender ‘qué nos dicen’ los datos y de esta manera construir un modelo para predecir y clasificar los elementos” [25]. “En Machine Learning nos referimos a label (‘etiqueta’) a lo que estamos intentando predecir con un modelo”, el resultado que se desea obtener. En cambio, a una variable de entrada la llamaremos feature (‘característica’)” [25].
37 Percepción de la acción en una imagen mediante algoritmos de aprendizaje automático Un modelo (model) es un bloque que trata de relacionar las características con las etiquetas (entradas con salidas). “Machine Learning es un amplio campo con una compleja taxonomía de algoritmos que se agrupan, en general, en tres grandes categorías:” [25] • Aprendizaje supervisado: Los datos que usamos para entrenar vienen etiquetados con la solución que debería dar. • Aprendizaje no supervisado: En este caso los datos pasados para el aprendizaje no tienen etiquetas, será el propio algoritmo el que los clasifique. • Reinforcement Learning (“aprendizaje por refuerzo”): Aprendizaje basado en premios o castigos. El modelo en este caso toma una decisión y lleva a cabo la acción, y en función del estado que genera, del resultado que surge, se dará una “recompensa” o una “penalización”. El objetivo del modelo es obtener recompensas. Es, quizás, el modelo más cercano al mundo real y a los escenarios que se presentan en él. Es el más novedoso de las tres categorías. 3.1.4.3 Deep Learning Se trata de una subparte, de una de las áreas, de la inteligencia artificial, de Machine Learning. Para explicar en qué consiste el Deep Learning (aprendizaje profundo) se suele asemejar con neuronas humanas las cuales conforman una red neuronal, pero en este caso hablamos de neuronas artificiales [25]. Estas neuronas están colocadas una detrás de otras conectadas entre sí de tal forma que pueden separarse en capas como puede verse en la imagen: Figura 3-6. Estructura de red básica [25]. Hablamos pues de Deep Learning cuando se superponen múltiples capas. De esta forma se consigue que, conforme avanzamos desde las capas de entradas (capas que reciben los parámetros de entrada), vayan adquiriendo mayor abstracción. La idea en base sigue siendo la misma, la red tiene que ajustar los parámetros de las distintas neuronas para que a partir de unas entradas determinadas (características) obtengamos una salida concreta (etiquetas). Estas redes pueden estar compuestas de muchas capas y cada una con distintas cantidades de neuronas, de aquí el concepto de “deep” (profundidad de la red). 3.1.4.4 Redes neuronales convolucionales “Una red neuronal convolucional (o CNN, ‘Convolutional Neural Network) es un caso concreto de redes neuronales de Deep Learning” [25] en el que partimos sabiendo que tratamos con imágenes, es decir, de estructuras espaciales. Estas redes se caracterizan por aplicar un tipo de capa que realizan convoluciones. De esta forma se extraen características en base a los patrones que pueda haber en una imagen. La red por tanto se ocupará de aprender los valores que tendrán los filtros que se aplican en la convolución que se les denomina
Máquina de aprendizaje automático 44 44 model_mnist.add(Conv2D(64, (5, 5), activation='relu')) model_mnist.add(MaxPooling2D((2, 2))) model_mnist.add(Flatten()) model_mnist.add(Dense(10, activation='softmax')) model_mnist.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['accuracy']) model_mnist.fit(train_images, train_labels, batch_size=100, epochs=5, verbose=1) Y tras probar que los resultados era los mismos que salían en el ejemplo del libro, pasé a crear la nueva máquina para el problema. model_mnist.pop() model_mnist.pop() model_mnist.trainable=False Las dos últimas capas van dedicadas a la clasificación final lo cual no nos interesa y podemos quitar mediante la función pop(). Luego el procedimiento es igual a lo ya explicado. model = tf.keras.Sequential([ InputLayer(input_shape=(250,250,1)), model_mnist, Conv2D(15, (3, 3), activation='relu'), MaxPooling2D((2, 2)), Flatten(), Dense(3, activation='softmax') ]) model.summary() model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) historyFE = model.fit(train_generator, validation_data = validation_generator, epochs=10, verbose=1) La única diferencia es que, en este caso, al ser un modelo propio, tenemos que ajustar los parámetros de entrada. Esto con los modelos anteriores no hacía falta porque la función de descarga de Keras ya lo hacía por nosotros al descargarlo. 3.3 Resultados En cada uno de los entrenamientos, al finalizar, se imprime en la salida del terminal el valor de accuracy obtenido ya que lo usa para entrenar al modelo. Si nos fijamos en la salida obtenemos: • Modelo Propio → Test Accuracy: 0.8333333134651184 • Modelo basado en MobileNetV2 → Test Accuracy: 0.8333333134651184 • Modelo basado en VGG16 → Test Accuracy: 0.8333333134651184 • Modelo basado en MNIST→ Test Accuracy: 0.8333333134651184
45 Percepción de la acción en una imagen mediante algoritmos de aprendizaje automático Podemos ver el valor accuracy es decir, en la proporción de datos que ha clasificado el modelo correctamente, la precisión. Parecen tener todos una buena precisión, sin embargo esto no es muy esclarecedor y requiere de otras pruebas para verificar que el entrenamiento ha sido efectivo. Como la precisión de todas es la misma, posiblemente las redes están clasificando bien exactamente las mismas imágenes de lo cual se desprende que son imágenes relativamente sencillas. La mejor forma que tenemos de evaluar el modelo es, como ya mencioné, usando el dataset de pruebas que había preparado que consta de 6 muestras. test_lost, test_acc = modelFE.evaluate(test_generator) Existen otras herramientas para evaluar los resultados como la matriz de confusión. Se trata de una matriz que contabiliza las predicciones comparándolas con los valores reales. Los resultados obtenidos en la matriz se pueden dividir en cuatro grandes grupos [25]: Figura 3-8. Tipos de resultados [25]. Donde: o VP: Son los positivos que el modelo ha predicho correctamente. o VN: Son los casos negativos que el modelo ha predicho correctamente. o FP Son los casos negativos que fueron clasificados incorrectamente como positivos. o FN: Son los casos positivos que fueron clasificados incorrectamente como negativos. A partir de esta matriz de confusión podemos obtener ciertas características representativas: o Accuracy: 𝐴𝑐𝑐𝑢𝑟𝑎𝑐𝑦= (𝑉𝑃+𝑉𝑁) (𝑉𝑃+𝑉𝑁+𝐹𝑃+𝐹𝑁) ( 3-1) o Sensitivity: Indica como de bien evita lo falsos positivos. 𝑆𝑒𝑛𝑠𝑖𝑡𝑖𝑣𝑖𝑡𝑦= 𝑉𝑃 (𝑉𝑃+𝐹𝑁) ( 3-2 ) La librería Scikit-learn, muy conocida también para la ciencia de datos, provee de funciones que calculan estos valores y generan la matriz junto con el accuracy obtenido [38]. • Modelo Propio
Máquina de aprendizaje automático 46 46 Figura 3-9. Matriz de confusión y resultados del modelo propio. • Modelo basado en MobileNetV2 Figura 3-10. Matriz de confusión y resultados del modelo con MobileNetV2. • Modelo basado en VGG16 Figura 3-11. Matriz de confusión y resultados del modelo con VGG16. • Modelo basado en MNIST Figura 3-12. Matriz de confusión y resultados del modelo con modelo basado en MNIST. Los resultados aquí no se corresponden el accuracy obtenido por el modelo. De hecho, al volverlo a ejecutar salen valores distintos. Cabe entonces plantearse qué scoring estamos obteniendo a la salida por cada vez que lo
47 Percepción de la acción en una imagen mediante algoritmos de aprendizaje automático ejecutamos, probamos a ejecutarlo dos veces seguidas a ver cuál es el resultado. • Modelo Propio Figura 3-13. Resultados de scorings en el test de prueba con modelo propio. El modelo propio parece ser coherente en cada una de sus salidas, en otras no tanto como en el tercer y cuarto array. • Modelo basado en MobileNetV2 Figura 3-14. Resultados de scorings en el test de prueba con MobileNetV2. En este modelo, los resultados son totalmente dispares, no sabe cómo clasificar y los valores que devuelve a la salida son casi aleatorios. • Modelo basado en VGG16
Máquina de aprendizaje automático 48 48 Figura 3-15. Resultados de scorings en el test de prueba con VGG16 Igual que en caso anterior, vuelve a arrojar valores que difieren mucho entre sí. • Modelo basado en MNIST Figura 3-16. Resultados de scorings en el test de prueba con modelo basado en MNIST. Aquí los valores son muy pequeños, clasifica de forma aleatoria prácticamente, ante cualquier mínima variación de los valores se ve modificado. Tras ver los resultados podemos concluir que ninguno de los modelos ha obtenido buenos resultados. El hecho de que dieran buenos accuracy durante los entrenamientos se estima que puede ser debido a que se han ajustado única y exclusivamente a los parámetros con los que se ha entrenado sin llegar a aprender ninguna característica singular o relevante para esta clasificación. Se puede deber a que hay ciertas imágenes que son muy fáciles de clasificar, esto se evita ampliando el dataset aumentando también la variedad. 3.4 Posibilidad de mejora Respecto a los resultados arrojados y como conclusión de la sección anterior, hay varios puntos de mejora que se me ocurren: • La mejora más significativa a realizar sería aumentar el tamaño del dataset. Tomando ejemplos de cursos, del propio libro de Jordi Torres, o de casos en foros, estimo que con unos 100 o 200 valores sería suficiente para obtener una mejoría. Esto teniendo en cuenta que es posible aplicar la técnica de Transfer Learning. • Otra posible opción sería separar las líneas del flujo óptico en imágenes independientes. Es decir, por cada línea obtenida que nace de una esquina obtener una imagen. Se predeciría una imagen con una línea que representaría un movimiento de un único borde. De esta forma estaríamos consiguiendo multiplicar el número de datos por las esquinas que pudiéramos sacar de un vídeo. • Durante el desarrollo descubrí otras librerías dedicadas a Machine Learning, algunas de ellas muy interesantes y con sus ventajas frente a TensorFlow y (aunque también tenían sus desventajas). Por ejemplo: PyTorch, Scikit-learn o MXNet. Sería un buen ejercicio analizarlas y plantearse cuáles son las bondades de cada una.
49 Percepción de la acción en una imagen mediante algoritmos de aprendizaje automático 3.5 Conclusiones finales El dataset usado es bastante pequeño y no da lugar a ajustar los valores de las neuronas para que “aprendan” cosas coherentes. En la búsqueda de soluciones encontré que, por lo general, se solía considerar un dataset pequeño cuando el número de sus muestras estaba en torno a unos centenares; sin embargo, en nuestro caso apenas llegamos a una centena de estos. Por esta razón las redes neuronales no han sido capaces de aprender correctamente y dar un mejor resultado. Sin duda, el ámbito del Deep Learning es muy variado y puede servir para crear soluciones a múltiples problemas. Pero para ello se requiere tener una buena base de conocimiento y mucha experiencia si se pretende investigar. Es fácil entrenar inteligencias artificiales con grandes dataset y de propósitos típicos como los que se suelen tomar de ejemplos en libros y cursos, pero cuando se pretenden indagar en nuevas posibles soluciones que no se han llegado a plantear es más complicado de implementar. Al principio pensaba que el flujo óptico era un buen método para caracterizar acciones, sin embargo, tras ver el potencial que tienen las redes convolucionales y los resultados obtenidos, me planteo si realmente fue una buena decisión. Por ejemplo, a finales del desarrollo descubrí el cálculo de densidad de flujo óptico de Gunnar Farneback [24] u otros métodos de en los que se usan otras redes neuronales [39]. O quizás no fuera necesario aplicar el flujo óptico sino seleccionar un área concreta de los vídeos e intentar mejorar la resolución. En cualquier caso, éste ha sido mi primer acercamiento a la inteligencia artificial y, a pesar de no haber tenido los resultados esperados, he aprendido mucho y aumentado las fuentes de información de las que seguir aprendiendo. Éste era el objetivo primario de este trabajo fin de grado y puede considerarse plenamente satisfecho. Además, gracias a la facilidad de Keras, he encontrado motivación para seguir haciendo pruebas e investigaciones.
Máquina de aprendizaje automático 50 50
51 Percepción de la acción en una imagen mediante algoritmos de aprendizaje automático ANEXOS: CÓDIGOS REALIZADOS 3.6 getData.py from fileinput import filename from tkinter.ttk import Combobox from cv2 import CAP_PROP_POS_FRAMES, cornerSubPix import numpy as np import cv2 as cv import imutils from tkinter import * from tkinter.filedialog import askopenfilename from turtle import width from PIL import Image from PIL import ImageTk import random as rng """ Definición de variables globales """ #Esta variable define el tamaño de la imagen de salida en el eje horizontal #El eje vertical queda proporcional según el tamaño de la imagen RESOLUTION_IMAGE_H = 720 #Marca la cantidad de los frames por segundo de un video FPS = 30 #Se usa como etiquita principal para construir la interfaz global labelVideo #Marca las esquinas calculadas, se pone como global para que la usen varios métodos entre sí e intercambiar información global corners ############################# Declaración de funciones ############################# """" Función: Definición: Parámetros: * return: """ def validateFrame(initFrame): ''' Función: validateFrame Definición: Asegura que el valor de frame introducido no sea negativo ni mayor que el máximo del vídeo. Parámetros: * initFrame: Número de frame de inicio. return: initFrame ''' if initFrame >= max_frame-1: initFrame = max_frame-1 elif initFrame < min_frame: initFrame = min_frame return initFrame '''
Anexos: Códigos realizados 52 52 Función: change_frame Definición: Función que se encgarga de llamar a otras funciones para cambiar los valores. Parámetros: * val: valor del frame a cambiar. return: null ''' #PROBAR A QUITAR Y VER QUE NO AFECTA #def change_frame(val): # frame_init_Spinbox # frame_init_Slider def frame_init_Slider(initFrame): ''' Función: frame_init_Slider Definición: Valida el valor introducido en el Slider y cambia el valor de este. Parámetros: * initFrame: valor de frame inicial return: null ''' frame=validateFrame(int(initFrame)) get_image(frame) initFrame = frame def frame_init_Spinbox(): ''' Función: frame_init_Spinbox Definición: Validar el valor inicial de frame introducido en el Spinbox y cambia el valor de este. Parámetros: return: null ''' frame=validateFrame(initFrame.get()) get_image(frame) initFrame.set(frame) def frame_end_Spinbox(): ''' Función: frame_end_Spinbox Definición: Validar el valor final de frame introducido en el Spinbox y cambia el valor de este. Parámetros: return: null ''' frame=validateFrame(endFrame.get()) get_image(frame) endFrame.set(frame) def get_image(initFrame): ''' Función: get_image Definición: Modifica el valor del frame inicial al que se apunta y luego lo muestra en pantalla a través de la interfaz. Parámetros: * initFrame: Valor del frame a tomar.
53 Percepción de la acción en una imagen mediante algoritmos de aprendizaje automático return: null ''' cap.set(CAP_PROP_POS_FRAMES, initFrame) ret, frame = cap.read() show_image(frame) def show_image(frame): ''' Función: show_image Definición: Muestra la imagen del frame tomado como valor inicial en la interfaz. Parámetros: * frame: Matriz de la imagen. return: null ''' frame = imutils.resize(frame, width=RESOLUTION_IMAGE_H) frame = cv.cvtColor(frame, cv.COLOR_BGR2RGB) im = Image.fromarray(frame) img = ImageTk.PhotoImage(image=im) labelVideo.configure(image=img) labelVideo.image = img def change_corners_features(maxCorners, qualityLevel, minDistance, blockSize): ''' Función: change_corners_features Definición: Valida los parámetros pasados por la interfaz y realiza el cálculo de las esquinas. Parámetros: * maxCorners: Máxima número de esquinas a detectar. * qualityLevel: Calidad mínima a la que detectar las esquinas en la imagen. El valor oscila entre 0 y 1. * minDistance: Distancia euclídea mínima entre las esquinas a detectar. * blockSize: Tamaño de un bloque usado para calcular la matriz de covarianza derivativa sobre cada vecinidad de píxeles. return: null ''' #Validaciones de todos los parámetros necesarios para calcular las esquinas. #Si no se especifican valores o están fuera de los rangos admitidos se toman otros por defecto #En cada uno se crea una variable auxiliar que pasa los valores tomados a variables que almacenen el tipo que corresponda. if maxCorners.get()=='' or 0 >= int(maxCorners.get()): print("ERROR: Debe haber un número positivo de esquinas") maxCorners_aux = 10 else: maxCorners_aux = int(maxCorners.get()) if qualityLevel.get()=='' or (float(qualityLevel.get()) > 1 or float(qualityLevel.get()) < 0): print("ERROR: qualityLevel debe estar entre 0 y 1") qualityLevel_aux=0.5 print(type(qualityLevel_aux)) else: qualityLevel_aux = float(qualityLevel.get()) if minDistance.get()=='' or int(minDistance.get()) < 0: print("ERROR: La distancia mínimo debe ser un número positivo") minDistance_aux = 5 else: minDistance_aux = int(minDistance.get()) if blockSize.get()=='' or int(blockSize.get()) < 0: print("ERROR: El tamaño del bloque debe ser positivo y entero") blockSize_aux = 10
Anexos: Códigos realizados 60 60 3.1 creacion_modelo_mnist_250_250.ipynb asdf import keras import os import tensorflow as tf import numpy as np from keras.datasets import mnist import cv2 as cv from tensorflow.keras.preprocessing.image import ImageDataGenerator import matplotlib.pyplot as plt from tensorflow.keras.utils import to_categorical from keras.models import Sequential from keras.layers import Dense, Activation, Conv2D, MaxPooling2D, Flatten, InputLayer import cv2 (x_train, train_labels), (x_test, test_labels) = mnist.load_data() train_images= np.empty((len(x_train),250,250), dtype='float32') for i in range(len(x_train)): img=x_train[i] img=cv2.resize(img, (250,250)) train_images[i]=img test_images= np.empty((len(x_test),250,250), dtype='float32') for i in range(len(x_test)): img=x_test[i] img=cv2.resize(img, (250,250)) test_images[i]=img train_images = train_images / 255 test_images = test_images / 255 train_labels = to_categorical(train_labels) test_labels = to_categorical(test_labels) model = Sequential() model.add(InputLayer(shape=(1,))), model.add(Conv2D(32,(5,5),activation='relu')) model.add(MaxPooling2D((2, 2))) model.add(Conv2D(64, (5, 5), activation='relu')) model.add(MaxPooling2D((2, 2))) model.add(Flatten()) model.add(Dense(10, activation='softmax')) model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['accuracy']) model.fit(train_images, train_labels, batch_size=100, epochs=5, verbose=1) model.save('mnist_250_250_2.h5') 3.2 modelo_propio.py import keras import os import tensorflow as tf import numpy as np from keras.datasets import mnist import cv2 as cv from tensorflow.keras.preprocessing.image import ImageDataGenerator
61 Percepción de la acción en una imagen mediante algoritmos de aprendizaje automático import matplotlib.pyplot as plt from tensorflow.keras.utils import to_categorical from keras.models import Sequential from keras.layers import Dense, Activation, Conv2D, MaxPooling2D, Flatten, InputLayer import cv2 train_datagen = ImageDataGenerator( rescale = 1. /255, width_shift_range=0.2, height_shift_range=0.2 ) validation_datagen = ImageDataGenerator( rescale = 1. /255, width_shift_range=0.2, height_shift_range=0.2 ) test_datagen = ImageDataGenerator( rescale = 1. /255, width_shift_range=0.2, height_shift_range=0.2 ) train_generator = train_datagen.flow_from_directory('dataset/train', class_mode='categorical', target_size=(250, 250), shuffle=True, color_mode='grayscale') validation_generator = validation_datagen.flow_from_directory('dataset/validation', class_mode='categorical', target_size = (250, 250), shuffle=True, color_mode='grayscale') test_generator = test_datagen.flow_from_directory('dataset/test', class_mode='categorical', target_size = (250, 250), shuffle=True, color_mode='grayscale') model = tf.keras.Sequential([ InputLayer(input_shape=(250,250,1)), Conv2D(32,(3,3),activation='relu'), MaxPooling2D((2, 2)), Conv2D(64, (5, 5), activation='relu'), MaxPooling2D((2, 2)), Flatten(), Dense(3, activation='softmax') ]) model.summary() model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) history = model.fit(train_generator, validation_data = validation_generator, epochs=10, verbose=1) test_lost, test_acc = model.evaluate(test_generator) print("Test Accuracy:", test_acc) model.save('modelo_propio.h5') 3.3 modelo_mnist.py import keras import os import tensorflow as tf import numpy as np from keras.datasets import mnist
Anexos: Códigos realizados 62 62 import cv2 as cv from tensorflow.keras.preprocessing.image import ImageDataGenerator import matplotlib.pyplot as plt from tensorflow.keras.utils import to_categorical from keras.models import Sequential from keras.layers import Dense, Activation, Conv2D, MaxPooling2D, Flatten, InputLayer import cv2 from sklearn.metrics import confusion_matrix train_datagen = ImageDataGenerator( rescale = 1. /255, width_shift_range=0.2, height_shift_range=0.2 ) validation_datagen = ImageDataGenerator( rescale = 1. /255, width_shift_range=0.2, height_shift_range=0.2 ) test_datagen = ImageDataGenerator( rescale = 1. /255, width_shift_range=0.2, height_shift_range=0.2 ) train_generator = train_datagen.flow_from_directory('dataset/train', class_mode='categorical', target_size=(250, 250), shuffle=True, color_mode='grayscale') validation_generator = validation_datagen.flow_from_directory('dataset/validation', class_mode='categorical', target_size = (250, 250), shuffle=True, color_mode='grayscale') test_generator = test_datagen.flow_from_directory('dataset/test', class_mode='categorical', target_size = (250, 250), shuffle=True, color_mode='grayscale') model_mnist=keras.models.load_model('mnist_250_250_2.h5') model_mnist.summary() model_mnist.pop() model_mnist.pop() model_mnist.summary() model_mnist.trainable=False model = tf.keras.Sequential([ InputLayer(input_shape=(250,250,1)), model_mnist, Conv2D(15, (3, 3), activation='relu'), MaxPooling2D((2, 2)), Flatten(), Dense(3, activation='softmax') ]) model.summary() model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) historyFE = model.fit(train_generator, validation_data = validation_generator, epochs=10, verbose=1) test_lost, test_acc = model.evaluate(test_generator) print("Test Accuracy:", test_acc)
63 Percepción de la acción en una imagen mediante algoritmos de aprendizaje automático model.save('modelo_mnist.h5') 3.4 modelo_vgg16.py import keras import os import tensorflow as tf import numpy as np from keras.datasets import mnist import cv2 as cv from tensorflow.keras.preprocessing.image import ImageDataGenerator import matplotlib.pyplot as plt from tensorflow.keras.utils import to_categorical from keras.models import Sequential from keras.layers import Dense, Activation, Conv2D, MaxPooling2D, Flatten, InputLayer import cv2 from tensorflow.keras.applications import VGG16 train_datagen = ImageDataGenerator( rescale = 1. /255, width_shift_range=0.2, height_shift_range=0.2 ) validation_datagen = ImageDataGenerator( rescale = 1. /255, width_shift_range=0.2, height_shift_range=0.2 ) test_datagen = ImageDataGenerator( rescale = 1. /255, width_shift_range=0.2, height_shift_range=0.2 ) train_generator = train_datagen.flow_from_directory('dataset/train', class_mode='categorical', target_size=(224, 224), shuffle=True, color_mode='rgb') validation_generator = validation_datagen.flow_from_directory('dataset/validation', class_mode='categorical', target_size = (224, 224), shuffle=True, color_mode='rgb') test_generator = test_datagen.flow_from_directory('dataset/test', class_mode='categorical', target_size = (224, 224), shuffle=True, color_mode='rgb') model_vgg16 = VGG16(input_shape = (224, 224, 3), include_top = False, weights = 'imagenet') model_vgg16.summary() model_vgg16.trainable = False model = tf.keras.Sequential([ model_vgg16, Conv2D(15, (3, 3), activation='relu'), MaxPooling2D((2, 2)), Flatten(), tf.keras.layers.Dense(3, activation='softmax') ]) model.summary() model.compile(loss='categorical_crossentropy',
Anexos: Códigos realizados 64 64 optimizer='adam', metrics=['accuracy']) history = model.fit(train_generator, validation_data = validation_generator, epochs=10, verbose=1) test_loss, test_acc = model.evaluate(test_generator) test_lost, test_acc = model.evaluate(test_generator) print("Test Accuracy:", test_acc) model.save('modelo_vgg16.h5') 3.5 modelo_MobileNetV2.py import keras import os import tensorflow as tf import numpy as np from keras.datasets import mnist import cv2 as cv from tensorflow.keras.preprocessing.image import ImageDataGenerator import matplotlib.pyplot as plt from tensorflow.keras.utils import to_categorical from keras.models import Sequential from keras.layers import Dense, Activation, Conv2D, MaxPooling2D, Flatten, InputLayer import cv2 from tensorflow.keras.applications import MobileNetV2 from sklearn.metrics import confusion_matrix import tensorflow as tf import tensorflow_hub as hub from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay, classification_report #url = "https://tfhub.dev/google/tf2-preview/mobilenet_v2/feature_vector/4" #modelo_mobilenetv2 = hub.KerasLayer(url, input_shape=(224,224,3)) train_datagen = ImageDataGenerator( rescale = 1. /255, width_shift_range=0.2, height_shift_range=0.2 ) validation_datagen = ImageDataGenerator( rescale = 1. /255, width_shift_range=0.2, height_shift_range=0.2 ) test_datagen = ImageDataGenerator( rescale = 1. /255, width_shift_range=0.2, height_shift_range=0.2 ) train_generator = train_datagen.flow_from_directory('dataset/train', class_mode='categorical', target_size=(224, 224), shuffle=True, color_mode='rgb') validation_generator = validation_datagen.flow_from_directory('dataset/validation', class_mode='categorical', target_size = (224, 224), shuffle=True, color_mode='rgb') test_generator = test_datagen.flow_from_directory('dataset/test', class_mode='categorical', target_size = (224, 224),
65 Percepción de la acción en una imagen mediante algoritmos de aprendizaje automático shuffle=True, color_mode='rgb') modelo_mobilenetv2 = MobileNetV2(input_shape = (224, 224, 3), include_top = False, weights = 'imagenet') modelo_mobilenetv2.summary() modelo_mobilenetv2.trainable = False model = tf.keras.Sequential([ modelo_mobilenetv2, Conv2D(15, (3, 3), activation='relu'), MaxPooling2D((2, 2)), Flatten(), Dense(3, activation='softmax') ]) model.summary() model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) history = model.fit(train_generator, validation_data = validation_generator, epochs=10, verbose=1) test_lost, test_acc = model.evaluate(test_generator) print("Test Accuracy:", test_acc) #model.save('modelo_MobileNetV2.h5') prediccion = model.predict(test_generator) lables_predict=np.zeros(len(prediccion)) for i in range(len(prediccion)): lables_predict[i]=np.argmax(prediccion[i]) cm = confusion_matrix(test_generator.labels, lables_predict) disp = ConfusionMatrixDisplay(confusion_matrix=cm) disp.plot() print(classification_report(test_generator.labels, lables_predict)) 3.6 matriz_confusion.ipynb import keras import os import tensorflow as tf import numpy as np from keras.datasets import mnist import cv2 as cv from tensorflow.keras.preprocessing.image import ImageDataGenerator import matplotlib.pyplot as plt from tensorflow.keras.utils import to_categorical from keras.models import Sequential from keras.layers import Dense, Activation, Conv2D, MaxPooling2D, Flatten, InputLayer import cv2 from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay, classification_report test_datagen = ImageDataGenerator( rescale = 1. /255)
Anexos: Códigos realizados 66 66 test_generator = test_datagen.flow_from_directory('dataset/test', class_mode='categorical', target_size = (250, 250), shuffle=True, color_mode='rgb') # Modelo MNIST model_mnist=keras.models.load_model('modelo_mnist.h5') # Modelo VGG16 model_vgg16=keras.models.load_model('modelo_vgg16.h5') # Model MobileNetV2 model_mobile=keras.models.load_model('modelo_MobileNetV2.h5') # Modelo propio model_propio=keras.models.load_model('modelo_propio.h5') # Resultados prediccion = model_vgg16.predict(test_generator) prediccion lables_predict=np.zeros(len(prediccion)) for i in range(len(prediccion)): lables_predict[i]=np.argmax(prediccion[i]) lables_predict cm = confusion_matrix(test_generator.labels, lables_predict) disp = ConfusionMatrixDisplay(confusion_matrix=cm) disp.plot() print(classification_report(test_generator.labels, lables_predict))
67 Percepción de la acción en una imagen mediante algoritmos de aprendizaje automático REFERENCIAS [1] “MATLAB - El lenguaje del cálculo técnico - MATLAB & Simulink.” https://es.mathworks.com/products/matlab.html (Accedido: Sep. 11, 2022). [2] “MATLAB - Wikipedia, la enciclopedia libre.” https://es.wikipedia.org/wiki/MATLAB (Accedido: Sep. 12, 2022). [3] “¿Qué es Java y por qué lo necesito?” https://www.java.com/es/download/help/whatis_java.html (Accedido: Sep. 11, 2022). [4] “C (lenguaje de programación) - Wikipedia, la enciclopedia libre.” https://es.wikipedia.org/wiki/C_(lenguaje_de_programaci%C3%B3n) (Accedido: Sep. 11, 2022). [5] “Download Python | Python.org.” https://www.python.org/downloads/ (Accedido: Sep. 11, 2022). [6] “Python - Wikipedia, la enciclopedia libre.” https://es.wikipedia.org/wiki/Python (Accedido: Sep. 12, 2022). [7] “Documentation for Visual Studio Code.” https://code.visualstudio.com/docs (Accedido: Sep. 11, 2022). [8] “Visual Studio Code - Wikipedia, la enciclopedia libre.” https://es.wikipedia.org/wiki/Visual_Studio_Code (Accedido: Sep. 12, 2022). [9] “OpenCV: OpenCV Tutorials.” https://docs.opencv.org/4.x/d9/df8/tutorial_root.html (Accedido: Sep. 11, 2022). [10] “tkinter — Interface de Python para Tcl/Tk — documentación de Python - 3.10.7.” https://docs.python.org/es/3/library/tkinter.html (Accedido: Sep. 11, 2022). [11] “DaVinci Resolve 18 | Blackmagic Design.” https://www.blackmagicdesign.com/es/products/davinciresolve (Accedido: Sep. 11, 2022). [12] “DaVinci Resolve - Wikipedia, la enciclopedia libre.” https://es.wikipedia.org/wiki/DaVinci_Resolve (Accedido: Sep. 11, 2022). [13] “Top 8 Most Demanded Programming Languages in 2022.” https://www.devjobsscanner.com/blog/top8-most-demanded-languages-in-2022/ (Accedido: Sep. 11, 2022). [14] “Noticias de última hora, programas y series de televisión - RTVE.es.” https://www.rtve.es/ (Accedido: Sep. 12, 2022). [15] M. Ruiz Arahal, “Visión Artificial,” Universidad de Sevilla. [16] “Flujo óptico - Wikipedia, la enciclopedia libre.” https://es.wikipedia.org/wiki/Flujo_%C3%B3ptico (Accedido: Sep. 11, 2022). [17] “OpenCV: Optical Flow.” https://docs.opencv.org/3.4/d4/dee/tutorial_optical_flow.html (Accedido: Sep. 11, 2022). [18] “OpenCV: Harris Corner Detection.” https://docs.opencv.org/3.4/dc/d0d/tutorial_py_features_harris.html (Accedido: Sep. 12, 2022). [19] Jianbo Shi and Tomasi, “Good features to track,” in Proceedings of IEEE Conference on Computer Vision and Pattern Recognition CVPR-94, 1994, pp. 593–600. doi: 10.1109/CVPR.1994.323794. [20] “OpenCV: Shi-Tomasi Corner Detector & Good Features to Track.” https://docs.opencv.org/3.4/d4/d8c/tutorial_py_shi_tomasi.html (Accedido: Sep. 12, 2022).
Referencias 68 68 [21] “OpenCV: Operations on arrays.” https://docs.opencv.org/3.4/d2/de8/group__core__array.html#gaa0f00d98b4b5edeaeb7b8333b2de353b (Accedido: Sep. 12, 2022). [22] “OpenCV: Feature Detection.” https://docs.opencv.org/3.4/dd/d1a/group__imgproc__feature.html (Accedido: Sep. 12, 2022). [23] “OpenCV: Object Tracking.” https://docs.opencv.org/3.4/dc/d6b/group__video__track.html (Accedido: Sep. 12, 2022). [24] G. Farnebäck, “Two-Frame Motion Estimation Based on Polynomial Expansion,” 2003, pp. 363–370. doi: 10.1007/3-540-45103-X_50. [25] “Deep Learning – Introducción práctica con Keras - Jordi TORRES.AI.” https://torres.ai/deep-learninginteligencia-artificial-keras (Accedido: Sep. 11, 2022). [26] “Project Jupyter | About Us.” https://jupyter.org/about (Accedido: Sep. 11, 2022). [27] “Proyecto Jupyter - Wikipedia, la enciclopedia libre.” https://es.wikipedia.org/wiki/Proyecto_Jupyter (Accedido: Sep. 12, 2022). [28] “Te damos la bienvenida a Colaboratory - Colaboratory.” https://colab.research.google.com/?hl=es (Accedido: Sep. 11, 2022). [29] “TensorFlow - Wikipedia, la enciclopedia libre.” https://es.wikipedia.org/wiki/TensorFlow (Accedido: Sep. 12, 2022). [30] “Keras - Wikipedia, la enciclopedia libre.” https://es.wikipedia.org/wiki/Keras (Accedido: Sep. 12, 2022). [31] F. Chollet, “Deep Learning with Python, Second Edition,” Deep Learning with Python, 2021. [32] “Deep Learning, Introducción práctica con Keras (SEGUNDA PARTE) - Jordi TORRES.AI.” https://torres.ai/deep-learning-inteligencia-artificial-keras-2a-parte/#AccesoLibro2aParte (Accedido: Sep. 11, 2022). [33] “tf.keras.preprocessing.image.ImageDataGenerator | TensorFlow v2.10.0.” https://www.tensorflow.org/api_docs/python/tf/keras/preprocessing/image/ImageDataGenerator (Accedido: Sep. 12, 2022). [34] “The Sequential class.” https://keras.io/api/models/sequential/ (Accedido: Sep. 12, 2022). [35] “VGG16 and VGG19.” https://keras.io/api/applications/vgg/ (Accedido: Sep. 12, 2022). [36] “VGG: ¿Qué es este modelo? ¡Daniel te lo cuenta todo!” https://datascientest.com/es/vgg-que-es-estemodelo-daniel-te-lo-cuenta-todo (Accedido: Sep. 12, 2022). [37] “MobileNet, MobileNetV2, and MobileNetV3.” https://keras.io/api/applications/mobilenet/ (Accedido: Sep. 12, 2022). [38] “Confusion matrix — scikit-learn 1.1.2 documentation.” https://scikitlearn.org/stable/auto_examples/model_selection/plot_confusion_matrix.html (Accedido: Sep. 12, 2022). [39] W. Bao, W.-S. Lai, C. Ma, X. Zhang, Z. Gao, and M.-H. Yang, “Depth-Aware Video Frame Interpolation.” [Online]. Disponible: https://github.com/baowenbo/DAIN
69 Percepción de la acción en una imagen mediante algoritmos de aprendizaje automático