universidad de extremadura

Anuncio
UNIVERSIDAD DE EXTREMADURA
Escuela Politécnica
MÁSTER UNIVERSITARIO EN INICIACIÓN A LA
INVESTIGACIÓN EN TECNOLOGÍA (MUIT)
ESPECIALIDAD EN:
TECNOLOGÍAS INFORMÁTICAS Y
DE LAS COMUNICACIONES (TINC)
Trabajo Fin de Máster MUIT-TINC
Implementación de algoritmos de análisis
hiperespectral en tarjetas gráficas programables
(GPUs)
Sergio Sánchez Martínez
Julio 2010
UNIVERSIDAD DE EXTREMADURA
Escuela Politécnica
MÁSTER UNIVERSITARIO EN INICIACIÓN A LA
INVESTIGACIÓN EN TECNOLOGÍA (MUIT)
ESPECIALIDAD EN:
TECNOLOGÍAS INFORMÁTICAS Y
DE LAS COMUNICACIONES (TINC)
Trabajo Fin de Máster MUIT-TINC
Implementación de algoritmos de análisis
hiperespectral en tarjetas gráficas programables
(GPUs)
Autor: Sergio Sánchez Martínez
Fdo:
Director: Antonio J. Plaza Miguel
Fdo:
Tribunal Calificador
Presidente: Juan Manuel Sánchez Pérez
Fdo:
Secretario: Javier Plaza Miguel
Fdo.:
Vocal: Juan Antonio Gómez Pulido
Fdo.:
CALIFICACIÓN:
FECHA:
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Resumen
El análisis de imágenes hiperespectrales en observación remota de la tierra
constituye una línea de investigación muy activa, con numerosas contribuciones en la
literatura científica reciente. Debido a la resolución espacial disponible en sensores de
observación remota de la tierra, la mayor parte de los píxels registrados por el sensor
constituyen una mezcla de diferentes substancias puras a nivel sub-píxel. Para solucionar
este problema, una de las técnicas más ampliamente utilizadas es el desmezclado espectral,
que comprende dos etapas: 1) extracción de firmas espectrales puras (endmembers), y 2)
estimación de la abundancia de dichos endmembers a nivel sub-píxel. Ambas etapas son
muy costosas desde el punto de vista computacional, lo cual supone un importante
inconveniente en aplicaciones que requieren una respuesta en tiempo casi real, tales como
monitorización y seguimiento de incendios, prevención y seguimiento de desastres
naturales, vertidos químicos y otros tipos de contaminación ambiental, etc. En el presente
trabajo, hemos desarrollado una implementación paralela del algoritmo pixel purity index
(PPI), un conocido método de extracción de endmembers disponible en software comercial
como el paquete Research Systems ENVI de Kodak. La implementación paralela propuesta
en este trabajo ha sido desarrollada en una tarjeta gráfica programable (GPU) de tipo
Nvidia Tesla C1060. Nuestra implementación paralela proporciona resultados muy
prometedores, tanto desde el punto de vista de su precisión a la hora de identificar
endmembers como desde el punto de vista del rendimiento paralelo obtenido,
proporcionando factores de aceleración (speedups) de 200x utilizando una única tarjeta
NVidia Tesla C1060. Estos resultados suponen un incremento muy notable del rendimiento
computacional del algoritmo con respecto a su ejecución serie en un PC de última
generación.
Palabras clave:
Hiperespectral, Endmember, PPI, Pixel Purity Index, GPU, CUDA, ENVI.
Trabajo Fin de Máster
3
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Abstract
Remotely sensed hyperspectral imaging is an active area of research in the
literature. Due to available spatial resolution, most of the pixels collected by hyperspectral
imaging instruments are in fact a mixture of different underlying substances. One of the
most widely used approaches for analyzing hyperspectral images is spectral unmixing,
which comprises two stages: 1) extraction of pure spectral signatures (endmembers), and
2) estimation of endmember fractional abundances at sub-pixel levels. Both stages are
computationally complex, which is a serious drawback in applications which require a
response in near real-time, such as forest fire monitoring and tracking, disaster
management and prevention, oil spill detection, etc. In this work, we develop an efficient
parallel implementation of the pixel purity index (PPI), a well-known algorithm for
endmember extraction available in commercial software such as Kodak's Research
Systems ENVI. The parallel implementation has been conducted in a commodity graphics
processing unit (GPU) of NVidia Tesla C1060 type. Our parallel implementation of the PPI
exhibits very promising results in terms of endmember extraction accuracy and parallel
performance, with speedups approaching 200x using just one NVidia Tesla C1060 card.
This represents a tremendous increase of performance with regards to the serial version of
the same algorithm, implemented in a latest-generation desktop PC.
Keywords
Hyperspectral, Endmember, PPI, Pixel Purity Index, GPU, CUDA, ENVI.
Trabajo Fin de Máster
4
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Índice de contenido
1 Motivaciones y Objetivos.................................................................................................10
1.1Motivaciones..............................................................................................................10
1.2Objetivos.....................................................................................................................11
2 Introducción......................................................................................................................14
2.1Concepto de imagen hiperespectral............................................................................14
2.2Sensores hiperespectrales considerados.....................................................................17
2.3Técnicas de análisis hiperespectral y necesidad de paralelismo.................................19
Técnicas basadas en el modelo lineal de mezcla........................................................20
Necesidad de paralelismo...........................................................................................21
El papel de las GPUs...................................................................................................22
3 Tarjetas gráficas programables GPUs..............................................................................23
3.1Las GPUs como un dispositivo de procesamiento de datos en paralelo....................23
Vortex shaders y píxel shaders....................................................................................24
Pipeline clásico de procesamiento en una GPU..........................................................25
Evolución del uso de GPUs en aplicaciones científicas.............................................27
3.2CUDA: una nueva arquitectura para el calculo en la GPU........................................29
Pipeline unificado.......................................................................................................29
Modelo de programación CUDA................................................................................30
3.3Procesamiento.............................................................................................................32
Un coprocesador multihilo..........................................................................................32
Hilos, Bloques y Grids................................................................................................33
Modelo de memoria....................................................................................................34
3.4Implementación hardware y modelo de ejecución.....................................................35
Nvidia Tesla C1060.....................................................................................................35
Modelo de ejecución...................................................................................................37
Especificaciones generales..........................................................................................39
4 Método..............................................................................................................................43
4.1Pixel Purity Index (PPI)..............................................................................................43
5 Procesamiento paralelo en GPU.......................................................................................47
5.1Generación de los skewers.........................................................................................47
5.2Proyección..................................................................................................................49
5.3Agrupación de resultados...........................................................................................52
5.4CUDA Occupancy Calculator....................................................................................53
5.5CUDA Visual Profiler.................................................................................................57
6 Resultados........................................................................................................................63
7 Conclusiones y líneas futuras...........................................................................................79
8 Apéndice...........................................................................................................................82
8.1Programación con CUDA...........................................................................................82
Compilación................................................................................................................82
Reserva y liberación de memoria de la GPU..............................................................83
Ejecutando código en la GPU.....................................................................................84
Trabajo Fin de Máster
5
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Modificadores de funciones........................................................................................84
Ejecución del kernel....................................................................................................85
Ejemplo de kernel.......................................................................................................85
Herramientas de sincronización..................................................................................88
9 Bibliografía.......................................................................................................................89
10 Publicaciones del Candidato..........................................................................................92
Trabajo Fin de Máster
6
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Índice de figuras
Figura 2.1:Concepto de imagen hiperespectral....................................................................15
Figura 2.2:Adquisición de una imagen hiperespectral por el sensor AVIRIS......................16
Figura 2.3:Tipos de pixeles en imagenes hiperespectrales...................................................17
Figura 2.4:Evolución de la relación SNR del sensor AVIRIS de NASA.............................19
Figura 2.5:Interpretación gráfica del modelo lineal de mezcla............................................20
Figura 3.1: Operaciones en coma flotante por segundo para CPU y GPU..........................23
Figura 3.2:Ancho de banda CPU y GPU..............................................................................24
Figura 3.3:Pipeline clásico de procesamiento en una GPU..................................................26
Figura 3.4:La GPU proporciona más transistores para el procesamiento de datos..............27
Figura 3.5:Pipeline clásico frente a pipeline unificado........................................................30
Figura 3.6:Pila de software de CUDA..................................................................................31
Figura 3.7:Operaciones de memoria gather (reunión) y scatter (dispersión).......................31
Figura 3.8:Shared memory...................................................................................................32
Figura 3.9: Grid, bloques e hilos..........................................................................................34
Figura 3.10:Acceso a diferentes espacios de memoria.........................................................35
Figura 3.11:Arquitectura de la GPU Tesla C1060 de NVidia..............................................36
Figura 3.12:Conjunto de multiprocesadores SIMD con memoria compartida on-chip.......37
Figura 4.1:Funcionamiento del algoritmo PPI.....................................................................46
Figura 5.1:Almacenamiento de los Skewers........................................................................49
Figura 5.2:Almacenamiento de los resultados parciales......................................................50
Figura 5.3:Agrupación de resultados....................................................................................53
Figura 5.4:CUDA Occupancy Calculator.............................................................................54
Figura 5.5:CUDA Occupancy Calculator:Gráfica asociada al número de hilos por bloque55
Figura 5.6:CUDA Occupancy Calculator:Gráfica asociada al número de registros por hilo
..............................................................................................................................................56
Figura 5.7: CUDA Occupancy Calculator:Gráfica asociada a la cantidad de memoria
compartida usada por cada bloque.......................................................................................57
Figura 5.8:CUDA Visual Profiler.........................................................................................59
Figura 5.9:CUDA Visual Profiler: Propiedades de la sesión................................................60
Figura 5.10:CUDA Visual Profiler: resultados parte 1.........................................................60
Figura 5.11:CUDA Visual Profiler: resultados parte 1.........................................................61
Figura 5.12:CUDA Visual Profiler: Gráfica resumen..........................................................61
Figura 5.13:CUDA Visual Profiler: Gráfica de tiempos......................................................62
Figura 6.1:Regiones y asignación de abundancias para la primera imagen sintética...........64
Figura 6.2:Mapas de abundancia empleados en la generación de la primera imagen
sintética. a) Mapa de abundancia para el endmember suelo. b) Mapa de abundancia para el
endmember vegetación.........................................................................................................65
Figura 6.3: Segunda imagen sintética. a) Distribución en la imagen de los cinco píxeles
puros (endmembers), denotados como r1, r2, r3, r4 y r5. b-f) Mapas de abundancia
empleados en la generación de la segunda imagen sintética................................................66
Figura 6.4:Ubicación de la imagen real AVIRIS Cuprite sobre una fotografía aérea de la
región minera Cuprite, Nevada, Estados Unidos.................................................................67
Figura 6.5:Precisión estricta PPI en imagen sintética 1.......................................................68
Trabajo Fin de Máster
7
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Figura 6.6:Precisión estricta PPI en imagen sintética 2.......................................................69
Figura 6.7:Precisión ponderada PPI en imagen sintética 1..................................................70
Figura 6.8:Precisión ponderada PPI en imagen sintética 2..................................................71
Figura 6.9:Resultados gráficos de PPI para la imagen sintética 1........................................73
Figura 6.10:Resultados gráficos de PPI para la imagen sintética 2......................................75
Figura 6.11:Resultados visuales para las versiones C y ENVI en imagen real....................76
Figura 6.12:Resultados visuales para las versiones CUDA y ENVI en imagen real...........77
Figura 6.13:Ejemplo de materiales detectados.....................................................................78
Figura 8.1:Vectores operandos (v_A, v_B) y resultado (v_C) para suma de vectores.........86
Figura 8.2:Partición lógica del problema en bloques...........................................................86
Figura 8.3:Comparación de código de función C y kernel CUDA......................................88
Trabajo Fin de Máster
8
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Índice de tablas
Tabla 3.1: Lecturas y escrituras en los diferentes tipos de memoria....................................34
Tabla 3.2:Especificaciones técnicas de la GPU Tesla C1060 de NVidia.............................36
Tabla 3.3:Recursos y limitaciones según la GPU que utilicemos para programar CUDA. .40
Tabla 3.4:Capacidad de cómputo y número de multiprocesadores de cada GPU de Nvidia
..............................................................................................................................................42
Tabla 5.1:CUDA Occupancy Calculator:Parámetros introducidos......................................54
Tabla 5.2:CUDA Occupancy Calculator: Resultados calculados en función de los
parámetros............................................................................................................................55
Tabla 6.1: Comparación de resultados CPU y GPU para PPI en imagen sintética 1...........72
Tabla 6.2: Comparación de resultados CPU y GPU para PPI en imagen sintética 2...........74
Tabla 6.3:Comparación de resultados para las versiones C (CPU), CUDA (GPU) y ENVI
(CPU) para 15360 iteraciones..............................................................................................75
Trabajo Fin de Máster
9
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
1 Motivaciones y Objetivos
1.1 Motivaciones
El presente trabajo se ha desarrollado dentro de las líneas de investigación actuales
del Grupo Hypercomp de la Universidad de Extremadura, y consiste en la implementación
eficiente de un algoritmo para extracción de referencias espectrales puras o endmembers
en imágenes hiperespectrales haciendo uso de GPUs para acelerar los cómputos relativos a
la extracción de endmembers. En concreto el algoritmo implementado es: Píxel Purity
Index (PPI). Las aplicaciones potenciales de este algoritmo son múltiples, destacando
aplicaciones de detección de minerales, aplicaciones militares tales como detección de
material armamentístico camuflado, anomalías, identificación de agentes contaminantes en
aguas y atmósfera, etc.
Conviene destacar los algoritmos de análisis hiperespectral generalmente necesitan
grandes cantidades de tiempo a la hora de proporcionar resultados, debido a la complejidad
computacional de los mismos (en el caso de PPI, el algoritmo deben ejecutar un gran
número de iteraciones) y también debido al gran tamaño de las imágenes a procesar, lo cual
requiere que la transferencia de datos entre el procesador y el dispositivo hardware
especializado utilizado como coprocesador deba optimizarse. Al requerir tantas iteraciones,
y tener dentro de estas operaciones no triviales, los algoritmos de análisis hiperespectral
generalmente se traducen en un consumo de CPU elevado, por lo que la utilización de
arquitecturas especializadas como coprocesadores puede resultar una alternativa altamente
interesante.
Hasta la fecha, las técnicas tradicionales en la literatura para abordar este problema han
optado por soluciones basadas en el uso de clusters y sistemas multiprocesador. La
computación cluster, a pesar de su adaptabilidad al problema del tratamiento de datos
hiperspectrales (especialmente, cuando dichos datos se encuentran almacenados en un
repositorio de datos en tierra), presenta problemas en cuanto al procesamiento de los datos
en tiempo real dado el alto coste y elevados requerimientos en cuanto a espacio, peso y
consumo (denominado payload en misiones de observación remota de la tierra). Por otra
Trabajo Fin de Máster
10
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
parte, la instalación de un cluster suele llevar asociada la disponibilidad de un número
elevado de ordenadores interconectados entre sí para que compartan el procesamiento de
datos a través de sus procesadores, lo cual hace incrementar la velocidad de ejecución y
procesamiento de las aplicaciones; sin embargo, cada nodo (u ordenador) lleva ligado un
precio y una serie de requerimientos en cuanto a espacio y consumo que alejan esta
aproximación de las características requeridas en cuanto a payload en misiones reales de
observación remota de la tierra.
Para solucionar estos problemas relativos a coste, consumo y peso, y además ofrecer
además mejoras sustanciales en cuanto al tiempo de procesamiento, en el presente TFM
proponemos una alternativa basada en un nuevo modelo de tratamiento de imágenes
hiperespectrales basado en la utilización de GPUs. Conviene destacar que, con una sola
GPU, pueden llegar a obtenerse mejoras notables a la hora de procesar cálculos de tipo
científico, como es el caso de los algoritmos de tratamiento de imágenes hiperespectrales, a
un coste razonable (no más de 400 euros) y además ocupando un espacio mínimo. No
obstante, no todas las tarjetas GPU disponibles en el mercado se ajustan a nuestros
requerimientos. Finalmente, indicar que en este trabajo se ha intentado ir un poco más allá,
y para trabajar de forma totalmente innovadora se ha utilizado la arquitectura CUDA
incorporada en las tarjetas gráficas de NVIDIA de las series 8 (o superiores), Quadro y
Tesla, siendo algunas de las tarjetas de la primera gama las que han sido objeto de estudio
en este TFM.
Para la realización de este trabajo se ha utilizado una maquina con un procesador
Intel Core i7 920 una GPU NVidia Tesla C1060 y el sistema operativo Linux Ubuntu 9.04.
Por otra parte otra de las actividades desarrolladas en el presente trabajo consiste en la
realización de un estudio cuantitativo y comparativo del algoritmo implementado tanto en
C++ para su ejecución en la CPU como en CUDA para su ejecución en la GPU así como
un análisis de la precisión de los resultados obtenidos.
1.2 Objetivos
Este trabajo pretende desarrollar sobre una GPU la implementación de un algoritmo
para la extracción de endmembers en imágenes hiperespectrales y establecer un estudio
cualitativo y comparativo de los resultados obtenidos tras la ejecución. Concretamente el
Trabajo Fin de Máster
11
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
algoritmo es: Píxel Purity Index (PPI) .
La consecución del objetivo general anteriormente mencionado se lleva a cabo en la
presente memoria abordando una serie de objetivos específicos, los cuales se enumeran a
continuación:
• Establecer un estudio sobre la utilidad y el funcionamiento del algoritmo PPI para
extraer el grado de paralelismo inherente que presenta y utilizarlo para su
implementación.
• Realizar un estudio sobre el lenguaje
de programación CUDA para poder
implementar los algoritmos.
• Implementar el algoritmo para su ejecución en la GPU y optimizar el tiempo de
ejecución.
• Implementar una serie de aplicaciones que nos permitan conocer la precisión que se
ha logrado en la ejecución del algoritmo para dos imágenes sintéticas de las cuales
se conocen previamente sus características.
• Realizar un estudio comparativo sobre los resultados obtenidos.
Teniendo presentes los anteriores objetivos concretos, procedemos a describir la
organización del resto de esta memoria, estructurada en una serie de capítulos cuyos
contenidos se describen a continuación:
2)
Introducción. En este capítulo introductorio se describen los conceptos
fundamentales relacionados con análisis hiperespectral y procesamiento de
datos, enfatizando la necesidad de técnicas de procesamiento paralelo en este
campo y proponiendo la utilización de las GPUs como procesador paralelo
para este tipo de técnicas.
3)
GPU. Este capítulo está dedicado a la GPU, hablaremos de cómo han ido
evolucionando las GPUs programables, de los recursos que ofrecen y su
capacidad de computo. Introduciremos una serie de conceptos para lograr
entender cómo se lleva a cabo el procesamiento en una GPU mediante
CUDA. Este capítulo también describe el hardware de la tarjeta Tesla C1060
con la que se ha realizado el trabajo.
4)
Método. Este capítulo describe y muestra la utilidad del algoritmo
Trabajo Fin de Máster
12
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
implementado para el análisis hiperespectral así como su funcionamiento y
filosofía.
5)
Procesamiento paralelo en GPU. En este capítulo se muestra como se lleva
a cabo el procesamiento paralelo en la GPU para el caso que nos ocupa,
centrándonos en cada una de las fases del algoritmo por separado.
6)
Resultados. En este capítulo se describen las imágenes que se han utilizado
para probar el algoritmo, se muestran las diferentes formas de calcular la
precisión, se muestran los resultados conseguidos y finalmente se comparan.
7)
Conclusiones y líneas futuras. Este capítulo está dedicado a resumir las
principales aportaciones realizadas por la presente memoria y a mostrar las
conclusiones derivadas. Además, el capítulo sugiere un conjunto de líneas de
trabajo que pueden ser abordadas en futuros trabajos. La memoria concluye
con una serie de referencias bibliográficas utilizadas en el estudio.
8)
Apéndice. En este capítulo se expone una guía de programación básica de
CUDA y se muestra el código de los algoritmos implementados tanto en C+
+ como en CUDA. También se muestra el código del algoritmo utilizado
para calcular la precisión de los resultados.
Trabajo Fin de Máster
13
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
2 Introducción
El presente capítulo se organiza de la siguiente forma. En primer lugar, describimos
el concepto de imagen hiperespectral, detallando las particularidades y características
propias de este tipo de imágenes de alta dimensionalidad. A continuación, describimos
algunas características genéricas de los sensores de adquisición de este tipo de datos,
haciendo énfasis en los sensores utilizados en el presente trabajo. Seguidamente, se
muestra una visión general de las técnicas de desmezclado disponibles en la actualidad,
con particular énfasis en el modelo lineal de mezcla, utilizado en el presente trabajo para
abordar el problema de la caracterización sub-píxel de una imagen hiperespectral a partir
de la identificación de los píxels espectralmente más puros en la misma. El capítulo
concluye destacando la necesidad de paralelismo en este campo y mostrando el papel de
las GPU para su tratamiento.
2.1 Concepto de imagen hiperespectral
El asentamiento de la tecnología hiperespectral en aplicaciones de observación
remota de la tierra ha dado como resultado el desarrollo de instrumentos de medida de muy
elevada resolución en los dominios espacial y espectral. Los sensores hiperespectrales
adquieren imágenes digitales en una gran cantidad de canales espectrales muy cercanos
entre sí, obteniendo, para cada porción de la escena o píxel, una firma espectral
característica de cada material.
El resultado de la toma de datos por parte de un sensor hiperespectral sobre una
determinada escena puede ser representado en forma de cubo de datos, con dos
dimensiones para representar la ubicación espacial de un píxel, y una tercera dimensión
que representa la singularidad espectral de cada píxel en diferentes longitudes de onda [ 1
]. La Figura 2.1 muestra la estructura de una imagen hiperespectral donde el eje X es el
indicador de las líneas, el eje Y es el indicador de las muestras y el eje Z es el número de
bandas, es decir, la longitud de onda de esa banda (canal).
Trabajo Fin de Máster
14
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Figura 2.1:Concepto de imagen hiperespectral
Como puede apreciarse en la Figura 2.1, el resultado de la toma de datos por parte
de un sensor hiperespectral sobre una determinada escena puede ser representado en forma
de cubo de datos, con dos dimensiones para representar la ubicación espacial de un píxel, y
una tercera dimensión que representa la singularidad espectral de cada píxel en diferentes
longitudes de onda. En concreto, la capacidad de observación de los sensores denominados
hiperespectrales permite la obtención de una firma espectral detallada para cada píxel de la
imagen, dada por los valores de reflectancia adquiridos por el sensor en diferentes
longitudes de onda, lo cual permite una caracterización muy precisa de la superficie de
nuestro planeta. Como ejemplo ilustrativo, la Figura 2.2 muestra el procedimiento de
análisis hiperespectral mediante un sencillo diagrama, en el que se ha considerado como
ejemplo el sensor AVIRIS (Airborne Visible Infra-Red Imaging Spectrometer),
desarrollado por NASA/Jet Propulsión Laboratory, el cual cubre el rango de longitudes de
onda entre 0.4 y 2.5 nm utilizando 224 canales y resolución espectral de aproximadamente
10 nm.
Trabajo Fin de Máster
15
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Figura 2.2:Adquisición de una imagen hiperespectral por el sensor AVIRIS
Como puede apreciarse en la Figura 2.2, la capacidad de observación de este sensor
permite la obtención de una firma espectral detallada para cada píxel de la imagen, dada
por los valores de reflectancia adquiridos por el sensor en diferentes longitudes de onda, lo
cual permite una caracterización muy precisa de la superficie de nuestro planeta. Conviene
destacar que, en este tipo de imágenes, es habitual la existencia de mezclas a nivel de
subpíxel, por lo que a grandes rasgos podemos encontrar dos tipos de píxeles en estas
imágenes: píxel puros y píxel mezcla [ 2 ]. Se puede definir un píxel mezcla como aquel en
el que cohabitan diferentes materiales. Este tipo de píxel son los que constituyen la mayor
parte de la imagen hiperespectral, en parte, debido a que este fenómeno es independiente
de la escala considerada ya que tiene lugar incluso a niveles microscópicos [ 3 ]. La Figura
2.3 muestra un ejemplo del proceso de adquisición de píxeles puros (a nivel macroscópico)
y mezcla en imágenes hiperespectrales.
Trabajo Fin de Máster
16
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Figura 2.3:Tipos de pixeles en imagenes hiperespectrales
El desarrollo tecnológico introducido por la incorporación de sensores
hiperespectrales en plataformas de observación remota de la tierra de última generación ha
sido particularmente notable durante los últimos años. En este sentido, conviene destacar
que dos de las principales plataformas de tipo satélite que se encuentran en funcionamiento
en la actualidad: Earth Observing-1 de NASA (http://eo1.gsfc.nasa.gov) y ENVISAT de la
Agencia Espacial Europea (http://envisat.esa.int), llevan incorporados sensores de este
tipo, permitiendo así la posibilidad de obtener imágenes hiperespectrales de la práctica
totalidad del planeta de manera casi continua. A pesar de la gran evolución en los
instrumentos de observación remota de la tierra, la evolución en las técnicas de análisis de
los datos proporcionados por dichos sensores no ha sido tan notoria. En particular, la
obtención de técnicas de análisis hiperespectral avanzadas, capaces de aprovechar
totalmente la gran cantidad de información espacial y espectral presente en imágenes
hiperespectrales, constituye un objetivo de gran interés para la comunidad científica. A
continuación, describimos en detalle las características del sensor hiperespectral utilizado
en el presente estudio.
2.2 Sensores hiperespectrales considerados
En la actualidad, existe una amplia gama de sensores hiperespectrales de
observación remota de la tierra. Dichos sensores pueden clasificarse según el modo en que
son transportados (plataforma de transporte) en el momento de la toma de datos [ 4 - 6 ].
La mayor parte de los sensores hiperespectrales actuales son aerotransportados (siendo el
Trabajo Fin de Máster
17
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
ejemplo más claro de este tipo de instrumentos el sensor AVIRIS, considerado en el
presente trabajo).
AVIRIS es un sensor hiperespectral aerotransportado con capacidades analíticas en
las zonas visible e infrarroja del espectro [ 8 - 10 ]. Este sensor está en funcionamiento
desde 1987. Fue el primer sistema de adquisición de imágenes capaz de obtener
información en una gran cantidad de bandas espectrales estrechas y casi contiguas. AVIRIS
es un instrumento único en el mundo de la teledetección, pues permite obtener información
espectral en 224 canales espectrales contiguos, cubriendo un rango de longitudes de onda
entre 0.4 y 2.5 m, siendo el ancho entre las bandas muy pequeño, aproximadamente 0.01
m.
En 1989, AVIRIS se convirtió en un instrumento aerotransportado. Desde ese
momento, se realizan varias campañas de vuelo cada año para tomar datos mediante
AVIRIS. El sensor ha realizado tomas de datos en Estados Unidos, Canadá y Europa,
utilizando para ello dos plataformas:
• Un avión ER-2 perteneciente a NASA/Jet Propulsion Laboratory. El ER-2 puede
volar a un máximo de 20 km sobre el nivel del mar, a una velocidad máxima de
aproximadamente 730 km/h.
• Un avión denominado Twin Otter, capaz de volar a un máximo de 4 km sobre el
nivel del mar, a velocidades de 130 km/h.
Algunas de las características más relevantes en cuanto al diseño interno del sensor
AVIRIS son las siguientes:
• El sensor utiliza un explorador de barrido que permite obtener un total de 614
píxeles por cada oscilación.
• La cobertura de la parte visible del espectro es realizada por un espectrómetro
EFOS- A, compuesto por un array de 32 detectores lineales.
• La cobertura en el infrarrojo es realizada por los espectrómetros EFOS-B, EFOS-C
y EFOS-D, compuestos todos ellos por arrays de 64 detectores lineales.
• La señal medida por cada detector se amplifica y se codifica utilizando 12 bits. Esta
señal se almacena en una memoria intermedia donde es sometida a una etapa de pre
Trabajo Fin de Máster
18
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
procesado, siendo registrada a continuación en una cinta de alta densidad de 10.4
GB a velocidad de 20.4 MB/s.
• El sensor dispone de un sistema de calibración a bordo, que utiliza una lámpara
halógena de cuarzo que proporciona la radiación de referencia necesaria para
comprobar el estado de los diferentes espectrómetros.
• A lo largo de los últimos años, el sensor ha ido mejorando sus prestaciones en
cuanto a la relación señal-ruido, como se muestra en la Figura 2.4 que describe la
evolución de la relación SNR del sensor a lo largo de los últimos años.
Figura 2.4:Evolución de la relación SNR del sensor AVIRIS de NASA
2.3 Técnicas de análisis hiperespectral y necesidad de
paralelismo
La mayoría de las técnicas de análisis hiperespectral desarrolladas hasta la fecha
presuponen que la medición obtenida por el sensor en un determinado píxel viene dada por
la contribución de diferentes materiales que residen a nivel sub-píxel. El fenómeno de la
mezcla puede venir ocasionado por una insuficiente resolución espacial del sensor, pero lo
cierto es que este fenómeno ocurre de forma natural en el mundo real, incluso a niveles
microscópicos, por lo que el diseño de técnicas capaces de modelar este fenómeno de
manera adecuada resulta imprescindible. No obstante, las técnicas basadas en este modelo
son altamente costosas desde el punto de vista computacional. A continuación, detallamos
las características genéricas de las técnicas basadas en este modelo y hacemos énfasis en la
Trabajo Fin de Máster
19
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
necesidad de técnicas paralelas para optimizar su rendimiento computacional.
Técnicas basadas en el modelo lineal de mezcla
El modelo lineal de mezcla expresa los píxeles mezcla [ 11 ] como una
combinación lineal de firmas asociadas a componentes espectralmente puros (llamados
endmembers) en la imagen [ 12 ]. Este modelo ofrece resultados satisfactorios cuando los
componentes que residen a nivel sub-píxel aparecen espacialmente separados, situación en
la que los fenómenos de absorción y reflexión de la radiación electromagnética incidente
pueden ser caracterizados siguiendo un patrón estrictamente lineal. En la actualidad, el
modelo lineal de mezcla es el más utilizado en análisis hiperespectral, debido a su sencillez
y generalidad.
Figura 2.5:Interpretación gráfica del modelo lineal de mezcla
El modelo lineal de mezcla puede interpretarse de forma gráfica en un espacio
bidimensional utilizando un diagrama de dispersión entre dos bandas poco correlacionadas
de la imagen, tal y como se muestra en la Figura 2.5. En la misma, puede apreciarse que
todos los puntos de la imagen quedan englobados dentro del triángulo formado por los tres
puntos más extremos (elementos espectralmente más puros). Los vectores asociados a
dichos puntos constituyen un nuevo sistema de coordenadas con origen en el centroide de
la nube de puntos, de forma que cualquier punto de la imagen puede expresarse como
Trabajo Fin de Máster
20
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
combinación lineal de los puntos más extremos, siendo estos puntos los mejores candidatos
para ser seleccionados como endmembers [ 13 ]. El paso clave a la hora de aplicar el
modelo lineal de mezcla consiste en identificar de forma correcta los elementos extremos
de la nube de puntos N-dimensional. En la literatura reciente se han propuesto numerosas
aproximaciones
al
problema
de
identificación
de
endmembers
en
imágenes
hiperespectrales. Por ejemplo, el método Píxel Purity Index (PPI) [ 9 ] se basa en la
generación repetitiva de vectores unitarios con orientación aleatoria en la nube de puntos
de forma que todos los puntos de la imagen hiperespectral se proyectan sobre cada vector
unitario, identificando los puntos extremos en la dirección definida por dicho vector e
incrementando un contador asociado a dichos puntos. Tras la ejecución de un número
amplio de iteraciones, se obtiene como resultado una imagen de pureza, formada por los
índices asociados a cada uno de los píxeles de la imagen, a partir de la cual se extrae un
conjunto final de endmembers utilizando técnicas de análisis y visualización interactiva.
Por su parte, el método N-FINDR utiliza un procedimiento totalmente automático para
extraer endmembers basado en identificar los vértices del simplex de mayor volumen que
puede formarse en la nube de puntos.
Necesidad de paralelismo
Conviene destacar que las técnicas de análisis hiperespectral anteriormente
descritas se basan en la realización de operaciones matriciales que resultan muy costosas
desde el punto de vista computacional [ 14 ]. Sin embargo, el carácter repetitivo de estas
operaciones las hace altamente susceptibles de ser implementadas en diferentes tipos de
arquitecturas paralelas, permitiendo así un incremento significativo de su rendimiento en
términos computacionales y dotando a dichas técnicas de la capacidad de producir una
respuesta rápida. Esta tarea es clave para la explotación de dichas técnicas en aplicaciones
que requieren una respuesta en tiempo casi real.
Las técnicas de computación paralela han sido ampliamente utilizadas para llevar a
cabo tareas de procesamiento de imágenes de gran dimensionalidad, facilitando la
obtención de tiempos de respuesta muy reducidos y pudiendo utilizar diferentes tipos de
arquitecturas [ 15 - 17 ]. En la actualidad, es posible obtener arquitecturas paralelas de bajo
Trabajo Fin de Máster
21
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
coste mediante la utilización de GPUs de última generación que cuentan con múltiples
procesadores.
El papel de las GPUs
La extracción de endmembers es una tarea crucial en la explotación de datos
hiperespectrales. En la última década se han desarrollado muchos algoritmos para la
extracción automática de endmembers desde un conjunto de datos hiperespectrales,
incluidos PPI, N-FINDR, análisis de componentes vértices, o IEA. Estas técnicas tratan los
datos hiperespectrales no como una imagen sino como una lista desordenada de
dimensiones espectrales, donde las coordenadas espaciales se pueden cambiar de forma
aleatoria sin que esto afecte al proceso de búsqueda de endmembers.
A pesar de que el desarrollo espacial y espectral es una promesa para el análisis
científico de imágenes terrestres, introduce un nuevo reto de procesamiento
particularmente para conjunto de datos de alta dimensionalidad. Desde un punto de vista
computacional cada algoritmo muestra un patrón de acceso a los datos regular y que
muestra un paralelismo inherente a muchos niveles: a nivel de vectores de píxeles, a nivel
de información espectral e incluso a nivel de tarea. Como resultado se asocian con sistemas
paralelos compuestos por CPUs (por ejemplo clusters Beowulf). Desafortunadamente estos
sistemas son caros y difíciles de adaptar a bordo de escenarios de procesamiento de
sensación remota.
Un nuevo desarrollo en el campo de la computación surge con los procesadores
gráficos programables (GPUs). Guiadas por la creciente demanda de la industria de los
videojuegos, las GPUs han evolucionado como sistemas programables altamente paralelos.
Sin embargo la arquitectura de las GPUs no encaja necesariamente con todos los tipos de
computación paralela.
En especial, el siempre creciente requerimiento computacional introducido por el
estado actual de los algoritmos de imágenes hiperespectrales pueden beneficiarse de este
hardware y tomar ventaja de su poco peso y bajo coste de sus unidades, lo que lo hace
llamativo para el procesamiento de datos a bordo por un coste mucho más bajo del que
tienen otros dispositivos hardware como las FPGAs.
Trabajo Fin de Máster
22
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
3 Tarjetas gráficas programables GPUs
En este capítulo hablamos de cómo han ido evolucionando las GPUs programables,
de los recursos que ofrecen y su capacidad de computo. Introduciremos una serie de
conceptos para lograr entender cómo se lleva a cabo el procesamiento en una GPU
mediante CUDA. Describiremos el hardware de la tarjeta Tesla C1060 con la que se ha
realizado el trabajo y finalmente trataremos el lenguaje CUDA en si dando unas nociones
básicas para llevar a cabo cualquier proyecto con este lenguaje.
3.1 Las GPUs como un dispositivo de procesamiento de datos
en paralelo
Desde un tiempo a esta parte, las GPUs programables han evolucionado como un
elemento con una gran carga de trabajo, como podemos apreciar en las Figuras 3.1 y 3.2
donde se muestra una comparativa de la evolución de la capacidad de computo y del ancho
de banda de las CPUs y las GPUs. Con múltiples núcleos y con un gran ancho de banda de
memoria, hoy por hoy las GPUs ofrecen prestaciones muy elevadas para procesamiento
gráfico y científico [ 19 - 22 ].
Figura 3.1: Operaciones en coma flotante por segundo para CPU y GPU
Trabajo Fin de Máster
23
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Figura 3.2:Ancho de banda CPU y GPU
Antes de examinar en detalle la arquitectura de una GPU Tesla C1060 de NVidia
(http://www.nvidia.com), consideramos relevante explicar cómo han funcionado las
operaciones en un pipeline de una GPU clásica a lo largo de los años. No obstante, para
entender este proceso, también debemos introducir los conceptos de vertex shaders y los
píxel shaders.
Vortex shaders y píxel shaders
Los shaders son pequeños programas que se encargan del procesamiento de vértices
(vertex shaders) y de píxeles (píxel shaders). La principal ventaja es que pueden ser
programados por el desarrollador, otorgando una flexibilidad que hasta antes de la
aparición de los shaders era algo impensable. Recursos como las operaciones condicionales
o los saltos se utilizan de forma similar que en los lenguajes más conocidos. Sin los
shaders, muchos de los efectos eran realizados en conjunto con la unidad de procesamiento
central, disminuyendo en gran medida el rendimiento y limitando el avance a nivel gráfico
de los mismos.
Un vertex shader es una función que recibe como parámetro un vértice. Sólo trabaja
con un vértice a la vez, y no puede eliminarlo, sólo transformarlo. Para ello, modifica
propiedades del mismo para que repercutan en la geometría del objeto al que pertenece.
Trabajo Fin de Máster
24
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Con esto se pueden lograr ciertos efectos específicos, como los que tienen que ver con la
deformación en tiempo real de un elemento; por ejemplo, el movimiento de una ola. Donde
toma una gran importancia es en el tratamiento de las superficies curvas.
En cambio, un píxel shader básicamente especifica el color de un píxel. Este
tratamiento individual de los píxeles permite que se realicen cálculos principalmente
relacionados con la iluminación del elemento del cual forman parte en la escena, y en
tiempo real.
La incorporación de los píxel shaders y vertex shaders permite a los programadores
una mayor libertad a la hora de diseñar gráficos en tres dimensiones, ya que puede tratarse
a cada píxel y cada vértice por separado. De esta manera, los efectos especiales y de
iluminación pueden crearse mucho más detalladamente, sucediendo lo mismo con la
geometría de los objetos.
Pipeline clásico de procesamiento en una GPU
Cuando revisamos las arquitecturas hardware, el flujo de datos, y las operaciones
pipeline, a menudo es bueno empezar por el nivel más alto, donde los datos llegan desde la
CPU a la GPU, y el proceso se desarrolla hacia abajo a través de múltiples fases de
procesamiento hasta que un píxel es dibujado definitivamente en la pantalla. Para
situarnos, las GPUs han utilizado diseños pipeline tradicionales, como los que aparecen
ilustrados en la Figura 3.3.
Trabajo Fin de Máster
25
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Figura 3.3:Pipeline clásico de procesamiento en una GPU
Después de que la GPU recibe los datos vertex (vértices) desde el host (CPU), la
fase vertex se ejecuta en primer lugar. La función de fijado transforma la imagen y el
hardware de luminosidad operado en esta fase se lleva a cabo; entonces los píxeles shaders
programables, y el control de flujo dinámico de los modelos shaders entran en juego. El
siguiente paso en el pipeline clásico es la configuración, donde los vértices son
ensamblados dentro de primitivas como triángulos, líneas o puntos. Las primitivas son
convertidas por la fase de “rasterización” en fragmentos de píxeles (o simplemente
fragmentos), pero no son considerados píxeles completos en esta fase. Los fragmentos
están sometidos a muchas otras operaciones como sombreado, Z-testing, la posible mezcla
en el buffer frame, y el antialiasing. Los fragmentos son finalmente considerados píxeles
cuando han sido escritos en el buffer frame.
A continuación, la siguiente fase es la de píxel shader, que debería ser denominada
técnicamente como fase fragment shader, pero utilizamos la notación estándar en la
literatura [ 19 - 22 ] debido a su aceptación. En el pasado, los fragmentos sólo podían haber
tenido valores de color aplicados de textura simple. Hoy en día, la capacidad de sombreado
de un píxel programado de la GPU permite numerosos efectos de sombreado para ser
aplicados mientras se trabaja de acuerdo con métodos complejos de multitextura.
Específicamente, los fragmentos sombreados (con color y valores Z) desde esta fase píxel
Trabajo Fin de Máster
26
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
son enviados al ROP (Raster Operations). La fase ROP es donde se chequea el buffer Z
para asegurar que sólo los fragmentos visibles son procesados rápidamente, y los
fragmentos visibles, si son parcialmente transparentes, son mezclados con el buffer de
frame existente, junto con los píxeles y aplicándoles antialiased. El píxel procesado final es
enviado a la memoria buffer para ser escaneado y visualizado en el monitor [ 19 ].
Evolución del uso de GPUs en aplicaciones científicas
La principal razón que justifica la gran popularidad de las arquitecturas GPU en
aplicaciones científicas es el hecho de que la GPU está especializada para cómputo
intensivo, computación paralela elevada (exactamente sobre lo que trata el renderizado de
gráficos) y por tanto se designan más transistores dedicados al procesamiento de datos, que
a la recolección de datos y control de flujo como se muestra en la Figura 3.4.
Figura 3.4:La GPU proporciona más transistores para el procesamiento de datos
Más específicamente, la GPU está especialmente pensada para direccionar
problemas que pueden ser expresados como computaciones de datos paralelos (el mismo
programa es ejecutado en muchos elementos de datos en paralelo) con gran intensidad
aritmética (el ratio de operaciones aritméticas respecto a operaciones de memoria). Como
el mismo programa es ejecutado para cada elemento de datos, hay menos requisitos para
un flujo de control sofisticado; y como es ejecutado en muchos elementos de datos y tiene
gran intensidad aritmética, la latencia de acceso a memoria puede ser ocultada con
cálculos, en vez de datos muy grandes de caché [ 20 ].
Trabajo Fin de Máster
27
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
El procesamiento de datos paralelos asocia los datos a elementos de proceso
paralelos. Muchas aplicaciones que procesan grandes conjuntos de datos como arrays
pueden usar un modelo de programación de datos paralelos para acelerar los cálculos. En
renderizado 3D los conjuntos de píxeles y vértices se asignan a hilos paralelos. De la
misma manera, aplicaciones de procesamiento de imágenes y multimedia como
postprocesado de imágenes renderizadas, codificación y decodificación de vídeo, escalado
de imágenes, visión estéreo, y patrones de reconocimiento pueden asociar bloques de la
imagen y píxeles a hilos de procesamiento paralelo. De hecho, muchos algoritmos fuera
del campo del renderizado
como el procesamiento de señales, simulaciones físicas
finanzas o biología, se aceleran con el procesamiento de datos en paralelo.
Hasta la fecha, sin embargo, a pesar de acceder a todo el poder de computación
contenido en al GPU y usarlo eficientemente para aplicaciones científicas, seguía siendo
difícil obtener las siguientes pautas:
•
La GPU solamente podía ser programada a través de la API (Application
Programming Interface) gráfica; esto provocaba que la curva de aprendizaje
para un desarrollador principiante fuese muy elevada, ya que tenía que trabajar
con una API inadecuada, que no estaba adaptada a la aplicación científica.
•
La DRAM de la GPU podía ser leída de manera general (los programas de GPU
pueden obtener elementos de datos de cualquier parte de la DRAM) pero no se
podía escribir de manera general (los programas de GPU no pueden esparcir la
información a cualquier parte de la DRAM), eliminando mucha de la
flexibilidad de programación ya disponible en la CPU.
•
Algunas aplicaciones tenían en problema del “cuello de botella”, debido al
ancho de banda de la memoria DRAM, utilizando escasamente el poder
computacional de la GPU.
En este sentido, una de las principales motivaciones del presente TFM es demostrar
que dichas limitaciones en la actualidad pueden superarse mediante la utilización de la
arquitectura CUDA para procesamiento de datos científicos en la GPU. Dicho aspecto será
abordado en detalle en el siguiente subapartado del presente capítulo de la memoria.
Trabajo Fin de Máster
28
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
3.2 CUDA: una nueva arquitectura para el calculo en la GPU
CUDA viene del inglés Compute Unified Device Architecture y es una nueva
arquitectura hardware y software, diseñada para dar y manejar procesamientos en la GPU
como un elemento de computación de datos paralelos sin la necesidad de mapearlos a una
API de gráficos. Está disponible para las versiones GeForce 8 Series, Quadro FX
5600/4600, Tesla y Fermi. El mecanismo de multitarea del sistema operativo es
responsable de manejar el acceso a la GPU mediante CUDA, y las aplicaciones gráficas
funcionan de forma simultánea. A continuación describimos el pipeline unificado del que
disponen las actuales GPUs de NVIDIA y que puede ser explotado de forma eficiente
mediante CUDA, así como la arquitectura completa de la Tesla C1060. El apartado finaliza
describiendo los diferentes modelos de programación en CUDA.
Pipeline unificado
A partir del modelo de pipeline clásico, con sus flujos de datos empezando en lo
más alto, donde los vértices con varios atributos, índices, comandos, y texturas son pasados
a la GPU desde la CPU. Las fases de procesamiento mayores siguen una manera lineal
segura incluyendo vertex shading, píxel shading, operaciones raster, (que son operaciones a
través de las cuales un área espacial queda dividida en celdas regulares, en las que cada
una de las cuales presentan unos atributos o valor, como pueden ser la altitud, reflectancia,
etc.) y escritura de píxeles en el buffer frame.
Con este pipeline unificado y la arquitectura “shader”, el diseño de la GPU Tesla
C1060 reduce significativamente el número de fases del pipeline y cambia el flujo
secuencial para estar más orientado a bucle. Las entradas son alimentadas en la parte alta
del núcleo shader unificado, y las salidas son escritas en registros y entonces vuelven otra
vez a la parte alta del núcleo shader para la próxima operación. Como resultado, en el
diagrama GPU unificado generalizado que se muestra en la Figura 3.5, los flujos de datos
bajan secuencialmente por el pipeline a través de diferentes tipos “shader”. La figura de la
derecha representa un núcleo “shader” unificado con uno o más procesadores “shader”
unificados estandarizados.
Trabajo Fin de Máster
29
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Figura 3.5:Pipeline clásico frente a pipeline unificado
Como puede apreciarse en la Figura 3.5, los datos vienen de la parte superior
izquierda del diseño unificado (como vértices), y son llevados al núcleo shader para su
procesamiento, y los resultados son enviados de vuelta a la parte superior del núcleo
shader, donde son llevados otra vez, procesados otra vez, mandados de vuelta a la parte
superior, y así hasta que todas las operaciones shader son ejecutadas y el fragmento de
píxel se pasa al subsistema ROP [ 19 ].
Modelo de programación CUDA
Antes de profundizar en el modelo de programación empleado por CUDA,
destacamos que la pila del software de CUDA se compone de varias capas, tal y como
muestra la Figura 3.6. En concreto, dichas capas son un controlador de hardware, una API
y su runtime, y dos librerías matemáticas de alto nivel para uso común, CUFFT y
CUBLAS. El hardware ha sido diseñado para soportar controladores ligeros y capas
runtime, dando como resultado una ejecución óptima. En este sentido, la API de CUDA es
una extensión del lenguaje de programación C, lo cual hace que tenga una curva de
aprendizaje mínima.
Trabajo Fin de Máster
30
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Figura 3.6:Pila de software de CUDA
Por otra parte, CUDA ofrece un direccionamiento de carácter general para la
memoria DRAM como ilustra la Figura 3.7. Este modelo de direccionamiento permite
obtener mayor flexibilidad en la programación, en el sentido de que ofrece tanto la
operación de reparto de datos como la de obtención de estos. Desde una perspectiva de
programación, esto se traduce en la habilidad de leer y escribir datos en cualquier lugar de
la DRAM, exactamente igual que en la CPU [ 20 ].
Figura 3.7:Operaciones de memoria gather (reunión) y scatter (dispersión)
CUDA proporciona una memoria compartida on-chip a modo de caché de datos que
permite accesos muy rápidos de lectura y de escritura. Mediante ésta los hilos pueden
compartir datos. Como se muestra en la Figura 3.8, las aplicaciones pueden beneficiarse de
esta memoria minimizando los accesos a memoria DRAM lo que les hace menos
Trabajo Fin de Máster
31
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
dependientes del ancho de banda de la DRAM que es mucho menor que el de la shared
memory.
Figura 3.8:Shared memory
3.3 Procesamiento
En la siguiente sección presenta a la GPU como un coprocesador multihilo para la
CPU, seguidamente se exponen los diferentes mecanismos de los que se hace uso para
llevar a cabo el procesamiento en la GPU, estos son los hilos de ejecución, los bloques en
los que se organizan estos hilos y los llamados grid en los que se estructuran estos bloques.
La sección finaliza con el modelo de memoria utilizado por CUDA.
Un coprocesador multihilo
Cuando se programa con CUDA, la GPU se ve como un dispositivo de cálculo
(device) capaz de ejecutar un gran número de hilos en paralelo. Éste opera como un
coprocesador de la CPU principal, o host. En otras palabras, los datos paralelos, cálculo
intensivo de porciones de aplicaciones ejecutándose en el host son cargados en el device.
De forma más precisa, una parte de una aplicación que se ejecuta muchas veces, pero con
datos diferentes e independientes, puede ser aislada en una función que es ejecutada en el
device como muchos hilos independientes. Como dicha función es compilada, la
instrucción obtenida a partir del device (en nuestro caso, la GPU) y el programa resultante,
Trabajo Fin de Máster
32
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
llamado kernel, se descargan en el device. Tanto el host como el device mantienen su
propia DRAM, referidas como memoria del host y memoria del device respectivamente.
Una puede copiar datos de una memoria a la otra a través de llamadas a una API
optimizada, que usa el Acceso Directo a Memoria (DMA) del device [ 20 ].
Hilos, Bloques y Grids
El tratamiento por lotes de los hilos que ejecuta el kernel está organizado como un
grid de bloques de hilos, ilustrado en la Figura 3.9. Un bloque de hilos es un lote de hilos
que pueden cooperar juntos compartiendo datos eficientemente a través de la memoria
compartida y sincronizar sus ejecuciones para coordinar los accesos a memoria. De forma
precisa, uno puede especificar puntos de sincronización en el kernel, donde los hilos en un
bloque están suspendidos hasta que todos ellos alcancen el punto de sincronización. Cada
hilo es identificado por su identificador de hilo (thread ID), que es el número de hilo dentro
de un bloque [ 20 ].
Hay un número máximo de hilos que un bloque puede contener (512 hilos
concretamente). Sin embargo, los bloques de misma dimensión y tamaño que ejecutan el
mismo kernel pueden ser tratados por lotes de forma conjunta, en un grid de bloques, así
que el número total de hilos puede ser lanzado en una única invocación del kernel es
mucho más grande. Esto se debe al gasto de reducir la cooperación entre hilos, porque los
hilos en diferentes bloques del mismo grid no pueden comunicarse ni sincronizarse con los
de los demás. Este modelo permite a los kernels ejecutarse eficientemente sin
recompilación en varios devices con diferentes capacidades paralelas: un device puede
ejecutar todos los bloques de un grid secuencialmente si tiene poca capacidad, o en
paralelo si tiene mucha, o normalmente una combinación de ambas [ 20 ]. La Figura 3.9
muestra como cada kernel se ejecuta como un grid de bloques de hilos.
Trabajo Fin de Máster
33
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Figura 3.9: Grid, bloques e hilos
Modelo de memoria
Un hilo que se ejecute en el device tiene solo acceso a la DRAM del device y a la
memoria on-chip a través de los siguientes espacios de memoria (Tabla 3.1) [ 20 ], como se
muestra en la Figura 3.10.
Registro de
hilo
Lectura
Escritura
Memoria
Memoria
compartida de local de hilo
bloque
✓
✓
✓
✓
✓
✓
Memoria
global de grid
Memoria
constante de
grid
Memoria de
texturas de
grid
✓
✓
✓
×
✓
×
Tabla 3.1: Lecturas y escrituras en los diferentes tipos de memoria
Los espacios de memoria global, constante y de textura pueden ser leídos o escritos
por el host y perduran durante las distintas ejecuciones del kernel en la misma aplicación.
Estos espacios se optimizan para diferentes usos de la memoria. Además la memoria de
texturas ofrece diferentes modos de direccionamiento, así como de filtrado de datos, para
diferentes formatos de datos.
Trabajo Fin de Máster
34
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Figura 3.10:Acceso a diferentes espacios de memoria
3.4 Implementación hardware y modelo de ejecución
En esta sección se presenta la arquitectura hardware de la GPU utilizada en el
trabajo y se explica el modelo de ejecución que sigue CUDA.
Nvidia Tesla C1060
A lo largo de la presente memoria hemos cubierto muchos de los puntos básicos de
la programación de GPUs de NVidia, así que ahora podemos echar un vistazo a los
aspectos específicos de la arquitectura Tesla C1060 [ 19 ], la tarjeta que estamos usando
para realizar este TFM. La Figura 3.11 describe la arquitectura hardware de dicha tarjeta.
El significado de las siglas que aparecen en el esquema es el siguiente: TPC
(Texture/Processor Cluster), SM (Streaming Multiprocessor), SP (Streaming Processor),
Tex (Texture), ROP (Raster Operation Processor).
Trabajo Fin de Máster
35
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Figura 3.11:Arquitectura de la GPU Tesla C1060 de NVidia
A continuación la Tabla 3.2 muestra las características técnicas de la GPU:
Specification
Description
Form Factor
10.5" x 4.376", Dual Slot
# of Streaming Processor Cores
240
Frequency of processor cores
1.3 GHz
Single Precision floating point 933
performance (peak)
Double Precision floating point 78
performance (peak)
Floating Point Precision
IEEE 754 single & double
Total Dedicated Memory
4 GDDR3
Memory Speed
800MHz
Memory Interface
512-bit
Memory Bandwidth
102 GB/sec
Max Power Consumption
187.8 W
System Interface
PCIe x16
Auxiliary Power Connectors
6-pin & 8-pin
Thermal Solution
Active fan sink
Tabla 3.2:Especificaciones técnicas de la GPU Tesla C1060 de NVidia
Trabajo Fin de Máster
36
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Modelo de ejecución
La GPU se implementa como un conjunto de multiprocesadores como se puede ver
en la Figura 3.12. Cada multiprocesador tiene una arquitectura SIMD: en cada ciclo de
reloj, cada procesador del multiprocesador ejecuta la misma instrucción, pero opera en
datos distintos. Los espacios de memoria local y global son implementados como regiones
de la memoria del device. Cada procesador tiene acceso a la memoria compartida del
multiprocesador en el que está integrado. Cada multiprocesador accede a la cache de
textura mediante una unidad de textura que implementa los diferentes modos de
direccionamiento y filtros de datos [ 20 ].
Figura 3.12:Conjunto de multiprocesadores SIMD con memoria compartida on-chip
Un grid de bloques de hilos se ejecuta en el device mediante la programación de los
bloques en cada multiprocesador. Cada multiprocesador procesa los bloques por lotes, un
lote tras otro. Un bloque solo se procesa en un multiprocesador, así el espacio de memoria
compartida reside en la memoria on-chip compartida por lo que se puede beneficiar de la
velocidad que proporciona dicha memoria.
Trabajo Fin de Máster
37
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
El numero de bloques que cada multiprocesador puede procesar en un lote depende
de cuantos registros necesita cada hilo y de cuanta memoria compartida necesita cada
bloque en un kernel ya que estos recursos se deben compartir entre todos los hilos de los
bloques del lote. Si no hay una cantidad suficiente de registros o de memoria compartida
disponible en un multiprocesador para procesar al menos un bloque, el kernel fallará en su
ejecución.
Los bloques que se procesan en un multiprocesador en un lote se llaman activos.
Cada bloque activo se divide en grupos de hilos SIMD llamados warps. Cada uno de estos
warps contiene el mismo número de hilos, este número se llama warp size, y se ejecuta en
un multiprocesador en modo SIMD. Los warps activos (los warps de los bloques activos)
se dividen en el tiempo: un programador de hilos cambia de un warp a otro para maximizar
el uso de recursos computacionales del multiprocesador. Se llama half warp a la primera o
a la segunda mitad de un warp.
La forma en la que un bloque se divide en warps es siempre la misma; cada warp
contiene hilos consecutivos, incrementando sus identificadores de hilo (thread ID). El
primer warp contiene al hilo 0.
El orden de emisión de los warps de un bloque no esta definido y no existe un
mecanismo de sincronización entre bloques para coordinar los accesos a memoria global o
compartida.
El orden de emisión de los bloques en un grid de bloques de hilos tampoco está
definido y no hay un mecanismo de sincronización entre bloques, así que los hilos de dos
bloques diferentes pertenecientes al mismo grid no se pueden comunicar de una forma
segura a través de la memoria global durante la ejecución de un grid.
Si una instrucción no atómica ejecutada por un warp escribe en la misma posición
ya sea de memoria global o compartida por más de un hilo de warp, el número de de
escrituras serializadas que ocurran en esa posición así como el orden de las escrituras es
indefinido, pero se garantiza que al menos una escritura tenga éxito. Si una instrucción
atómica ejecutada por un warp lee, modifica o escribe en una posición de memoria ya sea
global o compartida por más de un hilo del warp, cada lectura, modificación o escritura en
la posición ocurre de forma serializada aunque el orden es indefinido.
Trabajo Fin de Máster
38
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Especificaciones generales
A continuación en la Tabla 3.3 se listan una serie de especificaciones generales que
se deben tener en cuenta a la hora de programar una GPU con CUDA para lograr una
ejecución más eficiente en función de la capacidad de computo (Compute capability) de la
GPU.
Trabajo Fin de Máster
39
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Parámetro
Valor según gener. GPU
CUDA Compute Capabilities
1.0 y 1.1 1.2 y 1.3 Fermi
Multiprocesadores / GPU
Limitación Impacto
16
30
16
HW.
Escalabilidad
8
8
32
HW.
Escalabilidad
32
32
32
SW.
Throughput
8
8
8
SW.
Throughput
Hilos / Bloque
512
512
512
SW.
Paralelismo
Hilos / Multiprocesador
768
1024 1536
SW.
Paralelismo
Registros de 32 bits / Multiproc.
8192
16384 4096
HW.
Working Set
Memoria compartida / Multiproc.
16384
16384 16 K
48K
HW.
Working Set
Procesadores / Multiprocesador
Hilos / Warp
Bloques de hilos / Multiprocesador
Tabla 3.3:Recursos y limitaciones según la GPU que utilicemos para programar CUDA
A continuación en la Tabla 3.4 se muestra la capacidad de computo y el número de
multiprocesadores de cada GPU de Nvidia.
Trabajo Fin de Máster
40
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Número de
Multiprocesadore
s
(1
Multiprocesador
= 8 Procesadores)
Compute
Capability
2x30
1.3
GeForce GTX 285, GTX 280
30
1.3
GeForce GTX 260
24
1.3
GeForce 9800 GX2
2x16
1.1
GeForce GTS 250, GTS 150, 9800 GTX,
9800 GTX+, 8800 GTS 512
16
1.1
GeForce 8800 Ultra, 8800 GTX
16
1.0
GeForce 9800 GT, 8800 GT, GTX 280M,
9800M GTX
14
1.1
GeForce GT 130, 9600 GSO, 8800 GS,
8800M GTX, GTX 260M, 9800M GT
12
1.1
GeForce 8800 GTS
12
1.0
GeForce 9600 GT, 8800M GTS, 9800M GTS
8
1.1
GeForce 9700M GT
6
1.1
GeForce GT 120, 9500 GT, 8600 GTS, 8600 GT,
9700M GT, 9650M GS, 9600M GT, 9600M GS,
9500M GS, 8700M GT, 8600M GT, 8600M GS
4
1.1
GeForce G100, 8500 GT, 8400 GS, 8400M GT,
9500M G, 9300M G, 8400M GS, 9400 mGPU,
9300 mGPU, 8300 mGPU, 8200 mGPU,
8100 mGPU
2
1.1
GeForce 9300M GS, 9200M GS, 9100M G,
8400M G
1
1.1
Tesla S1070
4x30
1.3
Tesla C1060
30
1.3
Tesla S870
4x16
1.0
Tesla D870
2x16
1.0
Tesla C870
16
1.0
Quadro Plex 2200 D2
2x30
1.3
Quadro Plex 2100 D4
4x14
1.1
GeForce GTX 295
Trabajo Fin de Máster
41
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Quadro Plex 2100 Model S4
4x16
1.0
Quadro Plex 1000 Model IV
2.16
1.0
Quadro FX 5800
30
1.3
Quadro FX 4800
24
1.3
Quadro FX 4700 X2
2x14
1.1
Quadro FX 3700 M
16
1.1
Quadro FX 5600
16
1.0
Quadro FX 3700
14
1.1
Quadro FX 3600M
12
1.1
Quadro FX 4600
12
1.0
Quadro FX 2700 M
6
1.1
Quadro FX 1700, FX 570, NVS 320M, FX 1700M,
FX 1600M, FX 770M, FX 570M
4
1.1
Quadro FX 370, NVS 290, NVS 140M, NVS 135M,
FX 360M
2
1.1
Quadro FX 370M, NVS 130M
1
1.1
Tabla 3.4:Capacidad de cómputo y número de multiprocesadores de cada GPU de Nvidia
Trabajo Fin de Máster
42
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
4 Método
En este apartado se describe el método de extracción de endmembers considerado
en el presente estudio e implementado de forma eficiente utilizando tarjetas gráficas
programables de la gama Nvidia. La implementación paralela de este algoritmo en GPUs
será descrita en la próxima sección del presente documento.
4.1 Pixel Purity Index (PPI)
Uno de los algoritmos más satisfactorios para la extracción automática de
endmembers ha sido el método Píxel Purity Index (PPI) [ 9 ] desarrollado por Boardman,
Kruse, y Green en 1993, y que se incorporo al sistema de búsqueda ENVI de Kodak. El
algoritmo procede mediante la generación de un gran número de vectores aleatorios Ndimensionales llamados “skewers”. Cada punto de la imagen se proyecta en cada skewer y
los puntos que correspondan a los extremos en la dirección de un skewer se identifican y se
almacenan en una lista. Como se generan más skewers, la lista crece, y el número de veces
que un píxel dado se almacena en la lista también se incrementa. Los píxeles con mayores
incrementos son considerados como endmembers finales.
El algoritmo PPI pertenece al conjunto de los métodos interactivos y es el más
representativo. Su objetivo es localizar los puntos espectralmente más puros de la imagen
hiperespectral, basándose en la suposición de que los puntos más extremos del conjunto de
puntos son los mejores candidatos para ser utilizados como endmembers. Los parámetros
de entrada del algoritmo son el número de iteraciones a realizar y el valor umbral para
seleccionar píxeles puros. El funcionamiento del algoritmo se describe en los siguientes
pasos:
1)
En primer lugar, el algoritmo asigna un índice de pureza a todos los píxeles
de la imagen. El contador de cada punto se inicializa al valor 0.
2)
Seguidamente, se genera un vector unitario aleatorio, que recibe el nombre
de skewer o “divisor”. El objetivo de este vector es particionar el conjunto
de puntos, como veremos a continuación.
3)
El tercer paso consiste en proyectar todos los puntos de la imagen
Trabajo Fin de Máster
43
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
hiperespectral sobre el vector unitario antes generado, identificando los
puntos extremos en la dirección definida por el vector unitario. El índice de
pureza de los puntos extremos se incrementa en una unidad.
4)
Los pasos 2-3 del algoritmo se repiten tantas veces como el usuario
especifique en el parámetro de entrada, número de iteraciones.
5)
Tras la ejecución de un número amplio de iteraciones, se obtiene como
resultado una imagen de pureza formada por los índices asociados a cada
uno de los píxeles de la imagen.
6)
Utilizando el valor umbral especificado como parámetro, se seleccionan los
puntos de la imagen cuyo índice de pureza asociado supera dicho valor
umbral. Estos puntos son etiquetados como “puros”. La figura ilustra el
procedimiento seguido hasta este punto, suponiendo que se realizan tres
iteraciones y que se seleccionan como endmembers aquellos puntos que han
sido seleccionados como extremos una o más veces.
7)
Los píxeles seleccionados se cargan en una herramienta interactiva
denominada N-Dimensional Visualizar, la cual permite realizar diagramas de
dispersión de los primeros autovectores obtenidos tras la aplicación de una
transformación MNF sobre los datos originales.
8)
Utilizando la herramienta anteriormente descrita, el usuario selecciona
manualmente aquellos puntos o agrupaciones de puntos que aparecen como
extremos en proyecciones sucesivas, identificando un conjunto final de
endmembers. En el caso de seleccionar una agrupación de puntos, los
endmembers se obtienen a partir del cálculo del espectro promedio en cada
una de las regiones seleccionadas.
A continuación se exponen los pasos del algoritmo de una forma más analítica[ 24
]:
1. Las entradas del algoritmo son un cubo hiperespectral de datos
con
dimensiones; el número máximo de endmembers que se van a extraer,
número de vectores aleatorios que se van a generar durante el proceso,
Trabajo Fin de Máster
44
N
E ; el
K ; y un
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
valor umbral, usado para seleccionar como endmembers finales solo aquellos
píxeles que han sido seleccionados como extremos al menos
proceso PPI. La salida del algoritmo es el conjunto de
E
tv
veces durante el
endmembers finales
E
{e e }e=1 . El algoritmo se puede resumir en los siguientes pasos:
1. Generación de skewers. Produce un conjunto de
K
vectores generados
aleatoriamente {skewer j }Kj=1 .
2. Proyecciones de extremos. Para cada
fi
píxeles
skewer j , toda la muestra de vectores de
del conjunto de datos original
F
se proyecta sobre el
skewer j
mediante el producto escalar para encontrar una muestra de vectores en sus
extremos (máximo y mínimo), de esta manera se forma un conjunto de extremos
para el
skewer j
que se denota por
S extrema  skewer j  . El producto escalar es el
siguiente:
∣ f i⋅skewer j∣
A pesar del hecho de que un
S extrema  skewer j 
skewer j
diferente puede generar un
diferente, es muy probable que la misma muestra de vectores
aparezca en más de un conjunto de extremos. Para hacer frente a esta situación,
definiremos una función indicadora de un conjunto
S , a la que llamaremos
l s  x , para denotar la pertenencia la pertenencia de un elemento
x
a un
conjunto en particular de la siguiente manera:
l s  f i ={1 si x ∈S }
0 si x ∉S
3. Calculo de las puntuaciones PPI. Utilizando la función indicadora anterior,
calculamos la puntuación PPI asociada a un píxel vector muestra
fi
(por
ejemplo el número de veces que un píxel dado ha sido seleccionado como extremo
en el paso 2) usando la siguiente ecuación:
Trabajo Fin de Máster
45
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
N PPI  f i =∑ K I S
j=1
extrema
 skewer j 
 f i
