Code
from pyhere import here
import nibabel as nib
import numpy as np
Prompt: Explain the data structure of NIfTI file.
The NIfTI file format (Neuroimaging Informatics Technology Initiative) is primarily used for storing brain imaging data, such as MRI and fMRI scans. It is an evolution of the older Analyze 7.5 format, and it addresses many of its limitations by adding more flexibility and metadata related to spatial orientation and timing.
The NIfTI file format can store 3D or 4D volumes, typically representing neuroimaging datasets. It contains both image data and metadata in a well-defined structure.
A NIfTI file consists of two primary components:
.nii
): Both the header and image data are stored in a single file..hdr/.img
): The header and image data are stored in separate files (.hdr
for the header and .img
for the image data).The NIfTI header is a binary structure containing metadata that describes the dimensions and other properties of the image. It is 348 bytes long in the standard NIfTI format and stores the following key fields:
Field | Description |
---|---|
sizeof_hdr | The size of the header, typically 348 bytes. |
datatype | Specifies the data type (e.g., unsigned char, 16-bit integer, float). This defines how the voxel data is stored in the image section. |
dim[] | Array specifying the dimensions of the image. dim[0] is the number of dimensions (usually 3 or 4 for fMRI). dim[1] , dim[2] , dim[3] represent the size of the image in the x, y, and z directions, respectively. |
pixdim[] | Array specifying the pixel size or voxel dimensions in each direction. For example, pixdim[1] gives the voxel width in the x direction. |
vox_offset | Offset in bytes from the start of the file to the image data. Usually set to 352 bytes to account for the header length. |
scl_slope | Scale factor to apply to voxel values (used for scaling image intensity). |
scl_inter | Intercept to apply after scaling voxel values. |
qform_code | Code specifying whether the orientation information is present and what form it takes (affine transformations). |
qoffset_x, qoffset_y, qoffset_z | These fields specify the x, y, and z offsets for the spatial location of the image (important for spatial alignment). |
sform_code | Another code that specifies a different affine transformation if needed (more detailed spatial information). |
srow_x, srow_y, srow_z | These are 4x4 affine transformation matrix rows that provide detailed orientation and spatial information for the image. |
intent_code | Code specifying the purpose of the image (e.g., statistical parameter maps, Z-scores, etc.). |
descrip | Free text field describing the image (optional). |
sizeof_hdr : 348
dim[] : [4, 64, 64, 30, 150] # 4D dataset: 64x64x30 voxels, 150 time points
pixdim[] : [1, 2.0, 2.0, 3.0, 2.0] # Voxel sizes in mm (x=2, y=2, z=3) and temporal resolution
datatype : 16 # 16 corresponds to float32 data type
qform_code : 1 # Quaternion-based rotation
sform_code : 1 # Affine transform matrix present
qoffset_x : -90.0
qoffset_y : -126.0
qoffset_z : -72.0
descrip : "fMRI scan"
The image data in a NIfTI file immediately follows the header (or resides in a separate .img
file in the two-file format). It contains the voxel intensity values of the 3D or 4D image. The format and data type of the image data are specified by the datatype
field in the header.
dim[] array in the header defines the number of dimensions and the size of each dimension
datatype field specifies the type of data stored for each voxel.
Voxel Data: These are the raw intensity values stored in an array. If the image is 3D, this array will be 3-dimensional, and if the image is 4D (like in fMRI, where you have multiple time points), the array will be 4-dimensional.
For a 3D MRI scan of dimensions 64x64x30, the image data would contain 64x64x30 voxel intensity values. If the datatype
is 16 (float32), each voxel would be a 32-bit floating-point number.
In a 4D fMRI dataset with 150 time points, the image data would contain a 64x64x30x150 array of voxel values.
NIfTI uses affine transformation matrices to handle spatial orientation and alignment of images. The header contains two codes (qform_code
and sform_code
) that specify the type of transformation used.
The affine matrix transforms voxel indices into real-world spatial coordinates (e.g., mm in the scanner space).
| srow_x[0] srow_x[1] srow_x[2] qoffset_x |
| srow_y[0] srow_y[1] srow_y[2] qoffset_y |
| srow_z[0] srow_z[1] srow_z[2] qoffset_z |
| 0 0 0 1 |
This matrix describes how voxel indices map to real-world spatial coordinates, which is important for proper alignment and visualization of images.
For a .nii file (single-file format), you would have:
For a .hdr/.img file pair (two-file format):
Prompt: Give example of NIfTI file represented in Python data structure.
Here’s an example of how a NIfTI file’s structure can be represented in Python using a combination of dictionaries and NumPy arrays. The header information is typically stored as a dictionary-like structure, while the image data is stored as a NumPy array.
First, let’s assume you’ve loaded a NIfTI file using the popular Nibabel library, which is commonly used in Python for working with NIfTI files.
from pyhere import here
import nibabel as nib
import numpy as np
# Load the NIfTI file
= nib.load(here("data/nifti/avg152T1_LR_nifti.nii"))
nifti_image print(nifti_image.shape)
(91, 109, 91)
# Extract header and image data
= nifti_image.header
header
# This returns the image data as a NumPy array
= nifti_image.get_fdata() image_data
The header can be thought of as a dictionary-like object with key-value pairs that describe the image’s metadata:
# Example of NIfTI header represented as a dictionary-like object in Python
= {
nifti_header 'sizeof_hdr': header['sizeof_hdr'], # Size of the header
'datatype': header['datatype'], # Data type of the voxel values
'dim': header['dim'], # Dimensions of the image
'pixdim': header['pixdim'], # Voxel dimensions
'qform_code': header['qform_code'], # Code describing the q-form transformation
'sform_code': header['sform_code'], # Code describing the s-form transformation
'srow_x': header['srow_x'], # Row for the x affine transformation
'srow_y': header['srow_y'], # Row for the y affine transformation
'srow_z': header['srow_z'], # Row for the z affine transformation
'vox_offset': header['vox_offset'], # Offset where the voxel data starts
'scl_slope': header['scl_slope'], # Slope to scale the voxel values
'scl_inter': header['scl_inter'], # Intercept to scale the voxel values
'descrip': header['descrip'], # Description of the image
'xyzt_units': header['xyzt_units'], # Units of measurement for space and time
}
= {
nifti_header 'sizeof_hdr': 348,
'datatype': 16, # float32 data type
'dim': [3, 64, 64, 30, 1], # 3D image: 64x64x30 voxels
'pixdim': [1.0, 2.0, 2.0, 3.0, 1.0], # Voxel dimensions (x=2mm, y=2mm, z=3mm)
'qform_code': 1, # Quaternion-based transformation
'sform_code': 1, # Affine transformation matrix present
'srow_x': [-2.0, 0.0, 0.0, 90.0], # Affine matrix first row
'srow_y': [0.0, 2.0, 0.0, -126.0], # Affine matrix second row
'srow_z': [0.0, 0.0, 3.0, -72.0], # Affine matrix third row
'vox_offset': 352, # Offset where voxel data starts
'scl_slope': 1.0, # Scaling slope for voxel values
'scl_inter': 0.0, # Scaling intercept for voxel values
'descrip': "Sample MRI scan", # Descriptive text
'xyzt_units': 10 # Millimeters for spatial units
}
The image data is typically a multidimensional NumPy array that contains the voxel intensity values. In a 3D MRI scan, for example, this could be a 3D array where each element represents the intensity of a voxel.
# Example: 64x64x30 3D image data
= np.random.rand(64, 64, 30) # Random data to represent voxel intensities
image_data
# Alternatively, you can extract the actual data from the NIfTI object
= nifti_image.get_fdata() image_data
The affine matrix is a 4x4 transformation matrix stored in the header. This matrix helps map voxel indices to real-world coordinates (such as millimeters in the scanner space).
# Extract the affine transformation matrix
= nifti_image.affine affine_matrix
= np.array([
affine_matrix -2.0, 0.0, 0.0, 90.0],
[0.0, 2.0, 0.0, -126.0],
[ 0.0, 0.0, 3.0, -72.0],
[ 0.0, 0.0, 0.0, 1.0]
[ ])
This matrix helps map the voxel coordinates in the NIfTI image to real-world coordinates in the scanner’s space.
# Header as a Python dictionary
= {
nifti_header 'sizeof_hdr': 348,
'datatype': 16,
'dim': [3, 64, 64, 30, 1],
'pixdim': [1.0, 2.0, 2.0, 3.0, 1.0],
'qform_code': 1,
'sform_code': 1,
'srow_x': [-2.0, 0.0, 0.0, 90.0],
'srow_y': [0.0, 2.0, 0.0, -126.0],
'srow_z': [0.0, 0.0, 3.0, -72.0],
'vox_offset': 352,
'scl_slope': 1.0,
'scl_inter': 0.0,
'descrip': "Sample MRI scan",
'xyzt_units': 10
}
# Image data as a NumPy array (64x64x30)
= np.random.rand(64, 64, 30)
image_data
# Affine transformation matrix (4x4)
= np.array([
affine_matrix -2.0, 0.0, 0.0, 90.0],
[0.0, 2.0, 0.0, -126.0],
[ 0.0, 0.0, 3.0, -72.0],
[ 0.0, 0.0, 0.0, 1.0]
[ ])
This structure provides a clear separation of metadata and actual image data, making it easier to handle in research and clinical settings.