@@ -329,6 +329,21 @@ def _process_image_lines(image_lines, version):
329
329
return image_defs
330
330
331
331
332
+ def _direction_numbers (bvecs ):
333
+ """ Enumerate directions from an (N, 3) array of direction vectors.
334
+ """
335
+ cnt = 0
336
+ bvec_dict = {}
337
+ bvec_nos = np .zeros (len (bvecs ))
338
+ for i , bvec in enumerate (bvecs ):
339
+ bv = tuple (bvec )
340
+ if bv not in bvec_dict :
341
+ bvec_dict [bv ] = cnt
342
+ cnt += 1
343
+ bvec_nos [i ] = bvec_dict .get (bv )
344
+ return bvec_nos
345
+
346
+
332
347
def vol_numbers (slice_nos ):
333
348
""" Calculate volume numbers inferred from slice numbers `slice_nos`
334
349
@@ -634,7 +649,9 @@ def __init__(self, info, image_defs, permit_truncated=False,
634
649
recording is detected.
635
650
strict_sort : bool, optional, keyword-only
636
651
If True, a larger number of header fields are used while sorting
637
- the REC data array.
652
+ the REC data array. This may produce a different sort order than
653
+ `strict_sort=False`, where volumes are sorted by the order in which
654
+ the slices appear in the .PAR file.
638
655
"""
639
656
self .general_info = info .copy ()
640
657
self .image_defs = image_defs .copy ()
@@ -747,6 +764,11 @@ def get_bvals_bvecs(self):
747
764
bvecs = apply_affine (np .linalg .inv (permute_to_psl ), bvecs )
748
765
return bvals , bvecs
749
766
767
+ def get_def (self , name ):
768
+ """ Return a single image definition field. """
769
+ idef = self .image_defs
770
+ return idef [name ] if name in idef .dtype .names else None
771
+
750
772
def _get_unique_image_prop (self , name ):
751
773
""" Scan image definitions and return unique value of a property.
752
774
@@ -1011,7 +1033,9 @@ def get_sorted_slice_indices(self):
1011
1033
discarding any slice indices from incomplete volumes.
1012
1034
1013
1035
If `self.strict_sort` is True, a more complicated sorting based on
1014
- multiple fields from the .PAR file is used.
1036
+ multiple fields from the .PAR file is used. This may produce a
1037
+ different sort order than `strict_sort=False`, where volumes are sorted
1038
+ by the order in which the slices appear in the .PAR file.
1015
1039
1016
1040
Returns
1017
1041
-------
@@ -1034,22 +1058,18 @@ def get_sorted_slice_indices(self):
1034
1058
echos = self .image_defs ['echo number' ]
1035
1059
1036
1060
# try adding keys only present in a subset of .PAR files
1037
- try :
1038
- # only present in PAR v4.2+
1039
- asl_labels = self .image_defs ['label type' ]
1040
- asl_keys = (asl_labels , )
1041
- except :
1042
- asl_keys = ()
1061
+ idefs = self .image_defs
1062
+ asl_keys = (idefs ['label_type' ], ) if 'label_type' in \
1063
+ idefs .dtype .names else ()
1064
+
1043
1065
if not self .general_info ['diffusion' ] == 0 :
1044
- try :
1045
- # only present for .PAR v4.1+
1046
- bvals = self .image_defs ['diffusion b value number' ]
1047
- bvecs = self .image_defs ['gradient orientation number' ]
1048
- except :
1049
- bvals = self .image_defs ['diffusion_b_factor' ]
1050
- # use hash to get a single sortable value per direction
1051
- bvecs = [hash (tuple (
1052
- a )) for a in self .image_defs ['diffusion' ].tolist ()]
1066
+ bvals = self .get_def ('diffusion b value number' )
1067
+ if bvals is None :
1068
+ bvals = self .get_def ('diffusion_b_factor' )
1069
+ bvecs = self .get_def ('gradient orientation number' )
1070
+ if bvecs is None :
1071
+ # manually enumerate the different directions
1072
+ bvecs = _direction_numbers (self .get_def ('diffusion' ))
1053
1073
diffusion_keys = (bvecs , bvals )
1054
1074
else :
1055
1075
diffusion_keys = ()
@@ -1124,7 +1144,9 @@ def from_file_map(klass, file_map, mmap=True, permit_truncated=False,
1124
1144
:meth:`PARRECHeader.get_data_scaling`).
1125
1145
strict_sort : bool, optional, keyword-only
1126
1146
If True, a larger number of header fields are used while sorting
1127
- the REC data array.
1147
+ the REC data array. This may produce a different sort order than
1148
+ `strict_sort=False`, where volumes are sorted by the order in which
1149
+ the slices appear in the .PAR file.
1128
1150
"""
1129
1151
with file_map ['header' ].get_prepare_fileobj ('rt' ) as hdr_fobj :
1130
1152
hdr = klass .header_class .from_fileobj (
@@ -1162,7 +1184,9 @@ def from_filename(klass, filename, mmap=True, permit_truncated=False,
1162
1184
:meth:`PARRECHeader.get_data_scaling`).
1163
1185
strict_sort : bool, optional, keyword-only
1164
1186
If True, a larger number of header fields are used while sorting
1165
- the REC data array.
1187
+ the REC data array. This may produce a different sort order than
1188
+ `strict_sort=False`, where volumes are sorted by the order in which
1189
+ the slices appear in the .PAR file.
1166
1190
"""
1167
1191
file_map = klass .filespec_to_file_map (filename )
1168
1192
return klass .from_file_map (file_map ,
0 commit comments