Pular para o conteúdo principal

visualizando dados volumétricos 3D com Matplotlib

Neste tutorial de Python, você vai usar a API de manipuladores de evento do Matplotlib para exibir os cortes de um conjunto de dados de MRI.
Atualizado 17 de set. de 2026  · 10 min lido

Explorar com IA

ChatGPTClaudePerplexity

Muita gente já está familiarizada com dados de imagem capturados por câmeras comuns (frequentemente chamados de “imagens naturais” na literatura científica), mas também por instrumentos especializados, como microscópios ou telescópios. Ao trabalhar com imagens em Python, a forma mais comum de exibi-las é usando a função imshow do Matplotlib, a biblioteca de gráficos mais popular do Python.

Neste tutorial, vamos mostrar como estender essa função para exibir dados volumétricos 3D — pense nisso como uma pilha de imagens que, juntas, descrevem uma estrutura 3D. Por exemplo, exames de ressonância magnética (MRI) e tomografia computadorizada (CT) medem a estrutura 3D dentro do corpo humano; microtomografia por raios X mede a estrutura 3D dentro de materiais como vidro ou ligas metálicas; e microscópios de lâmina de luz medem partículas fluorescentes em tecidos biológicos.

Vamos demonstrar como baixar um conjunto de dados de MRI e exibir os cortes usando matplotlib. Você vai aprender a:

  • Obter seus dados com a ajuda da biblioteca nibabel, que oferece um leitor para arquivos no formato NIfTI. Para quem quiser pular essa etapa e ir direto ao plot, este tutorial também mostra como obter os dados com scikit-image.
  • Dar os primeiros passos rumo a um visualizador de cortes totalmente funcional com a API de manipuladores de evento do matplotlib, e
  • Reescrever parte do seu código para aproveitar a nova ferramenta.
  • Por fim, ver como limpar seu ambiente de trabalho quando terminar.

Mas antes, vamos ao básico: como exibir imagens com o imshow do Matplotlib.

Você pode começar ativando o modo interativo do matplotlib se estiver usando o Jupyter Notebook:

%matplotlib notebook

Agora importe o matplotlib e exiba alguns dados. Vamos carregar alguns exemplos incluídos no módulo data da biblioteca scikit-image:

import matplotlib.pyplot as plt
from skimage import data

astronaut = data.astronaut()
ihc = data.immunohistochemistry()
hubble = data.hubble_deep_field()

# Initialize the subplot panels side by side
fig, ax = plt.subplots(nrows=1, ncols=3)

# Show an image in each subplot
ax[0].imshow(astronaut)
ax[0].set_title('Natural image')
ax[1].imshow(ihc)
ax[1].set_title('Microscopy image')
ax[2].imshow(hubble)
ax[2].set_title('Telescope image');
matplotlib tutorial event handler API

Observação: ao executar o matplotlib no modo interativo do notebook, a figura aberta continua sendo a única figura ativa até você desativá-la usando o ícone de energia no canto superior direito. Faça isso antes de avançar para cada novo gráfico.

Essas imagens são chamadas de 2D (bidimensionais) porque estão dispostas em 2 dimensões: x e y, ou, na linguagem do NumPy, linhas e colunas (r e c).

Algumas imagens são 3D, pois têm uma dimensão adicional de profundidade (z, ou planos). Isso inclui imagens de ressonância magnética (MRI) e microscopia eletrônica de transmissão por seções seriadas (ssTEM), em que a amostra é fatiada finamente, como um salame, e cada fatia é imageada separadamente.

Para visualizar esse tipo de imagem no matplotlib, precisamos escolher um corte e exibir apenas esse corte. Vamos testar isso com um conjunto de dados de MRI disponível gratuitamente online.

Intervalo: baixando os dados

Vamos baixar um conjunto de dados descrito em Buchel e Friston, Cortical Interactions Evaluated with Structural Equation Modelling and fMRI (1997). Primeiro, criamos um diretório temporário para fazer o download. Lembre-se de apagá-lo quando terminar a análise! Se quiser manter esse conjunto de dados para uso futuro, mude d para um caminho permanente de sua preferência.

import tempfile

# Create a temporary directory
d = tempfile.mkdtemp()

Agora, vamos baixar os dados:

import os

# Return the tail of the path
os.path.basename('http://google.com/attention.zip')
from urllib.request import urlretrieve

# Define URL
url = 'http://www.fil.ion.ucl.ac.uk/spm/download/data/attention/attention.zip'

# Retrieve the data
fn, info = urlretrieve(url, os.path.join(d, 'attention.zip'))

E extrair o conteúdo do arquivo zip para nosso diretório temporário:

import zipfile

# Extract the contents into the temporary directory we created earlier
zipfile.ZipFile(fn).extractall(path=d)

Se você conferir o conteúdo do arquivo, vai encontrar vários arquivos ‘.hdr’ e ‘.img’.

# List first 10 files
[f.filename for f in zipfile.ZipFile(fn).filelist[:10]]

