Skip to content

Commit be00991

Browse files
committed
Merge pull request #79 from cindeem/master
ecat fix for mis-ordered mlist
2 parents ba18b33 + 29a101f commit be00991

File tree

2 files changed

+118
-19
lines changed

2 files changed

+118
-19
lines changed

nibabel/ecat.py

Lines changed: 70 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -442,9 +442,9 @@ def get_mlist(self, fileobj):
442442
return mlist
443443

444444
def get_frame_order(self):
445-
"""Returns the order of the frames in the file
446-
Useful as sometimes Frames are not stored in the file in
447-
chronological order, and can be used to extract frames
445+
"""Returns the order of the frames stored in the file
446+
Sometimes Frames are not stored in the file in
447+
chronological order, this can be used to extract frames
448448
in correct order
449449
450450
Returns
@@ -465,20 +465,69 @@ def get_frame_order(self):
465465
>>> mlist.get_frame_order()
466466
{0: [0, 16842758.0]}
467467
468+
468469
"""
469470
mlist = self._mlist
470-
ind = mlist[:,0] > 0
471-
nframes = ind.shape[0]
472-
tmp = mlist[ind,0]
473-
tmp.sort()
474-
frame_dict = {}
471+
ids = mlist[:, 0].copy()
472+
n_valid = np.sum(ids > 0)
473+
ids[ids <=0] = ids.max() + 1 # put invalid frames at end after sort
474+
valid_order = np.argsort(ids)
475+
if not all(valid_order == sorted(valid_order)):
476+
#raise UserWarning if Frames stored out of order
477+
warnings.warn_explicit('Frames stored out of order;'\
478+
'true order = %s\n'\
479+
'frames will be accessed in order '\
480+
'STORED, NOT true order'%(valid_order),
481+
UserWarning,'ecat', 0)
475482
id_dict = {}
476-
for fn, id in enumerate(mlist[ind,0]):
477-
mlist_n = np.where(mlist[:,0] == id)[0][0]
478-
id_dict.update({fn:[mlist_n,id]})
479-
483+
for i in range(n_valid):
484+
id_dict[i] = [valid_order[i], ids[valid_order[i]]]
485+
480486
return id_dict
481-
487+
488+
def get_series_framenumbers(self):
489+
""" Returns framenumber of data as it was collected,
490+
as part of a series; not just the order of how it was
491+
stored in this or across other files
492+
493+
For example, if the data is split between multiple files
494+
this should give you the true location of this frame as
495+
collected in the series
496+
(Frames are numbered starting at ONE (1) not Zero)
497+
498+
Returns
499+
-------
500+
frame_dict: dict mapping order_stored -> frame in series
501+
where frame in series counts from 1; [1,2,3,4...]
502+
503+
Examples
504+
--------
505+
>>> import os
506+
>>> import nibabel as nib
507+
>>> nibabel_dir = os.path.dirname(nib.__file__)
508+
>>> from nibabel import ecat
509+
>>> ecat_file = os.path.join(nibabel_dir,'tests','data','tinypet.v')
510+
>>> img = ecat.load(ecat_file)
511+
>>> mlist = img.get_mlist()
512+
>>> mlist.get_series_framenumbers()
513+
{0: 1}
514+
515+
516+
517+
"""
518+
frames_order = self.get_frame_order()
519+
nframes = self.hdr['num_frames']
520+
mlist_nframes = len(frames_order)
521+
trueframenumbers = np.arange(nframes - mlist_nframes, nframes)
522+
frame_dict = {}
523+
try:
524+
for frame_stored, (true_order, _) in frames_order.items():
525+
#frame as stored in file -> true number in series
526+
frame_dict[frame_stored] = trueframenumbers[true_order]+1
527+
return frame_dict
528+
except:
529+
raise IOError('Error in header or mlist order unknown')
530+
482531
class EcatSubHeader(object):
483532

484533
_subhdrdtype = subhdr_dtype
@@ -625,7 +674,8 @@ def data_from_fileobj(self, frame=0):
625674

626675

627676
class EcatImage(SpatialImage):
628-
"""This class returns a list of Ecat images, with one image(hdr/data) per frame
677+
"""This class returns a list of Ecat images,
678+
with one image(hdr/data) per frame
629679
"""
630680
_header = EcatHeader
631681
header_class = _header
@@ -650,7 +700,8 @@ def __init__(self, subheader):
650700
def __array__(self):
651701
''' Cached read of data from file
652702
This reads ALL FRAMES into one array, can be memory expensive
653-
use subheader.data_from_fileobj(frame) for less memeory intensive reads
703+
use subheader.data_from_fileobj(frame) for less memory intensive
704+
reads
654705
'''
655706
if self._data is None:
656707
self._data = np.empty(self.shape)
@@ -664,8 +715,9 @@ def __init__(self, data, affine, header,
664715
extra = None, file_map = None):
665716
""" Initialize Image
666717
667-
The image is a combination of (array, affine matrix, header, subheader,
668-
mlist) with optional meta data in `extra`, and filename / file-like objects
718+
The image is a combination of
719+
(array, affine matrix, header, subheader, mlist)
720+
with optional meta data in `extra`, and filename / file-like objects
669721
contained in the `file_map`.
670722
671723
Parameters
@@ -739,7 +791,7 @@ def get_affine(self):
739791

740792
def get_frame_affine(self, frame):
741793
"""returns 4X4 affine"""
742-
return self._subheader.get_affine(frame=frame)
794+
return self._subheader.get_frame_affine(frame=frame)
743795

744796
def get_frame(self,frame):
745797
return self._subheader.data_from_fileobj(frame)

nibabel/tests/test_ecat.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,57 @@ def test_mlist(self):
100100
dt = dt.newbyteorder('>')
101101
mats = np.recarray(shape=(32,4), dtype=dt, buf=dat)
102102
fid.close()
103+
#tests
103104
assert_true(mats['matlist'][0,0] + mats['matlist'][0,3] == 31)
104105
assert_true(mlist.get_frame_order()[0][0] == 0)
105106
assert_true(mlist.get_frame_order()[0][1] == 16842758.0)
106-
107+
# test badly ordered mlist
108+
badordermlist = mlist
109+
badordermlist._mlist = np.array([[ 1.68427540e+07, 3.00000000e+00,
110+
1.20350000e+04, 1.00000000e+00],
111+
[ 1.68427530e+07, 1.20360000e+04,
112+
2.40680000e+04, 1.00000000e+00],
113+
[ 1.68427550e+07, 2.40690000e+04,
114+
3.61010000e+04, 1.00000000e+00],
115+
[ 1.68427560e+07, 3.61020000e+04,
116+
4.81340000e+04, 1.00000000e+00],
117+
[ 1.68427570e+07, 4.81350000e+04,
118+
6.01670000e+04, 1.00000000e+00],
119+
[ 1.68427580e+07, 6.01680000e+04,
120+
7.22000000e+04, 1.00000000e+00]])
121+
assert_true(badordermlist.get_frame_order()[0][0] == 1)
122+
123+
def test_mlist_errors(self):
124+
fid = open(self.example_file, 'rb')
125+
hdr = self.header_class.from_fileobj(fid)
126+
hdr['num_frames'] = 6
127+
mlist = self.mlist_class(fid, hdr)
128+
mlist._mlist = np.array([[ 1.68427540e+07, 3.00000000e+00,
129+
1.20350000e+04, 1.00000000e+00],
130+
[ 1.68427530e+07, 1.20360000e+04,
131+
2.40680000e+04, 1.00000000e+00],
132+
[ 1.68427550e+07, 2.40690000e+04,
133+
3.61010000e+04, 1.00000000e+00],
134+
[ 1.68427560e+07, 3.61020000e+04,
135+
4.81340000e+04, 1.00000000e+00],
136+
[ 1.68427570e+07, 4.81350000e+04,
137+
6.01670000e+04, 1.00000000e+00],
138+
[ 1.68427580e+07, 6.01680000e+04,
139+
7.22000000e+04, 1.00000000e+00]])
140+
series_framenumbers = mlist.get_series_framenumbers()
141+
# first frame stored was actually 2nd frame acquired
142+
assert_true(series_framenumbers[0] == 2)
143+
order = [series_framenumbers[x] for x in sorted(series_framenumbers)]
144+
# true series order is [2,1,3,4,5,6], note counting starts at 1
145+
assert_true(order == [2, 1, 3, 4, 5, 6])
146+
mlist._mlist[0,0] = 0
147+
frames_order = mlist.get_frame_order()
148+
neworder =[frames_order[x][0] for x in sorted(frames_order)]
149+
assert_true(neworder == [1, 2, 3, 4, 5])
150+
assert_raises(IOError,
151+
mlist.get_series_framenumbers)
152+
153+
107154

108155
class TestEcatSubHeader(TestCase):
109156
header_class = EcatHeader

0 commit comments

Comments
 (0)