# NiBabel

### Access a cacophony of neuro-imaging file formats

# 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.float64)
    >>> 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