Eles estão no formato NIfTI, e vamos precisar de um leitor para isso. Felizmente, a excelente biblioteca nibabel fornece esse leitor. Instale com conda install -c conda-forge nibabel ou pip install nibabel e então:

import nibabel

Agora podemos finalmente ler nossa imagem e usar o método .get_data() para obter um array NumPy para visualização:

# Read the image 
struct = nibabel.load(os.path.join(d, 'attention/structural/nsM00587_0002.hdr'))

# Get a plain NumPy array, without all the metadata
struct_arr = struct.get_data()

Dica: se quiser ir direto para o plot do MRI, execute as linhas abaixo:

from skimage import io

struct_arr = io.imread("https://s3.amazonaws.com/assets.datacamp.com/blog_assets/attention-mri.tif")

Voltando aos gráficos

Vamos olhar um corte desse array:

plt.imshow(struct_arr[75])

python matplotlib tutorial

Uou! Parece meio amassado! Isso acontece porque a resolução no eixo vertical em muitos MRIs não é a mesma dos eixos horizontais. Dá para corrigir passando o parâmetro aspect para a função imshow:

plt.imshow(struct_arr[75], aspect=0.5)

event handler API

Mas, para facilitar, vamos simplesmente transpor os dados e olhar apenas para os cortes horizontais, que não precisam desse ajuste.

struct_arr2 = struct_arr.T
plt.imshow(struct_arr2[34])

event handler API

Bonito! Claro que, para ver outro corte ou um corte em outro eixo, precisamos fazer outra chamada a imshow:

plt.imshow(struct_arr2[5])

event handler API

Ficar repetindo essas chamadas cansa rápido. Por muito tempo, eu visualizava volumes 3D usando ferramentas fora do Python, como o ITK-SNAP. Mas dá para adicionar facilmente recursos de “rolagem” 3D ao visualizador do matplotlib! Assim, conseguimos explorar dados 3D dentro do Python, minimizando a troca de contexto entre exploração e análise.

O segredo é usar a API de manipuladores de evento do matplotlib, que permite definir ações no gráfico — incluindo trocar os dados exibidos! — em resposta a teclas específicas ou cliques do mouse.

No nosso caso, vamos associar as teclas J e K do teclado a “corte anterior” e “próximo corte”:

def previous_slice():
    pass

def next_slice():
    pass

def process_key(event):
    if event.key == 'j':
        previous_slice()
    elif event.key == 'k':
        next_slice()

Simples! Claro que precisamos decidir como implementar essas ações e informar à figura que ela deve usar a função process_key para processar as teclas! A segunda parte é fácil: basta usar o método mpl_connect do canvas da figura:

fig, ax = plt.subplots()
ax.imshow(struct_arr[..., 43])
fig.canvas.mpl_connect('key_press_event', process_key)

A documentação completa do mpl_connect está aqui, incluindo outros tipos de eventos que você pode associar (como cliques do mouse).

Descobri com um pouco de exploração que imshow retorna um objeto AxesImage, que “mora” dentro do objeto Axes do matplotlib onde acontece toda a renderização, no atributo .images. E esse objeto oferece um prático método set_array para trocar os dados da imagem exibida! Então, tudo o que precisamos fazer é:

  • plotar um índice arbitrário e armazená-lo, talvez como um atributo em tempo de execução no objeto Axes.
  • fornecer funções next_slice e previous_slice que alterem o índice e usem set_array para definir o respectivo corte do volume 3D.
  • usar o método draw do canvas da figura para redesenhar a figura com os novos dados.
def multi_slice_viewer(volume):
    fig, ax = plt.subplots()
    ax.volume = volume
    ax.index = volume.shape[0] // 2
    ax.imshow(volume[ax.index])
    fig.canvas.mpl_connect('key_press_event', process_key)

def process_key(event):
    fig = event.canvas.figure
    ax = fig.axes[0]
    if event.key == 'j':
        previous_slice(ax)
    elif event.key == 'k':
        next_slice(ax)
    fig.canvas.draw()

def previous_slice(ax):
    """Go to the previous slice."""
    volume = ax.volume
    ax.index = (ax.index - 1) % volume.shape[0]  # wrap around using %
    ax.images[0].set_array(volume[ax.index])

def next_slice(ax):
    """Go to the next slice."""
    volume = ax.volume
    ax.index = (ax.index + 1) % volume.shape[0]
    ax.images[0].set_array(volume[ax.index])

Vamos testar!

multi_slice_viewer(struct_arr2)

event handler API

Funcionou! Muito bom! Mas, se você testar aí, vai notar que rolar para cima com K também “amassa” a escala horizontal do gráfico. Ué? (Isso só acontece se o mouse estiver sobre a imagem.)

O que rola é que adicionar manipuladores de evento no Matplotlib simplesmente os empilha. Neste caso, K é um atalho nativo para mudar a escala do eixo x para logarítmica. Se quisermos usar K exclusivamente, precisamos removê-la dos mapas de teclas padrão do matplotlib. Eles vivem como listas no dicionário plt.rcParams, o repositório do matplotlib para configurações padrão do sistema:

