@@ -1021,6 +1021,54 @@ def get_rec_shape(self):
1021
1021
inplane_shape = tuple (self ._get_unique_image_prop ('recon resolution' ))
1022
1022
return inplane_shape + (len (self .image_defs ),)
1023
1023
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
+
1024
1072
def get_sorted_slice_indices (self ):
1025
1073
"""Return indices to sort (and maybe discard) slices in REC file.
1026
1074
@@ -1048,60 +1096,10 @@ def get_sorted_slice_indices(self):
1048
1096
slice_nos = self .image_defs ['slice number' ]
1049
1097
is_full = vol_is_full (slice_nos , self .general_info ['max_slices' ])
1050
1098
keys = (slice_nos , vol_numbers (slice_nos ), np .logical_not (is_full ))
1051
- sort_order = np .lexsort (keys )
1052
1099
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 ()
1104
1101
1102
+ sort_order = np .lexsort (keys )
1105
1103
# Figure out how many we need to remove from the end, and trim them.
1106
1104
# Based on our sorting, they should always be last.
1107
1105
n_used = np .prod (self .get_data_shape ()[2 :])
0 commit comments