1
2
3
4
5
6
7
8
9 """Utility functions for PyNifti"""
10
11 __docformat__ = 'restructuredtext'
12
13
14 import numpy as N
15 import nifticlib
16
17 -def time2vol( t, tr, lag=0.0, decimals=0 ):
18 """ Translates a time 't' into a volume number. By default function returns
19 the volume number that is closest in time. Volumes are assumed to be
20 recorded exactly (and completely) after tr/2, e.g. if 'tr' is 2 secs the
21 first volume is recorded at exactly one second.
22
23 't' might be a single value, a sequence or an array.
24
25 The repetition 'tr' might be specified directly, but can also be a
26 NiftiImage object. In the latter case the value of 'tr' is determined from
27 the 'rtime' property of the NiftiImage object.
28
29 't' and 'tr' can be given in an arbitrary unit (but both have to be in the
30 same unit).
31
32 The 'lag' argument can be used to shift the times by constant offset.
33
34 Please note that numpy.round() is used to round to interger value (rounds
35 to even numbers). The 'decimals' argument will be passed to numpy.round().
36 """
37
38 tmp = N.array(t)
39
40
41 if hasattr(tr, 'rtime'):
42 tr = tr.rtime
43
44 vol = N.round( ( tmp + lag + tr/2 ) / tr, decimals )
45
46 return vol
47
48
50 """ Apply a function on selected volumes of a timeseries.
51
52 'ts' is a 4d timeseries. It can be a NiftiImage or a ndarray.
53 In case of a ndarray one has to make sure that the time is on the
54 first axis. 'ts' can actually be of any dimensionality, but datasets aka
55 volumes are assumed to be along the first axis.
56
57 'vols' is either a sequence of sequences or a 2d array indicating which
58 volumes fx should be applied to. Each row defines a set of volumes.
59
60 'fx' is a callable function to get an array of the selected volumes as
61 argument. Additonal arguments may be specified as keyword arguments and
62 are passed to 'fx'.
63
64 The output will be a 4d array with one computed volume per row in the 'vols'
65 array.
66 """
67
68
69 if hasattr(ts, 'data') and isinstance(ts.data, N.ndarray):
70 data = ts.data
71 else:
72 data = ts
73
74 out = []
75
76 for vol in vols:
77 out.append( fx( data[ N.array( vol ) ], **kwargs ) )
78
79 return N.array( out )
80
82 """ Returns 4d array with peristimulus timeseries.
83
84 Parameters:
85 ts - source 4d timeseries
86 onsetvols - sequence of onsetvolumes to be averaged over
87 nvols - length of the peristimulus timeseries in volumes
88 (starting from onsetvol)
89 fx - function to be applied to the list of corresponding
90 volumes. Typically this will be mean(), so it is default,
91 but it could also be var() or something different. The
92 supplied function is to be able to handle an 'axis=0'
93 argument similiar to NumPy's mean(), var(), ...
94 """
95 selected = [ [ o + offset for o in onsetvols ] \
96 for offset in range( nvols ) ]
97
98 if fx == tuple:
99 return applyFxToVolumes( ts, selected, fx )
100 else:
101 return applyFxToVolumes( ts, selected, fx, axis=0 )
102
103
104 filetypes = [ 'ANALYZE', 'NIFTI', 'NIFTI_PAIR', 'ANALYZE_GZ', 'NIFTI_GZ',
105 'NIFTI_PAIR_GZ' ]
106 """Typecodes of all supported NIfTI image formats."""
107
108 N2nifti_dtype_map = { N.uint8: nifticlib.NIFTI_TYPE_UINT8,
109 N.int8 : nifticlib.NIFTI_TYPE_INT8,
110 N.uint16: nifticlib.NIFTI_TYPE_UINT16,
111 N.int16 : nifticlib.NIFTI_TYPE_INT16,
112 N.uint32: nifticlib.NIFTI_TYPE_UINT32,
113 N.int32 : nifticlib.NIFTI_TYPE_INT32,
114 N.uint64: nifticlib.NIFTI_TYPE_UINT64,
115 N.int64 : nifticlib.NIFTI_TYPE_INT64,
116 N.float32: nifticlib.NIFTI_TYPE_FLOAT32,
117 N.float64: nifticlib.NIFTI_TYPE_FLOAT64,
118 N.complex128: nifticlib.NIFTI_TYPE_COMPLEX128
119 }
120 """Mapping of NumPy datatypes to NIfTI datatypes."""
121
122 nifti2numpy_dtype_map = \
123 { nifticlib.NIFTI_TYPE_UINT8: 'u1',
124 nifticlib.NIFTI_TYPE_INT8: 'i1',
125 nifticlib.NIFTI_TYPE_UINT16: 'u2',
126 nifticlib.NIFTI_TYPE_INT16: 'i2',
127 nifticlib.NIFTI_TYPE_UINT32: 'u4',
128 nifticlib.NIFTI_TYPE_INT32: 'i4',
129 nifticlib.NIFTI_TYPE_UINT64: 'u8',
130 nifticlib.NIFTI_TYPE_INT64: 'i8',
131 nifticlib.NIFTI_TYPE_FLOAT32: 'f4',
132 nifticlib.NIFTI_TYPE_FLOAT64: 'f8',
133 nifticlib.NIFTI_TYPE_COMPLEX128: 'c16'
134 }
135 """Mapping of NIfTI to NumPy datatypes (necessary for handling memory
136 mapped array with proper byte-order handling."""
137
138
140 """Return the NIfTI datatype id for a corresponding NumPy datatype.
141 """
142
143 dtype = N.typeDict[str(array.dtype)]
144
145 if not N2nifti_dtype_map.has_key(dtype):
146 raise ValueError, "Unsupported datatype '%s'" % str(array.dtype)
147
148 return N2nifti_dtype_map[dtype]
149
150
151 nifti_xform_map = \
152 { 'unknown': nifticlib.NIFTI_XFORM_UNKNOWN,
153 'scanner': nifticlib.NIFTI_XFORM_SCANNER_ANAT,
154 'aligned': nifticlib.NIFTI_XFORM_ALIGNED_ANAT,
155 'talairach': nifticlib.NIFTI_XFORM_TALAIRACH,
156 'mni152': nifticlib.NIFTI_XFORM_MNI_152,
157 }
158
159
161 """Convert a NIfTI header struct into a python dictionary.
162
163 While most elements of the header struct will be translated
164 1:1 some (e.g. sform matrix) are converted into more convenient
165 datatypes (i.e. 4x4 matrix instead of 16 separate values).
166
167 :Parameters:
168 nhdr: nifti_1_header
169
170 :Returns:
171 dict
172 """
173 h = {}
174
175
176
177 auto_convert = [ 'session_error', 'extents', 'sizeof_hdr',
178 'slice_duration', 'slice_start', 'xyzt_units',
179 'cal_max', 'intent_p1', 'intent_p2', 'intent_p3',
180 'intent_code', 'sform_code', 'cal_min', 'scl_slope',
181 'slice_code', 'bitpix', 'descrip', 'glmin', 'dim_info',
182 'glmax', 'data_type', 'aux_file', 'intent_name',
183 'vox_offset', 'db_name', 'scl_inter', 'magic',
184 'datatype', 'regular', 'slice_end', 'qform_code',
185 'toffset' ]
186
187
188
189 for attr in auto_convert:
190 h[attr] = eval('nhdr.' + attr)
191
192
193
194 pixdim = nifticlib.floatArray_frompointer(nhdr.pixdim)
195 h['pixdim'] = [ pixdim[i] for i in range(8) ]
196
197
198 dim = nifticlib.shortArray_frompointer(nhdr.dim)
199 h['dim'] = [ dim[i] for i in range(8) ]
200
201
202 srow_x = nifticlib.floatArray_frompointer( nhdr.srow_x )
203 srow_y = nifticlib.floatArray_frompointer( nhdr.srow_y )
204 srow_z = nifticlib.floatArray_frompointer( nhdr.srow_z )
205
206 h['sform'] = N.array( [ [ srow_x[i] for i in range(4) ],
207 [ srow_y[i] for i in range(4) ],
208 [ srow_z[i] for i in range(4) ],
209 [ 0.0, 0.0, 0.0, 1.0 ] ] )
210
211
212 h['quatern'] = [ nhdr.quatern_b, nhdr.quatern_c, nhdr.quatern_d ]
213 h['qoffset'] = [ nhdr.qoffset_x, nhdr.qoffset_y, nhdr.qoffset_z ]
214
215 return h
216
217
219 """Update a NIfTI header struct with data from a dictionary.
220
221 The supplied dictionary might contain additonal data elements
222 that do not match any nifti header element. These are silently ignored.
223
224 Several checks are performed to ensure validity of the resulting
225 nifti header struct. If any check fails a ValueError exception will be
226 thrown. However, some tests are still missing.
227
228 :Parameters:
229 nhdr: nifti_1_header
230 To be updated NIfTI header struct (in-place update).
231 hdrdict: dict
232 Dictionary containing information intented to be merged into
233 the NIfTI header struct.
234 """
235
236
237 if hdrdict.has_key('data_type'):
238 if len(hdrdict['data_type']) > 9:
239 raise ValueError, \
240 "Nifti header property 'data_type' must not be longer " \
241 + "than 9 characters."
242 nhdr.data_type = hdrdict['data_type']
243 if hdrdict.has_key('db_name'):
244 if len(hdrdict['db_name']) > 79:
245 raise ValueError, "Nifti header property 'db_name' must " \
246 + "not be longer than 17 characters."
247 nhdr.db_name = hdrdict['db_name']
248
249 if hdrdict.has_key('extents'):
250 nhdr.extents = hdrdict['extents']
251 if hdrdict.has_key('session_error'):
252 nhdr.session_error = hdrdict['session_error']
253
254 if hdrdict.has_key('regular'):
255 if len(hdrdict['regular']) > 1:
256 raise ValueError, \
257 "Nifti header property 'regular' has to be a single " \
258 + "character."
259 nhdr.regular = hdrdict['regular']
260 if hdrdict.has_key('dim_info'):
261 if len(hdrdict['dim_info']) > 1:
262 raise ValueError, \
263 "Nifti header property 'dim_info' has to be a " \
264 + "single character."
265 nhdr.dim_info = hdrdict['dim_info']
266
267 if hdrdict.has_key('dim'):
268 dim = nifticlib.shortArray_frompointer(nhdr.dim)
269 for i in range(8):
270 dim[i] = hdrdict['dim'][i]
271 if hdrdict.has_key('intent_p1'):
272 nhdr.intent_p1 = hdrdict['intent_p1']
273 if hdrdict.has_key('intent_p2'):
274 nhdr.intent_p2 = hdrdict['intent_p2']
275 if hdrdict.has_key('intent_p3'):
276 nhdr.intent_p3 = hdrdict['intent_p3']
277 if hdrdict.has_key('intent_code'):
278 nhdr.intent_code = hdrdict['intent_code']
279 if hdrdict.has_key('datatype'):
280 nhdr.datatype = hdrdict['datatype']
281 if hdrdict.has_key('bitpix'):
282 nhdr.bitpix = hdrdict['bitpix']
283 if hdrdict.has_key('slice_start'):
284 nhdr.slice_start = hdrdict['slice_start']
285 if hdrdict.has_key('pixdim'):
286 pixdim = nifticlib.floatArray_frompointer(nhdr.pixdim)
287 for i in range(8):
288 pixdim[i] = hdrdict['pixdim'][i]
289 if hdrdict.has_key('vox_offset'):
290 nhdr.vox_offset = hdrdict['vox_offset']
291 if hdrdict.has_key('scl_slope'):
292 nhdr.scl_slope = hdrdict['scl_slope']
293 if hdrdict.has_key('scl_inter'):
294 nhdr.scl_inter = hdrdict['scl_inter']
295 if hdrdict.has_key('slice_end'):
296 nhdr.slice_end = hdrdict['slice_end']
297 if hdrdict.has_key('slice_code'):
298 nhdr.slice_code = hdrdict['slice_code']
299 if hdrdict.has_key('xyzt_units'):
300 nhdr.xyzt_units = hdrdict['xyzt_units']
301 if hdrdict.has_key('cal_max'):
302 nhdr.cal_max = hdrdict['cal_max']
303 if hdrdict.has_key('cal_min'):
304 nhdr.cal_min = hdrdict['cal_min']
305 if hdrdict.has_key('slice_duration'):
306 nhdr.slice_duration = hdrdict['slice_duration']
307 if hdrdict.has_key('toffset'):
308 nhdr.toffset = hdrdict['toffset']
309 if hdrdict.has_key('glmax'):
310 nhdr.glmax = hdrdict['glmax']
311 if hdrdict.has_key('glmin'):
312 nhdr.glmin = hdrdict['glmin']
313
314 if hdrdict.has_key('descrip'):
315 if len(hdrdict['descrip']) > 79:
316 raise ValueError, \
317 "Nifti header property 'descrip' must not be longer " \
318 + "than 79 characters."
319 nhdr.descrip = hdrdict['descrip']
320 if hdrdict.has_key('aux_file'):
321 if len(hdrdict['aux_file']) > 23:
322 raise ValueError, \
323 "Nifti header property 'aux_file' must not be longer " \
324 + "than 23 characters."
325 nhdr.aux_file = hdrdict['aux_file']
326
327 if hdrdict.has_key('qform_code'):
328 nhdr.qform_code = hdrdict['qform_code']
329
330 if hdrdict.has_key('sform_code'):
331 nhdr.sform_code = hdrdict['sform_code']
332
333 if hdrdict.has_key('quatern'):
334 if not len(hdrdict['quatern']) == 3:
335 raise ValueError, \
336 "Nifti header property 'quatern' must be float 3-tuple."
337
338 nhdr.quatern_b = hdrdict['quatern'][0]
339 nhdr.quatern_c = hdrdict['quatern'][1]
340 nhdr.quatern_d = hdrdict['quatern'][2]
341
342 if hdrdict.has_key('qoffset'):
343 if not len(hdrdict['qoffset']) == 3:
344 raise ValueError, \
345 "Nifti header property 'qoffset' must be float 3-tuple."
346
347 nhdr.qoffset_x = hdrdict['qoffset'][0]
348 nhdr.qoffset_y = hdrdict['qoffset'][1]
349 nhdr.qoffset_z = hdrdict['qoffset'][2]
350
351 if hdrdict.has_key('sform'):
352 if not hdrdict['sform'].shape == (4, 4):
353 raise ValueError, \
354 "Nifti header property 'sform' must be 4x4 matrix."
355
356 srow_x = nifticlib.floatArray_frompointer(nhdr.srow_x)
357 for i in range(4):
358 srow_x[i] = hdrdict['sform'][0][i]
359 srow_y = nifticlib.floatArray_frompointer(nhdr.srow_y)
360 for i in range(4):
361 srow_y[i] = hdrdict['sform'][1][i]
362 srow_z = nifticlib.floatArray_frompointer(nhdr.srow_z)
363 for i in range(4):
364 srow_z[i] = hdrdict['sform'][2][i]
365
366 if hdrdict.has_key('intent_name'):
367 if len(hdrdict['intent_name']) > 15:
368 raise ValueError, \
369 "Nifti header property 'intent_name' must not be " \
370 + "longer than 15 characters."
371 nhdr.intent_name = hdrdict['intent_name']
372
373 if hdrdict.has_key('magic'):
374 if hdrdict['magic'] != 'ni1' and hdrdict['magic'] != 'n+1':
375 raise ValueError, \
376 "Nifti header property 'magic' must be 'ni1' or 'n+1'."
377 nhdr.magic = hdrdict['magic']
378
379
381 """Split a NIfTI filename into basename and extension.
382
383 :Parameters:
384 filename: str
385 Filename to be split.
386
387 :Returns:
388 The function returns a tuple of basename and extension. If no valid
389 NIfTI filename extension is found, the whole string is returned as
390 basename and the extension string will be empty.
391 """
392 parts = filename.split('.')
393
394 if parts[-1] == 'gz':
395 if not parts[-2] in [ 'nii', 'hdr', 'img' ]:
396 return filename, ''
397 else:
398 return '.'.join(parts[:-2]), '.'.join(parts[-2:])
399 else:
400 if not parts[-1] in [ 'nii', 'hdr', 'img' ]:
401 return filename, ''
402 else:
403 return '.'.join(parts[:-1]), parts[-1]
404