Ana içeriğe geç

Matplotlib ile 3B Hacimsel Verileri Görüntüleme

Bu Python eğitiminde, bir MRI veri setinin dilimlerini görüntülemek için Matplotlib’in olay işleyici API’sinden yararlanacaksınız.
· 10 dk. oku

Yapay Zeka ile Keşfedin

Birçoğunuz sıradan kameralarla çekilen görüntü verilerine (bilimsel literatürde sıklıkla “doğal görüntüler” olarak adlandırılır) aşinasınız; ayrıca mikroskoplar veya teleskoplar gibi özel cihazlarla elde edilenlere de. Python'da görüntülerle çalışırken, bunları görüntülemenin en yaygın yolu, Python'un en popüler çizim kütüphanesi olan Matplotlib'in imshow işlevini kullanmaktır.

Bu eğitimde, bu işlevi 3B hacimsel verileri gösterecek şekilde nasıl genişleteceğinizi göstereceğiz; bunu, bir görüntü yığını olarak düşünebilirsiniz. Birlikte ele alındıklarında 3B bir yapıyı tanımlarlar. Örneğin manyetik rezonans görüntüleme (MRI) ve bilgisayarlı tomografi (BT) taramaları, insan vücudunun içindeki 3B yapıyı ölçer; X-ışını mikrotomografisi, cam veya metal alaşımlar gibi malzemelerin içindeki 3B yapıyı ölçer; ve ışık-tabakalı mikroskoplar, biyolojik dokular içindeki floresan parçacıkları ölçer.

matplotlib kullanarak bir MRI veri setini nasıl indireceğinizi ve dilimlerini nasıl görüntüleyeceğinizi göstereceğiz. Şunları öğreneceksiniz:

  • NIfTI dosya formatındaki dosyalar için bir okuyucu sağlayan nibabel kütüphanesinin yardımıyla verilerinizi alma. Bu adımı atlayıp doğrudan çizime başlamak isteyenler için, bu eğitim verileri scikit-image ile nasıl alacağınızı da gösterecek.
  • Ardından, matplotlib’in olay işleyici API’siyle tam işlevli bir dilim görüntüleyiciye doğru ilk adımları atacak ve
  • Yeni aracınızdan yararlanmak için bazı kodlarınızı yeniden yazacaksınız.
  • Son olarak, işiniz bittiğinde çalışma alanınızı nasıl temizleyebileceğinizi göreceksiniz.

Ama önce bazı temel bilgilere bakalım: Matplotlib’in imshow işleviyle görüntüler nasıl görüntülenir.

Eğer Jupyter Notebook ile çalışıyorsanız, etkileşimli matplotlib modunu etkinleştirerek başlayabilirsiniz:

%matplotlib notebook

Şimdi matplotlib'i içe aktarabilir ve bazı verileri görüntüleyebilirsiniz. scikit-image kütüphanesinin data modülüne dahil edilen bazı örnek verileri yükleyeceksiniz:

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 eğitimi olay işleyici API'si

Not: Matplotlib’i etkileşimli notebook modunda çalıştırdığınızda, açık şekil (figure) siz onu devre dışı bırakana kadar (şeklin sağ üstündeki güç simgesini kullanarak) etkin kalır. Her grafikten ilerlemeden önce bunu yaptığınızdan emin olun.

Bu görüntüler, 2 boyut boyunca düzenlendikleri için 2 boyutlu ya da 2D görüntüler olarak adlandırılır: x ve y ya da NumPy terminolojisiyle satırlar ve sütunlar veya r ve c.

Bazı görüntüler 3B’dir; çünkü ek bir derinlik boyutuna (z veya düzlemler) sahiptir. Bunlara manyetik rezonans görüntüleme (MRI) ve bir örneğin salam gibi ince ince dilimlendiği ve her dilimin ayrı ayrı görüntülendiği seri bölütleme transmisyon elektron mikroskopisi (ssTEM) dahildir.

Böyle görüntüleri matplotlib’de görmek için bir dilim seçip yalnızca o dilimi görüntülemeliyiz. Bunu çevrimiçi olarak serbestçe erişilebilen bazı MRI verileri üzerinde deneyelim.