4. Selección de endmember. Encontrar los píxeles cuyo valor de
N PPI  f i  esté por
encima de t v .
El método PPI contiene etapas totalmente automatizadas, como la fase de
generación de la imagen de pureza, pero es necesaria una etapa final, altamente interactiva,
en la que el usuario selecciona manualmente los píxeles que quiere utilizar como
endmembers. El usuario no conoce a priori cuál es el número apropiado de endmembers a
seleccionar, por lo que debe escoger el número de endmembers en base a su intuición. Este
hecho pone de manifiesto la conveniencia de cierto conocimiento a priori sobre la imagen.
Esta característica, unida a otras como la aleatoriedad en el proceso de generación de
vectores unitarios (ver Figura 4.1), representan los principales inconvenientes de esta
metodología [ 18 ]. Por otra parte, es importante destacar que el método PPI puede generar
endmembers artificiales en caso de que el usuario del método seleccione conjuntos de
puntos en el proceso interactivo de identificación de firmas espectrales extremas.
Figura 4.1:Funcionamiento del algoritmo PPI
Trabajo Fin de Máster
46
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
5 Procesamiento paralelo en GPU
Una vez introducidas las ventajas que aportan las GPUs para el procesamiento
paralelo pasemos a exponer como se lleva a cabo este procesamiento en el caso que nos
ocupa, es decir en el algoritmo PPI que ya ha sido descrito anteriormente. Como ya
sabemos contamos con una GPU NVidia Tesla C1060 la cual cuenta con 30
multiprocesadores cada uno de los cuales dispone a su vez de 8 procesadores. Pongamos
como caso de uso una imagen hipotética de dimensiones 350 x 350 x 188, esto es, 350
líneas, 350 columnas y 188 bandas.
Nuestra implementación del algoritmo PPI se puede dividir en 3 fases bien
diferenciadas. Por un lado tenemos la generación de los skewers, por otro lado la
proyección de todos los puntos de la imagen sobre cada skewer y la detección de los píxels
mas extremos. Y por otro lado la agrupación de los resultados para formar la imagen de
pureza.
5.1 Generación de los skewers
Como ya sabemos un skewer es un vector unitario cuyas componentes se generan
de forma aleatoria. El número de componentes de cada skewer debe ser igual al número de
bandas de la imagen hiperespectral, así tenemos que en nuestro caso de uso el número de
componentes de cada skewer es 188. En una iteración el algoritmo proyecta todos los
puntos de la imagen sobre un skewer, por tato necesitamos tantos skewers como
iteraciones. Supongamos por ejemplo que el número de iteraciones que queremos realizar
es de 15360, necesitaremos pues general un total de 15360 x 188 números aleatorios. A la
hora de generar tal cantidad de números hemos utilizado una función que realiza dicha
acción haciendo uso de la GPU. Esta función se encuentra dentro de los ejemplos que
proporciona CUDA, concretamente se trata de una función llamada RandomGPU que
aparece dentro del ejemplo MersenneTwister. Esta función permite generar y almacenar en
la memoria de la GPU 24002560 elementos en tan solo 10 milisegundos, muchos más de
los que necesitamos para este ejemplo. De esta manera con una sola llamada al kernel ya
tenemos solucionado el problema de generación de números en un tiempo ínfimo
comparado con el tiempo total. Cada grupo de 188 elementos formará un skewer.
Trabajo Fin de Máster
47
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
La configuración de la llamada al kernel se ha dejado tal y como viene en el
ejemplo, por lo que se van a usar 32 bloques de 128 hilos cada bloque. Los parámetros
necesarios para la ejecución de la función son 3, a continuación se detalla cada uno de
ellos.
1. Dirección a la estructura de datos donde se van a almacenar los números generados.
Éste parámetro debe de ser un puntero a un float y previamente se deberá reservar
espacio en la GPU para albergar esta estructura. A esta estructura la llamaremos
d_Random y es de tipo vector.
2. Número de elementos que generará cada hilo, se trata de un entero cuyo valor
indica el número de elementos que se va a generar durante la ejecución de un hilo.
De esta forma si este parámetro tiene el valor N, tras la ejecución del kernel
tendremos almacenados en la estructura num_bloques x num_hilos x N elementos.
3. Semilla. Se trata de un valor que sirve de semilla para la función de generación, su
valor está en función de la hora y fecha de la máquina mediante la llamada a
srand(time(NULL)); La utilización de un valor semilla se debe a que internamente
la computadora no dispone de un “dado” con el que poder generar valores sino que
utiliza funciones matemáticas y estadísticas complejas que necesitan de un valor
inicial. La asignación de un valor en función de la hora y fecha a la semilla nos
permite obtener valores distintos en cada ejecución. Si el valor de la semilla fuese
el mismo siempre obtendríamos los mismos resultados. De hecho como los
parámetros son comunes a todos los hilos del kernel todos los hilos generarían el
mismo conjunto de elementos aleatorios de no ser porque internamente este valor
de semilla se modifica multiplicándose por el identificador interno del hilo. De esta
forma cada hilo cuenta con una semilla distinta y por lo tanto genera un conjunto de
elementos distinto del resto de hilos.
Una vez finalizada la fase de generación de skewers el siguiente paso es la
proyección de todos los puntos de la imagen sobre cada skewer y la detección de los píxels
extremos. La forma en la que los skewers quedarán almacenados en la estructura
d_Random se muestra en la Figura 5.1, siendo N el número de iteraciones:
Trabajo Fin de Máster
48
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Figura 5.1:Almacenamiento de los Skewers
5.2 Proyección
Esta fase se realiza mediante una única llamada al kernel PPI. En ella cada hilo
realizará la proyección de todos los píxels de la imagen sobre un único skewer y detectará
los dos píxels más extremos para ese skewer. La configuración para la ejecución del kernel
es la siguiente: tendremos tantos hilos de ejecución como iteraciones queramos realizar en
el algoritmo, estos hilos se agruparán en bloques de 512, por tanto el número de bloques
será el número de iteraciones / 512. El hecho de que se escoja un tamaño de bloque de 512
hilos no es algo fortuito sino que se obtiene como resultado de una configuración que
garantiza una ejecución óptima del kernel y que al final se traducirá en una mejora
importante de rendimiento.
A continuación detallaremos cada uno de los parámetros de este kernel y
concluiremos esta fase con la explicación del funcionamiento del mismo.
1. El primer parámetro es la dirección de la imagen hiperespectral en memoria global.
Este parámetro se llama d_imagen y se trata de un puntero a un vector de floats. La
imagen esta almacenada en formato BSQ (secuencial por bandas) esto quiere decir
que primero se almacenan los píxels de la primera banda, después de la segunda y
así sucesivamente. Las primeras posiciones del vector se corresponden con los
primeros píxels de la primera banda de la imagen. Este parámetro es un parámetro
de entrada ya que la imagen no se va a modificar.
2. El segundo parámetro nos es conocido, se trata de la dirección de la estructura
donde se encuentran almacenados los skewers, su nombre es d_random y como ya
sabemos es un puntero a un vector de floats. También se trata de un parámetro de
entrada.
3. El tercer parámetro es la dirección en memoria global de la estructura donde se
Trabajo Fin de Máster
49
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
almacenarán los resultados parciales del algoritmo. Su nombre es d_res_parcial y se
trata de un vector de floats. El tamaño de este vector es el número de iteraciones x
2. Esto es así porque en cada iteración se almacenan las dos proyecciones extremas
sobre un skewer, esto es los píxels que obtienen el valor mas alto y mas bajo en el
producto escalar con el skewer. La forma en la que se almacenan estos valores en la
estructura está representada en la Figura 5.2:
Figura 5.2:Almacenamiento de los resultados parciales
4. Los tres parámetros restantes son tres enteros que representan las dimensiones de la
imagen hiperespectral: num_lines (número de lineas), num_samples (número de
columnas) y num_bands (número de bandas).
Pasamos ahora a explicar el funcionamiento del kernel. Como ya hemos comentado
lo que se pretende es que cada hilo realice una iteración del algoritmo, esto es que calcule
la proyección de todos los puntos de la imagen sobre un único skewer y obtenga los dos
píxels extremos. Para ello adames de las estructuras que tenemos como parámetros
necesitamos un par de estructuras más.
La primera de ellas es local a cada hilo y se almacena en memoria local, es decir en
los registros, se trata de una estructura de tipo vector de floats con tantos elementos como
bandas tenga la imagen, en nuestro caso de uso 188, el nombre de esta estructura es l_rand
y en ella el hilo i cargará desde memoria global el skewer que ocupe la posición i dentro de
la estructura d_random descrita anteriormente. Almacenamos esta estructura en memoria
local por dos razones: la primera es que los valores almacenados en esta estructura por
cada hilo no van a ser necesitados por el resto de hilos, por tanto no ubicamos esta
estructura en la memoria compartida. La segunda es que a lo largo de la ejecución del
algoritmo l_rand va a ser accedida continuamente, concretamente se accede a cada
Trabajo Fin de Máster
50
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
elemento una vez por cada píxel de la imagen. Por esto hacemos uso de la memoria local
que es cientos de veces mas rápida que la memoria global de la GPU.
La segunda estructura que necesitamos se almacena en memoria compartida, se
trata también de una estructura de tipo vector que tendrá capacidad para 10 píxels en
nuestro caso de uso. El nombre de la estructura es s_píxels Como cada hilo necesita
realizar el producto escalar por los mismos píxels es lógico hacer uso de una estructura
compartida a la que puedan acceder todos los hilos de un bloque sin necesidad de acceder
cada uno a memoria global por separado. Además esta memoria es también cientos de
veces mas rápida que la memoria global con lo que notamos una fuerte mejora en el
rendimiento al hacer uso de ella. Por esta estructura irán pasando todos los píxels de la
imagen en grupos de 10.
Ahora si veamos el funcionamiento del kernel paso a paso: lo primero que se hace
es cargar de memoria global un skewer de d_random y almacenarlo en l_rand, también
cargamos de d_imagen un grupo de 10 píxels y los almacenamos en s_píxels alojado en
memoria compartida, hagamos aquí un pequeño paréntesis para explicar como se hace esta
lectura de píxels que utiliza dos mecanismos que ofrece CUDA y que mejoran el
rendimiento en gran medida. A la hora de leer los píxels de memoria global estamos lo
hacemos mediante una lectura coalescente, la coalescencia permite realizar lecturas o
escrituras con un único acceso siempre que los datos se encuentren en un segmento de 32,
64 o 128 bytes, en nuestro caso cargamos de memoria píxels cuyos valores en la misma
banda se encuentran en posiciones consecutivas, cada valor en una banda es un float (4
bytes) como tenemos 10 píxels leemos 40 bytes que supondrían un único acceso a un
segmento de 64 bytes. Al tratarse la memoria global de una memoria lenta en comparación
con la compartida o la local, el hecho de reducir el número de accesos supone una mejora
en el rendimiento.
El otro mecanismo lo usamos al escribir estos píxels en memoria compartida. La
memoria compartida tiene un tamaño de 16 KB organizados en 16 bancos de 1KB. La
escritura en memoria se puede paralelizar siempre que estemos accediendo a bancos
distintos sino los accesos a memoria se serializan y resultan mas lentos. En nuestro caso
aunque todos los hilos de un bloque necesiten todos los píxels, la lectura de memoria
Trabajo Fin de Máster
51
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
globlal y escritura en memoria compartida la realizan exclusivamente los 10 primeros hilos
de cada bloque, cada uno lee de memoria el valor de un píxel en una banda y lo escribe en
memoria compartida en un banco distinto del resto por lo que la escritura se realiza en
paralelo.
Una vez que tenemos almacenados el skewer y un grupo de 10 píxels el siguiente
paso es calcular el producto escalar de cada píxel con el skewer. Cada hilo utiliza dos
variables pemin y pemax que almacenan los valores máximo y mínimo obtenidos en el
producto escalar, y otras dos variables imax e imin que almacenan el índice de los píxels
que han obtenido esos valores máximo y mínimo.
Cuando finaliza la proyección de un grupo de 10 píxels se carga otro grupo y se
repite el proceso hasta que hayamos cargado todos los píxels de la imagen.
Finalmente cada hilo en la posición 2 x identificador_hilo y 2 x identificador_hilo
+ 1 de la estructura d_res_parcial almacenará los valores imin e imax como vimos
anteriormente en la Figura 5.2.
5.3 Agrupación de resultados
Esta fase se realiza en la CPU y como su nombre indica se trata de aunar los
resultados en una imagen de pureza, en esta imagen el valor de cada píxel representará el
número de veces que ese píxel se ha seleccionado como extremo. El proceso a seguir es
recorrer la estructura h_res_parcial (que es la copia de d_res_parcial pero en memoria de
CPU) e incrementar en uno la posición de h_res_total (imagen de pureza) que indique cada
elemento de h_res_parcial. Este proceso se puede apreciar en la Figura 5.3:
Trabajo Fin de Máster
52
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Figura 5.3:Agrupación de resultados
Como podemos ver el píxel 1 aparece 2 veces en h_res_parcial y se incrementa 2
veces en h_res_total, el píxel 3 aparece 3 veces en h_res_parcial y se incrementa 3 veces
en h_res_total. De la misma forma hay píxels que ni siquiera se incrementa como es el
caso del píxel 0 que no aparece en h_res_parcial.
A continuación presentaremos dos herramientas de CUDA que son muy necesarias
a la hora de llevar a cabo cualquier mejora de optimización en nuestro código. Éstas son
Cuda Occupancy Calculator y CUDA Visual Profiler.
5.4 CUDA Occupancy Calculator
Es una herramienta que nos asesora en la elección de los parámetros de
configuración de un kernel. Se trata de una hoja de cálculo que nos presenta diversas
gráficas de la ocupación de un multiprocesador en función de la capacidad de cómputo, del
número de hilos que utilicemos en cada bloque, del número de registros que utilicemos por
hilo y de la cantidad de memoria compartida por bloque. El número de hilos por bloque es
fácil de conocer ya que es un parámetro fijado por nosotros. Para conocer tanto el número
de registros usados como la cantidad de memoria compartida necesitamos incluir como
modificador en el compilador el flag –ptxas-options="-v". Con ésto a la hora de compilar
se nos mostrarán esos valores.
Con estos cuatro parámetros CUDA Occupancy Calculator nos muestra entre otras cosas el
número de hilos activos por multiprocesador, el número de warps activos por
multiprocesador, el número de bloques de hilos activos por multiprocesador y la ocupación
Trabajo Fin de Máster
53
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
de cada multiprocesador. El principal objetivo de usar esta herramienta es conseguir un
100% de ocupación lo que significa que no hay ningún procesador ocioso en ningún
multiprocesador. A continuación la Figura muestra el aspecto de esta herramienta:
Figura 5.4:CUDA Occupancy Calculator
Seguidamente mostraremos los resultados de CUDA Occupancy Calculator para
una de nuestras ejecuciones, concretamente para una de nuestras imágenes sintéticas
descritas en el siguiente capítulo.
La Tabla 5.1 muestra los parámetros introducidos en la herramienta:
1.3
1.) Select Compute Capability:
2.) Enter your resource usage:
512
Threads Per Block
14
Registers Per Thread
8104
Shared Memory Per Block (bytes)
Tabla 5.1:CUDA Occupancy Calculator:Parámetros introducidos
Una vez que hemos introducido los parámetros se calcula la ocupación de cada
Trabajo Fin de Máster
54
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
multiprocesador y se muestra el estado de estos en las gráficas. La Tabla 5.2 muestra los
resultados calculados en base a estos parámetros:
3.) GPU Occupancy Data is displayed here and in the graphs:
1024
Active Threads per Multiprocessor
32
Active Warps per Multiprocessor
2
Active Thread Blocks per Multiprocessor
100%
Occupancy of each Multiprocessor
Tabla 5.2:CUDA Occupancy Calculator: Resultados calculados en función de los
parámetros
La herramienta además proporciona tres gráficas, cada una de ellas asociada a un
parámetro (nº de hilos por bloque, nº de registros por hilo y memoria compartida usada por
cada bloque), en éstas se muestra la ocupación conseguida con la configuración actual y
los posibles cambios en la ocupación si variamos el valor del parámetro asociado.
La ocupación se calcula como:
Ocupación=
Nº de warps ejecutandose concurrentemente en un multiprocesador
Nº máximo de warps
Máximo = 24 warps (1.0 y 1.1), 32 warps (1.3)
La Figura 5.5 muestra la gráfica asociada al número de hilos por bloque.
Figura 5.5:CUDA Occupancy Calculator:Gráfica asociada al número de hilos por bloque
La ocupación calculada con la configuración actual viene representada por la
Trabajo Fin de Máster
55
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
posición del triángulo rojo, éste está a la misma altura en las tres gráficas. De esta gráfica
podemos extraer que con 512 hilos por bloque conseguimos la máxima ocupación. Además
si no variamos ninguno de los otros dos parámetros, cualquier otra configuración en cuanto
al número de hilos por bloque perjudicaría a la ocupación y por tanto al rendimiento
conseguido. Por otra parte 512 hilos es el límite permitido para un tamaño de bloque de
una dimensión.
La Figura 5.6 Muestra la gráfica asociada al número de registros utilizado por cada
hilo.
Figura 5.6:CUDA Occupancy Calculator:Gráfica asociada al número de registros por
hilo
En esta gráfica podemos observar que se ha alcanzado la ocupación máxima usando
cada hilo un total de 14 registros. Como sabemos por los resultados de la Tabla 5.2 en cada
multiprocesador tenemos 2 bloques activos por tanto los recursos hardware de ese
multiprocesador se comparten entre los dos bloques, estos bloques podrán ejecutarse en
paralelo siempre y cuando ninguno de los dos consuma mas de la mitad de los recursos
disponibles. Cada multiprocesador tiene un total de 16K registros de 32 bits, es decir
16384 registros. La mitad de estos es 8192, tenemos en un bloque 512 hilos que consumen
14 registros cada uno lo que hace un total de 7168 registros, cantidad que no supera la
mitad pero que tampoco permite que alojar un tercer bloque. Como puede verse en la
Trabajo Fin de Máster
56
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
gráfica, en el supuesto de que cada hilo utilizase mas de 16 registros, la ocupación se
reduciría a la mitad, permitiendo solamente la ejecución de un bloque de hilos por
multiprocesador.
Finalmente la Figura 5.7 Muestra la gráfica asociada a la cantidad de memoria
compartida usada por cada bloque.
Figura 5.7: CUDA Occupancy Calculator:Gráfica asociada a la cantidad de memoria
compartida usada por cada bloque
En esta gráfica también podemos ver que la ocupación de los multiprocesador es
máxima, como sabemos el número de bloques activos en un multiprocesador es 2 por tanto
cada bloque no debe consumir más de la mitad de los recursos disponibles. Un total de 9
píxels en memoria compartida almacenados por cada bloque hacen que se usen 8104 bytes
que no alcanza por poco la mitad de la memoria compartida disponible que tiene un total
de 16KB. Si mantenemos el numero de hilos y el numero de registros por hilo y
aumentamos el uso de la memoria compartida a mas de la mitad del total, la ocupación se
reduciría a la mitad y afectaría al rendimiento gravemente.
5.5 CUDA Visual Profiler
CUDA Visual Profiler es otra herramienta mucho más completa y compleja que nos
proporciona ciertas mediciones estratégicas para la localización de problemas de
rendimiento como pueden ser la temporización entre GPU y CPU para las invocaciones al
Trabajo Fin de Máster
57
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
los kernels y las llamadas a memcpy o la evoluciona determinados pasos de tiempo. Sigue
la pista de ciertos eventos definidos por el programa a través de contadores hardware que
registran determinadas señales emitidas por el chip. Estos contadores representan eventos
dentro de un warp de hilos e involucran a un solo multiprocesador, esto es que los valores
no se corresponden con el número total de warps lanzados por un kernel dado. Los valores
se utilizan para cuantificar la mejora de rendimiento producida por una versión optimizada
del código.
Los contadores que proporciona el profiler entre muchos otros son:
•
Cargas y almacenamientos en memoria global que pueden ser coherentes
(“coalesced”) o incoherentes (“non-coalesced”)
◦ gld_incoherent
◦ gld_coherent
◦ gst_incoherent
◦ gst_coherent
•
Cargas y almacenamientos locales
◦ local_load
◦ local_store
•
Número total de bifurcaciones(“branches”) y bifurcaciones divergentes tomadas
por los hilos
◦ branch
◦ divergent_branch
•
Número de instrucciones ejecutadas
◦ instructions
•
Warps de hilos que han sido secuencializadios por los conflictos de
direccionamiento a la memoria compartida o a la memoria de constantes.
◦ warp_serialize
•
Bloques de hilos que han sido ejecutados
◦ cta_launched
Trabajo Fin de Máster
58
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Para conseguir un código optimizado debemos reducir gld/gst_incoherent,
divergent_branch y warp_serialize.
La Figura 5.8 muestra el aspecto de esta herramienta.
Figura 5.8:CUDA Visual Profiler
Usar esta herramienta es bastante fácil. Simplemente creamos una sesión y
editamos las propiedades en el siguiente cuadro de diálogo mostrado en la Figura 5.9.
Trabajo Fin de Máster
59
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Figura 5.9:CUDA Visual Profiler: Propiedades de la sesión
Como puede verse en la figura anterior debemos rellenar cinco campos: El nombre de la
sesión, la ruta del ejecutable, la ruta del directorio de trabajo, los parámetros del ejecutable
y el tiempo máximo de ejecución.
La información que nos muestra tras la ejecución de nuestro algoritmo para una de
las imágenes sintéticas es la siguiente:
Figura 5.10:CUDA Visual Profiler: resultados parte 1
La Figura 5.10 nos muestra de izquierda a derecha el instante en que se ha lanzado
cada kernel y las transferencias entre CPU y GPU (GPU Timestamp) y el tiempo que ha
durado cada una de las operaciones. Además para los kernels nos muestra el número de
bloques (grid size X), el número de hilos por bloque (block size X), la cantidad de
memoria compartida usada por cada bloque (static shared memory per block), el número
Trabajo Fin de Máster
60
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
de registros que usa cada hilo (registers per threads), la ocupación (Occupancy), el número
de saltos (branch) y el número de saltos divergentes (divergent branch).
Figura 5.11:CUDA Visual Profiler: resultados parte 1
La Figura 5.11 nos muestra para las transferencias CPU GPU la cantidad de datos
transferidos en bytes (mem transfer size), y para cada kernel el número de instrucciones
ejecutadas, el total de cargas y almacenamientos locales (local load y local store) y número
de cargas y almacenamientos de segmentos de 32, 64 y 128 bytes con la memoria global
(gld/gst 32b/64b/128b).
CUDA Visual Profiler además nos permite mostrar una serie de gráficas temporales
relacionadas con la evolución temporal de la ejecución, vamos a mostrar dos de ellas. La
primera gráfica mostrada en la Figura 5.12 muestra el porcentaje del tiempo total de
ejecución que se ha invertido en cada parte, este tipo de gráficas resultan muy utiles para
detectar cuellos de botella en las transferencias entre CPU y GPU.
Figura 5.12:CUDA Visual Profiler: Gráfica resumen
Podemos ver en nuestro caso que el 99% del tiempo total del algoritmo se emplea
en el kernel PPI y el resto en generar los skewers y en las transferencias.
La siguiente gráfica muestra el tiempo empleado por cada transferencia y por cada
kernel, se muestra en la Figura 5.13.
Trabajo Fin de Máster
61
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Figura 5.13:CUDA Visual Profiler: Gráfica de tiempos
Como se puede ver prácticamente todo el tiempo se emplea en el kernel PPI que
tarda 1.7 segundos en ejecutarse. El resto del tiempo se emplea en las transferencias y en la
generación de skewers.
Trabajo Fin de Máster
62
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
6 Resultados
En este apartado vamos a mostrar y analizar los resultados obtenidos. Utilizando
para realizar comparaciones las versiones en C+ y CUDA para las imágenes sintéticas y
además la versión de ENVI para la imagen real. Antes de comenzar con el análisis de los
resultados mostraremos y explicaremos las imágenes que se han utilizado en el estudio.
Las imágenes sintéticas permiten adoptar una forma objetiva a la hora de evaluar el
rendimiento de un algoritmo de análisis hiperespectral, ya que dichas imágenes se generan
de forma controlada y la información relativa al grado de pureza de cada píxel puede
controlarse a priori. Describimos a continuación el proceso de generación de una imagen
sintética mediante un sencillo ejemplo, detallando el proceso de selección de firmas
espectrales, la forma en que se establece la asignación de abundancias y el esquema
seguido para la generación de ruido.
En el ejemplo que nos ocupa, las firmas empleadas en la construcción de la imagen
simulada corresponden a una imagen obtenida por el sensor AVIRIS en 1997 sobre la
región Jasper Ridge en California. La disponibilidad pública de esta imagen en unidades de
reflectancia resulta muy atractiva con objeto de crear imágenes simuladas a partir de firmas
espectrales reales. A continuación, se establece la abundancia de cada una de las
referencias elegidas en cada píxel de la imagen, de forma que la contribución individual de
cada firma se expresa mediante un conjunto de coeficientes de abundancia que cumplen las
restricciones de no-negatividad y suma unitaria, propias del modelo lineal de mezcla. La
última etapa en la simulación consiste en añadir ruido a la imagen simulada. Nuestro
proceso de simulación de ruido en imágenes sintéticas se ha llevado a cabo utilizando un
generador aleatorio de números entre 1 y –1. Estos valores siguen un modelo de
distribución de probabilidad normal, con lo que su valor medio es 0 y su desviación
estándar es 1. El ruido se añade a cada píxel de la imagen en una determinada proporción
que determina la relación señal-ruido presente en la escena.
Una vez descrito el proceso de generación de imágenes sintéticas, pasamos a
describir las imágenes concretas que vamos a emplear en nuestro estudio. La primera
imagen simulada tiene dimensiones 100x100 píxeles, y viene caracterizada por una
situación de mezcla entre componentes progresiva, a intervalos de abundancia muy cortos.
Trabajo Fin de Máster
63
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Como resultado, los cambios en la abundancia en las regiones que componen esta imagen
son muy sutiles. En total, hay 100 regiones de 1 píxel de ancho y 100 píxeles de alto. La
abundancia del componente suelo decrece de forma lineal de izquierda a derecha, mientras
que la abundancia del componente vegetación crece de izquierda a derecha. La Figura 6.1
ilustra el proceso de construcción de esta imagen sintética, que proporciona un conjunto de
datos muy apropiado para analizar el proceso de estimación de abundancias realizado por
el algoritmo PPI. Este hecho se debe a la situación de mezcla particular que se representa
en la imagen.
Figura 6.1:Regiones y asignación de abundancias para la primera imagen sintética
Por otra parte, la Figura 6.2 muestra los mapas de abundancia empleados para la
generación de la primera imagen sintética utilizada en los experimentos, que comprende
mapas para dos endmembers simulados (vegetación y suelo). Conviene destacar que la
relación SNR en la imagen simulada (tras el proceso de simulación de ruido) es de 110:1
(proporción de señal de 110 veces a 1 sobre el ruido) y el número de bandas es de 224
(correspondiente a un espectro real de AVIRIS) comprendidas entre 0.4 y 2.5 micrómetros,
con una nueva banda aproximadamente cada 10 nanómetros. El tamaño total de la imagen
es de 4.27 MB.
Trabajo Fin de Máster
64
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Figura 6.2:Mapas de abundancia empleados en la generación de la primera imagen
sintética. a) Mapa de abundancia para el endmember suelo. b) Mapa de abundancia para
el endmember vegetación.
Finalmente, la Figura 6.3 muestra los mapas de abundancia empleados para la
generación de la segunda imagen sintética utilizada en los experimentos, que comprende
mapas para cinco endmembers simulados (cuatro tipos de vegetación y suelo). De nuevo,
la relación SNR en la imagen simulada (tras el proceso de simulación de ruido) es de 110:1
y el número de bandas es de 224, comprendidas entre 0.4 y 2.5 micrómetros. El tamaño
total de la imagen es, de nuevo, de 4.27 MB.
Trabajo Fin de Máster
65
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Figura 6.3: Segunda imagen sintética. a) Distribución en la imagen de los cinco píxeles
puros (endmembers), denotados como r1, r2, r3, r4 y r5. b-f) Mapas de abundancia
empleados en la generación de la segunda imagen sintética.
La Figura 6.4 muestra la imagen real utilizada en los experimentos. Esta imagen fue
adquirida en 1995 por el sensor AVIRIS sobre la región minera denominada Cuprite, en el
estado de Nevada, Estados Unidos. En concreto, la Figura 6.4 muestra la ubicación de la
imagen sobre una fotografía aérea de la zona. Visualmente, puede apreciarse la existencia
de zonas compuestas por minerales, así como abundantes suelos desnudos y una carretera
interestatal que cruza la zona en dirección norte-sur. La imagen consta de 350x350 píxeles,
cada uno de los cuales contiene 188 valores de reflectancia en el rango espectral 0.4 a 2.5
µm. Este rango, situado en la región SWIR-II del espectro, se caracteriza por que en él se
manifiestan singularidades que permiten discriminar entre una amplia gama de minerales
de tipo calizo. Cada valor espectral en el rango anteriormente mencionado equivale a 10
veces el tanto por ciento de la reflectancia en una determinada longitud de onda. Estos
valores han sido obtenidos como resultado de la aplicación del método de corrección
atmosférica ATREM, seguido de un post-procesado mediante el método EFFORT sobre la
imagen en unidades de radiancia, originalmente capturada por el sensor.
Trabajo Fin de Máster
66
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Figura 6.4:Ubicación de la imagen real AVIRIS Cuprite sobre una fotografía aérea de la
región minera Cuprite, Nevada, Estados Unidos.
Una vez mostradas las imágenes sobre las que se han realizado las pruebas pasamos
a describir las medidas que se han tomado en las distintas ejecuciones:
Nº iteraciones: es el número de iteraciones que ha realizado el algoritmo.
Tiempo (ms): el tiempo de ejecución del algoritmo medido en milisegundos.
Precisión E: porcentaje de precisión estricta (se explica a continuación).
Precisión P: porcentaje de precisión ponderada (se explica a continuación).
Seguidamente se explica cómo se ha llevado a cabo el análisis de precisión en cada
imagen.
El análisis de precisión se hace en base a la imagen de pureza final del algoritmo,
recordemos que esta es una matriz con tantas filas como líneas tiene la imagen y tantas
columnas como samples, en la que la posición (i,j) se almacena el número de veces que se
ha seleccionado al píxel (i,j) como extremo. Partiendo de esta base y de que sabemos cuál
es la organización exacta de cada una de las imágenes podemos hacer dos tipos de análisis
de precisión.
El primero y más inmediato es el análisis de precisión estricto que considera que se
ha producido un acierto en la elección de un píxel como endmember si este es realmente un
endmember. El análisis de precisión estricto se lleva a cabo en las 2 imágenes sintéticas.
Trabajo Fin de Máster
67
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Para la imagen sintética 1 el procedimiento es el siguiente: por un lado sumamos el
total de incrementos que ha realizado el algoritmo, es decir el valor de cada celda de la
matriz de resultados, a esta suma la llamaremos incrementos, por otro lado sumamos los
valores de las celdas que estén situadas en la primera y en la última columna (que es donde
sabemos que se encuentran los endmembers), a esta suma la llamaremos aciertos. La
Figura 6.5 muestra este proceso:
Figura 6.5:Precisión estricta PPI en imagen sintética 1.
Para la imagen sintética 2 el proceso es parecido, por un lado debemos sumar la
cantidad de incrementos que se han realizado en total, que llamaremos incrementos. Por
otro lado sumamos los valores de las celdas cuya posición coincida con la de un píxel que
sabemos que es puro. Para ver donde se sitúan estos endmembers hacemos uso de los
mapas de esta imagen. Estos están en escala de grises y asignan el valor 255 a la posición
de un endmember y a medida que nos alejamos de esa posición asignan valores más
pequeños hasta llegar a 0 donde la presencia de ese endmember es nula. De esta manera el
proceso para saber si un píxel de la matriz está en la misma posición que un endmember es
el siguiente. Se leen por separado cada uno de los mapas y se almacenan en matrices
diferentes. Se recorre celda a celda nuestra matriz de resultado y si en alguno de los mapas
el valor de la celda cuya posición coincide con la que se está recorriendo es de 255
entonces nos encontramos en una posición donde hay un endmember y por tanto sumamos
Trabajo Fin de Máster
68
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
el valor de nuestra celda a un contador que llamaremos aciertos. Finalmente:
Precisión_estricta = aciertos / incrementos * 100
El proceso se muestra en la Figura 6.6:
Figura 6.6:Precisión estricta PPI en imagen sintética 2.
El otro tipo de análisis de precisión es el ponderado. En el caso de la imagen
sintética 1 consiste en lo siguiente: si en el análisis de precisión estricto solo sumábamos
los incrementos de los píxeles que se habían considerado como endmembers (es decir los
de la primera y la última columna) en éste haremos una escala de aciertos multiplicando
por 100 en caso de que el acierto se haya producido en las columnas extremas y
multiplicando por cero en caso de que el acierto se haya producido en las columnas
centrales, de la misma forma multiplicaremos por 50 los aciertos que se hayan producido
en las columnas 25 y 75. Con este sistema es lógico que se obtenga un porcentaje de
acierto mayor ya que prácticamente suman todos los incrementos que se hayan producido
aunque en menor medida los más alejados de los endmembers.
En este método hay que diferenciar si el píxel se encuentra en la primera mitad de
la imagen (entre las columnas 0 y 49) o en la segunda (entre las columnas 50 y 99).
Trabajo Fin de Máster
69
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
De esta manera para un píxel que se encuentre en la primera mitad, su ratio de
acierto por el que hay que multiplicar el los incrementos que ha recibido sería:
ratio_acierto=100-(columna*2)
De la misma manera para un píxel que se encuentre en la segunda mitad, su ratio de
acierto por el que hay que multiplicar el los incrementos que ha recibido sería:
ratio_acierto=((columna+1)*2)-100
Así la variable aciertos será la suma de los incrementos de cada píxel por su ratio de
acierto.
Finalmente
aciertos=aciertos*ratio_aciertos
Precisión_ponderada = aciertos / incrementos
Este proceso se muestra en la Figura 6.7:
Figura 6.7:Precisión ponderada PPI en imagen sintética 1
Finalmente debemos calcular la precisión ponderada para el caso de la imagen
sintética 2, para ello podemos hacer uso de los mapas de abundancias de la imagen.
Mediante el uso de estos mapas podemos conocer las proporciones exactas en las que cada
endmember está presente en un píxel dado. En cada píxel los valores de estos mapas de
abundancias son positivos y suman 1. Para calcular la precisión en el píxel i nos situaremos
en el píxel i de los mapas de abundancias y nos quedaremos con la mayor abundancia, esta
pertenece al endmember que nuestro algoritmo pretendía encontrar. El valor de esta
Trabajo Fin de Máster
70
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
abundancia nos dará la precisión con la que se ha elegido el píxel i, de manera que si este
valor es 1(el resto de abundancias valdrían 0) el acierto habrá sido del 100% puesto que la
posición i se corresponde con la posición real de un endmember en la imagen. Si el valor
es 0 (el resto de abundancias valdrían también 0, sino se habría escogido otra) el acierto
sera de 0%. Si el valor es de 0.5 el acierto será del 50%. En definitiva se escala la precisión
entre 0 y 100 en función del valor de la abundancia máxima en un píxel dado.
aciertos=aciertos*ratio_aciertos(máxima abundancia)
Precisión_ponderada = aciertos / incrementos
La Figura 6.8 muestra como se establece el ratio de acierto para la precisión
ponderada en la imagen sintética 2 teniendo en cuenta la abundancia máxima en un píxel
Figura 6.8:Precisión ponderada PPI en imagen sintética 2
Una vez que hemos explicado como se ha llevado a cabo el análisis de resultados
en cuanto a precisión, a continuación expondremos los resultados de los experimentos con
las tres imágenes y compararemos las versiones CPU y GPU tanto en tiempo como en
precisión. Además mostraremos los resultados obtenidos en la imagen real con ENVI que
es una herramienta comercial para el estudio de imágenes y que permite ejecutar nuestro
Trabajo Fin de Máster
71
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
algoritmo sobre una imagen hiperespectral.
Comencemos pues con la imagen sintética 1, la Tabla 6.1 muestra el número de
iteraciones, el tiempo de ejecución, la precisión estricta, la precisión ponderada y el
speedup conseguido.
PPI versión C (CPU)
Imagen Sintética 1
Nº Iteraciones
Tiempo (ms)
Precisión E
Precisión P
15360
244300
45.18%
94.73%
PPI versión CUDA (GPU)
Imagen Sintética 1
Nº Iteraciones
Tiempo (ms)
Precisión E
Precisión P
15360
1834
45.11%
94.78%
Speedup = 133.21x
Tabla 6.1: Comparación de resultados CPU y GPU para PPI en imagen sintética 1
Para comenzar hay que decir que el número de iteraciones seleccionado no es
aleatorio sino que encaja perfectamente con la configuración de ejecución del kernel
lanzado a la GPU, éste tiene precisamente 30 bloques de 512 hilos cada bloque, como ya
sabemos cada hilo realiza una iteración, por tanto tenemos 15360 iteraciones. Siguiendo
con el análisis de precisión vemos que en ambas versiones los resultados son idénticos lo
que quiere decir que ambas versiones funcionan igual y son perfectamente comparables,
las pequeñas variaciones en los resultados se deben básicamente a la naturaleza aleatoria
del algoritmo. Recordemos que PPI utiliza un gran número de vectores construidos de
forma aleatoria por así que estos resultados de precisión pueden variar de una ejecución a
otra y por tanto de una versión a otra. Fijándonos en la precisión estricta podemos ver que
casi uno de cada dos píxels seleccionados como extremos en una iteración se corresponde
con un endmember en la imagen, es decir que se sitúa en la primera o en la última
columna. Por otra parte tenemos la precisión ponderada que presenta un porcentaje muy
alto (94%) lo que nos permite afirmar que en ambas versiones la mayoría de los píxels que
no se han seleccionado en la primera columna o en la última se han seleccionado en
columnas muy cercanas a estas, esto lo podremos corroborar viendo los resultados
gráficos. En cuanto al tiempo de ejecución de ambas versiones vemos una gran diferencia,
Trabajo Fin de Máster
72
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
concretamente la versión GPU tarda 133 veces menos en ejecutarse que la versión CPU
demostrando la gran potencia de cálculo que puede aprovecharse en una GPU.
A continuación la Figura 6.9 muestra los resultados gráficos para esta ejecución:
Figura 6.9:Resultados gráficos de PPI para la imagen sintética 1
Como puede verse en la figura, los resultados son prácticamente los mismos en
ambas versiones, situándose en torno a las columnas 1 y 100 los píxels que han sido más
veces seleccionados como extremos por nuestro algoritmo.
Sigamos ahora con el análisis de los resultados de nuestro algoritmo para la imagen
sintética 2. De la misma forma que len la imagen anterior la Tabla 6.2 muestra el número
de iteraciones, el tiempo de ejecución, la precisión estricta, la precisión ponderada y el
speedup conseguido.
Trabajo Fin de Máster
73
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
PPI versión C (CPU)
Imagen Sintética 2
Nº Iteraciones
Tiempo (ms)
Precisión E
Precisión P
15360
244550
42.95%
94.85%
PPI versión CUDA (GPU)
Imagen Sintética 2
Nº Iteraciones
Tiempo (ms)
Precisión E
Precisión P
15360
1827
43.44%
94.98%
Speedup = 133.85x
Tabla 6.2: Comparación de resultados CPU y GPU para PPI en imagen sintética 2
El numero de iteraciones ha sido seleccionado con el mismo criterio que con la
imagen sintética 1, encaja perfectamente con la configuración de ejecución del kernel
lanzado a la GPU, éste tiene precisamente 30 bloques de 512 hilos cada bloque, como ya
sabemos cada hilo realiza una iteración, por tanto tenemos 15360 iteraciones. Siguiendo
con el análisis de precisión vemos que en ambas versiones los resultados son idénticos lo
que quiere decir que ambas versiones funcionan igual y son perfectamente comparables,
las pequeñas variaciones en los resultados se deben básicamente a la naturaleza aleatoria
del algoritmo. Fijándonos en la precisión estricta podemos ver que casi uno de cada dos
píxels seleccionados como extremos en una iteración se corresponde con un endmember en
la imagen, es decir que se sitúa en una de las esquinas o en el centro de la imagen. Por otra
parte tenemos la precisión ponderada que presenta un porcentaje muy alto (94%) lo que
nos permite afirmar que en ambas versiones la mayoría de los píxels que no se han
seleccionado en una de las esquinas o en el centro se han seleccionado en zonas muy
cercanas a estas, esto lo podremos corroborar viendo los resultados gráficos. En cuanto al
tiempo de ejecución de ambas versiones vemos una gran diferencia, concretamente la
versión GPU tarda 133 veces menos en ejecutarse que la versión CPU demostrando la gran
potencia de cálculo que puede aprovecharse en una GPU.
A continuación la Figura 6.10 muestra los resultados gráficos para esta ejecución:
Trabajo Fin de Máster
74
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Figura 6.10:Resultados gráficos de PPI para la imagen sintética 2
Como puede verse en la figura, los resultados son prácticamente los mismos en
ambas versiones, situándose en torno a las esquinas y el centro los píxels que han sido más
veces seleccionados como extremos por nuestro algoritmo.
Seguidamente vamos a comparar los resultados obtenidos con la imagen real para
las versiones C (CPU), CUDA (GPU) y ENVI (CPU). La Tabla Muestra los resultados de
tiempo obtenidos por las tres ejecuciones con 15360 iteraciones:
Imagen real
C (CPU)
ENVI (CPU)
CUDA (GPU)
Tiempo (ms)
3454534
1078000
17590
Speedup GPU
196.3x
61.28
-
Tabla 6.3:Comparación de resultados para las versiones C (CPU), CUDA (GPU) y ENVI
(CPU) para 15360 iteraciones.
Los resultados en cuanto a velocidad son bastante buenos llegando a conseguir un
speedup de 196 con respecto a la versión C. La ejecución pasa de tardar algo mas de 57
minutos a tardar 17 segundos lo que supone una gran mejora.
Con respecto a la versión de ENVI el speedup no es tan alto y esto tiene su
explicación: ENVI aplica un proceso (denominado Fast PPI [ 25 ]) para incrementar el
número de píxeles que se seleccionan en cada proyección como puros. En la descripción
original del algoritmo [ 9 ], los autores indican que para cada proyección el máximo de
Trabajo Fin de Máster
75
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
píxeles etiquetados es de 2 (los píxeles que resultan en los valores de proyección máxima y
mínima). No obstante, en la versión Fast PPI de ENVI el número de píxeles seleccionados
depende de un valor umbral, lo cual hace que para un mismo número de proyecciones
dicha versión seleccione muchos más candidatos finalizando antes su ejecución.
Una buena forma de ver el acierto que se produce con nuestro algoritmo es
comparar visualmente los resultados obtenidos con los resultados que ofrece ENVI para la
imagen real. A continuación mostraremos figuras con imágenes de las tres versiones para
comparar los resultados con los de ENVI.
La Figura 6.11 muestra los resultados visuales para las versiones C y ENVI.
Figura 6.11:Resultados visuales para las versiones C y ENVI en imagen real.
Como puede apreciarse en la figura ambas versiones presentan una gran similitud
seleccionando píxels como extremos en las mismas regiones de la imagen. Esto nos
permite afirmar que ambas versiones son perfectamente comparables y que podemos
encontrar en ambas el mismo conjunto de endmembers.
La Figura 6.12 muestra los resultados visuales para las versiones CUDA y ENVI.
Trabajo Fin de Máster
76
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Figura 6.12:Resultados visuales para las versiones CUDA y ENVI en imagen real.
Los resultados mostrados en la figura anterior llaman especialmente la atención
porque ambas versiones detectan sus píxels extremos no sólo en las mismas regiones sino
casi exactamente en las mismas coordenadas. O lo que es lo mismo los resultados son
prácticamente los mismos, lo que nos permite decir que nuestra versión es igual de buena
que la de ENVI e incluso la supera ya que obtiene los mismos resultados 60 veces más
rápido.
Para finalizar y ver un poco la precisión alcanzada con la imagen real, en la Figura
6.13 mostraremos algunos de los materiales que se han detectado tras la ejecución,
comparando los resultados obtenidos en la versión CUDA con los de la versión comercial
de ENVI.
Trabajo Fin de Máster
77
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Figura 6.13:Ejemplo de materiales detectados
La imagen anterior muestra un ejemplo de tres materiales que han sido detectados
en las dos versiones (CUDA y ENVI) en las mismas posiciones de la imagen lo que nos
permite hacernos una idea del grado de precisión alcanzado por nuestra versión del
algoritmo PPI en una imagen real.
Trabajo Fin de Máster
78
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
7 Conclusiones y líneas futuras
En la presente memoria se ha realizado un estudio sobre la implementación de un
algoritmo de extracción de endmembers en imágenes hiperespectrales sobre GPUs
programables. Concretamente se ha implementado el algoritmo PPI en sus versiones serie
(C++) y paralela (CUDA) obteniendo una serie de resultados en base al tiempo y la
precisión que han sido comparados. Dichos resultados han sido obtenidos utilizando una
base de datos de imágenes simuladas (construidas mediante un proceso basado en
simulación de mezclas y generación de ruido) y reales, evaluando el algoritmo en términos
de precisión y rendimiento computacional en arquitecturas GPU de última generación.
Como resultado, se ha obtenido un detallado estudio de técnicas paralelas para tratamiento
de datos hiperespectrales en GPUs, realizando contribuciones sustanciales al estado del
arte en la materia dada la falta de implementaciones paralelas (en particular, en GPUs) de
algoritmos de análisis de imágenes hiperespectrales en la literatura actual.
Según los objetivos conseguidos a lo largo del trabajo, las principales aportaciones
del presente estudio pueden resumirse un conjunto de contribuciones que se enumeran a
continuación:
• Después de analizar los resultados de las ejecuciones podemos decir que se ha
conseguido obtener la misma precisión con la ejecución del algoritmo en serie y en
paralelo, además se ha conseguido obtener un speedup de en torno a 60 con
respecto a la versión de ENVI, de 133 en las imágenes sintéticas con respecto a la
versión C y de 196 en la imagen real con respecto a la versión C, por lo que
podemos decir que las GPUs son una buena alternativa a la hora de dar soporte de
una forma barata a algoritmos paralelos.
• Se ha conseguido utilizar la GPU para realizar la mayor parte de las fases del
algoritmo dejando a la CPU la tarea de cargar la imagen y agrupar los resultados.
• En cuanto a las precisiones obtenidas podemos decir que se ha logrado una gran
efectividad aun más si comparamos los resultados aportados por ENVI en la
imagen real.
• Conviene destacar, llegados a este punto, las ventajas económicas que la
Trabajo Fin de Máster
79
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
implementación GPU ofrece con respecto a otras soluciones paralelas como las
basadas en clusters de computadores. En concreto, mientras que el precio de una
GPU de última generación se sitúa en torno a los 400 euros, el precio de un cluster
puede ser mucho mayor, además de resultar en una serie de condiciones
desfavorables desde el punto de vista de su implantación como módulo de
procesamiento a bordo del sensor, con diferentes aspectos que pueden afectar de
forma negativa al payload de la misión (peso, consumo energético, calentamiento,
mantenimiento, etc.) En este sentido, las GPUs ofrecen una solución mucho más
compacta, si bien es cierto que es preciso realizar un estudio detallado de las
condiciones de tolerancia de las GPUs a requerimientos extremos en cuanto a
consumo y sensibilidad a radiación, necesario a la hora de calibrar la adaptabilidad
de esta plataforma hardware especializada a misiones reales de observación remota
de la tierra.
Para concluir este capítulo, planteamos algunas líneas futuras de trabajo adicionales
que serían interesante perseguir en futuras ampliaciones de este trabajo:
• Se ha hablado de que la principal ventaja de la implementación GPU con respecto
al uso de cluster es el aspecto económico; sin embargo no se ha podido realizar
ningún tipo de comparación real entre ambas soluciones en cuanto resultados y
tiempos de ejecución, luego sería interesante realizar dicho estudio en el futuro.
• Una alternativa interesante a la metodología propuesta viene dada por la posibilidad
de implementar algoritmos paralelos en clusters de GPUs, aprovechando las
ventajas de ambos paradigmas de computación paralela.
• Por otra parte, y con vistas a extrapolar los resultados obtenidos en el estudio a
otras aplicaciones, otra línea futura de trabajo deberá consistir en probar la
implementación GPU con distintos tipos de imágenes (uno de los problemas de
CUDA, es que hoy por hoy no dispone de funciones para realizar la carga de
ficheros de imágenes directamente en espacios de memoria, excepto para las
imágenes de tipo PPM y PGM, con lo cual se produce una carga de imagen quizá
poco optimizada).
• Finalmente, se plantea como futura extensión del trabajo desarrollado la
Trabajo Fin de Máster
80
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
implementación del algoritmo en otras arquitecturas paralelas como FPGAs,
clusters de computación paralela o sistemas Grid, de cara a evaluar las prestaciones
de dicho algoritmo en comparación otros algoritmos de extracción de endmembers
también disponibles en forma de implementación paralela.
Trabajo Fin de Máster
81
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
8 Apéndice
8.1 Programación con CUDA
A continuación vamos a incluir una pequeña guía de programación con CUDA, nos
centraremos en las nociones básicas necesarias para cualquier proyecto y que nos han
permitido realizar este. A parte de las funciones que se describirán en este capítulo existen
muchísimas otras así como un gran número de técnicas para mejorar los resultados de
cualquier proyecto, pero este apartado no pretende ser una guía completa de programación.
Para más información visitar: http://www.nvidia.com/object/cuda_develop.html
Compilación
CUDA permite utilizar las GPUs de Nvidia como coprocesadores para la ejecución
de trabajos paralelos de propósito general. El código fuente de las aplicaciones CUDA
consiste en una mezcla de C/C++ convencional para el código del host, a ejecutar en la(s)
CPU(s), y de una extensión del lenguaje C para implementar el código del device, kernel
que se ejecuta en la(s) GPU(s).
El proceso de compilación CUDA se encarga de separar los tipos de código, device
y
host,
de
forma
que
las
funciones
del
device
son
compiladas
con
los
compiladores/ensambladores propietarios de Nvidia, mientras que el resto del código
(host) se compila con cualquier compilador C/C++ presente en el host, como por ejemplo
los compiladores gcc/g++ de GNU.
Tras la compilación, el código objeto generado para el device se incrusta en el
ejecutable creado para el host, junto con las bibliotecas del runtime de CUDA que permiten
la llamada remota, ejecución y gestión de esas funciones en la GPU.
Para facilitar todo el proceso de compilación, el toolkit de CUDA proporciona el
frontal nvcc, que se encarga de llevar a cabo todas las tareas necesarias para generar los
ejecutables.
Supongamos que tenemos un código fuente que suma dos vectores en la GPU
(sumavectores.cu), para compilar con nvcc sin ningún parámetro de optimización
adicional:
Trabajo Fin de Máster
82
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
nvcc -o sumavectores sumavectores.cu
Para compilar kernels que utilicen doble precisión hay que utilizar la opción que le
indica a nvcc que genere código para GPUs con capacidad de cómputo igual o superior a
1.3.
nvcc -o sumavectores sumavectores.cu -arch=sm_13
Reserva y liberación de memoria de la GPU
La CPU y la GPU tienen espacios de memoria separados. El código del host (CPU)
mantiene la memoria del device (GPU), permitiendo realizar funciones como reserva y
liberación de memoria, copiar datos al device y desde el device o acceder a la DRAM
global del device.
cudaMalloc ()
cudaError_t cudaMalloc(void** devPtr, size_t count);
Reserva count bytes de memoria lineal en el device y retorna en *devPtr un puntero
a la memoria reservada. La memoria reservada se amolda a cualquier tipo de variable. La
memoria no se libera. cudaMalloc() devuelve cudaErrorMemoryAllocation en caso de
fallo.
cudaMemset()
cudaError_t cudaMemset(void* devPtr, int value, size_t
count);
Rellena el primer count byte de la zona de memoria apuntada por devPtr con el
valor constante value. cudaMemset() devuelve cudaErrorMemoryAllocation en caso de
fallo.
cudaFree()
cudaError_t cudaFree(void* devPtr);
Libera el espacio de memoria apuntado por devPtr, el cual debe de haber sido
retornado por una llamada cudaMalloc(). En caso contrario se devolverá un error. Si devPtr
es 0, no se ejecuta ninguna operación. Se devuelve cudaErrorInvalidDevicePointer en caso
de fallo.
Ejemplo de código:
int n = 1024;
Trabajo Fin de Máster
83
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
int nbytes = 1024*sizeof(int);
int *d_a = 0;
cudaMalloc( (void**)&d_a, nbytes );
cudaMemset( d_a, 0, nbytes);
cudaFree(d_a);
cudaMemcpy()
cudaError_t cudaMemcpy(void* dst, const void* src,
size_t count,
enum cudaMemcpyKind kind);
Copia count bytes desde la zona de memoria apuntada por src a la zona de memoria
apuntada
por
dst,
donde
kind
es
uno
de
los
siguientes
modificadores:
cudaMemcpyHostToHost, cudaMemcpyHostToDevice, cudaMemcpyDeviceToHost, o
cudaMemcpyDeviceToDevice, y especifican la dirección de la copia. La zona de memoria
no debe sobrepasarse.
Ejecutando código en la GPU
Los kernels son funciones C con algunas restricciones:
•
Solo pueden acceder a la memoria de la GPU
•
Deben devolver un tipo void
•
No pueden tener un número variable de parámetros
•
No son recursivos
•
No pueden usar variables estáticas.
Los parámetros de las funciones se copian automáticamente desde la CPU a la
GPU.
Modificadores de funciones
•
__global__: se invoca desde el código del host (CPU), no se puede llamar
desde el código del device y debe retornar void.
•
__device__: se invoca desde otras funciones GPU, no puede llamarse desde
el código del host.
•
__host__: solo se puede ejecutar en la CPU y llamarse desde el host.
•
__host__ y __device__ se pueden combinar.
Trabajo Fin de Máster
84
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Ejecución del kernel
Existe un modificador para funciones C que sirve para configurar la ejecución de
un kernel, esta configuración se refiere a la cantidad de hilos y bloques que se van a utilizar
durante su ejecución (“<<< >>>”).
kernel<<<dim3 grid, dim3 block>>>(…)
La dimensión del grid viene indicada por x, y, y la dimensión del bloque de hilos
por x,y,z.
dim3 grid(16, 16);
dim3 block(16,16);
kernel<<<grid, block>>>(...);
kernel<<<32, 512>>>(...);
Variables CUDA integradas en el device:
Todas las funciones __global__ y __device__ tienen acceso instantáneo a unas
variables definidas automáticamente, estas son las siguientes:
•
dim3 gridDim: Dimensiones del grid en bloques (como mucho 2D).
•
dim3 blockDim: Dimensiones del bloque en hilos.
•
dim3 blockIdx: Índice del bloque dentro del grid.
•
dim3 threadIdx: Índice del hilo dentro del bloque.
Ejemplo de kernel
A continuación mostraremos como ejemplo cómo se haría la suma de dos
vectores en CUDA. La Figura 8.1 muestra los 3 vectores: los vectores operandos v_A y
v_B y el vector resultado v_C.
Trabajo Fin de Máster
85
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Figura 8.1:Vectores operandos (v_A, v_B) y resultado (v_C) para suma de vectores
Como puede verse el tamaño del problema es N=16. Este número, aunque pequeño
viene perfecto para explicar como se lleva a cabo la partición del problema en el kernel
pudiendo dividirse de forma exacta en 4 bloques de 4 hilos cada uno.
La siguiente figura muestra este proceso de partición lógica del problema
mostrando para cada bloque el valor de las variables de entorno:
Figura 8.2:Partición lógica del problema en bloques
Como puede verse en la Figura 8.2 el valor de las variables de entorno para cada
bloque de ejecución es distinto. Expliquemos el valor de éstas una a una.
blockIdx.x representa el identificador del bloque en ejecución, es por esto que cada
bloque tiene un valor distinto para esta variable, el primer bloque tendrá el valor 0 en esta
variable. En nuestro caso el valor de esta variable para el último bloque será 3 ya que
tenemos 4 bloques.
blockDim.x especifica la dimensión del bloque, es decir el número de hilos que
forman cada bloque. Durante la ejecución de un kernel todos los bloques tienen la misma
dimensión por tanto esta variable presenta el mismo valor en todos los bloques. En nuestro
caso este valor es 4.
Trabajo Fin de Máster
86
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
threadIdx.x es un índice para referenciar a los hilos de un bloque, además es un
índice relativo a cada bloque. Esto quiere decir que el primer hilo de todos los bloques
tendrá el valor 0 en esta variable. En nuestro caso el último hilo de cada bloque tendrá el
valor 3 en esta variable.
Idx no es una variable de entorno sino que es una variable definida por nosotros.
Resulta muy útil ya que nos permite pasar de un índice local a cada bloque para referenciar
un hilo a un índice global al kernel donde cada hilo tiene un identificador único. De esta
forma sólo el primer hilo del primer bloque tendrá 0 en esta variable y en nuestro caso el
último hilo del último bloque tendrá el valor 15 en esta variable.
Se calcula en base a las otras 3 variables de la siguiente forma:
Idx=blockDim.x∗blockIdx.xthreadIdx.x
Para el primer hilo del primer bloque:
Idx=4∗00=0
Para el último hilo del último bloque:
Idx=4∗33=15
Para el segundo hilo del tercer bloque:
Idx=4∗21=9
La terminación “.x” en las variables de entorno hacen referencia a la dimensión x
de la estructura grid y de los bloques, esto es así porque estas estructuras pueden tener más
de una dimensión.
Seguidamente la Figura 8.3 muestra el código de la función C que realiza la suma
de vectores y lo compara con el kernel CUDA:
Trabajo Fin de Máster
87
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Figura 8.3:Comparación de código de función C y kernel CUDA
Herramientas de sincronización
Todas las ejecuciones de los kernels son asíncronas. El control es devuelto a la CPU
inmediatamente. Los kernels que se ejecuten después de llamadas CUDA previas tienen
que completarse. De la misma manera la llamada cudaMemcpy síncrona por lo que el
control se devuelve a la CPU después de que las copias se han completado. Las copias que
comienzan después de llamadas CUDA previas tienen que completarse. Para sincronizar
contamos con la siguiente llamada:
cudaThreadSynchronize()
cudaError_t cudaThreadSynchronize(void);
La CPU espera hasta que el device ha completado todas las áreas pendientes
anteriores. cudaThreadSynchronize() retorna error si una de las tareas pendientes falla.
Existe otra herramienta de sincronización interna a un kernel que se utiliza para
sincronizar hilos dentro de un bloque, esto resulta útil por ejemplo cuando todos los hilos
de un bloque necesitan leer un dato común que debe ser calculado previamente, suele
utilizarse también tras finalizar una escritura en la memoria compartida de manera que
ningún hilo lea de ésta hasta que no se haya finalizado la escritura. Utilizamos la siguiente
llamada:
synctrheads()
cudaError_t syncthreads(void);
Trabajo Fin de Máster
88
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
9 Bibliografía
1
Chang, C.-I, Hyperspectral Imaging: Techniques for Spectral Detection and
Classification, Kluwer Academic/Plenum Publishers, 2003.
2
Plaza, A., Chang, C.-I, “Impact of Initialization on Design of Endmember
Extraction Algorithms,” IEEE Transactions on Geoscience and Remote Sensing,
vol. 44, no. 11, pp. 3397-3407, 2006.
3
Plaza, A., Martínez, P., Pérez, R.M., Plaza, J.. “Spatial/Spectral Endmember
Extraction by Multidimensional Morphological Operations”. IEEE Transactions on
Geoscience and Remote Sensing, vol. 40, no. 9, pp. 2025-2041, 2002.
4
Green, R.O. et al., “Imaging spectroscopy and the airborne visible/infrared imaging
spectrometer (AVIRIS),” Remote Sens. Environ., vol. 65, pp. 227–248, 1998.
5
Landgrebe, D., “Hyperspectral Image Data Analysis”, IEEE Signal Processing
Magazine, vol. 19, no. 1, pp. 17-28, 2002.
6
Landgrebe, D., “Multispectral Data Analysis, A Signal Theory Perspective”
http://dynamo.ecn.purdue.edu/~biehl/MultiSpec/documentation.html, 1998.
7
Antonio Plaza, Pablo Martínez, Rosa Pérez, and Javier Plaza,”Spatial/Spectral
Endmember extraction by Multidimensional Morphological Operations” IEEE
Transactions on Geoscience and Remote Sensing, vol. 40, no. 9, SEPTEMBER
2002.
8
Goetz, A. F., & Kindel, B. (1999). "Comparison of Unmixing Result Derived from
AVIRIS, High and Low Resolution, and HYDICE images at Cuprite, NV". Proc.
IX NASA/JPL Airborne Earth Science Workshop.
9
Boardman, J., Kruse, F., & Green, R. (1995). "Mapping target signatures via partial
unmixing of AVIRIS data". Proc. Summaries JPL Airborne Earth Sci. Workshop,
23-26.
10
Green, R.O. y Pavri, B., “AVIRIS In-Flight Calibration Experiment, Sensitivity
Analysis, and Intraflight Stability”, en Proc. IX NASA/JPL Airborne Earth Science
Workshop, Pasadena, CA, 2000.
Trabajo Fin de Máster
89
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
11
Heinz, D. y Chang, C.-I, "Fully constrained least squares linear mixture analysis for
material quantification in hyperspectral imagery," IEEE Trans. on Geoscience and
Remote Sensing, vol. 39, no. 3, pp. 529-545, March 2001.
12
Plaza, A., Martínez, P., Pérez, R.M., Plaza, J., “A quantitative and comparative
analysis of endmember extraction algorithms from hyperspectral data,” IEEE
Trans. On Geoscience and Remote Sensing, vol. 42, no. 3, pp. 650-663, March
2004.
13
Bateson, C.A., Curtiss, B., “A method for manual endmember selection and spectral
unmixing,” Remote Sensing of Environment, vol. 55, pp.229–243, 1996.
14
Plaza, A., Valencia, D., Plaza, J. y Chang, C.-I, “Parallel Implementation of
Endmember Extraction Algorithms from Hyperspectral Data”. IEEE Geoscience
and Remote Sensing Letters, vol. 3, no. 3, pp. 334-338, July 2006.
15
Plaza, A., Plaza, J., Valencia, D., “AMEEPAR: Parallel Morphological Algorithm
for
Hyperspectral
Image
Classification
in
Heterogeneous
Networks
of
Workstations.” Lecture Notes in Computer Science, vol. 3391, pp. 888-891, 2006.
16
Setoain, J., Prieto, M., Tenllado, C., Plaza, A., Tirado, F., “Parallel Morphological
Endmember Extraction Using Commodity Graphics Hardware,” IEEE Geoscience
and Remote Sensing Letters, vol. 43, no. 3, pp. 441-445, 2007.
17
Pérez, R.M., Martinez, P., Plaza, A., Aguilar, P.L. “Systolic Array Methodology for
a Neural Model to Solve the Mixture Problem”, in: Neural Networks and Systolic
Array Design. Edited by D. Zhang and S.K. Pal. World Scientific, 2002.
18
Lavenier, D., Fabiani, E., Derrien, S., Wagner, C., “Systolic array for computing the
píxel purity index (PPI) algorithm on hyper spectral images”.
19
NVIDIA GeForce 8800 GPU Architecture Overview (November 2006 TB-02787001_v0.9)
20
NVIDIA CUDA Compute Unified Device Architecture – Programming Guide,
Versión 1.1 (29/11/07)
21
NVIDIA CUDA. Installation and Verification on Microsoft Windows XP and
Windows Vista (C Edition). (August 2008 | DU-04165-001_v01)
Trabajo Fin de Máster
90
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
22
CUDA Technical Training. Volumen 1: Introduction to CUDA Programming
(Prepared and Provided by NVIDIA. Q2 2008)
23
CUDA Technical Training. Volumen 2: CUDA Case Studies (Prepared and
Provided by NVIDIA. Q2 2008)
24
Antonio Plaza, Chein-I Chang, “Clusters Versus FPGA for Parallel Processing of
Hyperspactral Imagery” The International Journal of High Performance
Computing Applications, vol. 22 – no. 1 – pp.1-7, 2008.
25
C.-I Chang and A. Plaza. “A Fast Iterative Algorithm for Implementation of Pixel
Purity Index”, IEEE Geoscience and Remote Sensing Letters, vol. 3, no. 1, pp. 6367, January 2006.
Trabajo Fin de Máster
91
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
10 Publicaciones del Candidato
Revistas.• S. Sanchez, A. Paz, G. Martin and A. Plaza. Parallel Unmixing of Remotely
Sensed Hyperspectral Images on Commodity Graphics Processing Units.
Concurrency and Computation: Practice and Experience. Enviado, 2010.
Capítulos de libro.• A. Plaza, G. Martin, J. Plaza, M. Zortea and S. Sanchez. Recent Developments
in Spectral Unmixing and Endmember Extraction, in: Optical Remote Sensing Advances in Signal Processing and Exploitation Techniques. Edited by S. Prasad,
L. Bruce and J. Chanussot, Springer, 2010.
•
S. Sanchez, A. Paz, G. Martin and A. Plaza. Parallel Processing of Remotely
Sensed Hyperspectral Images Using Nvidia GPUs. NVidia GPU Gems, Wiley,
enviado, 2010.
Publicaciones online.• A. Plaza, J. Plaza and S. Sanchez. Hyperspectral Image Compression on NVidia
GPUs. NVidia CUDA Zone, 2009.
•
A. Plaza, S. Sanchez and J. Plaza. Hyperspectral Unmixing on NVidia GPUs.
NVidia CUDA Zone, 2009.
Congresos.• S. Sanchez and A. Plaza. Implementación Paralela del Algoritmo Pixel Purity
Index para Análisis Hiperespectral en GPUs. Jornadas de Paralelismo CEDI 2010.
•
S. Sanchez, G. Martin, K. Fisher, A. Plaza and C.-I Chang. GPU
Implementation of Fully Constrained Linear Spectral Unmixing for Remotely
Sensed Hyperspectral Data Exploitation. SPIE Optics and Photonics, Satellite Data
Compression, Communication, and Processing Conference, San Diego, CA, 2010.
•
S. Sanchez, G. Martin and A. Plaza. Parallel Implementation of the N-FINDR
Endmember Extraction Algorithm on Commodity Graphics Processing Units. IEEE
International Geoscience and Remote Sensing Symposium (IGARSS'10), Hawaii,
USA, 2010.
•
S. Sanchez, G. Martin, A. Plaza and J. Plaza. Near Real-Time Endmember
Extraction from Remotely Sensed Hyperspectral Data Using NVidia GPUs. SPIE
Photonics Europe, Real-Time Image and Video Processing Conference, Brussels,
Belgium, 2010.
•
A. Plaza, J. Plaza, H. Vegas and S. Sanchez. Parallel Implementation of
Trabajo Fin de Máster
92
Sergio Sánchez Martínez
Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas
programables (GPUs)
Endmember Extraction Algorithms Using NVidia Graphical Processing Units.
IEEE International Geoscience and Remote Sensing Symposium (IGARSS'09),
Cape Town, South Africa, 2009.
•
A. Plaza, J. Plaza, G. Martin and S. Sanchez. New Hyperspectral Unmixing
Techniques in the Framework of the Earth Observation Optical Data Calibration
and Information Extraction. (EODIX) Project. 3rd International Symposium on
Recent Advances in Quantitative Remote Sensing (RAQRS'10), Valencia, Spain,
2010.
•
A. Plaza, J. Plaza, S. Sanchez and A. Paz. Lossy Hyperspectral Image
Compression Tuned for Spectral Mixture Analysis Applications on NVidia Graphics
Processing Units. SPIE Optics and Photonics, Satellite Data Compression,
Communication, and Processing Conference, San Diego, 2009.
Trabajo Fin de Máster
93
Sergio Sánchez Martínez
Descargar