|
2 | 2 | The nibabel image object
|
3 | 3 | ########################
|
4 | 4 |
|
5 |
| -A nibabel image object is the association of three things: |
6 |
| - |
7 |
| -* an N-D array containing the image *data* |
8 |
| -* a (4, 4) *affine* matrix mapping array coordinates to coordinates in some real |
9 |
| - world |
10 |
| -* image metadata in the form of a *header* |
11 |
| - |
12 |
| -***************** |
13 |
| -Header and affine |
14 |
| -***************** |
15 |
| - |
16 |
| -There's an example image contained in the nibabel distribution |
17 |
| - |
18 |
| ->>> import os |
19 |
| ->>> import numpy as np |
20 |
| ->>> np.set_printoptions(precision=2, suppress=True) |
21 |
| - |
22 |
| ->>> import nibabel as nib |
23 |
| ->>> from nibabel.testing import data_path |
24 |
| ->>> example_file = os.path.join(data_path, 'example4d.nii.gz') |
25 |
| ->>> img = nib.load(example_file) |
26 |
| - |
27 |
| -You can get direct access to the *affine* and the *header* with: |
28 |
| - |
29 |
| ->>> print(img.affine) |
30 |
| -[[ -2. 0. 0. 117.86] |
31 |
| - [ -0. 1.97 -0.36 -35.72] |
32 |
| - [ 0. 0.32 2.17 -7.25] |
33 |
| - [ 0. 0. 0. 1. ]] |
34 |
| ->>> print(img.header) |
35 |
| -<class 'nibabel.nifti1.Nifti1Header'> object, endian='<' |
36 |
| -sizeof_hdr : 348 |
37 |
| -data_type : b'' |
38 |
| -db_name : b'' |
39 |
| -extents : 0 |
40 |
| -session_error : 0 |
41 |
| -regular : b'r' |
42 |
| -dim_info : 57 |
43 |
| -dim : [ 4 128 96 24 2 1 1 1] |
44 |
| -intent_p1 : 0.0 |
45 |
| -intent_p2 : 0.0 |
46 |
| -intent_p3 : 0.0 |
47 |
| -intent_code : none |
48 |
| -datatype : int16 |
49 |
| -bitpix : 16 |
50 |
| -slice_start : 0 |
51 |
| -pixdim : [ -1. 2. 2. 2.2 2000. 1. 1. 1. ] |
52 |
| -vox_offset : 416.0 |
53 |
| -scl_slope : 1.0 |
54 |
| -scl_inter : 0.0 |
55 |
| -slice_end : 23 |
56 |
| -slice_code : unknown |
57 |
| -xyzt_units : 10 |
58 |
| -cal_max : 1162.0 |
59 |
| -cal_min : 0.0 |
60 |
| -slice_duration : 0.0 |
61 |
| -toffset : 0.0 |
62 |
| -glmax : 0 |
63 |
| -glmin : 0 |
64 |
| -descrip : b'FSL3.3\x00 v2.25 NIfTI-1 Single file format' |
65 |
| -aux_file : b'' |
66 |
| -qform_code : scanner |
67 |
| -sform_code : scanner |
68 |
| -quatern_b : -1.9451068140294884e-26 |
69 |
| -quatern_c : -0.9967085123062134 |
70 |
| -quatern_d : -0.0810687392950058 |
71 |
| -qoffset_x : 117.8551025390625 |
72 |
| -qoffset_y : -35.72294235229492 |
73 |
| -qoffset_z : -7.248798370361328 |
74 |
| -srow_x : [ -2. 0. 0. 117.86] |
75 |
| -srow_y : [ -0. 1.97 -0.36 -35.72] |
76 |
| -srow_z : [ 0. 0.32 2.17 -7.25] |
77 |
| -intent_name : b'' |
78 |
| -magic : b'n+1' |
79 |
| - |
80 |
| -**************** |
81 |
| -Reading the data |
82 |
| -**************** |
83 |
| - |
84 |
| -We defend the data from you a little, because we want to be able to load images |
85 |
| -from disk without automatically loading all the data. Images loaded like this |
86 |
| -are called *proxy images* because the data in the image is not yet an array, but |
87 |
| -an placeholder or *proxy* for the array. You can get the object holding the |
88 |
| -image data with: |
89 |
| - |
90 |
| ->>> dataobj = img.dataobj |
91 |
| - |
92 |
| -Because this image has been loaded from disk, ``dataobj`` is not the array |
93 |
| -itself, but a *proxy* for the array that lets you get to the array data with: |
94 |
| - |
95 |
| ->>> data = np.asarray(dataobj) |
96 |
| ->>> data.shape |
97 |
| -(128, 96, 24, 2) |
98 |
| - |
99 |
| -If you created the image from an array in memory, ``dataobj`` will be the |
100 |
| -data array: |
101 |
| - |
102 |
| ->>> arr = np.arange(24, dtype=np.int16).reshape((2, 3, 4)) |
103 |
| ->>> arr_img = nib.Nifti1Image(arr, np.eye(4)) |
104 |
| ->>> arr_img.dataobj is arr |
105 |
| -True |
106 |
| - |
107 |
| -For either type of image (array or proxy) you can always get the data with: |
108 |
| - |
109 |
| ->>> data = img.get_data() |
110 |
| ->>> data.shape |
111 |
| -(128, 96, 24, 2) |
112 |
| ->>> data = arr_img.get_data() |
113 |
| ->>> data.shape |
114 |
| -(2, 3, 4) |
115 |
| - |
116 |
| -If you created ``img`` using an array in memory, ``img.get_data()`` just returns |
117 |
| -the data array: |
118 |
| - |
119 |
| ->>> data = arr_img.get_data() |
120 |
| ->>> data is arr_img.dataobj |
121 |
| -True |
122 |
| - |
123 |
| -If you loaded ``img`` from disk, and have a proxy image, then the the |
124 |
| -``get_data()`` method gets the data from the proxy, and returns the array. |
125 |
| - |
126 |
| ->>> proxy_img = nib.load(example_file) |
127 |
| ->>> type(proxy_img.dataobj) |
128 |
| -<class 'nibabel.arrayproxy.ArrayProxy'> |
129 |
| ->>> data = proxy_img.get_data() |
130 |
| ->>> data.shape |
131 |
| -(128, 96, 24, 2) |
132 |
| ->>> data is proxy_img.dataobj |
133 |
| -False |
134 |
| - |
135 |
| -After a call to ``get_data()``, the proxy image keeps a cached copy of the |
136 |
| -loaded array, so the next time you call ``img.get_data()``, we do not have to |
137 |
| -load the array off the disk again. |
138 |
| - |
139 |
| ->>> data_again = proxy_img.get_data() |
140 |
| ->>> data is data_again |
141 |
| -True |
142 |
| - |
143 |
| -If you call ``img.get_data()`` on a proxy image, the image object will get much |
144 |
| -larger in memory, because the image now stores a copy of the loaded array. If |
145 |
| -you want to avoid this memory load you can: |
146 |
| - |
147 |
| -* use ``np.asarray(img.dataobj)`` instead of ``img.get_data()`` or |
148 |
| -* run ``img.uncache()`` after calling ``img.get_data()``. This deletes the copy |
149 |
| - of the array inside the image, so the next time you call ``img.get_data()`` |
150 |
| - the image has to load the data from disk again. |
151 |
| - |
152 |
| -Here is ``uncache`` in action: |
153 |
| - |
154 |
| ->>> data_again = proxy_img.get_data() |
155 |
| ->>> data is data_again |
156 |
| -True |
157 |
| ->>> proxy_img.uncache() |
158 |
| ->>> data_once_more = proxy_img.get_data() |
159 |
| ->>> data_once_more is data_again |
160 |
| -False |
161 |
| - |
162 |
| -This means you need to be careful when you modify arrays returned by |
163 |
| -``get_data()`` on proxy images, because ``uncache`` will then change the result |
164 |
| -you get back from ``get_data()``: |
165 |
| - |
166 |
| ->>> data = proxy_img.get_data() |
167 |
| ->>> data[0, 0, 0, 0] |
168 |
| -0 |
169 |
| ->>> data[0, 0, 0, 0] = 99 |
170 |
| ->>> data_again = proxy_img.get_data() |
171 |
| ->>> data_again[0, 0, 0, 0] |
172 |
| -99 |
173 |
| ->>> proxy_img.uncache() |
174 |
| ->>> data_once_more = proxy_img.get_data() |
175 |
| ->>> data_once_more[0, 0, 0, 0] |
176 |
| -0 |
177 |
| - |
178 |
| -****************** |
179 |
| -Loading and saving |
180 |
| -****************** |
181 |
| - |
182 |
| -The ``save`` and ``load`` functions in nibabel should do all the work for you: |
183 |
| - |
184 |
| ->>> img = nib.load(example_file) |
185 |
| ->>> img.shape |
186 |
| -(128, 96, 24, 2) |
187 |
| ->>> import tempfile |
188 |
| ->>> temp_fname = tempfile.mktemp('.nii') |
189 |
| ->>> nib.save(img, temp_fname) |
190 |
| ->>> img_again = nib.load(temp_fname) |
191 |
| ->>> img_again.shape |
192 |
| -(128, 96, 24, 2) |
193 |
| ->>> os.unlink(temp_fname) |
194 |
| - |
195 |
| -You can also use the ``to_filename`` method: |
196 |
| - |
197 |
| ->>> temp_fname = tempfile.mktemp('.nii') |
198 |
| ->>> img.to_filename(temp_fname) |
199 |
| ->>> img_again = nib.load(temp_fname) |
200 |
| ->>> img_again.shape |
201 |
| -(128, 96, 24, 2) |
202 |
| ->>> os.unlink(temp_fname) |
203 |
| - |
204 |
| -You can get and set the filename with ``get_filename()`` and ``set_filename()``: |
205 |
| - |
206 |
| ->>> img.set_filename('my_image.nii') |
207 |
| ->>> img.get_filename() |
208 |
| -'my_image.nii' |
209 |
| - |
210 |
| -*************************** |
211 |
| -Details of files and images |
212 |
| -*************************** |
213 |
| - |
214 |
| -If an image can be loaded or saved on disk, the image will have an attribute |
215 |
| -called ``file_map``. ``img.file_map`` is a dictionary where the keys are the |
216 |
| -names of the files that the image uses to load / save on disk, and the values |
217 |
| -are ``FileHolder`` objects, that usually contain the filenames that the image |
218 |
| -has been loaded from or saved to. In the case of a NiFTI1 single file, this is |
219 |
| -just a single image file with a ``.nii`` extension: |
220 |
| - |
221 |
| ->>> list(proxy_img.file_map) |
222 |
| -['image'] |
223 |
| ->>> proxy_img.file_map['image'].filename |
224 |
| -'/Users/mb312/dev_trees/nibabel/nibabel/tests/data/example4d.nii.gz' |
225 |
| - |
226 |
| -Other file types need more than one file to make up the image. The NiFTI1 pair |
227 |
| -type is one example: |
228 |
| - |
229 |
| ->>> pair_img = nib.Nifti1Pair(arr, np.eye(4)) |
230 |
| ->>> sorted(pair_img.file_map) |
231 |
| -['header', 'image'] |
232 |
| - |
233 |
| -The older Analyze format is another: |
234 |
| - |
235 |
| ->>> ana_img = nib.AnalyzeImage(arr, np.eye(4)) |
236 |
| ->>> sorted(ana_img.file_map) |
237 |
| -['header', 'image'] |
238 |
| - |
239 |
| -It is ``img.file_map`` that gets changed when you use ``set_filename`` or |
240 |
| -``to_filename``: |
241 |
| - |
242 |
| ->>> ana_img.set_filename('another_image.img') |
243 |
| ->>> ana_img.file_map['image'].filename |
244 |
| -'another_image.img' |
245 |
| ->>> ana_img.file_map['header'].filename |
246 |
| -'another_image.hdr' |
| 5 | +The latest version of this page is now at :doc:`../nibabel_images`. |
0 commit comments