What is an image?

Download notebook Interact

In this page we explore the nature of NIfTI images.

Before you start, please follow the instructions on installing nibabel

First we load an example image to see if we can understand the image data.

Download the ds114 structural image.

# This is a Python module
import os  # module for interacting with the operating system

If you want to explore modules or objects, type their name followed by a period, and press tab to see what functions or classes are available.

The image we are going to explore should be the same directory as this exercise.

# Get the current working directory (CWD)
cwd = os.getcwd()
print(cwd)
/Volumes/dsfe/fbi2018/notebooks/02

# List files and directories in the current working directory
print(os.listdir(cwd))
['voxel_time_courses.Rmd', 'fmri_data.md', 'images_4d.ipynb', 'voxel_time_courses.ipynb', 'ds114_sub009_t2r1.nii', 'arrays_and_images.ipynb', 'ds114_sub009_t2r1_cond.txt', 'images_3d.Rmd', 'images_4d.Rmd', 'images_3d.ipynb', 'arrays_and_images.Rmd', 'what_is_an_image.Rmd', 'what_is_an_image.ipynb', 'ds114_sub009_highres.nii']

Read the image into memory.

# Open a file in Read Binary mode
fobj = open('ds114_sub009_highres.nii', 'rb')
print(fobj)
<_io.BufferedReader name='ds114_sub009_highres.nii'>

# read all the characters into a variable in memory
contents = fobj.read()

Show the type of object is attached to this variable called contents:

type(contents)
bytes

How big is this file in terms of bytes?

n_bytes = len(contents)
n_bytes
40894816

If 1 mebibyte (MiB) (http://en.wikipedia.org/wiki/Megabyte) is size 1024 * 1024, what is the file size in MiB?

n_mib = n_bytes / (1024 * 1024)
n_mib
39.000335693359375

This is a NIfTI1 format file. That means that the first 352 bytes contains the “header” that describes the parameters of the image and the data following.

We want to print out the contents of the first 352 bytes of contents to have a look at it.

To do this, we are going to need string slicing to get the first 352 bytes:

print(contents[:352])
b'\\\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00r\x00\x03\x00\x00\x01\x9c\x00\x00\x01\x01\x00\x01\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00 \x00\x00\x00\x00\x00\x80?\x00\x00\x80?\xafm\xa6?\x00\x00\x80?\xa3@\x1f<\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0C\x00\x00\x80?\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00FSL5.0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01\x00}\x96\xf0\xbd:w\x05<X+\xb8<c\xd3\x01\xc3_.\xee\xc2\xf3j\x0f\xc3\x0f\xb5\x7f?\xe8\xecw\xbd\xc5\x812<c\xd3\x01\xc3\xbe\x00/=\xad\xaa\xa1?T:o>_.\xee\xc2\xbd\xc0\xaf\xbcu\t\x9b\xbey\xe6x?\xf3j\x0f\xc3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00n+1\x00\x00\x00\x00\x00'

Which software wrote this image?

Here is the format of the NIfTI1 header : http://nifti.nimh.nih.gov/nifti-1/documentation/nifti1fields

We are now going to try and work out the datatype of this image. This is stored in the datatype field of the header. Careful - there is also a data_type field (with an underscore), which we will ignore.

Looking at the web page above, how many bytes is the datatype value stored in?

How would you get the bytes in contents that contain the datatype value? We need slicing again, and the information from Byte offset column in the NIfTI1 header page above:

data_type_chars = contents[70:72]
print(data_type_chars)
b'\x10\x00'

The datatype value is stored in binary form (rather than text form). The value for datatype is stored in the header in the same format that the computer stores the number in memory. We want to convert this binary format to a number that Python understands. To do that, we use the struct module.

import struct

We need to tell the struct.unpack function what format the data is in. If you are interested, have a look at the help on format strings in the Python documentation and the NIfTI web page above.

Here is the format specifier for our value, that tells Python about the binary format of the data.

fmt_specifier = 'h'  # Why? (check the web pages above)

Now we read the datatype value into a number that Python understands:

datatype = struct.unpack(fmt_specifier, data_type_chars)
print(datatype)
(16,)

This is a numerical code for a data type. To see what type this is, see: http://nifti.nimh.nih.gov/nifti-1/documentation/nifti1fields/nifti1fields\_pages/datatype.html

We could continue reading the NIfTI header in the same way, but luckily someone has done that work for us. Enter the nibabel package:

import nibabel

For now, we will use this package without worrying much about how it works. Have a look to see what nibabel can do by opening up a new cell with b and typing nibabel? and nibabel. followed by Tab.

As with most Python packages, you can check what version of nibabel you have by printing the __version__ variable of the package:

print(nibabel.__version__)
2.3.1

If you have a nibabel version below 2.0.0, please let your instructor know so they can fix that.

You can load an image into memory like this:

img = nibabel.load('ds114_sub009_highres.nii')

Let’s have a look at the header:

print(img.header)  # doctest: +SKIP
<class 'nibabel.nifti1.Nifti1Header'> object, endian='<'
sizeof_hdr      : 348
data_type       : b''
db_name         : b''
extents         : 0
session_error   : 0
regular         : b'r'
dim_info        : 0
dim             : [  3 256 156 256   1   1   1   1]
intent_p1       : 0.0
intent_p2       : 0.0
intent_p3       : 0.0
intent_code     : none
datatype        : float32
bitpix          : 32
slice_start     : 0
pixdim          : [1.        1.        1.3002223 1.        0.00972   0.        0.
 0.       ]
vox_offset      : 0.0
scl_slope       : nan
scl_inter       : nan
slice_end       : 0
slice_code      : unknown
xyzt_units      : 10
cal_max         : 0.0
cal_min         : 0.0
slice_duration  : 0.0
toffset         : 0.0
glmax           : 0
glmin           : 0
descrip         : b'FSL5.0'
aux_file        : b''
qform_code      : scanner
sform_code      : scanner
quatern_b       : -0.11747453
quatern_c       : 0.008146102
quatern_d       : 0.022481605
qoffset_x       : -129.82573
qoffset_y       : -119.09057
qoffset_z       : -143.41777
srow_x          : [ 9.9885648e-01 -6.0528666e-02  1.0895197e-02 -1.2982573e+02]
srow_y          : [ 4.2725317e-02  1.2630211e+00  2.3362094e-01 -1.1909057e+02]
srow_z          : [-2.1454209e-02 -3.0280653e-01  9.7226673e-01 -1.4341777e+02]
intent_name     : b''
magic           : b'n+1'

As you can see, nibabel has worked out the datatype for us.