Full text
l Equation Chapter 1 Section 1 Trabajo Fin de Grado en Ingeniería de las Tecnologías de Telecomunicación Diseño de plugin sobre Raspberry Pi IV para clasificación en tiempo real del Hemangioma Infantil Autor: Pablo Cabeza Rodriguez Tutor: José Antonio Pérez Carrasco Dpto. Teoría de la Señal y Comunicaciones Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2025
Trabajo Fin de Grado en Ingeniería de las Tecnologías de Telecomunicación Diseño de plugin sobre Raspberry Pi IV para clasificación en tiempo real del Hemangioma Infantil Autor: Pablo Cabeza Rodriguez Tutor: José Antonio Pérez Carrasco Profesor Titular Dpto. de Teoría de la Señal y Comunicaciones Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2025
Trabajo Fin de Grado: Diseño de plugin sobre Raspberry Pi IV para clasificación en tiempo real del Hemangioma Infantil Autor: Pablo Cabeza Rodriguez Tutor: José Antonio Pérez Carrasco El tribunal nombrado para juzgar el Proyecto arriba indicado, compuesto por los siguientes miembros: Presidente: Vocales: Secretario: Acuerdan otorgarle la calificación de: Sevilla, 2025 El Secretario del Tribunal
A mi familia
Agradecimientos Me gustaría mostrar mi agradecimiento a todas aquellas personas que han formado parte tanto en el desarrollo de este trabajo y han sido un apoyo a lo largo de mi carrera universitaria. En primer lugar, agradecer a mi tutor, el profesor José Antonio Pérez, por guiarme durante todo este proceso resolviendo mis dudas y acompañándome en cada paso. Además, agradecer, también al profesor J.A Leñero por dedicar su tiempo a orientarme sobre el dispositivo utilizado. A Guaxara, por ser un apoyo constante e incondicional durante todo este proceso ayudándome en los momentos más difíciles. A mis padres y a mis hermanos, por estar conmigo cuando lo he necesitado y por facilitarme todo el proceso de mi enseñanza. A mis abuelos, por animarme a seguir aprendiendo. A todos mis compañeros y amigos, tanto de la ETSI como fuera de ella, que han creado en mi memoria recuerdos tan bonitos y divertidos durante la carrera y mi vida en Sevilla. A todas estas personas, les atribuyo un pedacito de este TFG que considero tanto mío como suyo. Pablo Cabeza Rodríguez Estepa, 2025
Índice de Figuras Figura 1-1. Ejemplo de Hemangioma superficial [3]. 3 Figura 1-2. Ejemplo de Hemangioma profundo [3]. 3 Figura 1-3. Ejemplo de Hemangioma mixto [3]. 4 Figura 2-1. Parte frontal del dispositivo [9]. 8 Figura 2-2. Interfaz gráfica del simulador. 9 Figura 2-3. Menú desplegable con las rutinas. 9 Figura 3-1. Estructura del proceso de adaptación. 11 Figura 3-2. Espacio de color RGB [11]. 12 Figura 3-3. Espacio de color CIE L*u*v* [13]. 13 Figura 3-4. Espacio de color CIE L*a*b* [14]. 13 Figura 3-5. Ventana de recorte en la imagen. 15 Figura 3-6. Imagen del recorte. 16 Figura 3-7. Imagen de distancias. 16 Figura 3-8. Imagen de la lesión segmentada. 17 Figura 3-9. Optimización del parámetro K. 23 Figura 3-10. Matriz de confusión del modelo kNN. 23 Figura 3-11. Algoritmo iterativo de balance de pesos. 24 Figura 3-12. Matriz de confusión del modelo SVM. 25 Figura 3-13. Pérdidas por época durante entrenamiento. 26 Figura 3-14. Matriz de confusión de la técnica MLP. 27 Figura 4-1. Ventana de selección de zona de recorte. 31 Figura 4-2. Apariencia de la clasificación. 31 Figura 4-3. Carga del modelo entrenado. 34
Notación IR Infrarrojo HI Hemangioma Infantil kNN k-nearest-Neighbours (k vecinos más cercanos) SVM Support Vector Machine (Máquina de vectores de soporte) MLP Multilayer Perceptron (Perceptrón Multicapa) RBF Radial Basis Function (Función Base Radial) LOO Leave-One-Out
1 1 INTRODUCCIÓN A motivación personal en la elección de este proyecto se ha fundamentado esencialmente en su carácter multidisciplinar. En las últimas décadas, la Ingeniería se ha convertido en una disciplina que abarca casi todos los elementos de nuestra vida cotidiana en el que se incluye la Medicina. Siempre he intentado orientar mi curiosidad en temas científicos y técnicos, los cuales, tienen un impacto directo en la vida de las personas. Por ello, he escogido este proyecto que permite integrar técnicas y conocimientos de ámbitos tan aparentemente distantes tales como: el procesamiento de imágenes, la Inteligencia Artificial y la Medicina. 1.1 Objetivos del proyecto El objetivo del presente Trabajo de Fin de Grado es proporcionar en al ámbito de la medicina una herramienta que facilite la clasificación de Hemangiomas Infantiles. Este proyecto pretende materializar los avances desarrollados por el Departamento de Teoría de la Señal y Comunicaciones, en un plugin que permita clasificar imágenes de Hemangiomas Infantiles de forma instantánea. Por ello, la idea central de este proyecto es adaptar e integrar el trabajo de Pérez Carrasco et al. [1] en un dispositivo realizado por el Instituto de Microelectrónica de Sevilla (IMSE) donde se permita la clasificación en tiempo real del Hemangioma Infantil. A raíz de este objetivo base, este proceso conlleva una serie de objetivos más específicos: - Comprender y analizar los algoritmos de segmentación y extracción de características de la imagen para una correcta adaptación a lenguaje Python. - Migrar las técnicas de clasificación de Hemangioma Infantil desde lenguaje MATLAB a Python buscando optimizar los resultados. - Implementar estas adaptaciones en un plugin que pueda ejecutarse en el dispositivo. - Minimizar las dependencias externas con el objetivo de maximizar la compatibilidad con el dispositivo. - Realizar el proceso de simulación y pruebas del plugin para asegurar su funcionalidad. Cabe destacar, que la naturaleza de esta herramienta tiene un carácter estrictamente complementario e informativo al diagnóstico realizado por los propios médicos, ya que, la motivación de este trabajo no se centra únicamente en los resultados finales sino en el proceso que lleva a la integración del trabajo de Pérez Carrasco et al. [1] a un dispositivo real. L
Introducción 2 1.2 Estructura del proyecto En base a los objetivos marcados, la estructura de trabajo se divide en dos grandes bloques: - Adaptación: abarca la adaptación y migración de las técnicas de segmentación, extracción de características y clasificación desde MATLAB a Python, respetando al máximo su funcionamiento original y garantizando su posterior implementación. Esta parte incluye también el análisis de los fundamentos teóricos que sustentan estos algoritmos. - Implementación: constituye la fase esencial del proyecto. Centrada en el desarrollo del plugin, su integración en el dispositivo y la validación mediante simulaciones y pruebas para garantizar su funcionamiento estable en un entorno real. A continuación, se contextualizan los tres pilares fundamentales de este proyecto: el Hemangioma Infantil, el Aprendizaje Automático y los avances científicos recientes relacionados con esta área de aplicación. 1.3 El Hemangioma Infantil El hemangioma infantil (HI) es el tumor vascular más común en la infancia [2]. La diferencia principal del hemangioma infantil, con respecto a otros tumores vasculares, radica en que tienden a involucionar tras su fase proliferativa [3]. Sin embargo, la importancia de diagnosticar y clasificar este tipo de tumor vascular reside en que existe una parte de los HI que desarrollan complicaciones graves provocando fallo cardíaco, ulceración o malformaciones permanentes [2]. No se puede obviar, por tanto, la incidencia que tiene el HI en la población actual debido a una incidencia que oscila entre el 2 y el 10 % de los recién nacidos, siendo 10-15% de los mismos los que puedan desarrollar futuras complicaciones como las anteriormente mencionadas [2]. Estos datos demuestran que es esencial que los profesionales médicos especialistas cuenten con la mayor cantidad de recursos posible para poder determinar el grado de riesgo del hemangioma infantil de forma temprana para poder actuar en consecuencia paliando a tiempo los efectos adversos que puedan ir desarrollando los pacientes. El HI sigue un desarrollo dinámico, presentando dos fases clave en su ciclo de vida: la proliferación y la involución. La proliferación se suele dar durante los primeros 12 meses de vida en los que aumenta de tamaño y adquiere una cierta consistencia. Es en esta etapa, donde puede darse el problema de la ulceración. La involución comienza a darse, por lo general, entre los 6 y 12 meses de vida [3]. El diagnóstico general en este tipo de lesión vascular se fundamenta en la historia clínica y la exploración física. En caso de diagnóstico incierto, o si es necesaria una evaluación de la extensión, se recomienda la realización de pruebas de imagen de la lesión [3]. Esto último es el eje central del presente proyecto. Durante la fase de proliferación, este examen físico permite clasificar los HI en tres tipos según su profundidad de tejido blando: profundos, mixtos y superficiales. - HI superficiales: Se caracterizan por presentar una mancha de color rojo y superficies sutilmente discernibles al tacto. Este tipo de HI suele aparecer de forma más prematura que los profundos, y de la misma manera, tienden a empezar fase de involución antes [3]. En la Figura 1-1, se puede apreciar lo anteriormente comentado donde se observa el color típico de este tipo de Hemangiomas.
3 3 Diseño de plugin sobre Raspberry Pi IV para clasificación en tiempo real del Hemangioma Infantil Figura 1-1. Ejemplo de Hemangioma superficial [3]. - HI profundos: La capacidad de discernir y diagnosticar este tipo de hemangioma infantil supone una complicación adicional en comparación con el superficial. Este tipo de HI puede resultar en ligeros matices azulados acompañados de una moderada hinchazón. También pueden no presentar evidencias aparentes de forma superficial. Dado que los signos de aparición de este tipo de HI suelen ser mucho más sutiles que los superficiales, es esencial que sigan un seguimiento más minucioso de su evolución y que se empleen técnicas alternativas a las convencionales para su inspección y seguimiento [3]. La Figura 1-2 destaca un HI profundo que presenta un abultamiento considerable pero no presenta componentes en la superficie de la piel. Figura 1-2. Ejemplo de Hemangioma profundo [3]. - HI mixtos: Lo componen aquellos HI cuyos signos de aparición y características son combinación de las dos anteriores, superficial y profundo [3]. La Figura 1-3 refleja esta combinación de características de ambos tipos de HI. Se puede observar una hinchazón considerable y una mancha superficial de un color entre rojizo y azulado.
Introducción 4 Figura 1-3. Ejemplo de Hemangioma mixto [3]. En ocasiones, el tratamiento del HI se realiza suministrando al paciente propranolol oral [2]. Sin embargo, es fundamental un monitoreo constante de la evolución del HI para prevenir posibles complicaciones. 1.4 Aprendizaje máquina (Machine Learning) Actualmente, es evidente cómo la Inteligencia Artificial ha ocupado casi todas las disciplinas y oficios de nuestra sociedad. La Inteligencia Artificial es la actividad dedicada a hacer que las máquinas sean inteligentes, y la inteligencia es esa cualidad que permite a una entidad funcionar de manera adecuada y con previsión en su entorno [4]. Los modelos de Aprendizaje Máquina o Machine Learning forman un subgrupo dentro de esta disciplina tan amplia como es la de la Inteligencia Artificial. El Machine Learning es el campo de estudio que da a los ordenadores la capacidad de aprender sin ser programadas explícitamente [5]. En la última década, los avances técnicos en capacidad de computación y ciencia de datos han dado cabida a modelos de Machine Learning muy robustos y precisos. La relevancia de modelos de Aprendizaje Máquina en soluciones a problemas de tan amplio espectro está fundamentada en la capacidad de captar relaciones no lineales entre los datos, mejorar el modelo en función de los datos de entrenamiento o la detección de patrones complejos, entre otros. Todos estos avances en el campo del Aprendizaje Máquina han propiciado que esta disciplina se considere como una opción altamente recomendable dentro del presente proyecto, debido a su eficacia para resolver problemas en múltiples aspectos dentro de la ciencia y la ingeniería. 1.5 Aplicación actual del Aprendizaje Máquina en el Hemangioma Infantil En este apartado, se pretende exponer los trabajos y estudios existentes en la actualidad sobre la clasificación del HI mediante técnicas de Aprendizaje Máquina. Entre ellos, es necesario mencionar el artículo de Pérez Carrasco et al. [1] ya que él se fundamenta el presente proyecto. Dicho trabajo, presenta varias técnicas de clasificación del HI, así como la selección óptima de características para entrenar los modelos. Además, describe detalladamente los procesos de segmentación y extracción de características de las imágenes médicas. La importancia del artículo de Pérez Carrasco et al. [1] en este proyecto es vital ya que se busca adaptar la metodología desarrollada originalmente en MATLAB a Python, optimizando su funcionalidad y adaptándola para ejecutarse en un dispositivo Raspberry Pi IV.
5 5 Diseño de plugin sobre Raspberry Pi IV para clasificación en tiempo real del Hemangioma Infantil
Adaptación 12 12 3.1 Segmentación y extracción de características Este es el primer paso en el procesamiento de la imagen del Hemangioma Infantil. Tiene como objetivo recibir la imagen, segmentar la zona perteneciente a la lesión de la piel circundante y extraer un total de 28 características estadísticas de color y textura de la zona deseada. Para ello, se ha desarrollado un código en lenguaje Python que desempeña ordenadamente las siguientes tareas para llegar los resultados especificados: 1. Lectura y recorte de imagen. 2. Transformación a espacio de color L*u*v*. 3. Cálculo de centroides e imagen de distancias. 4. Aplicación de método de segmentación. 5. Cálculo estadístico de características de la zona segmentada. Una vez expuestos estos pasos seguidos en la elaboración de este bloque del proyecto, se entrará en detalle de cada uno de ellos clarificando previamente algunos aspectos teóricos necesarios para la compresión de los mismos. 3.1.1 Fundamento teórico 3.1.1.1 Espacios de color Para comprender la extracción de características de color de la imagen, es fundamental esclarecer cuales son las distintas conversiones entre los distintos espacios de color que se aplican en este apartado. El espacio de color en el cual está representado la Figura 3-2 es el espacio sRGB. El espacio de color sRGB, también es conocido por el nombre de su estándar RGB establecido en su norma [10]. Este espacio de color es un modelo de color aditivo formado por los tres colores primarios: rojo, verde y azul. Figura 3-2. Espacio de color RGB [11]. La primera conversión realizada es del espacio de color sRGB al espacio de color CIE XYZ 1931 [12]. La transformación del espacio sRGB al espacio CIE XYZ se realiza siguiendo la Ecuación 3.1 obtenida del estándar [12]. Esta conversión presente en Ecuación 3.1 permite obtener las componentes X, Y, Z de un píxel a partir de las componentes R, G y B.
13 Diseño de plugin sobre Raspberry Pi IV para clasificación en tiempo real del Hemangioma Infantil ! 𝑋 𝑌 𝑍 % =' ! 0.4124 0.3578 0.1805 0.2126 0.7152 0.0722 0.0193 0.1192 0.9505 %! 𝑅 𝐺 𝐵 % (3-1) Uno de los espacios de color con más relevancia es el CIE 1976 L*u*v* [12] que es una transformación del espacio de color CIE XYZ. Esta transformación de espacio de color tiene como objetivo representar los colores de forma más uniforme perceptualmente. Presenta tres componentes: - L*: representa la componente de luminosidad. - u*: es la coordenada cromática que indica la tendencia del color hacia el eje rojo-verde. - v*: es la coordenada cromática que indica la tendencia del color hacia el eje azul-amarillo. Esta conversión se implementa siguiendo la norma oficial CIE 15:2004 [13]. Una vez obtenidos los componentes del espacio de color CIE L*u*v*, es fundamental registrar dos parámetros derivados que forman parte de las 28 características extraídas en el artículo base [1] de este proyecto. Estos son: la saturación (su,v) y el tono o hue (huv), los cuales se calculan siguiendo lo establecido en la norma citada [12]. El último espacio de color que se emplea es el CIE 1976 L*a*b*, también es una derivación del espacio de color CIE XYZ. De forma similar al CIE L*u*v*, sus parámetros representan la luminosidad (L*), la componente cromática en el eje rojo-verde (a*) y la componente cromática en el eje amarillo-azul (b*). Esta conversión de espacio de color también se realiza siguiendo también la norma mencionada [12]. En la Figura 3-3 y la Figura 3-4, se plasma la representación gráfica que suele hacerse de ambos espacios de color, el CIE L*u*v* y el CIE L*a*b*, respectivamente. Figura 3-3. Espacio de color CIE L*u*v* [13]. Figura 3-4. Espacio de color CIE L*a*b* [14].
Adaptación 14 14 3.1.1.2 Imagen de distancias El cálculo de la imagen de distancias es el primer paso en la segmentación de la lesión y tiene como objetivo estimar la similitud perceptual entre píxeles para aislar correctamente la región de interés. Para ello se emplea el espacio CIE L*u*v*, ya que sus distancias reflejan diferencias de color perceptibles. Primero se calculan los centroides (Lcentr , ucentr y vcentr) de las coordenadas L*u*v* como promedio de cada componente para todos los píxeles. Luego, se construye una imagen de distancias donde cada píxel (i) representa la diferencia entre su color y el color promedio [9]. Este cálculo queda reflejado en la Ecuación 3.2. 𝐷𝑖𝑠𝑡𝑎𝑛𝑐𝑖𝑎!=' = (𝐿! ∗−𝐿#$%&')(+(𝑢! ∗−𝑢#$%&')(+(𝑣! ∗−𝑣#$%&')( (3-2) 3.1.1.3 Método Otsu de segmentación El método Otsu es un algoritmo que determina el umbral óptimo para segmentar una imagen en dos regiones [15]. El método Otsu recibe como entrada un histograma en formato vectorial que representa la frecuencia de aparición de cada nivel de intensidad en la escala de grises y devuelve el umbral óptimo para separar en dos regiones. Este proyecto se fundamenta en una versión de Otsu llamada Otsu Modificado, basada en el trabajo de Acha et al. [16], que permite un refinamiento iterativo del umbral para mejorar la segmentación. 3.1.1.4 Cálculo estadístico de características Tras segmentar la lesión del HI, el trabajo de Pérez Carrasco et al. [1] proporciona aquellas características esenciales de la lesión que tienen la información necesaria para lograr una correcta clasificación. Se extraen un total de 28 características estadísticas de los tres planos en los espacios de color previamente mencionados [1], incluyendo entre otras: la media, la desviación estándar, la curtosis y la asimetría. Las características extraídas corresponden a los espacios de color RGB [µR, µG, µB], CIE L*u*v* [µL, µu, µv, σL, σu, σv, sL, su, sv, kL, ku, kv , µh , σh , θh, µs , σs ] y CIE L*a*b* [µa, µb, σa, σb, sa, sb, ka, kb].
15 Diseño de plugin sobre Raspberry Pi IV para clasificación en tiempo real del Hemangioma Infantil 3.1.2 Desarrollo Expuesto el marco teórico del procesamiento de la imagen del Hemangioma Infantil, se procede ahora a mostrar el proceso de adaptación del lenguaje original MATLAB a Python. Esta adaptación busca ser lo más fiel posible al comportamiento original del código y garantizar su correcta integración en el plugin. Para verificar que la adaptación es correcta, se aplicarán los pasos descritos en el Apartado 3.1.1 a una imagen que muestra un Hemangioma Infantil, comprobando que cada paso se ejecuta adecuadamente. 3.1.2.1 Preprocesamiento En este subapartado, se abordan los primeros pasos en el tratamiento de la imagen del Hemangioma que son la lectura de la imagen, el recorte dinámico y la conversión al espacio de color CIE L*u*v*. Primero, se lee y se muestra la imagen a recortar mediante la biblioteca OpenCV. La figura 3-5 muestra la ventana de inicio al ejecutar el código en la cual se muestra una imagen y se permite la opción de elegir manualmente el tamaño y la posición del recorte. Figura 3-5. Ventana de recorte en la imagen. La imagen recortada se describe con tres matrices que corresponden a las componentes R, G y B. Luego, se vectorizan las tres matrices RGB y se organizan en una sola matriz, donde cada fila representa un píxel y cada columna una coordenada de color (R, G o B). Esta matriz es la que se envía a la función rgb2luv que devuelve las componentes correspondientes en el espacio de color CIE L*u*v*, incluyendo el tono y la saturación.
Adaptación 16 16 3.1.2.2 Imagen de distancias El objetivo de este apartado es generar una imagen de distancias que refleja la similitud de los píxeles en el espacio CIE L*u*v*, en función de su cercanía a los centroides de las coordenadas L*, u* y v*. Primero, se calcula el vector de distancias mediante la Ecuación 3.2, se reorganiza como matriz con las dimensiones del recorte y se normaliza. Esta matriz representa la luminosidad en escala de grises; los píxeles más oscuros corresponden a zonas con distancias menores (No Hemangioma) y los más claros a zonas con distancias mayores (Hemangioma). La Figura 3-6 muestra el recorte original y la Figura 3-7 la imagen de distancias. En la Figura 3-7, se muestra la eficacia de este método para captar diferencias perceptivas en las dos zonas que se buscan segmentar. Realizado todo lo anterior, se procede a la segmentación de la imagen mediante el método Otsu Modificado [16]. Figura 3-6. Imagen del recorte. Figura 3-7. Imagen de distancias. 3.1.2.3 Segmentación El objetivo es encontrar un umbral óptimo que permita separar la lesión de la piel sana usando la matriz de distancias mediante la función otsu_Seg, implementada siguiendo el método de Acha et al. [16]. Con este objetivo, se genera un histograma de intensidades a partir de la matriz de distancias y se pasa como entrada a la función otsu_Seg. Esta función devuelve el valor de umbral óptimo normalizado. Este umbral permite clasificar los píxeles como lesión o no lesión y segmentar así, la imagen recortada. Para visualizar de forma gráfica la correcta segmentación de la imagen, se han puesto en color blanco estos índices correspondientes a la zona no lesiva. La Figura 3-8 muestra que la segmentación se ha realizado con éxito visualizándose en color prácticamente la totalidad de la lesión.
17 Diseño de plugin sobre Raspberry Pi IV para clasificación en tiempo real del Hemangioma Infantil Figura 3-8. Imagen de la lesión segmentada Con este procedimiento, se ha podido confirmar la correcta adaptación de los mecanismos de segmentación implementados originalmente en MATLAB a su equivalente en Python, garantizando así su correcta implementación en el plugin final. 3.1.2.4 Extracción de características Una vez completada la segmentación de la lesión, se extraen las características necesarias para describirla y clasificarla con precisión. Estas 28 características, detalladas en el Apartado 3.1.1.4, constituyen la base para las técnicas de clasificación utilizadas posteriormente. Con todo lo anterior, se tiene el código del tratamiento de la imagen, para segmentar y extraer las características de la lesión, listo para su implementación en el plugin
Adaptación 18 18 3.2 Migración de las técnicas de clasificación Este apartado pretende exponer los retos y aspectos técnicos implicados en la migración de las técnicas de clasificación desde su desarrollo original [1] en MATLAB a un entorno más enfocado en la implementación real en el plugin en Python. Por tanto, se ha buscado optimizar estas técnicas con el objetivo de los resultados sean lo más similares posibles a los de su concepción original. Las técnicas de clasificación que se han considerado en este proyecto han sido tres: kNN, SVM y Perceptrón Multicapa (MLP). La optimización de estos modelos ha sido clave para asegurar la fiabilidad de los resultados en el nuevo entorno de ejecución. Por ello, se entra brevemente en algunos de los aspectos técnicos involucrados en este proceso de adaptación y ajuste. A continuación, se describe la metodología seguida en esta parte del proyecto estableciendo previamente los fundamentos teóricos esenciales para su comprensión. 3.2.1 Fundamento teórico El fundamento teórico de esta sección del proyecto no busca entrar en todos los conceptos teóricos implicados en los distintos modelos de Machine Learning, sino poner en contexto de las ideas claves en las que se fundamenta el proceso de adaptación. 3.2.1.1 Clasificación multiclase En Machine Learning, es indispensable determinar cuál es el tipo de problema que se quiere solucionar. La finalidad es clasificar entre tres clases (mixto, profundo y superficial), definiendo así un problema de clasificación multiclase. Además, el aprendizaje es de tipo supervisado, puesto que los datos de entrada incluyen etiquetas que indican su clase. Los clasificadores multiclase son esenciales en el campo del aprendizaje automático supervisado, cuyo objetivo fundamental es asignar una observación a una de entre tres o más clases posibles. 3.2.1.2 Procesamiento de datos Los datos son la base de cualquier modelo de Machine Learning y se componen de características y de etiquetas (aprendizaje supervisado). Cada muestra, se describe mediante un conjunto N de características que definen un espacio N-dimensional y aportan la información necesaria para que el modelo pueda aprender. Por tanto, a medida que aumenta el número de características aumenta la complejidad del modelo. Para desarrollar un modelo fiable, los datos suelen dividirse en tres conjuntos: - Datos de entrenamiento: Representan más de la mitad de los datos y se usan para que el modelo ajuste sus parámetros según las etiquetas o valores objetivo. - Datos de validación: Se emplean para evaluar el rendimiento del modelo ya entrenado y optimizar sus parámetros. - Datos de prueba (test): son datos que solamente se utilizan para medir el desempeño final del modelo. Estos datos de entrada necesitan una serie de transformaciones para optimizar el rendimiento del modelo. Por tanto, la transformación más importante es la estandarización de los datos. Los algoritmos de Machine Learning no funcionan correctamente cuando los datos de las distintas características están en escalas distintas [17].
19 Diseño de plugin sobre Raspberry Pi IV para clasificación en tiempo real del Hemangioma Infantil 3.2.1.3 Entrenamiento del modelo El método de validación presente en las tres técnicas de clasificación de este proyecto es el Leave-One-Out (LOO). Muchos estudios adoptan la validación cruzada leave-one-out para evaluar el rendimiento de un algoritmo de clasificación cuando el número de instancias en un conjunto de datos o el número de instancias para un valor de clase es pequeño [18]. Por tanto, LOO permite aprovechar al máximo los datos disponibles y obtener una estimación fiable del rendimiento del modelo en conjuntos reducidos. 3.2.1.4 Técnica kNN La primera técnica de clasificación que se desea adaptar es kNN (k-Nearest Neighbors o k vecinos más cercanos) es un algoritmo de clasificación de aprendizaje supervisado y no paramétrico, que utiliza la proximidad para realizar clasificaciones o predicciones sobre la agrupación de un dato individual [17]. El algoritmo kNN organiza los datos de entrenamiento en un espacio vectorial según sus características. Para clasificar un nuevo dato, calcula la distancia euclidiana a todos los puntos y selecciona los k más cercanos. La clase más frecuente entre estos vecinos se asigna como resultado final. 3.2.1.5 Técnica SVM La máquina de vectores de soporte (SVM) es un algoritmo de Machine Learning supervisado que clasifica los datos mediante la búsqueda de una línea o un hiperplano óptimos que maximice la distancia entre cada clase en un espacio N-dimensional [17]. Para ello, utilizan los vectores de soporte, que son los puntos de entrenamiento más cercanos a la frontera de decisión. Si los datos no pueden separarse linealmente, se emplea un kernel que proyecta los datos a un espacio de mayor dimensión para encontrar una separación adecuada. De este modo, SVM es capaz de generar fronteras de decisión no lineales, adaptándose a distribuciones complejas y mejorando su capacidad de clasificación. 3.2.1.6 Técnica MLP El Perceptrón Multicapa o Multilayer Perceptron (MLP) consiste en un conjunto de capas totalmente conectadas (fully connected) que transforman los datos de entrada de una dimensión a otra. Se denomina multicapa porque incluye una capa de entrada, una o varias capas ocultas y una capa de salida [17]. Las capas están compuestas por neuronas. Cada neurona, recibe valores multiplicados por pesos desde la capa anterior, los procesa con una función de activación no lineal y transmite el resultado a la siguiente capa, permitiendo al modelo aprender relaciones complejas entre las variables. Sin embargo, lo esencial a destacar en esta técnica de clasificación son aquellos parámetros o aspectos que han jugado un papel clave para que la migración a Python se realice correctamente y de forma fiel a los resultados originales. Destacan la arquitectura de la red, la función de pérdidas, el optimizador, la normalización de datos o la prevención del sobreajuste (overfitting) entre otros.
Adaptación 20 20 3.2.1.7 Figuras de mérito El proceso de migración de las técnicas de clasificación debe tener una serie de métricas que permitan comprobar que los resultados son acordes con los obtenidos en el código original de MATLAB. Por ello, es imprescindible evaluar de forma cuantitativa el desempeño cada una de las tres técnicas de clasificación consideradas. Las figuras de mérito elegidas son muy comunes para evaluar los modelos de clasificación, estas son: - Exactitud (Accuracy), - Precisión (Precision), - Sensibilidad (Recall) - Especificidad (Especificity). - F1-Score. La exactitud (Accuracy) es una medida del total de predicciones correctas sobre todas las muestras. Es una medida global que permite servir para medir el desempeño general de la clasificación. Se calcula mediante la Ecuación 3.3, donde TP (True Positive), es cada muestra clasificada correctamente para cada clase i-ésima y N, el número total de muestras. 𝐸𝑥𝑎𝑐𝑡𝑖𝑡𝑢𝑑 = *∑𝑇𝑃!! 𝑁 (3-3) La precisión (Precision) es una medida calculada por cada clase. Indica cuántos de los elementos predichos como pertenecientes a una clase realmente lo son. Se calcula para cada clase i, mediante la Ecuación 3.4, con las muestras correctas, TP, y los falsos positivos, FP. 𝑃𝑟𝑒𝑐𝑖𝑠𝑖ó𝑛!= * 𝑇𝑃! 𝑇𝑃!+𝐹𝑃! (3-4) Otro parámetro calculado por clase es la sensibilidad (Recall), que mide la capacidad del modelo para detectar correctamente los casos positivos. Se calcula de la misma manera que la precisión con la diferencia que se calcula con los falsos negativos de cada clase, FN, en lugar de con los falsos positivos. Esto se refleja en la Ecuación 3.5. 𝑆𝑒𝑛𝑠𝑖𝑏𝑖𝑙𝑖𝑑𝑎𝑑!= * 𝑇𝑃! 𝑇𝑃!+𝐹𝑁! (3-5)
21 Diseño de plugin sobre Raspberry Pi IV para clasificación en tiempo real del Hemangioma Infantil La siguiente figura de mérito es la especificidad (Especificity). Indica la proporción de negativos correctamente clasificados, TN (True Negative). Es el complemento de la precisión, pero aplicado a la clase negativa, TN. También es calculado para cada clase i-ésima mediante la Ecuación 3.6. 𝐸𝑠𝑝𝑒𝑐𝑖𝑓𝑖𝑐𝑖𝑑𝑎𝑑!= * 𝑇𝑁! 𝑇𝑁!+𝐹𝑃! (3-6) Finalmente, se incluye el F1-Score, un parámetro ampliamente utilizado en la evaluación de modelos de clasificación en Machine Learning. Este parámetro se calcula a partir de la Precisión calculada en la Ecuación 3.4 y la sensibilidad (Recall) obtenida con la Ecuación 3.5. Su cálculo para cada clase (i) se detalla en la Ecuación 3.7. La figura de mérito contemplada en esta memoria es el valor medio del F1-Score respecto a las 3 clases. 𝐹1!= 2 ×* 𝑟𝑒𝑐𝑎𝑙𝑙!" *× **𝑝𝑟𝑒𝑐𝑖𝑠𝑖𝑜𝑛! 𝑟𝑒𝑐𝑎𝑙𝑙!**+**𝑝𝑟𝑒𝑐𝑖𝑠𝑖𝑜𝑛! (3-7)
Adaptación 28 28 3.2.3 Comparativa de la migración de las técnicas de clasificación La migración de las tres técnicas de clasificación no se puede considerar exitosa hasta que no se la compara con sus resultados obtenidos originalmente en MATLAB [9]. Al analizar la exactitud (Accuracy) y el F1-Score de cada técnica, se observa lo siguiente: - Técnica kNN: La adaptación en Python ha logrado una leve mejora, alcanzando una exactitud de 0.759 y un F1-Score de 0.747, frente a los 0.725 y 0.731 obtenidos respectivamente en MATLAB. - Técnica SVM: Esta técnica implementada en Python también supera los resultados originales, alcanzando una exactitud de 0.819 y un F1-Score de 0.81, frente a los 0.783 y 0.7826 obtenidos respectivamente en MATLAB. - Técnica MLP: En esta técnica, aunque la exactitud en Python (0.747) es ligeramente inferior a la obtenida en MATLAB (0.7527), el F1-Score mejora notablemente, pasando de 0.683 en MATLAB a 0.724 en la adaptación en Python.
29 Diseño de plugin sobre Raspberry Pi IV para clasificación en tiempo real del Hemangioma Infantil
Implementación 30 30 4 IMPLEMENTACIÓN n este capítulo se va a describir la parte más importante del presente proyecto que es el proceso de creación de un plugin que permita la clasificación del Hemangioma Infantil en el dispositivo Raspberry Pi IV. Esta etapa abarca todas las fases desde la creación del plugin hasta la verificación de su correcta implementación, poniendo especial atención en los retos técnicos afrontados y las soluciones aplicadas durante el proceso. La creación del plugin se realiza partiendo de los algoritmos adaptados a Python del capítulo anterior. En este contexto, se desea integrar en el plugin el código de extracción de características de la lesión y una de las tres técnicas de clasificación. 4.1 Requisitos del entorno y contexto de implementación El punto de partida de la implementación es conocer cuáles son aquellos requisitos y especificaciones que debe reunir el plugin para su correcta integración en la aplicación del dispositivo. Estos requisitos deben guiar en todo momento el proceso de implementación, y son los siguientes: - Dependencias: No se permite utilizar ni importar ninguna librería externa distinta de OpenCV y NumPy. - Estructura: El archivo del plugin debe estar en una carpeta llamada “plugins” y tener extensión .py. Además, su clase debe heredar de la clase base de la aplicación llamada PluginBase. - Constructor: Debe declarar el atributo name con el nombre de la rutina el cual aparecerá en el menú desplegable de rutinas. Conocidos estos requisitos, se ha afrontado el proceso de adaptación garantizando en todo momento su cumplimiento. Uno de los aspectos más exigentes ha sido la restricción al no poder utilizar dependencias externas más allá de OpenCV y NumPy. Este desafío se ha abordado como una oportunidad para optimizar el diseño del plugin, implementando manualmente algunos algoritmos de procesamiento y la predicción del modelo de Machine Learning. Además, se cuenta con un emulador denominado “PluginTester”. Este está diseñado para replicar de manera precisa el comportamiento y funcionamiento de la aplicación en la Raspberry Pi IV. Este emulador facilita la simulación completa del entorno operativo, lo que permite pruebas y ajustes detallados del plugin sin necesidad de disponer físicamente de la Raspberry Pi IV. E
31 Diseño de plugin sobre Raspberry Pi IV para clasificación en tiempo real del Hemangioma Infantil 4.2 Estructura del plugin El plugin desarrollado busca integrar una serie de funcionalidades con el objetivo de que el usuario pueda realizar la clasificación del Hemangioma Infantil de forma intuitiva y rápida. Estas funcionalidades se encuentran estructuradas en el mismo código del plugin, organizadas de manera modular para facilitar su mantenimiento, comprensión o posible ampliación. Además, todas las funciones necesarias para que el plugin funcione correctamente están integradas en el mismo archivo del plugin. Esto hace que no dependa de librerías externas adicionales, salvo OpenCV y NumPy. Esta estructura está compuesta por los siguientes bloques: - Interfaz de usuario. - Clase Principal (hemangiomaClass). - Funciones de segmentación y extracción de características. - Predicción mediante uno de los modelos de clasificación entrenados. Los siguientes apartados detallan el funcionamiento de cada uno de estos bloques en el plugin. 4.3 Interfaz de usuario Este proyecto busca ejecutarse en un entorno real. Por tanto, necesita de una forma en la que el usuario (profesional médico) pueda interactuar correctamente con el dispositivo. La interfaz de usuario de este plugin es simple e intuitiva. Su objetivo principal es permitir al profesional seleccionar la zona donde se localiza el hemangioma y dar la orden de ejecutar los algoritmos correspondientes para obtener de forma inmediata su clasificación. Ahora, se hace un seguimiento del proceso de interacción usuario-plugin en el que se muestra también su ejecución y apariencia gráfica. El funcionamiento de la interfaz es el siguiente: 1. El usuario selecciona la rutina, “IH Classifier” del menú desplegable de la aplicación y automáticamente se le abre una ventana llamada, “Seleccionar Recorte”. 2. Esta ventana permite al usuario definir con precisión la zona donde se realizará el recorte de la imagen para segmentar la lesión. Al hacer clic, aparece un rectángulo en la zona deseada, identificado con el texto, "Zona Recorte", que se puede ajustar para seleccionar exactamente el área de interés, garantizando una segmentación más precisa y efectiva. Sin embargo, sí es ajustable en el plugin mediante la variable tamMarco. La Figura 4-1 muestra la apariencia de esta ventana. 3. La zona del recorte puede actualizar su posición todas las veces necesarias hasta que se haga doble clic. Tras esto, se ejecutan automáticamente los algoritmos de segmentación, extracción de características y clasificación del Hemangioma. 4. El resultado de la clasificación se obtiene automáticamente al hacer doble clic. La Figura 4-2 permite visualizar cómo sería la apariencia del resultado de la clasificación del HI con este plugin. Esta interfaz facilita un flujo de trabajo eficiente y accesible, permitiendo al usuario interactuar de manera directa y sencilla con el sistema.
Implementación 32 32 Figura 4-1. Ventana de selección de zona de recorte. Figura 4-2. Apariencia de la clasificación.
33 Diseño de plugin sobre Raspberry Pi IV para clasificación en tiempo real del Hemangioma Infantil 4.4 Clase principal La clase hemangiomaClass es el componente central del plugin, cuya función principal es coordinar y controlar todo el flujo de ejecución. Esta clase incluye el método clave processImage, que se ejecuta cada vez que la aplicación invoca cualquiera de los plugins. Por tanto, cada vez que se selecciona una rutina en el menú desplegable, se ejecuta este método dentro del plugin. El propósito de processImage es recibir una imagen de entrada y devolver otra imagen procesada. Por esta razón, la adaptación del plugin para la Raspberry Pi IV se ha centrado en procesar esta imagen de entrada mediante los algoritmos de extracción de características para luego realizar la clasificación. Como lo que devuelve es una imagen, se entrega la imagen resultante con la clasificación del tipo de HI superpuesta, facilitando así, la interpretación de los resultados. Dentro de processImage se gestionan y orquestan todos los algoritmos involucrados en el proceso. Las funcionalidades que realiza durante su ejecución son: 1. Abrir y configurar la ventana de selección de recorte. 2. Capturar la posición del clic y definir la zona de recorte. 3. Realizar el recorte y enviar la imagen recortada a las funciones de extracción de características. 4. Cargar los parámetros del modelo de clasificación entrenado. 5. Llamar a la función de clasificación del HI. 6. Recibir resultado de la clasificación y mostrarlo adecuadamente por la pantalla en la aplicación. 4.5 Funciones de segmentación y extracción de características Todos los algoritmos adaptados en el Apartado 3.1 se han integrado en la estructura del plugin de forma modular. Además, se ha optimizado el código para que sea lo más eficiente posible, eliminando todo aquello que no contribuya directamente a la segmentación y extracción de características. Cada uno de los distintos algoritmos implicados en este proceso, se han implementado manualmente mediante las siguientes funciones: 1. Función skewP(x): Esta función se utiliza para calcular el parámetro estadístico de la asimetría sin depender de la biblioteca SciPy. Recibe como entrada el dato (x) y devuelve su valor de asimetría, replicando lo más fielmente posible el cálculo interno de la función original, basado en la información disponible en su documentación [21]. 2. Función kurtP(x): De forma análoga, se ha replicado el cálculo estadístico de la curtosis siguiendo la documentación oficial de SciPy [22]. 3. Función rgb2luv (RGBrgb): Esta función, explicada en el Apartado 3.1, realiza la conversión del espacio de color CIE RGB al espacio CIE L*u*v* según la norma [12]. Toma como entrada la matriz RGBrgb, con cada componente (R, G y B) representado como vector columna, y devuelve las componentes L, u, v, junto con el tono y la saturación. 4. Función RGB2lab (RGBrgb): De forma similar a la anterior, la función realiza la conversión al espacio de color CIE L*a*b* devolviendo sus tres coordenadas (L, a* y b*). 5. Función otsu_seg (histLuv): Esta función implementa el método Otsu Modificado [16] para segmentar
Implementación 34 34 automáticamente el HI en función de su histograma de luminancia histLuv. Es la misma que se explica en el Apartado 3.1 y devuelve el umbral óptimo que permite segmentar la lesión de la zona no lesiva. 6. Función getFeatures (r_img): Es la función más importante de este proceso y es la que se invoca desde el método principal (processImage) con el objetivo de realizar todo el proceso de segmentación y extracción de características. Recibe como único parámetro la imagen recortada r_img. A partir de este parámetro, realiza todo el proceso que incluye: a. Preprocesamiento del recorte. b. Cálculo de imagen de distancias. c. Segmentación mediante Otsu. d. Cálculo de las 28 características de la lesión. Para completar estas tareas, utiliza las funciones auxiliares descritas anteriormente, invocándolas de forma modular según se requiera en cada etapa. La función getFeatures, devuelve un vector, llamado caracteristicas, que contiene las 28 características de la lesión, ordenadas exactamente en el mismo formato requerido por las técnicas de clasificación. Esta parte del plugin se ha diseñado para extraer las características con precisión. Además, se ha buscado que tenga una estructura clara que facilite entender tanto cada componente por separado como su funcionamiento en conjunto. 4.6 Implementación de la técnica de clasificación La implementación del clasificador en el plugin ha sido la parte del proceso de implementación que ha conllevado un desafío mayor. Esto se debe a que, debido a las restricciones en las dependencias permitidas, no fue posible cargar el modelo ni realizar la predicción utilizando las funciones habituales de bibliotecas como scikit-learn, lo que requirió buscar soluciones alternativas adaptadas al entorno. Para abordar la implementación del clasificador, se han evaluado las tres técnicas de clasificación consideradas: kNN, SVM y MLP. La elección final se ha inclinado hacia la técnica SVM por ser la que presenta resultados más balanceados y exactos. Buscando adaptar esta técnica de clasificación al entorno de la aplicación, se definen una serie de pasos que permiten su correcta implementación: 1. Extracción de parámetros del clasificador SVM. 2. Carga de parámetros en el plugin. 3. Implementación del método de predicción de clase. 4. Verificación externa de la clasificación. 4.6.1 Extracción de parámetros del clasificador SVM La extracción típica de un modelo SVM entrenado para su posterior uso es en formato pkl, usando bibliotecas como joblib. Sin embargo, debido a las restricciones de dependencias, la solución alternativa ha sido extraer cada uno de los parámetros imprescindibles para la clasificación en formato NumPy que sí es compatible con la aplicación. Dado que la aplicación requiere incorporar el modelo SVM previamente entrenado, ha sido necesario identificar y extraer los parámetros que definen su funcionamiento. Para ello, se ha recurrido a la documentación oficial del módulo SVC de la biblioteca sklearn para conocer cuáles son los parámetros esenciales de este clasificador [23]. Estos son los siguientes:
35 Diseño de plugin sobre Raspberry Pi IV para clasificación en tiempo real del Hemangioma Infantil 1. support_vectors: Vectores de soporte. 2. dual_coef: Coeficientes duales que son los pesos que determinan la influencia de cada vector de soporte [23]. 3. Intercept: valor constante de la frontera de decisión [23]. 4. n_support y support: número e índices de los vectores de soporte, respectivamente. 5. gamma: parámetro del kernel RBF. Además, es necesario contar con los valores de los parámetros usados para estandarizar los datos de entrada del modelo, que son: 1. scaler_mean: Media de cada característica. 2. scaler_scale: Desviación estándar de cada característica. Estos parámetros se guardan en archivos con extensión NumPy (.npy) y son los que debe cargar el plugin. 4.6.2 Carga de parámetros en el plugin Estos parámetros necesitan incluirse en el código del plugin. Para ello, se utiliza el método load de la librería NumPy, que permite cargar cada uno de estos parámetros en variables internas del plugin. El único parámetro que necesita este método es la ruta a cada uno de estos archivos de tipo NumPy. Con esto, se implementa el clasificador SVM sin recurrir a librerías externas. La Figura 4-3 muestra el código del plugin encargado de la implementación de los parámetros necesarios del modelo SVM. Figura 4-3. Carga del modelo entrenado.
Implementación 36 36 4.6.3 Implementación del método de predicción de clase Debido a las particularidades del entorno, no ha sido posible utilizar directamente el método predict de la biblioteca sklearn.SVC para predecir el tipo de HI con el modelo SVM. Esto ha llevado a implementar manualmente los pasos que dicha clase ejecuta, asegurando así un control más preciso sobre el proceso de predicción. La función creada para realizar la predicción se llama predecir y recibe los parámetros del modelo junto con las características extraídas de la lesión mediante getFeatures. Cabe destacar que, las características enviadas corresponden al subconjunto específico utilizado por la técnica SVM. La función predecir devuelve el tipo de clase como una cadena de texto, mapeando el resultado a uno de los tres valores posibles: “superficial”, “mixto” o “profundo”. La creación de esta función ha buscado realizar lo más fielmente posible a lo que implementa la función predict de sklearn. Para realizar la clasificación de un modelo SVM multiclase, se ha seguido el enfoque uno contra uno (one-vsone) descrito en la documentación de sklearn.SVC [23]. Los pasos que realiza para lograr la clasificación son: 1. Escalado de la muestra: Estandarizar la muestra de entrada con la desviación típica y la media. 2. Separación de vectores de soporte por clase: Usando los índices de la variable support, se organiza los vectores de soporte en sus respectivas clases. 3. Contador de votos: Se inicializa un contador de votos, uno por cada clase, que se actualizará según los resultados de las comparaciones. 4. Comparación por pares: Se realizan tres enfrentamientos binarios: clase 1 contra clase 2, clase 1 contra clase 3 y clase 2 contra clase 3. Para cada uno, se calcula la similitud entre la muestra y los vectores de soporte usando el kernel RBF. Luego, utilizando esos valores junto con los parámetros dual_coef e intercept, se calcula la decisión que determina qué clase gana. 5. Decisión final: Se decide como la clase final en función de la que más votos tiene. Por tanto, esta implementación manual permite replicar el proceso de predicción del modelo SVM con un control más detallado sobre cada paso y adaptándose a las restricciones del entorno. 4.6.4 Verificación externa de la clasificación La técnica de clasificación implementada en el plugin no puede considerarse completamente validada hasta asegurar que su comportamiento es coherente con el de una implementación estándar, como la que proporciona scikit-learn. Para ello, es necesario comprobar que la función desarrollada manualmente (predecir) genera los mismos resultados que el método predict de sklearn, utilizando el mismo conjunto de características extraídas. Con el fin de llevar a cabo esta validación, se ha desarrollado un entorno de prueba externo (donde sí es posible utilizar la biblioteca sklearn) que permite comparar ambas predicciones de manera controlada. En este entorno, se aplican los mismos algoritmos de extracción de características a todas las imágenes del conjunto de test, y se contrastan los resultados obtenidos con cada método. El código utilizado para llevar a cabo esta verificación se presenta en el Anexo B de esta memoria. En él, se evalúa cada muestra del conjunto de características (featuresSVM), comprobando que la función de predicción manual implementada en el plugin genera el mismo resultado que el método predict de sklearn. Este
37 Diseño de plugin sobre Raspberry Pi IV para clasificación en tiempo real del Hemangioma Infantil procedimiento se ha aplicado a todo el conjunto de test, confirmando la coherencia entre ambas implementaciones. 4.7 Integración del plugin en el dispositivo Una vez verificado el correcto funcionamiento del plugin, se detalla el procedimiento para su integración en el dispositivo Raspberry Pi IV. Este proceso es el siguiente: 1. Verificación de requisitos: Verificar que el archivo del plugin cumple con los requisitos de compatibilidad de la aplicación principal, manteniendo la extensión .py, ubicado en la carpeta “plugins”, con herencia correcta de la clase base “PluginBase” y utilizando únicamente las dependencias permitidas: OpenCV y NumPy. 2. Preparación de compilación: Copiar el plugin y los archivos de parámetros del modelo a la carpeta “plugins” del proyecto, asegurando su ubicación correcta para que queden listos para compilar. 3. Compilación: Generar la nueva versión de la aplicación con el plugin integrado compilando el proyecto mediante la orden “python build_release.py”. 4. Transferencia: Integrar la nueva versión generada al dispositivo Raspberry Pi IV mediante conexión USB, asegurando que todos los archivos necesarios queden correctamente copiados para su ejecución local. Con este procedimiento, se asegura que el plugin y la aplicación principal queden correctamente integrados y listos para su ejecución en el dispositivo Raspberry Pi IV.
ANEXO A 44 44 # Aplanamiento imgRr_vect = np.reshape(imgRr,(Mr,1)) imgGr_vect = np.reshape(imgGr,(Mr,1)) imgBr_vect = np.reshape(imgBr,(Mr,1)) # Matriz con los tres vectores RGBr = np.column_stack((imgRr_vect, imgGr_vect, imgBr_vect)) # CÁLCULO COORDENADAS L*u*v del recorte [Lr,ur,vr,tetar,saturationr] = rgb2luv(RGBr) # Centroides de recortes L_cent = np.mean(Lr) u_cent = np.mean(ur) v_cent = np.mean(vr) dist = np.sqrt((Lr-L_cent)**2+(ur-u_cent)**2+(vr-v_cent)**2) dist_fig = np.reshape(dist,(fil_recorte,col_recorte)) # Conv a matriz dist_fig = dist_fig/np.max(dist_fig) dist_fig_med = cv.bilateralFilter(dist_fig.astype(np.float32), d=9, sigmaColor=75, sigmaSpace=75) ## MÉTODO OTSU dist_fig_med8 = (dist_fig_med*255).astype(np.uint8) dist_fig_med8 = np.reshape(dist_fig_med8, (dist_fig_med8.shape[0]*dist_fig_med8.shape[1],1)) histLuv = np.zeros((256,20)) hist, _ = np.histogram(dist_fig_med8,bins=256, range=(0,255)) # Cúantos píxeles tiene cada nivel de intensidad 1x256 histLuv[:, 0] = hist # Guardar el histograma en la primera columna level = [otsu_seg(histLuv[:, 0])] # Primer umbral ### MÁSCARA lab_img = RGB2lab(img) # img ya está normalizada (rango [0, 1]) # Aplanamos los canales LAB L_flat = lab_img[:, :, 0].reshape(Mr, 1) a_flat = lab_img[:, :, 1].reshape(Mr, 1) b_flat = lab_img[:, :, 2].reshape(Mr, 1) #color_mask = (a_flat > 10) & (b_flat < 25)
45 Diseño de plugin sobre Raspberry Pi IV para clasificación en tiempo real del Hemangioma Infantil ## Segmentación dist_fig_med_vec = np.reshape(dist_fig_med,(Mr,1)) ind = np.where(dist_fig_med_vec <= level)[0] ############################ ## IMAGEN FINAL RGBfinal_r = imgRr_vect.copy() RGBfinal_g = imgGr_vect.copy() RGBfinal_b = imgBr_vect.copy() RGBfinal_r[ind] = 1.0 RGBfinal_g[ind] = 1.0 RGBfinal_b[ind] = 1.0 #RGBfinal_r[~color_mask] = 1.0 #RGBfinal_g[~color_mask] = 1.0 #RGBfinal_b[~color_mask] = 1.0 # Crear máscara booleana con valores que son hemangioma mask_inv = np.ones(Mr, dtype=bool) mask_inv = (RGBfinal_r != 1.0) & (RGBfinal_g != 1.0) & (RGBfinal_b != 1.0) mask_inv = mask_inv.ravel() # Asegurarse de que sea vector 1D # Matriz completa RGBfinal = np.stack([RGBfinal_r, RGBfinal_g, RGBfinal_b], axis=-1) RGBfinal = np.reshape(RGBfinal, (fil_recorte, col_recorte, 3)) RGBfinal = (RGBfinal * 255).astype(np.uint8) # Aplicar la máscara inversa a los vectores de canales Rfinal = imgRr_vect[mask_inv] Gfinal = imgGr_vect[mask_inv] Bfinal = imgBr_vect[mask_inv] RGB_indice = np.column_stack((Rfinal , Gfinal , Bfinal)) # FINAL PARÁMETROS [Lfinal,ufinal,vfinal,tetafinal,saturationfinal] = rgb2luv(RGB_indice)
ANEXO A 46 46 RGB_normalizado = RGB_indice # 3. Convertir a L*a*b* Lab_final = RGB2lab(RGB_normalizado) # 4. Separar canales L_lab_final = Lab_final[:, 0] # L* a_final = Lab_final[:, 1] # a* b_final = Lab_final[:, 2] # b* data = [ {"Media R": np.mean(Rfinal), "Media G": np.mean(Gfinal), "Media B": np.mean(Bfinal), "Media L": np.mean(Lfinal), "Media h": np.mean(tetafinal), "Media c": np.mean(13 * saturationfinal), "Std L": np.std(Lfinal, ddof=1), "Std h": np.std(tetafinal, ddof=1), "Std c": np.std(13 * saturationfinal, ddof=1), "Media a": np.mean(a_final), "Media b": np.mean(b_final), "Std a": np.std(a_final, ddof=1), "Std b": np.std(b_final, ddof=1), "Skew L": skewP(Lfinal), "Kurt L": kurtP(Lfinal), "Skew a": skewP(a_final), "Kurt a": kurtP(a_final), "Skew b": skewP(b_final), "Kurt b": kurtP(b_final), "Media Theta Circular": 0.5 * np.arctan2(np.mean(np.sin(2*np.pi*tetafinal)), np.mean(np.cos(2*np.pi*tetafinal))), "Media u": np.mean(ufinal), "Media v": np.mean(vfinal), "Std u": np.std(ufinal, ddof=1),
47 Diseño de plugin sobre Raspberry Pi IV para clasificación en tiempo real del Hemangioma Infantil "Std v": np.std(vfinal, ddof=1), "Skew u": skewP(ufinal), "Kurt u": kurtP(ufinal), "Skew v": skewP(vfinal), "Kurt v": kurtP(vfinal) } ] caracteristicas = np.array(list(data[0].values())) return caracteristicas ################################# ## Funciones Auxiliares def rgb2luv (RGBrgb): ''' Convierte de RGB a L*u*v ''' # Blanco de referencia un = 0.1978 vn = 0.4683 XYZ = np.array([[0.4124, 0.3576, 0.1805], [0.21026, 0.7152, 0.0722], [0.0193, 0.1192, 0.9505]]) XYZoriginal = RGBrgb @ np.transpose(XYZ) # Matrices X,Y,Z X = XYZoriginal[:,0] Y = XYZoriginal[:,1] Z = XYZoriginal[:,2] uprima = 4*X / (X+15*Y+3*Z) vprima = 9*Y / (X+15*Y+3*Z) p = np.where((X==0)&(Y==0)&(Z==0)) uprima[p] = 4/19 vprima[p] = 9/19 # Cálculo y ajuste de L L = 116*(Y**(1/3))-16 t = np.where(Y <= 0.008856) L[t] = 903.3 * Y[t] # Cálculo de u*v u = 13*L * (uprima-un) v = 13*L * (vprima-vn)
ANEXO A 48 48 teta = np.arctan2(v,u) # El *13 se la mete luego saturation = np.sqrt((uprima-un)**2+(vprima-vn)**2) return L,u,v,teta,saturation # FUNCIÓN OTSUN def otsu_seg (histLuv): """ Método de Otsu para encontrar un umbral de binarización basado en un histograma. :param histLuv: Histograma de la imagen. :return: Nivel de umbral normalizado en el rango [0, 1]. """ num_bins = 256 counts = np.array(histLuv, dtype = np.float64) # Probabilidad de cada nivel de gris p = counts / sum(counts) omega = np.cumsum(p) # Suma acumulativa mu = np.cumsum(p * np.arange(1,num_bins+1)) mu_t = mu[-1] # Evitar divisiones por 0 con np.errstate with np.errstate(divide='ignore', invalid = 'ignore'): sigma_b_squared = (mu_t * omega - mu) ** 2 / (omega * (1 - omega)) # Encontrar el indice del máximo valor de sigma_b squared maxval = np.nanmax(sigma_b_squared) if np.isfinite(maxval): idx = np.mean(np.where(sigma_b_squared == maxval)) level = (idx - 1 ) / (num_bins -1 ) # Normalización else: level = 0.0 return level def skewP(x): """ Calculo de la asimetría o skew Basado en el cálculo de la biblioteca de Python """
49 Diseño de plugin sobre Raspberry Pi IV para clasificación en tiempo real del Hemangioma Infantil x = np.asarray(x) n = len(x) if n < 3: return np.nan mean_x = np.mean(x) m2 = np.sum((x - mean_x)**2) / (n - 1) m3 = np.sum((x - mean_x)**3) / n g1 = m3 / m2**1.5 skewness = (np.sqrt(n * (n - 1)) / (n - 2)) * g1 return skewness def kurtP(x): """ Calculo de la curtosis Basado en el cálculo de la biblioteca de Python fisher=False, bias=False """ x = np.asarray(x) n = len(x) if n < 4: return np.nan # igual que scipy: necesita al menos 4 valores para kurtosis sin sesgo mean_x = np.mean(x) m2 = np.sum((x - mean_x) ** 2) / (n - 1) m4 = np.sum((x - mean_x) ** 4) / n # igual que scipy g2 = m4 / (m2 ** 2) # Corrección por sesgo: kurtosis = ((n * (n + 1)) / ((n - 1) * (n - 2) * (n - 3))) * ((n - 1) * g2 - 3 * (n - 1)) + 3 return kurtosis def RGB2lab(RGBrgb): """ Conversión de imagen RGB a CIE Lab. Equivalente a skimage.color.rgb2lab(img) """ # Asegurar que está en el rango correcto img = np.clip(RGBrgb, 0, 1) # Matriz de conversión RGB -> XYZ (sRGB, D65)
ANEXO A 50 50 M = np.array([[0.4124564, 0.3575761, 0.1804375], [0.2126729, 0.7151522, 0.0721750], [0.0193339, 0.1191920, 0.9503041]]) # Corrección gamma inversa (de sRGB a lineal RGB) def gamma_inv(c): mask = c > 0.04045 c_linear = np.empty_like(c) c_linear[mask] = ((c[mask] + 0.055) / 1.055) ** 2.4 c_linear[~mask] = c[~mask] / 12.92 return c_linear img_lin = gamma_inv(img) # Reordenar para aplicar la matriz: (H, W, 3) → (H*W, 3) shape = img.shape img_flat = img_lin.reshape(-1, 3) XYZ = np.dot(img_flat, M.T) # Normalizar a referencia blanca D65 Xn, Yn, Zn = 0.95047, 1.00000, 1.08883 X = XYZ[:, 0] / Xn Y = XYZ[:, 1] / Yn Z = XYZ[:, 2] / Zn # Función auxiliar f(t) def f(t): delta = 6/29 mask = t > delta**3 f_t = np.empty_like(t) f_t[mask] = np.cbrt(t[mask]) f_t[~mask] = t[~mask] / (3 * delta**2) + 4/29 return f_t fX = f(X) fY = f(Y) fZ = f(Z) L = 116 * fY - 16 a = 500 * (fX - fY) b = 200 * (fY - fZ) lab = np.stack([L, a, b], axis=1).reshape(shape) return lab def predecir(featuresSVM, support_vectors, dual_coef, intercept, gamma, classes, scaler_mean, scaler_scale, n_support):
51 Diseño de plugin sobre Raspberry Pi IV para clasificación en tiempo real del Hemangioma Infantil # === Escalar === X = (featuresSVM - scaler_mean) / scaler_scale X = X.reshape(1, -1) # === Ínidices === sv_class_idx = [] start = 0 for count in n_support: sv_class_idx.append((start, start + count)) start += count votes = np.zeros(len(classes), dtype=int) # === Clasificador 1 vs 2 === i_start, i_end = sv_class_idx[0] j_start, j_end = sv_class_idx[1] sv = np.vstack([support_vectors[i_start:i_end], support_vectors[j_start:j_end]]) coef = dual_coef[0, :len(sv)] K = np.exp(-gamma * np.sum((sv - X)**2, axis=1)) decision12 = np.sum(coef * K) + intercept[0] if decision12 > 0: votes[0] += 1 else: votes[1] += 1 # === Clasificador 1 vs 3 === i_start, i_end = sv_class_idx[0] k_start, k_end = sv_class_idx[2] sv = np.vstack([support_vectors[i_start:i_end], support_vectors[k_start:k_end]]) coef = dual_coef[1, :len(sv)] K = np.exp(-gamma * np.sum((sv - X)**2, axis=1)) decision13 = np.sum(coef * K) + intercept[1] if decision13 > 0: votes[0] += 1 else: votes[2] += 1 # === Clasificador 2 vs 3: combinación === # sklearn: decision23 = decision13 - decision12 decision23 = decision13 - decision12 if decision23 > 0: votes[1] += 1
ANEXO A 52 52 else: votes[2] += 1 log_debug(f"Votos: {votes}") pred_class = classes[np.argmax(votes)] # === Mapping igual que sklearn === mapping = { 1: "superficial", 2: "mixto", 3: "profundo" } clase = mapping.get(pred_class, "desconocida") return clase ##################################### ###################################### ## CLASE DEL PLUGIN class hemangiomaClass(PluginBase): def __init__(self) -> None: name = "IH Classifier" is_enable = True super().__init__(name, is_enable) self.click_position = None # Guarda la posición del clic self.doble_click = False log_debug("Plugin DetectaMarco inicializado.") def set_click_position(self, event, x, y, flags, param): """ Método para manejar eventos de click """ if event == cv.EVENT_LBUTTONDOWN: self.click_position = (x, y) #log_debug(f"Clic registrado en: {self.click_position}") # Depuración if event == cv.EVENT_LBUTTONDBLCLK: self.doble_click = True def ProcessImage(self, img_in: npt.NDArray) -> npt.NDArray: # Configurar la ventana y el callback de clic dinámicamente cv.namedWindow("Seleccionar Recorte") cv.setMouseCallback("Seleccionar Recorte", self.set_click_position) """ Procesa la imagen detectando la posición del clic. """ cv_img_original = self.from_cv16_to_cv8(img_in).copy()
53 Diseño de plugin sobre Raspberry Pi IV para clasificación en tiempo real del Hemangioma Infantil r_img = None # Variable para almacenar el recorte scale_factor = 6 while cv.getWindowProperty("Seleccionar Recorte", cv.WND_PROP_VISIBLE) >= 1: cv_img = cv_img_original.copy() cv_img = escalaImg(cv_img, scale_factor) if self.click_position: # Calcular posición del click y rodearlo con el marco x, y = self.click_position h, w = cv_img.shape[:2] tamMarco = 150 dynamic_size = max(tamMarco, min(w, h) // tamMarco) # Tamaño dinámico del marco x1 = max(0, x - dynamic_size // 2) y1 = max(0, y - dynamic_size // 2) x2 = min(w, x + dynamic_size // 2) y2 = min(h, y + dynamic_size // 2) cv.rectangle(cv_img, (x1, y1), (x2, y2), (255, 255, 255), 2) x1_orig = x1 // scale_factor y1_orig = y1 // scale_factor x2_orig = x2 // scale_factor y2_orig = y2 // scale_factor r_img = cv_img_original[y1_orig:y2_orig, x1_orig:x2_orig].copy() # Posición del texto: centrado arriba del recuadro text = "Zona Recorte" font = cv.FONT_HERSHEY_PLAIN font_scale = 1.2 thickness = 1 # Calcula tamaño del texto (text_width, text_height), baseline = cv.getTextSize(text, font, font_scale, thickness) # Centrado horizontalmente sobre el recuadro text_x = x1 + (x2 - x1) // 2 - text_width // 2 # Justo arriba del recuadro, con un margen text_y = y2 + text_height + 10 cv.putText(cv_img, text, (text_x, text_y), font, font_scale, (0, 0, 0), thickness, cv.LINE_AA) cv.imshow("Seleccionar Recorte", cv_img)