plt.rcParams['keymap.<command>'] = ['<key1>', '<key2>']

onde pressionar qualquer tecla da lista (isto é, <key1> ou <key2>) faz com que <command> seja executado.

Assim, vamos escrever uma função auxiliar para remover as teclas que quisermos usar, onde quer que apareçam nesse dicionário. (Essa função ainda não existe no matplotlib, mas provavelmente seria uma contribuição bem-vinda!)

def remove_keymap_conflicts(new_keys_set):
    for prop in plt.rcParams:
        if prop.startswith('keymap.'):
            keys = plt.rcParams[prop]
            remove_list = set(keys) & new_keys_set
            for key in remove_list:
                keys.remove(key)

um visualizador de cortes completo

Beleza, vamos reescrever nossa função para usar essa nova ferramenta:

def multi_slice_viewer(volume):
    remove_keymap_conflicts({'j', 'k'})
    fig, ax = plt.subplots()
    ax.volume = volume
    ax.index = volume.shape[0] // 2
    ax.imshow(volume[ax.index])
    fig.canvas.mpl_connect('key_press_event', process_key)

def process_key(event):
    fig = event.canvas.figure
    ax = fig.axes[0]
    if event.key == 'j':
        previous_slice(ax)
    elif event.key == 'k':
        next_slice(ax)
    fig.canvas.draw()

def previous_slice(ax):
    volume = ax.volume
    ax.index = (ax.index - 1) % volume.shape[0]  # wrap around using %
    ax.images[0].set_array(volume[ax.index])

def next_slice(ax):
    volume = ax.volume
    ax.index = (ax.index + 1) % volume.shape[0]
    ax.images[0].set_array(volume[ax.index])

Agora, devemos conseguir ver todos os cortes do nosso volume de MRI sem interferência chata do keymap padrão!

multi_slice_viewer(struct_arr2)

event handler API

Uma vantagem desse método é que ele funciona em qualquer backend do matplotlib! Então, se você testar no console do IPython, ainda terá a mesma interação que no navegador! E o mesmo vale para um app Qt ou Tkinter incorporando um gráfico do matplotlib. Essa ferramenta simples permite construir aplicações cada vez mais robustas em cima dos recursos de visualização do matplotlib.

antes de ir

Não esqueça de fazer a limpeza e apagar o diretório temporário (se você criou um):

import shutil

# Remove the temporary directory
shutil.rmtree(d)

conclusão

Parabéns! Foi uma boa jornada, e você chegou ao fim deste tutorial de Matplotlib!

Fique de olho no segundo post desta série, em que você vai aprender mais sobre redimensionar subplots, miras que mostram onde cada gráfico foi cortado e interatividade com o mouse.

Confira o Matplotlib Tutorial: Python Plotting da DataCamp.

Se quiser saber mais sobre visualização de dados em Python, considere fazer o curso Introduction to Data Visualization in Python da DataCamp.

Tópicos
Visualização de dados
Python

Saiba mais sobre Python

Curso

Introdução à Visualização de Dados com a Matplotlib

4 h
234.8K
Saiba como criar, personalizar e compartilhar visualizações de dados usando o Matplotlib.
Ver detalhesRight Arrow
Iniciar Curso
Ver maisRight Arrow
Relacionado

Tutorial

Introdução à plotagem com Matplotlib em Python

Este tutorial demonstra como usar o Matplotlib, uma poderosa biblioteca de visualização de dados em Python, para criar gráficos de linha, barra e dispersão com dados do mercado de ações.

Kevin Babitz

25 min

Tutorial

Histogramas no Matplotlib

Aprenda sobre histogramas e como você pode usá-los para obter insights dos dados com a ajuda do matplotlib.
Aditya Sharma's photo

Aditya Sharma

8 min

line plot python

Tutorial

Gráficos de linhas no MatplotLib com Python

Este tutorial prático se aprofunda na criação e na personalização de gráficos de linhas com o Matplotlib, uma biblioteca avançada de visualização de dados em Python.
Arunn Thevapalan's photo

Arunn Thevapalan

11 min

Tutorial

Gráfico de linha de série temporal do Matplotlib

Este tutorial explora como criar e personalizar gráficos de linha de séries temporais no matplotlib.
Elena Kosourova's photo

Elena Kosourova

8 min

Tutorial

Guia passo a passo para criar mapas em Python usando a biblioteca Plotly

Faça seus dados se destacarem com mapas impressionantes criados com Plotly em Python
Moez Ali's photo

Moez Ali

7 min

Tutorial

Tipos de gráficos de dados e como criá-los em Python

Explore vários tipos de gráficos de dados, desde os mais comuns até os avançados e não convencionais, o que eles mostram, quando usá-los, quando evitá-los e como criá-los e personalizá-los em Python.
Elena Kosourova's photo

Elena Kosourova

15 min

Ver MaisVer Mais