1.. -*- mode: rst -*-
2
3##################################################
4Relationship between images and io implementations
5##################################################
6
7********************
8Summary and sign-off
9********************
10
11These were some meditations about splitting the image into two API parts.
12
13The first part would be the lower level IO implementation.  This part is
14rather like a fusion of the :class:`.Header` and :class:`.ArrayProxy` objects
15in current nibabel.  It takes care of lower level details like i/o data dtype,
16shape, offset, and it might help with slicing to get the data.  On top of that
17would be a high level interface implementing ``load``, ``save``, ``filename``,
18``data``.  The top-level image also had the novel idea of a ``mode`` parameter
19which, if ``'r'``, would raise an error on attempting to ``save``.
20
21******
22Images
23******
24
25An image houses the association of the:
26
27* data array
28* affine
29* output space
30* metadata
31* mode
32
33These are straightforward attributes, and have no necessary relationship
34to stuff on disk.
35
36By ''disk'', we mean, file-like objects - not necessarily on disk.
37
38The *io implementation* manages the relationship of images and stuff on
39disk.
40
41Specifically, it manages ``load`` of images from disk, and ``save`` of
42images to disk.
43
44The user does not see the io implementation unless they ask to.  In
45standard use of images they will not need to do this.
46
47******************
48IO implementations
49******************
50
51By use case.
52
53::
54
55    Creating array image, saving
56
57    >>> import tempfile
58    >>> from nibabel.images import Image
59    >>> from nibabel import load, save
60    >>> fp, fname = tempfile.mkstemp('.nii')
61    >>> data = np.arange(24).reshape((2,3,4))
62    >>> img = Image(data)
63    >>> img.filename is None
64    True
65    >>> img.save()
66    Traceback (most recent call last):
67       ...
68    ImageError: no filespec to save to
69    >>> save(img)
70    Traceback (most recent call last):
71       ...
72    ImageError: no filespec to save to
73    >>> img2 = save(img, 'some_image.nii') # type guessed from filename
74    >>> img2.filename == fname
75    True
76    >>> img.filename is None # still
77    True
78    >>> img.filename = 'some_filename.nii' # read only property
79    Traceback (most recent call last):
80       ...
81    AttributeError: can't set attribute
82
83    Load, futz, save
84
85    >>> img3 = load(fname, mode='r')
86    >>> img3.filename == fname
87    True
88    >>> np.all(img3.data == data)
89    True
90    >>> img3.data[0,0] = 99
91    >>> img3.save()
92    Traceback (most recent call last):
93       ...
94    ImageError: trying to write to read only image
95    >>> img3.mode = 'rw'
96    >>> img3.save()
97    >>> load(img4)
98    >>> img4.mode # 'r' is the default
99    'r'
100    >>> mod_data = data.copy()
101    >>> mod_data[0,0] = 99
102    >>> np.all(img4.data = mod_data)
103    True
104
105    Prepare image for later writing
106
107    >>> img5 = Image(np.zeros(2,3,4))
108    >>> fp, fname2 = tempfile.mkstemp('.nii')
109    >>> img5.set_filespec(fname2)
110    >>> # then do some things to the image
111    >>> img5.save()
112
113    This is an example where you do need the io API
114
115    >>> from nibabel.ioimps import guessed_imp
116    >>> fp, fname3 = tempfile.mkstemp('.nii')
117    >>> ioimp = guessed_imp(fname3)
118    >>> ioimp.set_data_dtype(np.float64)
119    >>> ioimp.set_data_shape((2,3,4)) # set_data_shape method
120    >>> slice_def = (slice(None), slice(None), 0)
121    >>> ioimp.write_slice(data[slice_def], slice_def) # write_slice method
122    >>> slice_def = (2, 3, 1)
123    >>> ioimp.write_slice(data[slice_def], slice_def) # write_slice method
124    Traceback (most recent call last):
125       ...
126    ImageIOError: data write is not contiguous
127