Skip to content

Commit c78c786

Browse files
committed
RF: ArrayProxy and whitespace
Changed AFNIArrayProxy to subclass from ArrayProxy. Removed whitespace.
1 parent c418a1f commit c78c786

File tree

1 file changed

+49
-87
lines changed

1 file changed

+49
-87
lines changed

nibabel/brikhead.py

Lines changed: 49 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
#
88
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
99

10-
# https://github.com/florisvanvugt/afnipy/blob/master/afni.py
11-
1210
from __future__ import print_function, division
1311

1412
from copy import deepcopy
@@ -17,11 +15,11 @@
1715

1816
import numpy as np
1917

20-
from .fileslice import fileslice, strided_scalar
18+
from .arrayproxy import ArrayProxy
19+
from .fileslice import strided_scalar
2120
from .keywordonly import kw_only_meth
22-
from .openers import ImageOpener
2321
from .spatialimages import SpatialImage, SpatialHeader
24-
from .volumeutils import Recoder, array_from_file
22+
from .volumeutils import Recoder
2523

2624
_attr_dic = {
2725
'string': str,
@@ -62,6 +60,7 @@ class AFNIError(Exception):
6260
"""
6361

6462

63+
DATA_OFFSET = 0
6564
TYPE_RE = re.compile('type\s*=\s*(string|integer|float)-attribute\s*\n')
6665
NAME_RE = re.compile('name\s*=\s*(\w+)\s*\n')
6766

@@ -79,14 +78,11 @@ def _unpack_var(var):
7978
(key, value)
8079
Example: ('BRICK_TYPES', [1])
8180
"""
82-
8381
# data type and key
8482
atype = TYPE_RE.findall(var)[0]
8583
aname = NAME_RE.findall(var)[0]
86-
8784
atype = _attr_dic.get(atype, str)
8885
attr = ' '.join(var.strip().split('\n')[3:])
89-
9086
if atype is not str:
9187
attr = [atype(f) for f in attr.split()]
9288
if len(attr) == 1:
@@ -100,19 +96,15 @@ def _unpack_var(var):
10096
def _get_datatype(info):
10197
""" Gets datatype from `info` header information
10298
"""
103-
10499
bo = info['BYTEORDER_STRING']
105100
bt = info['BRICK_TYPES']
106-
107101
if isinstance(bt, list):
108102
if len(np.unique(bt)) > 1:
109103
raise AFNIError('Can\'t load dataset with multiple data types.')
110104
else:
111105
bt = bt[0]
112-
113106
bo = _endian_dict.get(bo, '=')
114107
bt = _dtype_dict.get(bt, None)
115-
116108
if bt is None:
117109
raise AFNIError('Can\'t deduce image data type.')
118110

@@ -125,104 +117,71 @@ def parse_AFNI_header(fobj):
125117
Parameters
126118
----------
127119
fobj : file-object
128-
AFNI HEAD file objects
120+
AFNI HEAD file object
129121
130122
Returns
131123
-------
132124
all_info : dict
133125
Contains all the information from the HEAD file
134126
"""
135-
136127
head = fobj.read().split('\n\n')
137-
138128
all_info = {key: value for key, value in map(_unpack_var, head)}
139129

140130
return all_info
141131

142132

143-
def _data_from_brik(fobj, shape, dtype, scalings=None, mmap=True):
144-
""" Load and return array data from BRIK file
145-
146-
Parameters
147-
----------
148-
fobj : file-like
149-
The file to process.
150-
shape : tuple
151-
The data shape as specified from the HEAD file.
152-
dtype : dtype
153-
The datatype.
154-
scalings : {None, sequence}, optional
155-
Scalings to use. If not None, a length N sequence, where N is equal to
156-
`shape[-1]`
157-
mmap : {True, False, 'c', 'r', 'r+'}, optional
158-
`mmap` controls the use of numpy memory mapping for reading data. If
159-
False, do not try numpy ``memmap`` for data array. If one of {'c',
160-
'r', 'r+'}, try numpy memmap with ``mode=mmap``. A `mmap` value of
161-
True gives the same behavior as ``mmap='c'``. If `rec_fileobj` cannot
162-
be memory-mapped, ignore `mmap` value and read array from file.
163-
164-
Returns
165-
-------
166-
data : array
167-
The scaled and sorted array.
168-
"""
169-
brik_data = array_from_file(shape, dtype, fobj, mmap=mmap)
170-
if scalings is not None:
171-
brik_data = brik_data * scalings.astype(dtype)
172-
return brik_data
173-
174-
175-
class AFNIArrayProxy(object):
176-
177-
def __init__(self, file_like, header, mmap=True):
133+
class AFNIArrayProxy(ArrayProxy):
134+
@kw_only_meth(2)
135+
def __init__(self, file_like, header, mmap=True, keep_file_open=None):
178136
""" Initialize AFNI array proxy
179137
180138
Parameters
181139
----------
182140
file_like : file-like object
183-
Filename or object implementing ``read, seek, tell``
184-
header : AFNIHeader instance
185-
Implementing ``get_data_shape, get_data_dtype``,
186-
``get_data_scaling``.
141+
File-like object or filename. If file-like object, should implement
142+
at least ``read`` and ``seek``.
143+
header : AFNIHeader object
187144
mmap : {True, False, 'c', 'r'}, optional, keyword only
188145
`mmap` controls the use of numpy memory mapping for reading data.
189146
If False, do not try numpy ``memmap`` for data array. If one of
190147
{'c', 'r'}, try numpy memmap with ``mode=mmap``. A `mmap` value of
191148
True gives the same behavior as ``mmap='c'``. If `file_like`
192149
cannot be memory-mapped, ignore `mmap` value and read array from
193150
file.
151+
keep_file_open : { None, 'auto', True, False }, optional, keyword only
152+
`keep_file_open` controls whether a new file handle is created
153+
every time the image is accessed, or a single file handle is
154+
created and used for the lifetime of this ``ArrayProxy``. If
155+
``True``, a single file handle is created and used. If ``False``,
156+
a new file handle is created every time the image is accessed. If
157+
``'auto'``, and the optional ``indexed_gzip`` dependency is
158+
present, a single file handle is created and persisted. If
159+
``indexed_gzip`` is not available, behaviour is the same as if
160+
``keep_file_open is False``. If ``file_like`` is an open file
161+
handle, this setting has no effect. The default value (``None``)
162+
will result in the value of ``KEEP_FILE_OPEN_DEFAULT`` being used.
194163
"""
195-
self.file_like = file_like
196-
self._shape = header.get_data_shape()
197-
self._dtype = header.get_data_dtype()
198-
self._mmap = mmap
164+
super(AFNIArrayProxy, self).__init__(file_like,
165+
header,
166+
mmap=mmap,
167+
keep_file_open=keep_file_open)
199168
self._scaling = header.get_data_scaling()
200169

201170
@property
202-
def shape(self):
203-
return self._shape
204-
205-
@property
206-
def dtype(self):
207-
return self._dtype
208-
209-
@property
210-
def is_proxy(self):
211-
return True
171+
def scaling(self):
172+
return self._scaling
212173

213174
def __array__(self):
214-
with ImageOpener(self.file_like) as fileobj:
215-
return _data_from_brik(fileobj,
216-
self._shape,
217-
self._dtype,
218-
scalings=self._scaling,
219-
mmap=self._mmap)
175+
raw_data = self.get_unscaled()
176+
# apply volume specific scaling
177+
if self._scaling is not None:
178+
return raw_data * self._scaling.astype(self.dtype)
220179

221-
def __getitem__(self, slicer):
222-
with ImageOpener(self.file_like) as fileobj:
223-
raw_data = fileslice(fileobj, slicer, self._shape, self._dtype, 0,
224-
'F')
180+
return raw_data
225181

182+
def __getitem__(self, slicer):
183+
raw_data = super(AFNIArrayProxy, self).__getitem__(slicer)
184+
# apply volume specific scaling
226185
if self._scaling is not None:
227186
scaling = self._scaling.copy()
228187
fake_data = strided_scalar(self._shape)
@@ -235,7 +194,6 @@ def __getitem__(self, slicer):
235194
class AFNIHeader(SpatialHeader):
236195
""" Class for AFNI header
237196
"""
238-
239197
def __init__(self, info):
240198
"""
241199
Parameters
@@ -246,7 +204,6 @@ def __init__(self, info):
246204
"""
247205
self.info = info
248206
dt = _get_datatype(self.info)
249-
250207
super(AFNIHeader, self).__init__(data_dtype=dt,
251208
shape=self._calc_data_shape(),
252209
zooms=self._calc_zooms())
@@ -293,7 +250,6 @@ def _calc_zooms(self):
293250
"""
294251
xyz_step = tuple(np.abs(self.info['DELTA']))
295252
t_step = self.info.get('TAXIS_FLOATS', ())
296-
297253
if len(t_step) > 0:
298254
t_step = (t_step[1],)
299255

@@ -320,13 +276,14 @@ def get_space(self):
320276
-------
321277
space : str
322278
"""
323-
324279
listed_space = self.info.get('TEMPLATE_SPACE', 0)
325280
space = space_codes.label[listed_space]
326281

327282
return space
328283

329284
def get_affine(self):
285+
""" Returns affine of dataset
286+
"""
330287
# AFNI default is RAI/DICOM order (i.e., RAI are - axis)
331288
# need to flip RA sign to align with nibabel RAS+ system
332289
affine = np.asarray(self.info['IJK_TO_DICOM_REAL']).reshape(3, 4)
@@ -336,17 +293,25 @@ def get_affine(self):
336293
return affine
337294

338295
def get_data_scaling(self):
296+
""" AFNI applies volume-specific data scaling
297+
"""
339298
floatfacs = self.info.get('BRICK_FLOAT_FACS', None)
340-
341299
if floatfacs is None or not np.any(floatfacs):
342300
return None
343-
344301
scale = np.ones(self.info['DATASET_RANK'][1])
345302
floatfacs = np.asarray(floatfacs)
346303
scale[floatfacs.nonzero()] = floatfacs[floatfacs.nonzero()]
347304

348305
return scale
349306

307+
def get_slope_inter(self):
308+
""" Use `self.get_data_scaling()` instead
309+
"""
310+
return None, None
311+
312+
def get_data_offset(self):
313+
return DATA_OFFSET
314+
350315
def get_volume_labels(self):
351316
""" Returns volume labels
352317
@@ -355,7 +320,6 @@ def get_volume_labels(self):
355320
labels : list of str
356321
"""
357322
labels = self.info.get('BRICK_LABS', None)
358-
359323
if labels is not None:
360324
labels = labels.split('~')
361325

@@ -370,10 +334,8 @@ class AFNIImage(SpatialImage):
370334
valid_exts = ('.brik', '.head')
371335
files_types = (('image', '.brik'), ('header', '.head'))
372336
_compressed_suffixes = ('.gz', '.bz2')
373-
374337
makeable = False
375338
rw = False
376-
377339
ImageArrayProxy = AFNIArrayProxy
378340

379341
@classmethod

0 commit comments

Comments
 (0)