Skip to content

Commit cd45556

Browse files
committed
MAINT: split the strict sort logic into a separate _strict_sort_keys subfunction
vol_numbers was removed from the list of sort keys for strict_sort as it is not necessary as long as the set of sort keys produces a unique sort order.
1 parent e201289 commit cd45556

File tree

1 file changed

+50
-52
lines changed

1 file changed

+50
-52
lines changed

nibabel/parrec.py

Lines changed: 50 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,6 +1021,54 @@ def get_rec_shape(self):
10211021
inplane_shape = tuple(self._get_unique_image_prop('recon resolution'))
10221022
return inplane_shape + (len(self.image_defs),)
10231023

1024+
def _strict_sort_keys(self):
1025+
""" Determine the sort order based on several image definition fields.
1026+
1027+
Data sorting is done in two stages:
1028+
- run an initial sort using several keys of interest
1029+
- call `vol_is_full` to identify potentially missing volumes
1030+
and add the result to the list of sort keys
1031+
"""
1032+
# Sort based on a larger number of keys. This is more complicated
1033+
# but works for .PAR files that get missorted by the above method
1034+
slice_nos = self.image_defs['slice number']
1035+
dynamics = self.image_defs['dynamic scan number']
1036+
phases = self.image_defs['cardiac phase number']
1037+
echos = self.image_defs['echo number']
1038+
image_type = self.image_defs['image_type_mr']
1039+
1040+
# try adding keys only present in a subset of .PAR files
1041+
idefs = self.image_defs
1042+
asl_keys = (idefs['label_type'], ) if 'label_type' in \
1043+
idefs.dtype.names else ()
1044+
1045+
if not self.general_info['diffusion'] == 0:
1046+
bvals = self.get_def('diffusion b value number')
1047+
if bvals is None:
1048+
bvals = self.get_def('diffusion_b_factor')
1049+
bvecs = self.get_def('gradient orientation number')
1050+
if bvecs is None:
1051+
# manually enumerate the different directions
1052+
bvecs = _direction_numbers(self.get_def('diffusion'))
1053+
diffusion_keys = (bvecs, bvals)
1054+
else:
1055+
diffusion_keys = ()
1056+
1057+
# Define the desired sort order (last key is highest precedence)
1058+
keys = (slice_nos, echos, phases) + \
1059+
diffusion_keys + asl_keys + (dynamics, image_type)
1060+
1061+
initial_sort_order = np.lexsort(keys)
1062+
is_full = vol_is_full(slice_nos[initial_sort_order],
1063+
self.general_info['max_slices'])
1064+
1065+
# have to "unsort" is_full to match the other sort keys
1066+
unsort_indices = np.argsort(initial_sort_order)
1067+
is_full = is_full[unsort_indices]
1068+
1069+
# final set of sort keys
1070+
return keys + (np.logical_not(is_full), )
1071+
10241072
def get_sorted_slice_indices(self):
10251073
"""Return indices to sort (and maybe discard) slices in REC file.
10261074
@@ -1048,60 +1096,10 @@ def get_sorted_slice_indices(self):
10481096
slice_nos = self.image_defs['slice number']
10491097
is_full = vol_is_full(slice_nos, self.general_info['max_slices'])
10501098
keys = (slice_nos, vol_numbers(slice_nos), np.logical_not(is_full))
1051-
sort_order = np.lexsort(keys)
10521099
else:
1053-
# Sort based on a larger number of keys. This is more complicated
1054-
# but works for .PAR files that get missorted by the above method
1055-
slice_nos = self.image_defs['slice number']
1056-
dynamics = self.image_defs['dynamic scan number']
1057-
phases = self.image_defs['cardiac phase number']
1058-
echos = self.image_defs['echo number']
1059-
image_type = self.image_defs['image_type_mr']
1060-
1061-
# try adding keys only present in a subset of .PAR files
1062-
idefs = self.image_defs
1063-
asl_keys = (idefs['label_type'], ) if 'label_type' in \
1064-
idefs.dtype.names else ()
1065-
1066-
if not self.general_info['diffusion'] == 0:
1067-
bvals = self.get_def('diffusion b value number')
1068-
if bvals is None:
1069-
bvals = self.get_def('diffusion_b_factor')
1070-
bvecs = self.get_def('gradient orientation number')
1071-
if bvecs is None:
1072-
# manually enumerate the different directions
1073-
bvecs = _direction_numbers(self.get_def('diffusion'))
1074-
diffusion_keys = (bvecs, bvals)
1075-
else:
1076-
diffusion_keys = ()
1077-
1078-
# Define the desired sort order (last key is highest precedence)
1079-
keys = (slice_nos, echos, phases) + \
1080-
diffusion_keys + asl_keys + (dynamics, image_type)
1081-
1082-
"""
1083-
Data sorting is done in two stages:
1084-
- run an initial sort using the keys defined above
1085-
- call vol_is_full to identify potentially missing volumes
1086-
- call vol_numbers to assign unique volume numbers if for some
1087-
reason the keys defined above don't provide a unique sort
1088-
order (e.g. this occurs for the Trace volume in DTI)
1089-
- run a final sort using the vol_numbers and is_full keys
1090-
"""
1091-
initial_sort_order = np.lexsort(keys)
1092-
is_full = vol_is_full(slice_nos[initial_sort_order],
1093-
self.general_info['max_slices'])
1094-
vol_nos = vol_numbers(slice_nos[initial_sort_order])
1095-
1096-
# have to "unsort" is_full and vol_nos to match the other sort keys
1097-
unsort_indices = np.argsort(initial_sort_order)
1098-
is_full = is_full[unsort_indices]
1099-
vol_nos = np.asarray(vol_nos)[unsort_indices]
1100-
1101-
# final set of sort keys
1102-
keys += (vol_nos, np.logical_not(is_full)) # highest priority
1103-
sort_order = np.lexsort(tuple(keys))
1100+
keys = self._strict_sort_keys()
11041101

1102+
sort_order = np.lexsort(keys)
11051103
# Figure out how many we need to remove from the end, and trim them.
11061104
# Based on our sorting, they should always be last.
11071105
n_used = np.prod(self.get_data_shape()[2:])

0 commit comments

Comments
 (0)