๐ŸฉปfMRI

1. Python for (f)MRI analysis

date
Apr 14, 2023
slug
fmri-glm-1
author
status
Public
tags
NI-edu
Dartbrain
summary
Briefly learn about the nifti files
type
Post
thumbnail
category
๐ŸฉปfMRI
updatedAt
Apr 18, 2023 05:40 AM

1.1 What is โ€œniftiโ€

  • nifti ํŒŒ์ผ(.nii or .nii.gz)์€ ๋‰ด๋กœ์ด๋ฏธ์ง• ํŒจํ‚ค์ง€์˜ ์ƒˆ๋กœ์šด ํ‘œ์ค€ ๋ฒ„์ „์ž„
  • fMRI ๋ฐ์ดํ„ฐ ์ „์ฒ˜๋ฆฌ์˜ ์ฒซ๋ฒˆ์งธ ๋‹จ๊ณ„
    • ์Šค์บ๋„ˆ ์ „์šฉ ํŒŒ์ผ(DCM/PAR/REC)๋ฅผ nifti ํŒŒ์ผ๋กœ ๋ณ€ํ™˜(dcm2niix ํŒจํ‚ค์ง€ ํ™œ์šฉ)
  • + afni_proc.py์—์„œ ํŽธํ•˜๊ฒŒ preprocessing์„ ํ•  ์ˆ˜ ์žˆ์Œ
  • ์•„๋ž˜์˜ ์˜ˆ์‹œ๋Š” .nii files๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ฝ”๋“œ์ž„
import nibabel as nib

mri_file = 'anat.nii.gz'
img = nib.load(mri_file)

print(type(img))
>>> <class 'nibabel.nifti1.Nifti1Image'>

print(img.shape)
>>> (240, 240, 220) # x, y ์ถ•์— 240๊ฐœ, z ์ถ•์— 220๊ฐœ์˜ voxel ๋ฐ์ดํ„ฐ ์กด์žฌ

1.2 The three parts of nifti images

1.2.1 The header with metadata

  • ํ—ค๋”๋Š” ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Œ
    • ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ: ๋ฐ์ดํ„ฐ์— ๊ด€ํ•œ ๊ตฌ์กฐํ™”๋œ ๋ฐ์ดํ„ฐ(ex. ์‹คํ—˜ ๋‹จ์œ„, ๋ณต์…€ ์‚ฌ์ด์ฆˆ ๋“ฑ)
# ์œ„์—์„œ ์ด๋ฏธ์ง€๋กœ ๋ณ€ํ™˜ํ•œ ๋ฐ์ดํ„ฐ์˜ ํ—ค๋”๋ฅผ ๋ถˆ๋Ÿฌ์˜ด
hdr = img.header

hdr.get_zooms()
>>> (1.0, 1.0, 1.0) # 1x1x1mm MRI ํŒŒ์ผ์ž„

# get_xyzt_units(): x, y, z(spatial), t(time) ์ฐจ์› ๋‹จ์œ„ ์ •๋ณด
hdr.get_xyzt_units()
>>> ('mm', 'sec')

1.2.2 The image data

  • MRI ์Šค์บ” ํŒŒ์ผ(์‚ฌ์ง„, ์˜์ƒ)์€ 3D/4D-array๋กœ ์ €์žฅ๋˜์–ด ์žˆ์Œ
    • ์‹ ํ˜ธ์˜ ์ ˆ๋Œ€์ˆ˜์น˜๋ณด๋‹ค ์ฃผ๋ณ€๊ณผ์˜ ์ƒ๋Œ€์  ๋น„๊ต ์ˆ˜์น˜๊ฐ€ ๋” ์ค‘์š”ํ•จ
img_data = img.get_fdata()

print(type(img_data))
>>> <class 'numpy.ndarray'> # ์ด๋ฏธ์ง€ ๋ฐ์ดํ„ฐ๋Š” numpy array๋กœ ๊ตฌ์„ฑ๋จ

print(img_data.shape)
>>> (240, 240, 220)
  • ์˜ˆ์‹œ
# ํŠน์ • ์ถ•์„ ๊ธฐ์ค€์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์Šฌ๋ผ์ด์‹ฑ
mid_slice_x = img_data[119, :, :] # ์ฒซ๋ฒˆ์งธ ๋ณต์…€ ์ถ•(x)์˜ 120๋ฒˆ์งธ ๋ฐ์ดํ„ฐ
plt.imshow(mid_slice_x.T, cmap='gray', origin='lower') # Transposeํ•ด์„œ ์‹œ๊ฐํ™”ํ•˜๊ธฐ

