Buscar

2015-ThalesHenriqueDantas

Prévia do material em texto

DISSERTAÇÃO DE MESTRADO
RECONSTRUÇÃO DE IMAGENS DE RESSONÂNCIA MAGNÉTICA
ACELERADA POR PLACAS DE PROCESSAMENTO GRÁFICO
Thales Henrique Dantas
Brasilia, outubro de 2015
UNIVERSIDADE DE BRASÍLIA
FACULDADE DE TECNOLOGIA
 
 
UNIVERSIDADE DE BRASÍLIA 
FACULDADE DE TECNOLOGIA 
DEPARTAMENTO DE ENGENHARIA ELÉTRICA 
 
 
 
 
 
 
RECONSTRUÇÃO DE IMAGENS DE RESSONÂNCIA 
MAGNÉTICA ACELERADA POR PLACAS DE 
PROCESSAMENTO GRÁFICO 
 
 
THALES HENRIQUE DANTAS 
 
 
 
 
 
ORIENTADOR: JOÃO LUIZ A. DE CARVALHO 
 
DISSERTAÇÃO DE MESTRADO EM ENGENHARIA ELÉTRICA 
 
PUBLICAÇÃO: PPGEA 605/15 
 
 
BRASÍLIA/DF: OUTUBRO – 2015
Agradecimentos
Gostaria de agradecer o apoio da CAPES e do PROUNI, que contribuíram para viabilizar
este trabalho.
Thales Henrique Dantas
iv
Dedicatória
Faço uma dedicação especial a meus pais e irmãos, verdadeiras fortalezas, portos seguros
com os quais sempre posso contar. Dedico especialmente a minha esposa, que esteve ao
meu lado em todos os momentos. Dedicação especial também ao meu orientador, que
acreditou em mim e não me deixou desistir e ao meu sócio, que segurou as pontas em
minhas muitas ausências.
Thales Henrique Dantas
v
RESUMO
RECONSTRUÇÃO DE IMAGENS DE RESSONÂNCIA MAGNÉTICA
ACELERADA POR PLACAS DE PROCESSAMENTO GRÁFICO
Autor: Thales Henrique Dantas
Orientador: João Luiz de A. Carvalho
Programa de Pós Graduação em Engenharia Elétrica
Brasília, outubro of 2015
Fourier velocity encoding (FVE) é útil no diagnóstico de doenças valvulares, visto que
pode eliminar os efeitos de volume parcial que podem causar perda de informação de
diagnóstico no imageamento de fluxo cardiovascular por contraste de fase. FVE tam-
bém foi proposto como método para a medição da taxa de cisalhamento da superfície
das artérias carótidas. Apesar de o tempo de aquisição para FVE no espaço de Fou-
rier bi-dimensional(2DFT) ser proibitivamente longo, o uso de FVE espiral se mostra
bastante promissor, uma vez que este é substancialmente mais rápido. Contudo, a re-
construção dos dados de FVE em espiral é longo, devido à sua multi-dimensionalidade
e ao uso de amostragem não Cartesiana. Isso é particularmente importante para aqui-
sições de múltiplos cortes, volumes e(ou) de múltiplos canais. Os conjuntos de dados
de FVE em espiral consistem em pilhas de espirais resolvidas no espaço k
x
-k
y
-k
v
. A
distribuição de velocidade espaço-temporal, m(x, y, v, t), é tipicamente obtida a partir
dos dados no espaço-k, M(kx, ky, kv, t), aplicando uma transformada inversa de Fourier
não uniforme ao longo de k
x
-k
y
, seguida de uma transformada Cartesiana ao longo de
k
v
. Com esta abordagem, toda a matriz m(x, y, v, t) é calculada. Entretanto estamos
tipicamente interessados nas distribuições de velocidade associadas com uma pequena
região de interesse dentro do plano x-y. Nós propomos o uso da reconstrução de um
único voxel usando a transformada direta de Fourier (DrFT) para reconstruir os dados
da FVE espiral. Ao passo que o tempo de reconstrução por DrFT de toda a imagem
é ordens de magnitude maior que a reconstrução por gridding ou Non Uniform Fast
Fourier Transform(NUFFT), a equação da DrFT permite a reconstrução de voxels
individuais com uma quantidade consideravelmente reduzida de esforço computacio-
nal. Adicionalmente, propomos o uso de placas de processamento gráfico de uso geral
(GPGPUs) para acelerar ainda mais a reconstrução e alcançar reconstruções de FVE
espirais de maneira aparentemente instantânea. É apresentada também uma proposta
para, potencialmente, acelerar também a reconstrução por gridding ou NUFFT uti-
lizando o algoritmo de Goertzel para reconstruir uma quantidade limitada de pontos
também por estes métodos.
vi
ABSTRACT
MAGNETIC RESONANCE IMAGE RECONSTRUCTION ACCELERATED
BY GENERAL PURPOSE GRAPHIC PROCESSING UNITS
Author: Thales Henrique Dantas
Supervisor: João Luiz de A. Carvalho
Programa de Pós Graduação em Engenharia Elétrica
Brasília, october of 2015
Fourier velocity encoding (FVE) is useful in the assessment of valvular disease, as
it eliminates partial volume effects that may cause loss of diagnostic information in
phase-contrast imaging. FVE has also been proposed as a method for measuring
wall shear rate in the carotid arteries. Although the scan-time of Two-dimensional
Fourier Transform (2DFT) FVE is prohibitively long for clinical use, the spiral FVE
method shows promise, as it is substantially faster. However, the reconstruction of
spiral FVE data is time-consuming, due to its multidimensionality and the use of non-
Cartesian sampling. This is particularly true for multi-slice/3D and/or multi-channel
acquisitions. Spiral FVE datasets consist of temporally-resolved stacks-of-spirals in
k
x
-k
y
-k
v
space. The spatial-temporal-velocity distribution, m(x, y, v, t), is typically ob-
tained from the k-space data, M(kx, ky, kv, t), by first using a non-Cartesian inverse
Fourier transform along k
x
-k
y
, followed by a Cartesian inverse Fourier transform along
k
v
. With this approach, the entire m(x, y, v, t) matrix is calculated. However, we are
typically only interested in the velocity distributions associated with a small region-
of-interest within the x-y plane. We propose the use of single-voxel direct Fourier
transform (DrFT) to reconstruct spiral FVE data. While whole-image DrFT is or-
ders of magnitude slower than the gridding and Non Uniform Fast Fourier Trans-
form(NUFFT) algorithms, the DrFT equation allows the reconstruction of individual
voxels of interest, which considerably reduces the computation time. Additionally, we
propose the use of general-purpose computing on graphics processing units (GPG-
PUs) to further accelerate computation and achieve seemingly instantaneous spiral
FVE reconstruction. We also propose a method that shows potential to accelerate
reconstructions using gridding and NUFFT by means of using the Goertzel algorithm
to reconstruct onlya small number of pixels.
vii
SUMÁRIO
1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1 Motivação................................................................................ 1
1.2 Estado da arte ......................................................................... 2
1.3 Objetivos e contribuições .......................................................... 3
1.4 Abordagem............................................................................... 3
1.5 Apresentação do manuscrito ...................................................... 4
2 Ressonância Magnética . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.1 Princípios................................................................................. 5
2.2 Aquisição ................................................................................. 7
2.2.1 Seleção de corte ...................................................................... 7
2.2.2 Codificação Espacial ................................................................. 8
2.3 Reconstrução........................................................................... 11
2.4 Amostragem não Cartesiana....................................................... 11
2.5 Reconstrução não Cartesiana .................................................... 12
2.6 Imageamento de Fluxo............................................................... 12
2.6.1 Contraste de Fase .................................................................... 12
2.6.2 Fourier Velocity Encoding ................................................................ 13
2.6.3 Multi-dimensionalidade .............................................................. 14
2.6.4 Tempo de reconstrução ............................................................. 15
3 Processamento Paralelo em CUDA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.1 Histórico .................................................................................16
3.2 Arquitetura............................................................................. 19
3.3 Definição da linguagem utilizada................................................ 22
3.4 Fundamentos da linguagem ........................................................ 23
4 Reconstrução em Cuda. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
4.1 CUDA DrFT ............................................................................. 29
4.2 CUDA Gridding .......................................................................... 32
4.3 NUFFT .................................................................................... 34
4.4 Resultados e Discussão .............................................................. 35
5 Reconstrução de voxel único por DrFT em CUDA . . . . . . . . . . . . . . . . . . 37
viii
5.1 Proposta.................................................................................. 37
5.2 Implementação.......................................................................... 38
5.3 Resultados ............................................................................... 40
6 Reconstrução de voxel único por gridding em CUDA. . . . . . . . . . . . . . . 41
6.1 Introdução .............................................................................. 41
6.2 Algoritmo de Goertzel ............................................................. 42
6.3 Implementação.......................................................................... 44
6.4 Avaliação do algoritmo proposto ............................................... 46
7 Conclusões . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
REFERÊNCIAS BIBLIOGRÁFICAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
Anexos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
ix
LISTA DE FIGURAS
2.1 Sequências de pulsos e suas trajetórias correspondentes no espaço-k.
(a) 2DFT (b) espiral. [Reproduzido de [8]] ....................................... 10
2.2 Sequência de pulsos para aquisição FVE espiral. (a) seleção do corte (b)
gradiente bipolar para codificação de velocidade, que muda de intensi-
dade para a codificação das velocidades (c) leitura espiral (d) gradientes
de refocalização. [Adaptado de [8]] ................................................. 14
3.1 Crescimento do poder de processamento das CPUs. [Adaptado de [11]]. 17
3.2 Crescimento do poder de processamento das GPUs comparado ao das
CPUs (GFLOPS - Giga Floating point Operations Per Second). [Adap-
tado de [12]]. .............................................................................. 18
3.3 Modelo de memória hierárquica usada nas GPGPUs. [Adaptado de [12]]. 21
3.4 Operações executadas em paralelo .................................................. 22
3.5 Cálculo de um resultado da matriz C. [Adaptado de [12]].................... 25
3.6 Cálculo de um conjunto de resultados da matriz C. [Adaptado de [12]].. 26
4.1 Áreas influenciadas pela interpolação de cada ponto da trajetória espi-
ral. [Reproduzido de [14]]. ............................................................ 34
4.2 Agrupamentos das interpolações por região. [Reproduzido de [14]]. ...... 35
5.1 Comparação da reconstrução total e da reconstrução de voxel único. .... 38
5.2 Geração de dados de velocidade de fluxo de maneira interativa. Ilus-
tração de um corte axial do pescoço. (a), (c) - aorta (b), (d) - carótida. 39
6.1 Representação do Filtro Goertzel ................................................... 44
x
LISTA DE TABELAS
4.1 Tempo de reconstrução para o algoritmo CUDA DrFT ...................... 32
5.1 Comparativo de velocidade para DrFT de voxel único........................ 40
6.1 Comparativo de tempo entre FFT, GPUFFT e GPU Goertzel (valores
em ms). ..................................................................................... 46
xi
LISTA DE SÍMBOLOS E
ABREVIAÇÕES
FVE Fourier Velocity Encoding
2DFT 2 Dimensional Fourier Transform
DrFT Direct Fourier Transform
NUFFT Non Uniform Fast Fourier Transform
PET Positron Emission Tomography
SPECT Single Photon Emission Computed Tomography
GPGPU General Purpose Graphics Processing Unit
FFT Fast Fourier Transform
CPU Central Processing Unit
CUDA Compute Unified Device Architecture
TR Repetition Time
MIPS Million Instructions Per Second
GPU Graphics Processing Unit
CMOS Complementary Metal Oxide Semiconductor
ULA Unidade Lógica Aritmética
SIMD Single Instruction Multiple Data
SIMT Single Instruction Multiple Thread
IFFT Inverse Fast Fourier Transform
DFT Discrete Fourier Transform
xii
Capítulo 1
Introdução
Este capítulo apresenta as principais moti-
vações do trabalho, apresentando o estado
da arte e a abordagem adotada para cri-
armos uma contribuição relevante para a
área. Também são apresentados os tópicos
tratados em cada capítulo do trabalho.
1.1 Motivação
Apesar do ainda alto custo dos equipamentos, o uso da ressonância magnética
para o imageamento médico é bastante difundido. O uso de radiação não ionizante se
mostra um grande diferencial em relação a outras técnicas de imageamento médico
como a tomografia computadorizada, PET (positron emission tomography) e SPECT
(single photon emission computed tomography). A qualidade das imagens obtidas e
a possibilidade de, em um único aparelho, ter imagens anatômicas, metabólicas e
de fluxo colocam a ressonância magnética na posição de única tecnologia capaz de
fornecer um exame completo do ponto de vista de imageamento cardiovascular.
Um aspecto negativo é que as aquisições em ressonância magnética são relativa-
mente mais longas do que as aquisições em outras tecnologias de imageamento, como
a tomografia computadorizada e a ultassonografia, especialmente quando estamos in-
teressados em dados multi-corte, 3D e(ou) informações de fluxo e(ou) resolvidos no
tempo. Para estes tipos de aquisições, os tempos de aquisição em ressonância mag-
nética se tornam ainda mais longos com o uso da tradicional trajetória 2DFT (two
dimensional Fourier Transform). Contudo as aquisições em espiral têm se mostrado
adequadas para a aquisição rápida de dados.
Os tempos de aquisição podem aumentar ainda mais quando além de informações
anatômicas se deseja adquirir informações de fluxo. A técnica de constraste de fase [1]
permite a captura de informação de velocidade adicionando apenas mais uma etapa
de codificação para cada eixo de velocidade que se quer medir. Essa técnica, contudo,
só permite extrair uma informação de velocidade para cada voxel adquirido, o que se
1
mostra inadequado quando um único voxel engloba regiões com matérias em diferentes
velocidades, como é o caso junto às paredes dos vasos sanguíneos. Essas limitações nas
medidas de velocidade podem ser superadas com a técnica de FVE (Fourier Velocity
Encoding) [2], ao custo de adição de etapas de codificação. O número de etapas
adicionais é proporcional à resolução desejada para a informação de velocidade. A
spiral FVE [3] aplica a trajetória em espiral à FVE para acelerar a aquisição.
A reconstrução de uma grande quantidade de dados amostrados de forma não car-
tesiana, contudo, se mostra uma tarefa que é computacionalmente muito mais intensa
do que a reconstrução de dados uniformemente amostrados. Algoritmos de recons-
trução rápidos, como a NUFFT (non unoform Fourier transform) [4] tem tomado
o lugar da da FFT (fast Fourier transform), que era usada para a reconstrução dos
dados adquiridos de forma Cartesiana, voltando a dar agilidade na reconstrução das
imagens de ressonância magnética.
Nos casos em que a ressonância magnética é utilizada para estudar fluxo sanguíneo,
a quantidade de dados a serem reconstruídos aumenta em até 6 vezes para contraste
de fase e mais ainda para FVE, pois passa a ser necessária a reconstrução dos dados
referentes à codificação de velocidade. Nestes casos, geralmenteestamos interessados
na informação de fluxo sanguíneo apenas para alguns pontos de interesse no plano x-y,
onde temos veias e artérias. Para a reconstrução deste tipo de informação, algoritmos
como a NUFFT “desperdiçam” esforço computacional para reconstruir as informações
de fluxo para todo o plano x-y, ou seja, calculam e armazenam as informações de
velocidade para regiões da imagem que na verdade representam tecidos estáticos ou
ausência de sinal. O esforço computacional desnecessário se torna ainda mais evidente
quanto estudamos dados tridimensionais e(ou) resolvidos no tempo. Tudo isso faz com
que, mesmo com o emprego dos algoritmos rápidos, a reconstrução das informações
possa levar minutos.
1.2 Estado da arte
Utilizando a trajetória Cartesiana tradicional, que na área de ressonância mag-
nética é chamada de 2DFT, com codificação dos dados de velocidade, o tempo de
aquisição para vários quadros temporais de múltiplos cortes se torna proibitivo no
caso de uso de FVE. Como os tempos de aquisição são longos, para capturar um de-
terminado quadro temporal de um batimento cardíaco, por exemplo, são necessárias
várias aquisições parciais. Através da técnica CINE, é possível sincronizar as aqui-
sições parciais com o batimento cardíaco para fazer uma reconstrução completa [5].
Contudo, é necessário que durante todo o tempo de captura o paciente permaneça
imóvel. Os pacientes geralmente têm relativa facilidade em atender este requisito no
que diz respeito a sua parte motora, podendo permanecer imóveis durante vários mi-
nutos. Este requisito se torna mais complexo no que diz respeito à parte respiratória.
2
É possível segurar o fôlego, interrompendo o movimento do tórax, mas aquisições
que durem mais que 10 segundos podem se apresentar como irrealizáveis para alguns
pacientes.
Carvalho e Nayak mostraram que, utilizando trajetórias de aquisição em espiral
com codificação de velocidade de Fourier (spiral FVE)[3], foram alcançados tempos de
aquisição adequados. Utilizando técnicas de aceleração para a aquisição, reduziram
o tempo necessário para aquisição de um volume de referência de 216 batimentos
cardíacos para apenas 12 batimentos. Isso tornou possível a aquisição em apenas uma
curta retenção de fôlego de aproximadamente 10 segundos.
Os dados não uniformemente amostrados, contudo, impossibilitam o uso direto
das tradicionais transformadas rápidas de Fourier para a reconstrução dos dados, pois
estas pressupõem amostras uniformemente espaçadas. A reconstrução de amostras
não uniformemente espaçadas pode ser feita aplicando a chamada a transformada
direta de Fourier (DrFT)[6], mas o esforço computacional é muito maior (proporcional
a N2, contra Nlog(N) da FFT, em que N é o número de amostras no espaço-k). Para
acelerar a reconstrução, os dados são interpolados para uma grade uniforme (processo
conhecido como gridding)[7], para então ser aplicada a FFT.
Na técnica de gridding, a escolha do kernel de interpolação afeta consideravelmente
a qualidade da imagem reconstruída. É possível utilizar técnicas de minimização de
erros para ajustar o kernel de interpolação para melhorar a qualidade da reconstrução,
como faz a técnica de NUFFT.
1.3 Objetivos e contribuições
O objetivo deste trabalho é mostrar que é possível uma reconstrução rápida e ana-
liticamente exata, explorando as propriedades da reconstrução por DrFT e o poder de
processamento paralelo das placas de processamento gráfico de uso geral ou GPGPUs
(General Purpose Graphics Processing Units). Também propomos uma técnica para
acelerar a reconstrução por gridding, quando a região de interesse para a reconstrução
é limitada a poucos voxels, utilizando o algoritmo de Goertzel também em GPGPU.
1.4 Abordagem
Para alcançar os objetivos propostos, foi feito um estudo sobre a tecnologia de
ressonância magnética e também sobre suas técnicas de aquisição e reconstrução de
imagens. Vários algoritmos de reconstrução de imagens simples foram implementados
em MATLAB e em linguagem C para aprofundar o conhecimento e testar algumas
hipóteses para o processo de reconstrução acelerada.
Foram estudadas então as tecnologias de programação para processadores paralelos
3
e foi feita a opção pelo aprofundamento no estudo da linguagem CUDA, que é a
linguagem de programação criada pelo fabricante de placas de processamento gráfico
Nvidia para o uso do poder de processamento paralelo de suas placas de processamento
gráfico. Os algoritmos de reconstrução foram implementados em CUDA e comparados
com os resultados originais em MATLAB e em linguagem C para validação. Após as
implementações e testes iniciais, onde foram utilizados dados simulados, passamos a
utilizar um conjunto de dados de FVE espiral da artéria carótida, com vários cortes,
quadros temporais e canais de aquisição.
1.5 Apresentação do manuscrito
No capítulo 2 é feita uma revisão sobre a tecnologia de ressonância magnética, seus
princípios físicos e a aquisição dos sinais.
Em seguida, no capítulo 3 é apresentada a tecnologia de processamento paralelo
disponível nas placas de processamento gráfico de uso geral atualmente disponíveis no
mercado, assim como conceitos de programação para o uso dessa tecnologia com foco
na linguagem CUDA.
No capítulo 4 se discute a implementação de alguns algoritmos de reconstrução
de imagens de ressonância magnética em CUDA e discute os resultados, comparando
com as implementações tradicionais em CPU (central processing unit).
O capítulo 5 traz uma proposta de reconstrução de apenas um voxel em CPU e
em GPGPU (general purpose graphics processing unit), comparando os resultados
obtidos com os resultados do capítulo 4.
Uma alternativa para acelerar a reconstrução por gridding é discutida no capítulo
6 para imagens onde as regiões de interesse se limitam a poucos voxels. Os resultados
são comparados com os resultados dos dois capítulos anteriores.
Os resultados gerais são analisados e as conclusões são apresentadas no capítulo 7.
4
Capítulo 2
Ressonância Magnética
Este capítulo faz uma revisão sobre os
princípios físicos da ressonância magné-
tica. São mostrados os conceitos tanto
pasa as aquisições tradicionais quanto para
as aquisições aceleradas. São discutidas
as principais técnicas de reconstrução dos
dados adquiridos através de ressonância
magnética. São apresentadas também as
tecnologias de aquisição de dados para es-
tudos de fluxo sanguíneo, assim como as
principais limitações para a aquisição e re-
construção deste tipo de dado.
2.1 Princípios
Atualmente, a ressonância magnética nuclear é a única tecnologia de imageamento
médico que tem capacidade de fornecer tanto dados de anatomia e metabolismo quanto
de fluxo. A aquisição se baseia no uso de fortes campos magnéticos e energia na faixa
de rádio-frequência(RF). Tanto os campos magnéticos quanto a energia de RF são
não-ionizantes e são consideradas completamente seguros para os pacientes. É uma
tecnologia que, apesar do ainda alto custo, já é bastante difundida.
A descrição da mecânica quântica é complexa e não está diretamente ralacionada
ao objetivo desta dissertação. Uma vez que a menor quantidade de matéria repre-
sentada pela resolução atual máxima das técnicas de ressonância magnética é grande
o suficiente, será apresentado um tratamento semi-clássico para os princípios físicos
utilizados.
Átomos que possuem spin não nulo, como núcleos de hidrogênio, possuem um
pequeno campo magnético. Em condições normais, os núcleos apontam os eixos de
seus campos magnéticos em direções aleatórias, resultando em uma magnetização to-
tal nula. Entretanto, na presença de uma campo magnético externo B0, os núcleos
5
tendem a alinhar os eixos de seus campos com o eixo desse campo externo. Aproxi-
madamente metade dos núcleos irão apontar seus eixos na mesma direção e sentido
de B0, ao passo que o restante dos núcleos irão apontar seus eixos no sentido oposto.
Quanto maior for a intensidade do campo B0, mais núcleos apontarão no mesmo sen-
tido deste, criando uma magnetização total resultante passívelde ser detectada. Esta
magnetização resultante será tão maior quanto for a intensidade do campo externo
B0.
Os equipamentos de ressonância magnética usam supercondutores para gerar um
forte campo magnético (comumente de 1,5 ou 3 Tesla). Este campo permanece ligado
mesmo quando o equipamento não está em uso. Estes equipamentos contam também
com bobinas para gerar perturbações intencionais neste campo na forma de gradientes
de campos magnéticos. Tipicamente são utilizadas 3 bobinas, uma para cada direção
espacial. Os gradientes gerados por estas bobinas (G
x
, G
y
e G
z
) geram variações
lineares em cada direção no campo magnético total.
Outro efeito do campo magnético externo sobre os spins é que ele faz com os eixos
magnéticos dos mesmos precessem ao redor do eixo magnético do campo externo. A
frequência de precessão dos eixos magnéticos(!) dos núcleos varia de acordo com a
intensidade do campo(B), segundo a fórmula:
! = �B (2.1)
em que � é a constante giromagnética (42,6 MHz/Tesla para prótons 1H). A frequên-
cia de precessão quando B = B0 é chamada de frequência de Larmor. Para estudos de
ressonância magnético em tecidos humanos, nos concentramos em estudar o compor-
tamento do hidrogênio, por ser parte das moléculas de água, que é muito abundante
no corpo.
Usando os gradientes, podemos variar os campos magnéticos percebidos pelos nú-
cleos de hidrogênio em diferentes posições do material sob estudo, efetivamente codi-
ficando informação espacial na frequência ou na fase dos spins.
O terceiro componente de um equipamento de ressonância magnética é a bobina
de RF. Esta bobina é utilizada para emitir pulsos de RF na mesma frequência que a
frequência de precessão dos núcleos de hidrogênio que se deseja excitar. Na presença
dos gradientes, diferentes posições do material sob estudo sentem campos magnéticos
diferentes. Com isso, prótons em posições diferentes precessam em frequências dife-
rentes. Isso permite que possamos excitar regiões específicas utilizando pulsos de RF
em frequências adequadas.
Os núcleos que precessam na mesma frequência do pulso de RF estão em ressonân-
cia com este campo e absorvem energia do pulso. Ao absorverem a energia do pulso
de RF, os núcleos aumentam o angulo entre a direção do campo B0 e a direção de seu
eixo magnético. Por definição, o campo B0 aponta na direção do eixo z. Ao aplicarmos
6
o pulso de RF, os núcleos excitados deslocam seu eixo magnético na direção do plano
x-y, criando a chamada magnetização transversal.
A magnetização transversal diminui de forma diferente para cada tecido após o
desligamento do pulso de RF. Devido à interação entre os campos dos próprios spins,
eles vão saindo de fase e a magnetização líquida diminui segundo uma constante de
tempo denominada relaxamento spin-spin ou relaxamento T2. Existe ainda o tempo
de relaxamento T2*, que é ainda menor que o tempo de relaxamento T2 e se deve
a não homogeneidades nos campos observados pelos spins. Este relaxamento pode
ser corrigido aplicando um pulso de 180�, que força um realinhamento no sentido
inverso. A magnetização no eixo z é recuperada segundo uma terceira constante de
tempo diferente, denominada relaxamento spin-lattice ou relaxamento T1. O tempo
de relaxamento T1 é mais longo que o tempo de relaxamento T2. Isso se deve ao fato de
que antes dos spins realinharem seus eixos magnéticos com o campo magnético externo
(relaxamento T1), eles perdem a coerência de fase, o que diminui a magnetização
líquida (relaxamento T2).
A bobina utilizada para a transmissão do pulso de RF também é muitas vezes
responsável por captar o sinal. A bobina de RF consegue captar a onda eletromag-
nética resultante da variação do campo magnético no plano x-y devido à precessão
dos núcleos. É comum o uso de várias bobinas em posições diferentes para realizar a
captura dos sinais.
2.2 Aquisição
As bobinas de recepção captam o sinal combinado, resultante da oscilação de todos
os prótons excitados. Para obtermos informação espacial sobre os prótons, temos que
selecionar o corte a ser adquirido e realizar codificação espacial em frequência e em
fase.
2.2.1 Seleção de corte
Se aplicarmos, por exemplo, um gradiente na direção z (G
z
) no momento do pulso
de RF, teremos que os núcleos ao longo do eixo z precessarão em frequências diferentes
segundo
!(z) = �(B0 +Gzz). (2.2)
Com isso o pulso de RF excitará apenas os núcleos que estejam precessando na
mesma frequência do pulso. Controlando a frequência central do pulso, podemos
selecionar a fatia ao longo do eixo z que iremos excitar e, controlando a largura de
banda do pulso (�!) e a intensidade de gradiente (G
z
), podemos definir a largura do
7
corte conforme
�z =
�!
�G
z
(2.3)
Após o pulso de RF, apenas os spins do corte selecionado irão ter componentes
de magnetização transversal. Isso implica que apenas estes spins contribuirão para o
sinal captado pela bobina de RF.
Para fins didáticos, vamos supor que este primero pulso faz com que o eixo mag-
néticos dos núcleos sofram um deslocamento de 90� na direção do plano x-y, sendo
assim denominado pulso de 90�.
2.2.2 Codificação Espacial
2.2.2.1 Codificação em frequência
Após dois pulsos de 90�, separados por um intervalo de tempo TR (repetition time),
sendo o primeiro em t = �TR e o segundo em t = 0, a magnetização transversal nas
posições (x,y) do corte selecionado será dada por:
M
xy
(t) = M0(x, y)(1� e
�TR
T1
)e
� t
T
⇤
2 . (2.4)
Vamos supor agora que, no momento da aquisição, aplicamos um gradiente na
direção x (G
x
). Isso fará com que, ao longo do eixo x, os núcleos precessem com
frequências diferentes, variando linearmente suas frequências de precessão em função
de suas posições ao longo do eixo x, segundo:
!(x) = �(B0 +Gxx). (2.5)
Consequentemente, a magnetização transversal será dada por:
M
xy
(t) = M0(x, y)(1� e
�TR
T1
)e
� t
T
⇤
2 e
�i�
tR
0
G
x
(⌧)d⌧x
e�i�B0 . (2.6)
A bobina de RF captará então não mais uma única frequência, mas uma banda
de frequências. A intensidade do sinal em cada frequência dará a quantidade de sinal
gerada pelos núcleos de uma determinada posição ao longo do eixo x.
2.2.2.2 Codificação em fase
Após a emissão do pulso de 90�, todos os spins excitados precessam com a mesma
fase �0. Se, antes do momento da aquisição, aplicamos um gradiente na direção y (Gy)
por um período T
f
, isso fará com que os núcleos ao longo do eixo y precessem com
8
frequências diferentes. Estas frequências variam linearmente com a posição ao longo
do eixo y. Ao desligarmos o gradiente na direção y, todos os núcleos voltam a oscilar
na mesma frequência, mas devido ao período em que ficaram oscilando em frequências
diferentes, teremos diferenças de fase entre os núcleos ao longo do eixo y. A fase final
correspondente a cada posição no eixo y pode ser calculada integrando a frequência
de precessão ao longo do período T
f
:
�(y) = �0 + �
T
fZ
0
G
y
(t) dy. (2.7)
Com isso, podemos determinar a quantidade de sinal gerada pelos prótons de uma
determinada posição ao longo do eixo y, de acordo com a informação de fase do sinal
capturado pela bobina de RF.
2.2.2.3 Espaço-k
Quando os gradientes são reproduzidos, o sinal recebido pela bobina em um deter-
minado instante de tempo, representa a soma de diferentes sinais senoidais gerados
por núcleos em diferentes partes do corte excitado, cada um com frequência e fase
correspondentes à sua localização espacial. Tomando como exemplo uma excitação
que selecione um corte axial do corpo, o valor do sinal demodulado em um dado
instante t representa uma amostra da transformada de Fourier M(k
x
, k
y
) da imagem
m(x, y) segundo:
M(k
x
, k
y
) =
Z
x
Z
y
m(x, y)e�i2⇡(kxx+kyy) dx dy. (2.8)
em que as coordenadas k
x
e k
y
variam segundo os gradientes de leitura:
k
x
(t) =
�
2⇡
tZ
0
G
x
(⌧)d⌧ (2.9)
k
y
(t) =
�
2⇡
tZ
0
G
y
(⌧)d⌧. (2.10)
e
m(x, y) = M0(x, y)(1� e
�TR
T1
)e
� t
T2 e
� t
T
⇤
2 . (2.11)
O termo
e�i�B0 (2.12)
9
Figura 2.1: Sequências de pulsos e suas trajetórias correspondentesno espaço-k. (a) 2DFT (b)
espiral. [Reproduzido de [8]]
é calcelado pela demodulação de M
x,y
(t).
Na prática, os gradientes permitem o deslocamento no chamado domínio de Fourier
ou espaço-k. As sequências de gradientes e pulsos são ilustradas em um diagrama
chamado de sequência de pulsos. O projeto das sequências de pulsos é feito de forma
a causar uma determinada trajetória para a aquisição das amostras no espaço-k. Dessa
forma, é possível personalizar o percurso no espaço-k durante a aquisição. A Fig. 2.1
ilustra algumas sequências de pulsos e suas correspondentes trajetórias. O diagrama
de pulso que representa a aquisição 2DFT mostra sequência necessária para percorrer
uma linha da aquisição. A linha mais clara na representação do campo G
y
ilustra como
seria a aquisição de outra linha, que tem seu percurso correspondente representado
na trajetória também pela linha mais clara.
Após a aquisição de todos os dados no domínio de Fourier, podemos obter uma
imagem aplicando a transformada de Fourier inversa. A imagem resultante representa
a distribuição de densidade de prótons ponderada por duas funções. A primeira função
descreve o crescimento da componente longitudinal (relaxamento T1) e a segunda
função descreve o decaimento da componente transversal (relaxamento T2 e T ⇤2 ). Isso
implica o fato de que as imagens de ressonância magnética não representam puramente
a densidade de prótons, mas sim uma ponderação da densidade pelos fatores T1 e
T2, que dependem das características dos tecidos, e dos parâmetros TR (tempo de
10
repetição) e TE (tempo de leitura), os quais são definidos pelo operador.
2.3 Reconstrução
Como visto anteriormente, as informações geradas através de ressonância magné-
tica são captadas pela bobina de RF na forma de intensidades, frequências e fases, no
que chamamos de espaço-k. Foi também visto como é feita a seleção dos cortes e como
são codificadas as informações do plano x-y ao longo deste espaço-k. Para reconstruir
as imagens adquiridas no espaço-k utilizamos a transformada inversa de Fourier.
Se os dados são uniformemente amostrados, ou seja, em uma grade uniforme no
espaço-k, podemos aplicar diretamente as transformadas rápidas de Fourier, ou FFTs.
Existem várias algoritmos para o cálculo de FFTs e é comum a várias arquiteturas
de processadores a existência de hardware dedicado para a aceleração do cálculo de
FFTs.
Na aquisição cartesiana dos dados, cada linha da grade de amostragem no espaço-k
é adquirida em um tempo TR. O tempo necessário então para uma aquisição completa
é de tantos períodos TR quantas linhas forem necessárias para a resolução necessária.
2.4 Amostragem não Cartesiana
A aquisição dos dados em uma grade Cartesiana uniforme facilita a reconstrução
dos dados através da aplicação da FFT. Este tipo de aquisição, contudo, representa
uma escolha pobre no que diz respeito ao tempo de aquisição.
Assumindo a abstração de que, para adquirir todo o espaço-k, devemos percorrê-lo
linha por linha de maneira uniforme, encontramos dificuldades práticas para definir a
sequência de pulsos necessária para criar este caminho de maneira rápida.
Um longo tempo de aquisição dificulta ou até mesmo impossibilita a aplicação da
ressonância magnética para estudos cardíacos, em que existe a presença de movimento
no tecido a ser estudado. É possível a aplicação de técnicas que sincronizam a aquisição
dos dados com o ciclo cardíaco, como a técnicas CINE. Através destas técnicas, é
possível adquirir uma determinada posição do ciclo através de aquisições parciais feitas
na mesma posição relativa ao longo de vários batimentos. Ainda assim, a influência
de outros movimentos, como o respiratório, quando a aquisição é mais longa que a
capacidade de segurar o fôlego do paciente, prejudicam a qualidade final das imagens
que são geradas através destes métodos.
Para acelerar a aquisição dos dados, pode-se realizar amostragens não Cartesianas,
onde os percursos gerados são mais fáceis de serem realizados através dos gradientes
e campos das máquinas de ressonância magnética de forma rápida.
11
Através da aquisição não-Cartesiana, é possível capturar todas as fases do ciclo
cardíaco em apenas um curto fôlego, minimizando assim os artefatos gerados pelos
movimentos de respiração.
2.5 Reconstrução não Cartesiana
A reconstrução de dados amostrados de forma não Cartesiana não pode ser feita
através da aplicação direta da FFT, uma vez que tais algoritmos tiram vantagem
exatamente das simetrias decorrentes do espaçamento regular das amostras.
A aplicação da transformada direta de Fourier (DrFT) é possível, uma vez que não
impõe sobre os dados qualquer restrição quanto ao espaçamento das amostras.
Temos como alternativa a utilização dos algoritmos de gridding, onde as amostras
não uniformes são interpolados para uma grade uniforme, para então serem recons-
truídos por FFT.
Os algorítmos de DrFT e gridding são abordados em maior detalhe no capítulo 4.
2.6 Imageamento de Fluxo
Através da ressonância magnética, além da aquisição de informações anatômicas,
é possível também adquirir informações de fluxo sanguíneo. A avaliação do fluxo
sanguíneo através de ressonância magnética apresenta algumas vantagens sobre ul-
trassonografia Doppler.
A ultrassonografia Doppler ainda é o método mais usual para estudos de fluxo
sanguíneo pela comunidade médica. Esta tecnologia é bastante barata, mas apresenta
algumas limitações práticas como a dificuldade para estudar vasos mais profundos e a
necessidade de uma janela acústica livre desde a pele até os vasos a serem estudados.
Outro fator importante para o uso da ultrassonografia Doppler é a habilidade do
operador para conseguir realizar os estudos. Através da ressonância magnética, é
possível avaliar fluxo em vasos profundos, ainda que em regiões oclusas por ossos e de
maneira menos dependente do operador.
2.6.1 Contraste de Fase
Na técnica imageamento de fluxo por contraste de fase, um gradiente bipolar ali-
nhado com o eixo do fluxo é usado para se obter uma medida de velocidade para
cada pixel, ou voxel, da imagem. Na prática, são feitas duas aquisições, variando o
primeiro momento do gradiente bipolar. Com esta técnica, spins estacionários sofrem
um defasamento total nulo, uma vez que ambos os pulsos do gradiente bipolar têm
a mesma amplitude e duração. Já os spins que estão em movimento irão sofrer um
12
defasamento não nulo, já que, com o deslocamento, se encontram em uma posição
diferente ao longo do gradiente quando percebem o segundo pulso, causando uma di-
ferença de fase acumulada. A informação de velocidade é obtida diretamente a partir
dessa diferença de fase.
É possível combinar o contraste de fase com CINE, para gerar imagens ao longo de
um ciclo cardíaco, e mostrar informações de movimento e fluxo sanguíneo. O contraste
de fase também pode ser usado para aquisições em tempo real usando codificações
sequenciais, periódicas e contínuas.
A técnica de constraste de fase apresenta problemas de inconsistência de dados,
efeitos de volume parcial e dispersão de fase dentro do voxel, o que pode levar a me-
didas subestimadas de velocidade de pico [1]. Com o uso de voxels grandes, spins
estacionários e em movimento coexistem. Isso resulta em perda de sinal, distorção e
erros de estimação de velocidade, uma vez que o sinal de fase medido representa uma
média das velocidades de todos os spins de um voxel. Por estas razões, o contraste de
fase não se mostra adequado para a medição precisa de velocidades de pico, especial-
mente em jatos de fluxo complexo ou turbulentos, como os normalmente observados
em válvulas com estenose ou refluxo.
2.6.2 Fourier Velocity Encoding
A técnica Fourier Velocity Encoding, ou simplesmente FVE[9], pode ser conside-
rada o equivalente em ressonância magnética da ultrassonografia espectral Doppler.
Nesta técnica, todo o espectro de velocidades dos spins em cada voxel é medido, co-
dificando em fase no domínio de Fourier (k
v
) a informação de velocidade. Com isso,
as limitações mencionadaspara a técnica de contraste de fase são superadas.
Esta técnica ainda não é utilizada clinicamente pois, em princípio, o seu tempo de
aquisição é consideravelmente mais longo que o tempo necessário para a aquisição por
contraste de fase, porém várias técnicas para acelerar a aquisição do FVE têm sido
propostas.
Enquanto a técnica de contraste de fase provê apenas a média das velocidades
dos spins de um determinado pixel, FVE permite a aquisição de um histograma das
velocidades dos spins que compõem cada pixel. Isso é feito aplicando etapas adicionais
de codificação de fase ao longo da dimensão de velocidade. Isso adiciona a dimensão de
velocidade(k
v
) ao espaço-k, demandando várias aquisições adicionais para percorrê-la.
Para mover as aquisições ao longo da dimensão de velocidade no espaço-k, gradi-
entes bipolares são aplicado antes das aplicações dos gradientes de leitura espacial de
cada aquisição. A aplicação de gradientes bipolares consistem em aplicar um gradi-
ente, fazendo com que as frequências de precessão variem ao longo do eixo z por um
curto período de tempo e depois aplicar o mesmo gradiente com sinal invertido ao
longo do mesmo eixo para que as frequências retornem ao seu valor inicial. Isso faz
13
Figura 2.2: Sequência de pulsos para aquisição FVE espiral. (a) seleção do corte (b) gradiente
bipolar para codificação de velocidade, que muda de intensidade para a codificação das velocidades
(c) leitura espiral (d) gradientes de refocalização. [Adaptado de [8]]
com que surja uma diferença de fase ao longo do eixo do gradiente. Cada aquisição ao
longo de k
v
é chamada de uma codificação de velocidade. O número de codificações
de velocidade em k
v
depende da resolução de velocidades que se deseja alcançar e da
faixa de velocidades que se quer medir.
A Fig. 2.2 mostra uma sequência de pulsos para aquisição em espiral com FVE.
Na figura é apresentada a aquisição de uma codificação de velocidade, com as linhas
mais claras e a seta indicando a variação da intensidade do pulso de 180� para cada
codificação de velocidade.
2.6.3 Multi-dimensionalidade
Para o estudo de fluxo, ao invés de capturar simples imagens bidimensionais, temos
que adquirir dados de velocidade. Além disso, geralmente adquirimos também vários
cortes e vários quadros temporais. Também utilizamos informações de 4 bobinas de
captação. Com isso os nossos dados são do tipo M(k
x
, k
y
, z, k
v
, t). A captura dos sinais
pode ser feita utilizando mais de uma bobina de leitura, resultando em um sinal do
tipo M(k
x
, k
y
, z, k
v
, t, b) em que b denota o número do canal.
O volume de dados para estudo do fluxo sanguíneo, por exemplo, é de aproxi-
madamente 1GB de dados, para 32 codificações de velocidade, 5 cortes, 4 bobinas
de captação, 43 quadros temporais e com resolução espacial de 115⇥ 115 pixels. Essa
quantidade de dados gera implicações não só para o processamento, mas também para
o armazenamento e para a transmissão.
14
2.6.4 Tempo de reconstrução
O tempo de reconstrução de uma imagem de ressonância magnética, mesmo que
sem a utilização de algoritmos rápidos, não seria tão relevante se analisada de forma
isolada. Contudo, apesar do impacto no tempo de reconstrução para apenas uma ima-
gem não ser sensível para o usuário mesmo que leve alguns segundos, a quantidade
de reconstruções necessárias para dados com tantas dimensões passa a ser bastante
significativo. Caso a amostragem não seja feita em grade uniforme e não utilizemos
técnicas aceleradas de reconstrução, a reconstrução da informação de fluxo sanguíneo
do dataset de 1 GB mencionado anteriormente, pode levar até 52 horas em um com-
putador com processador Intel Core i7 operando a 2,9 GHz e 8 GB de memória RAM
executando a DrFT em Matlab.
Dessa forma, fica evidente a importância do desenvolvimento de algoritmos e ferra-
mentas que possibilitem a reconstrução rápida das imagens de ressonância magnética,
uma vez que são esperados incrementos nas resoluções de todas as dimensões dos
dados adquiridos e, consequentemente, do volume de dados.
15
Capítulo 3
Processamento Paralelo em CUDA
Este capítulo se inicia com um pequeno
histórico da tecnologia de processamento
paralelo, juntamente com argumentos que
compelem à migração das arquiteturas se-
riais para arquiteturas paralelas de pro-
cessamento. São apresentados então os
fundamentos da linguagem de programação
CUDA.
3.1 Histórico
Em 1965, Gordon Moore criou o que se tornaria mais tarde conhecido como a Lei
de Moore, em que predizia que o número de transistores em um circuito integrado
dobraria a cada ano, estimativa que mais tarde foi revisada para 18 meses. O que
muitos pensam hoje, contudo, é que a lei de Moore diz que o poder de processamento
dos processadores dobraria a cada ano. O que aconteceu é que com mais transistores,
os engenheiros conseguiram por vários anos melhorar o desempenho dos processadores
através de mudanças e melhorias de uma mesma arquitetura. Estas melhorias foram
em grande parte viabilizadas pela maior capacidade de integração dos dispositivos
semicondutores. Foram desenvolvidas novas tecnologias que permitiram executar ins-
truções em menos ciclos de processamento, aumento da velocidade das portas lógicas
nos semicondutores, etc. Somando todos esses fatores, temos o ganho de desempenho
de aproximadamente 52% ao ano para processadores single-threaded [10].
Contudo, este crescimento começou a diminuir para o patamar de 20% ao ano por
volta de 2001 [10] e hoje gira em torno de 5% a 10%. A figura Fig. 3.1 mostra a evolu-
ção da razão entre MIPS (million instructions per second) e o clock dos processadores
ao longo do tempo.
Vários outros fatores além da frequência de operação do processador contribuem
para o seu desempenho, como velocidade das memórias e largura dos barramentos. São
exatamente esses outros fatores que ainda vêm permitindo a evolução na capacidade
16
Figura 3.1: Crescimento do poder de processamento das CPUs. [Adaptado de [11]].
de processamento das CPUs nos últimos anos, já que a frequência de operação tem se
mantido constante. Ainda assim, a frequência de operação é um dos principais fatores
a serem considerados ao avaliar o desempenho de computadores.
Uma nova abordagem para o aumento do poder de processamento é o paralelismo,
ou seja, utilizar vários processadores e distribuir o processamento para realizar a
mesma tarefa em menos tempo.
Os primeiros produtos de massa a utilizar os princípios de processamento paralelo
e memória hierárquica distribuída foram as placas de processamento gráfico (graphics
processing unit - GPU) para computadores. A quantidade de cálculos necessários para
a construção de cenas ou objetos em três dimensões se torna muito grande quando
o número de polígonos cresce. Para tirar o peso destes cálculos da CPU, foram cri-
adas placas dedicadas que criavam imagens a partir das informações dos vértices dos
polígonos, cores e de imagens de textura. Estas placas utilizavam a arquitetura de
processamento paralelo, mas como tinham uma finalidade muito específica, seus algo-
ritmos eram definidos no próprio silício dos circuitos integrados. Isso permitia maiores
17
Figura 3.2: Crescimento do poder de processamento das GPUs comparado ao das CPUs (GFLOPS
- Giga Floating point Operations Per Second). [Adaptado de [12]].
velocidades de operação, mas representava um problema evolutivo, uma vez que cada
novo recurso ou algoritmo desenvolvido para a geração das imagens demandava um
novo projeto de circuito.
Em paralelo a isso, alguns pesquisadores começaram a tentar utilizar o mecanismo
interno das placas de vídeo para realizar operações que não estivessem relacionadas
a gráficos tridimensionais, substituindo as texturas e polígonos enviados para a GPU
por matrizes cuidadosamente criadas de forma que quando a mesma operasse nesses
dados, o resultado fosse, por exemplo, a transformada de Fourier de uma matriz.
A própria indústria de placas gráficas viu o potencial para criar placas que utili-
zassem a arquitetura paralela,mas que permitissem uma programação dos algoritmos
a serem executados. Dessa forma, novos algoritmos para computação gráfica podiam
ser incorporados através de uma atualização de drivers. Isso acabou permitindo que a
comunidade científica escrevesse seus próprios algoritmos genéricos para fins diversos
utilizando a arquitetura paralela. Com isso nasceram as Unidades de Processamento
Gráfico de Uso Geral (General Purpose Graphics Processing Units - GPGPUs). Com
o tempo, outras funções computacionalmente intensas foram sendo delegadas de forma
nativa para as GPGPUs, como a codificação e decodificação de vídeo.
Desde seu surgimento, o crescimento do poder de processamento das GPGPUs
vem crescendo de forma bastante acelerada, aumentando cada vez mais a vantagem
em relação às CPUs, como mostrado na Fig. 3.2.
18
Um dos principais fatores para a dificuldade em aceitação das arquiteturas parale-
las é a necessidade de se escrever os algoritmos voltados especificamente para execução
paralela. Durante vários anos os fabricantes de CPUs se esforçaram ao máximo para
que códigos escritos para uma geração anterior de CPUs pudessem ser facilmente re-
compilados para serem executados em uma nova geração de CPUs, sem que fossem
necessárias modificações, mas fazendo uso do novo poder de processamento. Vários
recursos foram incorporados às CPUs para que além do ganho na frequência do clock,
os programas pudessem ser executados mais rapidamente de forma transparente ao
desenvolvedor. Estruturas como caches e pipelines tentam melhorar o desempenho
do processador criando formas de manter tanto os barramentos de dados e de ende-
reços, como a CPU ocupados, para acelerar a execução de programas e dos sistemas
operacionais.
Contudo, para utilizar o processamento paralelo, os programas têm que ser escritos
levando em consideração este novo paradigma. Com isso, deve-se investir muito tempo
por parte dos desenvolvedores tanto para aprender esta nova tecnologia quanto para
reescrever os programas. Este investimento de tempo é facilmente justificado, uma
vez que algoritmos reescritos de forma paralela podem chegar a ser centenas de vezes
mais rápidos que seus equivalentes em CPU.
Da mesma forma que para CPUs, o desempenho de algoritmos em GPU dependem
do hardware onde o algoritmo é executado, assim como da habilidade do programador
em escrever um código bem organizado e otimizado. Para a programação em GPGPU,
contudo, existe um outro fator que tem bastante peso: o quanto um algoritmo é
paralelizável. Algoritmos em que vários cálculos podem ser executados de forma
descorrelacionada ou paralela apreciam uma grande aceleração quando escritos para
GPGPU, ao passo que algoritmos onde existe uma característica muito serializada
podem se beneficiar menos das capacidades das GPGPUs.
3.2 Arquitetura
A abordagem para criar circuitos integrados com grande poder de processamento
paralelo não consiste em simplesmente aumentar o número de processadores, pois
hoje um dos principais fatores limitantes para o aumento do número de processadores
em um chip é a potência dissipada. Para atacar este problema, foi analisado como a
potência é consumida em um processador para então estudar as possibilidades para
minimizá-la. Dessa forma, a evolução não poderia mais ficar preocupada apenas com
o desempenho final das unidades de processamento, mas também com a eficiência de
cada uma.
Em um circuito integrado CMOS, o consumo de energia se dá primariamente nas
transições de estado dos transistores, onde os transistores variam entre os estados
ligado e desligado, ou conduzindo e não conduzindo. Quando ocorre uma transição
19
no terminal de controle do transistor, antes desse novo estado ser traduzido para sua
saída, é necessário que exista um fluxo de elétrons para carregar as capacitâncias pa-
rasitas formadas entre os condutores que levam os sinais da saída de uma porta até
as entradas dos transistores conectados. Essas capacitâncias crescem com as áreas
dos capacitores. Como dentro de uma unidade lógica aritmética (ULA) as distâncias
entre as portas são muito pequenas, também são pequenas as capacitâncias parasitas
presentes nessas estruturas. Contudo, quando queremos mover os operandos ou os
resultados, os sinais enfrentam uma capacitância que cresce linearmente com a dis-
tância a ser percorrida, pois estamos aumentando uma das dimensões da capacitância
da linha.
Verificou-se que uma unidade lógica aritmética consome 50 pJ para realizar uma
operação de multiplicação e acumulação em operandos de ponto flutuante de 64 bits e
que a energia necessária para mover tais palavras de 64 bits (operandos e resultado) é
de aproximadamente 25 pJ/mm dentro do chip. Caso os dados precisem ser movidos
para fora do circuito integrado, o consumo chega a 1 nJ [10]. Assim, gasta-se muitas
vezes mais energia movendo os operandos e os resultados do que propriamente reali-
zando os cálculos. Essa discrepância é ainda maior se usarmos operandos de precisão
simples ou inteiros, uma vez que a complexidade e o consumo da ULA diminuem, mas
a energia para mover os dados não diminui. Para resolver esse problema foi necessário
o desenvolvimento de arquiteturas que minimizassem a necessidade de mover os dados
por longas distâncias.
Para suprir essas necessidades, foram criadas arquiteturas onde cada circuito inte-
grado tem vários processadores, mas com memórias distribuídas e conectadas de forma
hierárquica. Isso permite que um processador possa operar em um conjunto de dados
e passar os resultados para outro processador através de uma memória compartilhada
próxima aos mesmos. Isso evita que os resultados intermediários precisem passar por
uma memória externa mais distante, diminuindo em muito a distância percorrida por
estes dados e consequentemente a potência dissipada no processo.
Nas arquiteturas atuais, as unidades de processamento das GPUs são bem mais
simples que nas CPUs tradicionais, mas aparecem em grande número e com uma
estrutura de memória hierárquica, em que cada unidade de processamento conta com
uma pequena quantidade de memória. Cada grupo de unidades de processamento
conta com um pouco de memória compartilhada e a placa de vídeo conta com uma
memória global acessível por todas as unidades de processamento, como ilustrado
na Fig. 3.3. Existem também memórias com características de acesso diferenciadas,
para atender determinados algoritmos que têm padrões de acesso à memória muito
específicos. O tempo de acesso para cada uma destas memórias é diferenciado, sendo
que as memórias mais próximas aos processadores são mais rápidas (e consomem
menos energia) que as memórias mais distantes.
Um componente muito sofisticado nas arquiteturas atuais é o escalonador de ta-
20
Figura 3.3: Modelo de memória hierárquica usada nas GPGPUs. [Adaptado de [12]].
refas. Ele é o responsável por gerenciar como os trechos de código, ou kernels, são
distribuídos e executados pelas unidades de processamento. Em uma placa de pro-
cessamento gráfico de uso geral, existe um escalonador para cada conjunto de um
número determinado de unidades de processamento, ou thread block. Os escalonado-
res controlam a distribuição das tarefas para cada thread e também os passos de sua
execução.
Várias arquiteturas de CPUs apresentam estruturas conhecidas como SIMD (single
instruction multiple data), em que o processador consegue operar a mesma instrução
em um conjunto de dados de tamanho definido, de forma simultânea. As GPGPUs
funcionam com estruturas do tipo SIMT (single instruction multiple threads). Ao
passo que em SIMD, o processador executa uma única instrução em múltiplos dados
de forma paralela, em SIMT pode-se paralelizar programas inteiros, com operações,
21
Figura 3.4: Operações executadas em paralelo
comparações e execução condicional.
Acessos de leitura podem ser feitos de forma simultânea por várias threads, uma
vez que isso não traz prejuízo à integridade e à coerência dos dados. Do ponto de vista
de projeto de circuito integrado, isso também não gera nenhum tipode dificuldade
técnica. Contudo, o acesso simultâneo para escrita é um fator crítico para o conceito
de processamento paralelo. Sem nem ao menos nos preocupar com a implementação
do circuito integrado, o próprio conceito de acesso paralelo para escrita já se mostra
inviável. Se duas ou mais threads tentam escrever resultados diferentes em uma mesma
posição de memória, não há como arbitrar o valor final a ser armazenado. Por isso,
já na concepção dos algoritmos paralelos, os acessos de escrita devem ser pensados
de forma a não causar conflitos. São admitidas escritas simultâneas nas memórias,
porém não no mesmo endereço. As escritas paralelas devem acontecer como ilustrado
na Fig. 3.4.
No caso de execuções condicionais que desviam o fluxo de um código, como, por
exemplo, em um if...else, enquanto as threads que avaliaram a condição if como
verdadeira executam o código correspondente, as threads que avaliaram a condição
como falso ficam congeladas. Quando as threads que tomaram o ramo verdadeiro da
condicional terminarem de executar as instruções correspondentes, estas ficam con-
geladas enquanto as demais threads executam as instruções relativas ao ramo falso da
condicional. Esta coerência na execução só é necessária dentro de um thread block.
Thread blocks diferentes podem executar códigos diferentes de forma simultânea, fi-
cando o escalonador de maior nível responsável pelo agendamento de execução das
thread blocks.
3.3 Definição da linguagem utilizada
Durante o processo evolutivo dos processadores paralelos, várias linguagens —
ou simples extensões para outras linguagens — foram criadas para suportar estas
arquiteturas, mas a primeira linguagem a se destacar nesse cenário, por acompanhar
22
um produto realmente massificado, foi a linguagem CUDA (Compute Unified Device
Architecture), criada pela NVIDIA para suas placas de processamento gráfico. Com
um produto de prateleira amplamente disponível, a adoção da linguagem foi rápida
tanto no meio acadêmico quanto no meio comercial.
O segundo grande fabricante de placas gráficas, a ATI Technologies, mais tarde
comprada pela AMD, lançou a linguagem ATI Stream, mas sua aceitação foi baixa.
Mais tarde, já após sua aquisição pela AMD, as placas gráficas da ATI passaram defen-
der o padrão OpenCL, proposto pela Apple Computer. O OpenCL hoje é suportado
por praticamente todos os maiores fabricantes de CPUs e GPUs e representa para o
processamento paralelo o que o openGL representa para a aceleração de gráficos em
duas e três dimensões.
À época do início deste trabalho, a linguagem CUDA se apresentava como a mais
madura e já contava com mais ferramentas, bibliotecas e disseminação na comunidade
científica. Porém estas desvantagens vêm sendo reduzidas rapidamente e, hoje, o
OpenCL tem apresentado grande adoção devido principalmente a sua natureza multi-
plataforma, estando presente, já há algum tempo, até mesmo em equipamentos em-
barcados como smart phones e tablets.
Em openCL, detalhes específicos das arquiteturas onde os códigos serão executados
são transparentes para o programador. Este grau de abstração facilita a portabilidade
do mesmo código para diversas plataformas de computação paralela diferentes. Existe,
inclusive, a capacidade de distribuição do processamento em ambientes com proces-
samento heterogêneo, paralelizando execuções entre CPUs e GPUs.
Originalmente, na linguagem CUDA o programador tinha mais controle sobre a
arquitetura, podendo, por exemplo, definir como será o uso da memória hierárquica.
Eventualmente, o OpenCL passou a suportar o uso de flags específicas para melhor
aproveitar algumas características específicas do hardware.
3.4 Fundamentos da linguagem
A linguagem CUDA é, na verdade, uma extensão da linguagem C. Programas
escritos em C ou C++ utilizam uma Application Programming Interface, ou API, para
copiar dados para a GPGPU e configurar a execução das unidades de processamento.
Os programas em C ou C++ dispara através das APIs a execução dos reais algoritmos
paralelos na GPU. O compilador nvcc da NVIDIA traduz os códigos escritos para o
processamento paralelo para o formato executável, que é carregado para a placa gráfica
no momento da execução. Após a execução na GPGPU, o programa em C retoma
o controle de execução e busca os resultados na GPGPU através da mesma API da
NVIDIA.
Atualmente, as APIs para o acesso aos recursos de programação paralela estão dis-
23
poníveis também em outras linguagens. É possível fazer as transferências de memória,
assim como configurar e executar os kernels, a partir de linguagens de mais alto nível,
como Python ou até mesmo a partir do Matlab.
Os algoritmos que serão executados na GPGPU devem ser escritos na forma de
kernels, que são basicamente os trechos de código que cada unidade de processamento
executará em paralelo. Todas as unidades de processamento executam o mesmo có-
digo, apenas diferenciando os operandos. A linguagem de programação provê para os
kernerls variáveis que aparecem com valores diferentes para cada uma das instâncias
da execução. Essas variáveis são usadas para decidir, por exemplo, qual parte dos
dados cada kernel deve processar e onde guardar os resultados.
O kernel é o código escrito, enquanto que e as instâncias desse código, enquanto
executadas por uma unidade de processamento, são chamadas de threads. Cada kernel
pode receber argumentos como valores ou ponteiros para memórias globais. A variável
blockIdx.x disponibiliza o identificador do bloco da thread corrente, threadIdx.x possui
o identificador da thread dentro do bloco e blockDim.x traz a dimensão total do bloco.
Assim podemos calcular blockIdx.x*blockDim.x+threadIdx.x para identificar a posição
da thread dentro da cadeia de processamento total. Cada uma dessas constantes
podem ter valores em x, y e z. Esta separação em três dimensões é meramente
lógica e serve para facilitar o trabalho do programador no momento da concepção dos
algoritmos e da codificação dos kernels.
Para ilustrar este conceito, tomemos uma multiplicação de duas matrizes A e B,
com o resultado em uma matriz C, toda com dimensões N ⇥ N . Para calcular o
resultado da multiplicação na coluna x, linha y, ou seja, na posição (x,y), precisamos
multiplicar toda a linha y da matriz A por toda a coluna x da matriz B e somar os
resultados. Para calcular o resultado na posição (x,y+1), precisamos multiplicar toda
a linha y+1 da matriz A por toda a coluna x da matriz B e somar os resultados.
Podemos ilustrar esse processo conforme mostrado na Fig. 3.5.
Perceba que, do ponto de vista de acesso de leitura aos dados, essas operações são
passíveis de serem executadas em paralelo, uma vez que ambos os cálculos acessam os
mesmos valores simultaneamente (coluna x da matriz A). Os resultados intermediários
(acúmulo das multiplicações das células) são guardados nas memórias locais a cada
thread. Do ponto de vista de acesso de memória para escrita dos resultados finais,
essas threads também podem ser executadas em paralelo, já que, ao final dos cálculos,
cada thread escreve seu resultado em uma posição diferente da memória. Dessa forma,
podemos atribuir o cálculo de cada valor da matriz C a uma unidade de processamento
da nossa placa de processamento gráfico. Se nossa placa tem mais de N⇥N unidades de
processamento, podemos realizar toda a multiplicação em paralelo. Caso nossa placa
de processamento gráfico tem menos de N ⇥N unidades de processamento, podemos
calcular o resultado C por partes, como ilustrado na figura Fig. 3.6.
Segue abaixo o código de um kernel que executa a multiplicação discutida acima.
24
Figura 3.5: Cálculo de um resultado da matriz C. [Adaptado de [12]].
// Matrizes armazenadas linha a linha
// M(linha, coluna) = *(M.valores + linha * M.largura + coluna)
typedef struct
{
int largura;
int altura;
int* valores;
} Matriz;
__global__ void MatMulKernel(Matriz A, Matriz B, Matriz C)
{ // cada thread calcula um elemento da matriz C acumulando os resultados na variavel
tempC.
int tempC = 0;
int linha = blockIdx.y *blockDim.y + threadIdx.y;
25
Figura 3.6: Cálculo de um conjunto de resultados da matriz C. [Adaptado de [12]].
int coluna = blockIdx.x * blockDim.x + threadIdx.x;
for (int i = 0; i < A.largura; i++)
{
tempC += A.valores[linha * A.largura + i] * B.valores[i * B.largura + coluna];
}
C.valores[linha * C.largura + coluna] = tempC;
}
Os dados a serem processados devem ser transferidos para a memória da GPGPU
para poderem ser acessados pelas unidades de processamento. Esta transferência é
realizada através de APIs específicas da linguagem. Da mesma maneira, os resultados
são colocados na memória da GPGPU e devem ser resgatados para a memória da
CPU através de API específica. Segue abaixo um exemplo de código em linguagem
C para realizar as transferências de dados para a GPGPU, disparar a execução do
processamento e recuperar os resultados de volta para a memória da CPU.
26
// Definicao do tamanho do bloco de threads
#define BLOCK_SIZE 16
// prototipo de funcao do kernel CUDA
__global__ void MatMulKernel(const Matriz, const Matriz, Matriz);
// Codigo da CPU
// para simplificar a ilustracao, as matrizes sao consideradas
// como tendo dimensoes multiplas de BLOCK_SIZE
void MatMul(const Matriz A, const Matriz B, Matriz C)
{
// Carrega as matrizes A e B para a memoria da GPGPU
Matriz d_A;
d_A.largura = A.largura;
d_A.altura = A.altura;
size_t size = A.largura * A.altura * sizeof(int);
cudaMalloc(&d_A.valores, size);
cudaMemcpy(d_A.valores, A.valores, size, cudaMemcpyHostToDevice);
Matriz d_B;
d_B.largura = B.largura;
d_B.altura = B.altura;
size = B.largura * B.altura * sizeof(int);
cudaMalloc(&d_B.valores, size);
cudaMemcpy(d_B.valores, B.valores, size, cudaMemcpyHostToDevice);
// Aloca memoria para a matriz C na GPGPU
Matriz d_C;
d_C.largura = C.largura;
d_C.altura = C.altura;
size = C.largura * C.altura * sizeof(int);
cudaMalloc(&d_C.valores, size);
// dispara a execucao do kernel
dim3 dimBlock(BLOCK_SIZE, BLOCK_SIZE);
dim3 dimGrid(B.largura / dimBlock.x, A.altura / dimBlock.y);
MatMulKernel<<<dimGrid, dimBlock>>>(d_A, d_B, d_C);
// Le os resultados da matriz C de volta para a memoria da CPU
cudaMemcpy(C.valores, Cd.valores, size, cudaMemcpyDeviceToHost);
// libera as memorias alocadas na GPGPU
cudaFree(d_A.valores);
cudaFree(d_B.valores);
cudaFree(d_C.valores);
}
27
Seria ainda possível realizar alterações no código para melhorar seu desempenho,
fazendo uso mais intenso da memória compartilhada [12], para diminuir o tempo
de acesso total a valores que são recorrentemente utilizados. Além disso, seriam
necessárias alterações para o uso de matrizes com dimensões arbitrárias.
É possível utilizar os dados resultantes de uma etapa de processamento realizado
na GPGPU como entrada para uma etapa seguinte de processamento paralelo, sem
a necessidade de transferir esses resultados intermediários de volta para a CPU e
novamente para a GPGPU. Isso permite melhorar a performance de algoritmos mais
complexos, onde é necessário a execução de kernels diferentes para alcançar o objetivo
final. Para a apresentação e visualização de grandes volumes de dados, estes podem
ser mostrados diretamente da placa de vídeo, para visualização em três dimensões de
um conjunto de dados de ressonância magnética, por exemplo.
28
Capítulo 4
Reconstrução em Cuda
Este capítulo discute a implementação dos
algorítmos de reconstrução de imagens de
ressonância magnética em linguagem de
programação CUDA, fazendo uma análise
dos resultados obtidos.
4.1 CUDA DrFT
Como discutido no Capítulo 2, os dados adquiridos por um equipamento de resso-
nância magnética estão no espaço-k, ou domínio de Fourier. Para obtermos imagens
a partir dos dados adquiridos precisamos aplicar a transformada inversa de Fourier.
Como os dados adquiridos são discretos, devemos utilizar, na verdade, a transformada
discreta de Fourier inversa (inverse discrete Fourier transform - IDFT).
A IDFT é dada por:
s
n
=
1
N
N�1X
k=0
S
k
ei2⇡
k
N
n, (4.1)
onde s é a amostra reconstruída no domínio da imagem, S é a amostra no domínio de
Fourier e N é o número total de amostras adquiridas.
Existem algoritmos rápidos para o cálculo das transformadas de Fourier. Tais al-
goritmos são chamados de Fast Fourier Transforms ou FFTs. Como a transformada
inversa de Fourier pode ser expressada em termos da transformada direta, tais al-
goritmos também se aplicam à transformada inversa, com o nome de Inverse Fast
Fourier Transform ou simplesmente IFFTs.
Tais algoritmos exploram propriedades da matriz da DFT para obter o mesmo
resultado com um número muito menor de operações. Ao passo que a transformada,
se calculada simplesmente pela fórmula, tem complexidade O(N2), as transformadas
rápidas têm complexidade O(N log(N)).
Alguns dos algoritmos mais conhecidos operam em conjuntos com uma quantidade
29
de dados que seja uma potência de 2. Entretanto, existem versões de algoritmos que
operam em conjuntos de dados com quantidades quaisquer de amostras com o mesmo
grau de complexidade computacional.
No caso de imagens, utilizamos a versão bidimensional da transformada inversa,
que, para dados uniformemente amostrados em uma grade N ⇥M , é dada por:
s
xy
=
1
NM
N�1X
n=0
M�1X
m=0
S
ij
· ei2⇡
nx
N · ei2⇡
my
M (4.2)
No caso de dados não uniformemente amostrados, com M amostras do espaço-k, a
equação para a reconstrução é dada pela DrFT inversa:
s
n
=
1
M
M�1X
m=0
W
m
S
m
ei2⇡[kx(m)x(n)+ky(m)y(n)]. (4.3)
Na equação acima, os termos W
m
representam os fatores de ponderação das amos-
tras, que são utilizados para compensar a densidade não uniforme das amostras (pon-
deração). Os fatores de ponderação são calculados fazendo um diagrama de Voronoi,
que define regiões baseadas nas distâncias entre as localizações no espaço-k das amos-
tras, e tomando o inverso do valor da área de cada célula resultante. Se a grade
de amostragem não for uniforme, as simplificações exploradas pela FFT não mais
são válidas e devemos realizar todas as operações da equação original para obter a
transformada.
A reconstrução direta por Fourier é um algoritmo altamente paralelizável, uma vez
que cada pixel s
xy
da imagem pode ser reconstruído de forma totalmente independente
dos demais pixels (do ponto de vista de acessos de escrita na memória). Isso favorece
a utilização de GPGPUs para reconstrução paralela.
Em CUDA, escrevemos os chamados kernels, que são executados por cada unidade
de processamento da GPGPU de forma independente. Cada instância em execução
do kernel é chamada de thread, que têm a capacidade de saber suas “posições"em
relação às demais threads, de forma que é possível determinar qual pixel cada kernel
deve reconstruir. Como cada pixel é reconstruído de forma independente, cada thread
escreve um pixel diferente na matriz de resultados, evitando conflitos de escrita. O
acesso paralelo para leitura é permitido.
São criados tantas threads quantos pixels existirem na imagem reconstruída. A
linguagem de programação é responsável por agendar a execução das threads nas
unidades de processamento disponíveis na GPGPU, de forma que o desenvolvedor não
precisa se preocupar com detalhes específicos do hardware, como o modelo de GPGPU
que está sendo utilizado e o número máximo de threads simultâneas. O desenvolvedor
paraleliza o algoritmo de uma forma que faça sentido do ponto de vista lógico e a
linguagem de programação paraleliza isso de forma física na GPGPU. Isso permite
30
que a velocidade de execução dos algoritmos possa ser escalonada automaticamente
quando utilizamos GPGPUs com mais unidades de processamento. Os kernels só
operam em memória que seja interna à GPGPU; logo, todos os dados necessários para
os cálculos devem ser copiados para a GPGPU antes de se iniciar o processamento e
o resultado deve ser copiado para fora da GPGPU.
Para a reconstrução por DrFT, foi implementado um algoritmo onde os kernels
são organizados logicamente em uma matriz de dimensões iguais às dimensões da
imagem a ser reconstruída. O kernel calcula os coeficientesde Fourier que irá utilizar,
baseando-se na posição do pixel a ser reconstruído no espaço x-y e nas posições das
amostras no espaço k
x
-k
y
. As amostras são multiplicadas pelos pesos correspondentes
às suas posições (ponderação) e, então, pelos coeficientes de Fourier. Todos os ensaios
foram realizados em um computador com processador Intel Core i7 operando a 2,9GHz
e 8GB de memória RAM com uma placa de processamento gráfico nVIDIA GTX570.
O código fonte abaixo traz uma versão simplificada do kernel criado para realizar
a DrFT inversa de uma imagem.
__global__ ivoid drft( const float * datar, const float * datai, const float * kx,
const float * ky, const int dataLength, const int Nim, float * mr, float * mi )
{
// datar - parte real dos dados de entrada
// datai - parte imaginaria dos dados de entrada
// kx - posicao ao longo do eixo x das amostras
// ky - posicao ao longo do eixo y das amostras
// Nim - tamanho da imagem (lado)
// mr - parte real do resultado
// mi - parte imaginaria do resultado
// definicao da posicao da thread atual dentro da imagem
int x = (blockIdx.x * blockDim.x) + threadIdx.x;
int y = (blockIdx.y * blockDim.y) + threadIdx.y;
uint sample = 0; // variavel temporaria para controlar a posicao
float tempValue;
float tempCos; // valor temporario do coseno
float tempSin; // valor temporario do seno
int tempPos = x + y * Nim; // posicao linear do pixel
float Nim2 = Nim/2; // meio tamanho da imagem
mr[tempPos] = 0;
mi[tempPos] = 0;
for(sample = 0; sample < dataLength; sample++)
{
31
Tabela 4.1: Tempo de reconstrução para o algoritmo CUDA DrFT
Resolução Tempo médio (ms)
115⇥ 115 29
128⇥ 128 35
256⇥ 256 140
512⇥ 512 520
tempValue = 2 * M_PI * ((kx[sample] * (x-Nim2)) + (ky[sample] * (y-Nim2))); //
calculo de omega
tempCos = cos(tempValue);
tempSin = sin(tempValue);
mr[tempPos] += (datar[sample] * tempCos) + (datai[sample] * tempSin); // parte
real
mi[tempPos] += (datai[sample] * tempCos) - (datar[sample] * tempSin); // parte
imaginaria
}
}
O código simplificado não mostra algumas otimizações como, por exemplo, o cálculo
prévio dos coeficientes para a memória compartilhada como forma de otimizar os
tempos de acesso a valores recorrentes. Também não são mostradas as verificações
dos limites do tamanho da imagem, que impedem que threads calculem valores fora
das dimensões da imagem.
A tabela 4.1 mostra os tempos médios (100 repetições) de reconstrução para ima-
gens de diversas resoluções pelo código DrFT em CUDA. O tempo total de recons-
trução para 32 codificações de velocidade, 5 cortes, 4 bobinas de captação, 43 quadros
temporais e com resolução de 115 ⇥ 115 pixels utilizando a DrFT é de 18 minutos.
Para referência, o tempo de reconstrução para os mesmos dados utilizando DrFT em
Matlab foi de 52 horas.
4.2 CUDA Gridding
A reconstrução de imagens de ressonância magnética por gridding tenta aprovei-
tar o ganho de esforço computacional dado pelo uso da FFT, convertendo os dados
amostrados de forma não uniforme em dados uniformemente amostrados para, então,
aplicação da IFFT.
Isso é feito através da interpolação bidimensional dos dados não uniformemente
amostrados com um função de interpolação, também chamada de kernel, para uma
grade uniforme. Obviamente, os dados da grade uniforme são aproximações que serão
32
tão boas quanto a qualidade da função de interpolação utilizada. Podem ser utilizados
kernels triangulares, que simplificam os cálculos ao custo de pior qualidade de imagem
resultante, assim como kernels do tipo Keiser–Bessel [13], que apresentam maior
dificuldade computacional, mas com ganho na qualidade da imagem resultante.
Para a reconstrução por gridding, é necessária a compensação da densidade irre-
gular original das amostras antes de convolução com o kernel de interpolação. Esta
etapa é chamada de ponderação e minimiza os efeitos de que, em uma trajetória em
espiral, existem geralmente muito mais amostras no centro do espaço-k do que em
sua periferia. Como etapa final da reconstrução por gridding, é necessário corrigir o
efeito causado pela convolução com o kernel de interpolação. Esta etapa é chamada
de deapodização.
Do ponto de vista de paralelização, o processo de interpolação para uma grade
uniforme incorre em um problema para a implementação, uma vez que os resultados
(pontos na grade uniforme) sofrem influência da interpolação de mais de um ponto
da trajetória não uniforme, como ilustrado na Fig. 4.1. Se pensarmos uma imple-
mentação do kernel em que cada thread fica responsável por calcular a influência na
matriz final de um ponto da trajetória original, teremos conflito no acesso de escrita
da memória, uma vez que várias threads podem contribuir para um mesmo ponto.
A aceleração em CUDA da etapa de convolução com os kernels de interpolação
já é obra de estudo por parte da comunidade científica. Em 2008, Sorensen [15]
mostrou os obstáculos envolvidos em otimizar o algoritmo para o uso do paralelismo
das GPGPUs. Propôs metodologias para minimizar o problema de colisão de escrita,
agrupando a interpolação dos pontos por região. Cada região é calculada de forma
independente em memórias separadas e posteriormente as áreas de interferência são
resolvidas. Este processo é ilustrado na Fig. 4.2. Ganhos de até 85⇥ na velocidade
em relação à interpolação em CPU foram reportados.
Mais tarde, Gregerson [14] reportou ganhos de até 114⇥ em relação ao tempo de
execução em CPU através de otimizações que tiravam melhor proveito da arquitetura
das GPGPUs, como uso de memória compartilhada e calculo prévio do kernel de
convolução.
Os algoritmos de FFT tentam acelerar o cálculo final utilizando estratégias para
reduzir o número de operações necessárias, geralmente eliminando operações repeti-
das e combinando resultados. Esses algorítmos também são paralelizáveis, mas não
totalmente. Utilizando uma forma de FFT que fatora recursivamente uma FFT de
N amostras de comprimento em 2 FFTs de comprimento N/2, e assim por diante.
Dessa forma, fica claro que o cálculo tem uma característica serial, onde uma etapa
do processamento depende do resultado da etapa anterior, prejudicando a capacidade
de paralelilzação do algoritmo.
Tendo em vista os trabalhos realizados anteriormente, este trabalho não se pre-
ocupou em repetir esses experimentos. A técnica de gridding é apresentada como
33
Figura 4.1: Áreas influenciadas pela interpolação de cada ponto da trajetória espiral. [Reproduzido
de [14]].
fundamento para a técnica de NUFFT apresentada a seguir.
4.3 NUFFT
Com uma metodologia difundida principalmente por Fessler [4] [16], é possível
melhorar a qualidade de imagens de ressonância magnética reconstruídas a partir
de dados adquiridos em trajetórias não uniformes. Este método, conhecido como
NUFFT (non-uniform FFT), otimiza os parâmetros do kernel de interpolação utili-
zado no gridding de maneira iterativa, através de técnicas de minimização de erro. A
NUFFT foi bastante difundida, talvez pelo fato de que Fessler e seus contribuidores
disponibilizaram uma biblioteca em Matlab com várias ferramentas de reconstrução
que, entre outras coisas, continha as rotinas da NUFFT.
Imagens de referência reconstruídas utilizando a NUFFT apresentam erros míni-
mos, comparáveis aos erros de quantização dos dados. Neste trabalho a NUFFT é
34
Figura 4.2: Agrupamentos das interpolações por região. [Reproduzido de [14]].
utilizada como base de comparação no que diz respeito a tempo de reconstrução. Não
consideramos que faz sentido comparar o tempo de reconstrução com gridding, pois
a NUFFT é gridding executado iterativamente para obter melhor qualidade de ima-
gem. Faz sentido sim compará-la com DrFT, pois a DrFT, que é a solução analítica,
também está preocupada com a qualidade final da imagem reconstruída.
O tempo total de reconstrução para 32 codificações de velocidade, 5 cortes, 4 bobi-
nas de captação, 43 quadros temporais e com resolução de 115⇥ 115 pixels, utilizando
o algoritmo NUFFT, disponibilizado para Matlab por Fessler, foi de 5 minutos.
4.4 Resultados

Continue navegando