Ara: Verileri Alma

Buchel ve Friston’un, Cortical Interactions Evaluated with Structural Equation Modelling and fMRI (1997) çalışmasında tanımlanan bir veri setini indireceğiz. Önce verileri indireceğimiz geçici bir dizin oluşturuyoruz. Analizimiz bittiğinde bunu silmeyi unutmamalıyız! Bu veri setini daha sonra kullanmak üzere saklamak isterseniz, d değerini seçeceğiniz daha kalıcı bir dizin konumuyla değiştirin.

import tempfile

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

Şimdi verileri indirelim:

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'))

Ve bunları zip dosyasından geçici dizinimize çıkaralım:

import zipfile

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

Dosyanın gerçek içeriğine bakarsanız, bir sürü ‘.hdr’ ve ‘.img’ dosyası bulacaksınız.

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

Bunlar NIfTI dosya formatındadır ve bunlar için bir okuyucuya ihtiyacımız olacak. Neyse ki, mükemmel nibabel kütüphanesi böyle bir okuyucu sağlıyor. Mutlaka conda install -c conda-forge nibabel ya da pip install nibabel ile yükleyin ve ardından:

import nibabel

Artık nihayet görüntümüzü okuyabilir ve görüntülemek için bir NumPy dizisi almak üzere .get_data() yöntemini kullanabiliriz:

# 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()

İpucu: Doğrudan MRI verilerini çizmeye devam etmek isterseniz, aşağıdaki kod satırlarını çalıştırın:

from skimage import io

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

Yeniden Çizime Dönelim

Şimdi bu dizideki bir dilime bakalım:

plt.imshow(struct_arr[75])

python matplotlib eğitimi

Vay! Oldukça basık görünüyor! Bunun nedeni, birçok MRI’da düşey eksendeki çözünürlüğün yatay eksenlerdekinden farklı olması. Bunu, imshow işlevine aspect parametresi vererek düzeltebiliriz:

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

olay işleyici API'si

Ama işleri kolaylaştırmak için verileri transpoze edeceğiz ve yalnızca yatay dilimlere bakacağız; böyle ayarlamalara gerek kalmaz.

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

olay işleyici API'si

Güzel! Elbette, başka bir dilimi veya farklı bir eksen boyunca bir dilimi görmek için, imshow’u yeniden çağırmamız gerekir:

plt.imshow(struct_arr2[5])

olay işleyici API'si

Tüm bu çağrılar kısa sürede oldukça yorucu hale geliyor. Uzun süre boyunca, 3B hacimleri Python dışında, ITK-SNAP gibi araçlarla görüntülüyordum. Ancak, meğerse matplotlib görüntüleyiciye 3B “kaydırma” yetenekleri eklemek oldukça kolaymış! Bu, 3B verileri Python içinde keşfetmemizi sağlar ve veri keşfi ile veri analizi arasında bağlam değiştirme ihtiyacını en aza indirir.

Buradaki kilit nokta, belirli tuş vuruşlarına veya fare tıklamalarına yanıt olarak grafikte yapılacak eylemleri — grafiğin verilerini değiştirmek de dahil! — tanımlamamıza olanak tanıyan matplotlib olay işleyici API’sini kullanmaktır.

Bizim durumumuzda, klavyedeki J ve K tuşlarını “önceki dilim” ve “sonraki dilim” olarak bağlayalım:

def previous_slice():
    pass

def next_slice():
    pass

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

Yeterince basit! Elbette, bu eylemleri gerçekte nasıl uygulayacağımızı bulmamız ve şekle, klavye tuşlarına basmaları işlemek için process_key işlevini kullanması gerektiğini söylememiz gerekiyor! İkincisi basit: şekil tuvali üzerinde mpl_connect yöntemini kullanmamız yeterli:

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

mpl_connect için tam belgeleri, hangi diğer tür olaylara bağlanabileceğinizi (ör. fare tıklamaları) da içerecek şekilde, burada bulabilirsiniz.