# ํŠน์ • ์˜์—ญ์„ ๊ธฐ์ค€์œผ๋กœ ๋ชจ๋“  ์‹œ๊ฐ„์„ ์Šฌ๋ผ์ด์‹ฑ
mid_vox_ts = f_img_data[39, 39, 21, :]  # (x, y, z)=(40, 40, 22)๋ฒˆ์งธ ๋ณต์…€์˜ ๋ชจ๋“  ์‹œ๊ฐ„์—์„œ์˜ ๋ฐ์ดํ„ฐ
plt.plot(mid_vox_ts, 'o-', ms=4) # ํŠน์ • ์˜์—ญ์„ ๊ธฐ์ค€์œผ๋กœ ์‹œ๊ฐ„ ์ˆœ์œผ๋กœ์˜ ์‹ ํ˜ธ ๊ฐ•๋„
notion image
ย 
ย 
notion image
ย 

1.2.3 The affine

fig, ax = plt.subplots(ncols=3, figsize=(15, 5))

ax[0].imshow(img_data[69, :, :].T, origin='lower', cmap='gray')
ax[1].imshow(img_data[:, 99, :].T, origin='lower', cmap='gray')
ax[2].imshow(img_data[:, :, 99].T, origin='lower', cmap='gray')

fig.tight_layout()
notion image
  • ์œ„์˜ ์ฒซ ๋ฒˆ์งธ ์‚ฌ์ง„์„ ๋ถ„์„ํ•  ๋•Œ, ๋‡Œ์˜ ์™ผ์ชฝ์„ ๋ณด๊ณ  ์žˆ๋Š”์ง€ ์˜ค๋ฅธ์ชฝ์„ ๋ณด๊ณ  ์žˆ๋Š”์ง€ ์„ค๋ช…ํ•  ์ˆ˜ ์—†์Œ
  • ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ affine matrix๋ฅผ ํ™œ์šฉํ•จ
  • ์—ฌ๊ธฐ์„œ ๊ฐ์ž์˜ ์ขŒํ‘œ๋Š”(x, y, z) ๊ฐ์ž์˜ ์ฐจ์›์—์„œ ๋ช‡๋ฒˆ์งธ ์ฐจ๋ก€(์ธ๋ฑ์Šค, i, j, k)์ž„
ย 
  • ์ฐธ๊ณ ๋กœ image data๋Š” ์ฒซ ๋ฒˆ์งธ ์ถ•์€ left to right, ๋‘ ๋ฒˆ์งธ ์ถ•์€ posterior to anterior, ์„ธ ๋ฒˆ์งธ ์ถ•์€ inferior to superior์œผ๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ์ˆœ์„œ๋Œ€๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์Œ
  • affine matrix๋ฅผ ํ†ตํ•ด ์ •๊ฐ€์šด๋ฐ(isocenter)๋ฅผ ์ค‘์‹ฌ์œผ๋กœ RAS+(Right, Anterior, Superior)์˜ ๋ถ€ํ˜ธ๋กœ ์œ„์น˜๋ฅผ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์Œ
np.set_printoptions(suppress=True, precision=3) # 3์งธ์ž๋ฆฌ์—์„œ ๋ฐ˜์˜ฌ๋ฆผ
A = img.affine

print(A)
>>>
[[  -0.999   -0.035   -0.006  122.245]
 [  -0.035    0.997    0.066 -117.457]
 [  -0.004   -0.066    0.998  -51.112]
 [   0.       0.       0.       1.   ]]

xyz1 = A @ np.array([69, 119, 109, 1])
print(xyz1)
>>> [48.472  5.972 49.516  1.   ]
  • x์˜ ์ขŒํ‘œ๋Š” 48.472๋กœ, ์‹ค์ œ๋กœ ์ค‘์‹ฌ์œผ๋กœ๋ถ€ํ„ฐ ์˜ค๋ฅธ์ชฝ์œผ๋กœ 48.472mm ๋งŒํผ ๊ฑฐ๋ฆฌ๊ฐ€ ๋–จ์–ด์ ธ ์žˆ์Œ