NiBabel

Access a cacophony of neuro-imaging file formats

Table Of Contents

Reggie -- the one

Relationship between images and io implementations

Summary and sign-off

These were some meditations about splitting the image into two API parts.

The first part would be the lower level IO implementation. This part is rather like a fusion of the Header and ArrayProxy objects in current nibabel. It takes care of lower level details like i/o data dtype, shape, offset, and it might help with slicing to get the data. On top of that would be a high level interface implementing load, save, filename, data. The top-level image also had the novel idea of a mode parameter which, if 'r', would raise an error on attempting to save.

Images

An image houses the association of the:

  • data array
  • affine
  • output space
  • metadata
  • mode

These are straightforward attributes, and have no necessary relationship to stuff on disk.

By ‘’disk’‘, we mean, file-like objects - not necessarily on disk.

The io implementation manages the relationship of images and stuff on disk.

Specifically, it manages load of images from disk, and save of images to disk.

The user does not see the io implementation unless they ask to. In standard use of images they will not need to do this.

IO implementations

By use case.

Creating array image, saving

>>> import tempfile
>>> from nibabel.images import Image
>>> from nibabel import load, save
>>> fp, fname = tempfile.mkstemp('.nii')
>>> data = np.arange(24).reshape((2,3,4))
>>> img = Image(data)
>>> img.filename is None
True
>>> img.save()
Traceback (most recent call last):
   ...
ImageError: no filespec to save to
>>> save(img)
Traceback (most recent call last):
   ...
ImageError: no filespec to save to
>>> img2 = save(img, 'some_image.nii') # type guessed from filename
>>> img2.filename == fname
True
>>> img.filename is None # still
True
>>> img.filename = 'some_filename.nii' # read only property
Traceback (most recent call last):
   ...
AttributeError: can't set attribute

Load, futz, save

>>> img3 = load(fname, mode='r')
>>> img3.filename == fname
True
>>> np.all(img3.data == data)
True
>>> img3.data[0,0] = 99
>>> img3.save()
Traceback (most recent call last):
   ...
ImageError: trying to write to read only image
>>> img3.mode = 'rw'
>>> img3.save()
>>> load(img4)
>>> img4.mode # 'r' is the default
'r'
>>> mod_data = data.copy()
>>> mod_data[0,0] = 99
>>> np.all(img4.data = mod_data)
True

Prepare image for later writing

>>> img5 = Image(np.zeros(2,3,4))
>>> fp, fname2 = tempfile.mkstemp('.nii')
>>> img5.set_filespec(fname2)
>>> # then do some things to the image
>>> img5.save()

This is an example where you do need the io API

>>> from nibabel.ioimps import guessed_imp
>>> fp, fname3 = tempfile.mkstemp('.nii')
>>> ioimp = guessed_imp(fname3)
>>> ioimp.set_data_dtype(np.float)
>>> ioimp.set_data_shape((2,3,4)) # set_data_shape method
>>> slice_def = (slice(None), slice(None), 0)
>>> ioimp.write_slice(data[slice_def], slice_def) # write_slice method
>>> slice_def = (2, 3, 1)
>>> ioimp.write_slice(data[slice_def], slice_def) # write_slice method
Traceback (most recent call last):
   ...
ImageIOError: data write is not contiguous