Biraz inceleme yaptıktan sonra, imshow’un, tüm çizimlerin gerçekleştiği matplotlib Axes nesnesinin “içinde” .images özniteliğinde bulunan bir AxesImage nesnesi döndürdüğünü öğrendim. Ve bu nesne, görüntülenen görüntü verilerini değiştiren kullanışlı bir set_array yöntemi sağlıyor! Öyleyse yapmamız gereken tek şey:

  • rasgele bir indisi çizmek ve bu indisi, belki de çalışma zamanında Axes nesnesine ek bir öznitelik olarak kaydetmek.
  • indisi değiştiren ve 3B hacmin ilgili dilimini ayarlamak için set_array kullanan next_slice ve previous_slice işlevlerini sağlamak.
  • yeni verilerle şekli yeniden çizmek için şekil tuvalinin draw yöntemini kullanmak.
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])

Hadi deneyelim!

multi_slice_viewer(struct_arr2)

olay işleyici API'si

Çalıştı! Harika! Ancak bunu kendi bilgisayarınızda denerseniz, K ile yukarı kaydırmanın aynı zamanda grafiğin yatay ölçeğini de bastırdığını fark edeceksiniz. Hı? (Bu yalnızca fareniz görüntünün üzerindeyse olur.)

Olan şu ki, Matplotlib’e olay işleyicileri eklemek, onları birbirinin üzerine yığar. Bu durumda, K, x eksenini logaritmik ölçek kullanacak şekilde değiştiren yerleşik bir klavye kısayoludur. K tuşunu tamamen bize özel kullanmak istiyorsak, onu matplotlib’in varsayılan tuş eşlemelerinden kaldırmamız gerekir. Bunlar, matplotlib’in sistem genelindeki varsayılan ayarlarının deposu olan plt.rcParams sözlüğünde listeler olarak bulunur:

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

burada listedeki tuşlardan herhangi birine basmak (yani <key1> veya <key2>) <command> komutunun çalışmasına neden olur.

Dolayısıyla, bu sözlükte nerede görünürlerse görünsünler, kullanmak istediğimiz tuşları kaldıracak yardımcı bir işlev yazmamız gerekecek. (matplotlib’de bu işlev henüz yok, ancak muhtemelen memnuniyetle karşılanacak bir katkı olurdu!)

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)

Tam İşlevli Bir Dilim Görüntüleyici

Tamam, bu yeni araçtan yararlanmak için işlevimizi yeniden yazalım:

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])

Artık, varsayılan tuş eşlemelerinden kaynaklanan can sıkıcı müdahaleler olmadan MRI hacmimizdeki tüm dilimleri görüntüleyebilmeliyiz!

multi_slice_viewer(struct_arr2)

olay işleyici API'si

Bu yöntemin güzel bir özelliği, herhangi bir matplotlib arka ucunda çalışmasıdır! Yani bunu IPython terminal konsolunda denerseniz, tarayıcıda olduğu gibi aynı etkileşimi elde edersiniz! Ve matplotlib grafiğini gömen bir Qt veya Tkinter uygulaması için de durum aynıdır. Dolayısıyla bu basit araç, matplotlib’in görselleştirme yetenekleri etrafında giderek daha karmaşık uygulamalar oluşturmanıza olanak tanır.

Gitmeden Önce

Kendimizden sonra temizlik yapmayı ve geçici dizini (oluşturduysanız) silmeyi unutmayalım:

import shutil

# Remove the temporary directory
shutil.rmtree(d)

Sonuç

Tebrikler! Oldukça uzun bir yolculuk oldu, ancak bu Matplotlib eğitiminin sonuna ulaştınız!

Bu serinin ikinci yazısında, alt grafiklerin ölçeklendirilmesi, her grafiğin nereden dilimlendiğini gösteren nişangâhlar ve fare etkileşimi hakkında daha fazlasını öğreneceğiniz için bizi takipte kalın.

DataCamp'in Matplotlib Eğitimi: Python Plotlama sayfasına göz atın.

Python’da veri görselleştirme hakkında daha fazla bilgi edinmek isterseniz, DataCamp’in Introduction to Data Visualization in Python kursunu değerlendirin.

Konular

İlgili