Skip to content

Commit 53dc4b2

Browse files
committed
STY: clarify/streamline code based on feedback
1 parent 5abeaac commit 53dc4b2

File tree

2 files changed

+55
-64
lines changed

2 files changed

+55
-64
lines changed

bin/parrec2nii

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,17 @@ def get_opt_parser():
6868
"""The magnetic field strength of the recording, only needed
6969
for --dwell-time. The field strength must be supplied
7070
because it is not encoded in the PAR/REC format.""")))
71+
p.add_option(
72+
Option("-i", "--dimension-info", action="store_true", dest="dim_info",
73+
default=False,
74+
help=one_line(
75+
"""Export .PAR dimension labels corresponding to the fourth
76+
dimension of the data. The dimension info will be stored in
77+
JSON format with a filename matching the input .PAR, but
78+
with the extension .ordering.json. Only labels that vary
79+
along the 4th dimension are exported. (e.g. for a single
80+
a single volume structural scan there are no dynamic labels
81+
and no output file will be created)""")))
7182
p.add_option(
7283
Option("--origin", action="store", dest="origin", default="scanner",
7384
help=one_line(
@@ -135,22 +146,14 @@ def error(msg, exit_code):
135146
sys.exit(exit_code)
136147

137148

138-
def sort_info_to_lists(sort_info):
139-
sort_info = deepcopy(sort_info)
140-
# convert from elements from numpy array to lists for easy JSON export
141-
for key in sort_info:
142-
ndim = sort_info[key].ndim
143-
if ndim == 1:
144-
sort_info[key] = list(sort_info[key])
145-
elif ndim == 2:
146-
k = sort_info[key]
147-
k_list = []
148-
for row in range(k.shape[0]):
149-
k_list.append(list(k[row]))
150-
sort_info[key] = k_list
151-
else:
152-
raise ValueError("only 1D and 2D arrays supported")
153-
return sort_info
149+
def convert_arrays_to_lists(input_dict):
150+
# convert dictionary of numpy arrays to a dictionary of lists
151+
output_dict = deepcopy(input_dict)
152+
for key, value in output_dict.items():
153+
if not isinstance(value, np.ndarray):
154+
raise ValueError("all dictionary entries must be numpy arrays")
155+
output_dict[key] = value.tolist()
156+
return output_dict
154157

155158

156159
def proc_file(infile, opts):
@@ -282,17 +285,13 @@ def proc_file(infile, opts):
282285
fid.write('%s ' % val)
283286
fid.write('\n')
284287

285-
opts.dim_info = True # TODO: remove hard-coded value
286-
if opts.dim_info and pr_img.dataobj._dim_4_labels is not None:
287-
labels = pr_img.dataobj._dim_4_labels
288-
elif opts.dim_info and pr_img.dataobj._dim_3_4_labels is not None:
289-
labels = pr_img.dataobj._dim_3_4_labels
290-
else:
291-
labels = None
292-
if labels is not None and len(labels) > 0:
293-
sort_info = sort_info_to_lists(labels)
294-
with open(basefilename + '.ordering.json', 'w') as fid:
295-
json.dump(sort_info, fid, sort_keys=True, indent=4)
288+
# export data labels varying along the 4th dimensions
289+
if opts.dim_info:
290+
labels = pr_img.header.get_dimension_labels(collapse_slices=True)
291+
if len(labels) > 0:
292+
labels = convert_arrays_to_lists(labels)
293+
with open(basefilename + '.ordering.json', 'w') as fid:
294+
json.dump(labels, fid, sort_keys=True, indent=4)
296295

297296
# write out dwell time if requested
298297
if opts.dwell_time:

nibabel/parrec.py

Lines changed: 29 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ def _unique_rows(a):
257257
if a.ndim != 2:
258258
raise ValueError("expected 2D input")
259259
b = np.ascontiguousarray(a).view(
260-
np.dtype((np.void, a.dtype.itemsize * a.shape[1])))
260+
np.dtype((np.void, a.dtype.itemsize * a.shape[1])))
261261
return np.unique(b).view(a.dtype).reshape(-1, a.shape[1])
262262

263263

@@ -581,21 +581,6 @@ def __init__(self, file_like, header, mmap=True, scaling='dv'):
581581
self._slice_scaling = header.get_data_scaling(scaling)
582582
self._rec_shape = header.get_rec_shape()
583583

584-
try:
585-
# store additional slice & volume label information
586-
self._dim_4_labels = header.sorted_labels(
587-
sorted_indices=self._slice_indices,
588-
collapse_slices=True)
589-
self._dim_3_4_labels = None
590-
except:
591-
# if non-unique slice orientations or image angulations, need to
592-
# also keep labels for the 3rd (slice) dimension
593-
self._dim_4_labels = None
594-
self._dim_3_4_labels = header.sorted_labels(
595-
sorted_indices=self._slice_indices,
596-
collapse_slices=False)
597-
598-
599584
@property
600585
def shape(self):
601586
return self._shape
@@ -1141,25 +1126,27 @@ def get_sorted_slice_indices(self):
11411126
n_used = np.prod(self.get_data_shape()[2:])
11421127
return sort_order[:n_used]
11431128

1144-
def sorted_labels(self, sorted_indices=None, collapse_slices=True):
1145-
""" return a dictionary of lists of the varying values in
1146-
``self.image_defs``. This is useful for custom data sorting. only
1147-
keys that have more than 1 unique value across the dataset will exist
1148-
in the returned `sort_info` dictionary.
1129+
def get_dimension_labels(self, collapse_slices=True):
1130+
""" Dynamic labels corresponding to the final data dimension(s).
1131+
1132+
This is useful for custom data sorting. A subset of the info in
1133+
``self.image_defs`` is returned in an order that matches the final
1134+
data dimension(s). Only labels that have more than one unique value
1135+
across the dataset will be returned.
11491136
11501137
Parameters
11511138
----------
1152-
sorted_indices : array, optional
1153-
sorted slice indices as returned by
1154-
``self.get_sorted_slice_indices``.
11551139
collapse_slices : bool, optional
1156-
if True, only return indices corresponding to the first slice
1140+
If True, only return volume indices (corresponding to the first
1141+
slice). If False, return indices corresponding to individual
1142+
slices as well (with shape matching self.shape[2:]).
11571143
11581144
Returns
11591145
-------
11601146
sort_info : dict
11611147
Each key corresponds to a dynamically varying sequence dimension.
1162-
The ordering of values corresponds to that in sorted_indices.
1148+
The ordering of each value corresponds to that returned by
1149+
``self.get_sorted_slice_indices``.
11631150
"""
11641151

11651152
# define which keys to store sorting info for
@@ -1176,21 +1163,25 @@ def sorted_labels(self, sorted_indices=None, collapse_slices=True):
11761163
'gradient orientation number',
11771164
'diffusion b value number']
11781165

1179-
if sorted_indices is None:
1180-
sorted_indices = self.get_sorted_slice_indices()
1166+
sorted_indices = self.get_sorted_slice_indices()
1167+
image_defs = self.image_defs
11811168

11821169
if collapse_slices:
11831170
dynamic_keys.remove('slice number')
11841171

1185-
non_unique_keys = []
1172+
# remove dynamic keys that may not be present in older .PAR versions
1173+
for key in dynamic_keys:
1174+
if key not in image_defs.dtype.fields:
1175+
dynamic_keys.remove(key)
11861176

1177+
non_unique_keys = []
11871178
for key in dynamic_keys:
1188-
ndim = self.image_defs[key].ndim
1179+
ndim = image_defs[key].ndim
11891180
if ndim == 1:
1190-
num_unique = len(np.unique(self.image_defs[key]))
1181+
num_unique = len(np.unique(image_defs[key]))
11911182
elif ndim == 2:
11921183
# for 2D cases, e.g. 'image angulation'
1193-
num_unique = len(_unique_rows(self.image_defs[key]))
1184+
num_unique = len(_unique_rows(image_defs[key]))
11941185
else:
11951186
raise ValueError("unexpected image_defs shape > 2D")
11961187
if num_unique > 1:
@@ -1203,18 +1194,19 @@ def sorted_labels(self, sorted_indices=None, collapse_slices=True):
12031194
if 'image angulation' in non_unique_keys:
12041195
raise ValueError("for non-unique image angulation, need "
12051196
"collapse_slice=False")
1206-
if 'image image_display_orientation' in non_unique_keys: # ???
1197+
if 'image_display_orientation' in non_unique_keys: # ???
12071198
raise ValueError("for non-unique display orientation, need "
12081199
"collapse_slice=False")
1209-
sl1_indices = self.image_defs['slice number'][sorted_indices] == 1
1200+
sl1_indices = image_defs['slice number'][sorted_indices] == 1
12101201

12111202
sort_info = {}
12121203
for key in non_unique_keys:
1213-
12141204
if collapse_slices:
1215-
sort_info[key] = self.image_defs[key][sorted_indices][sl1_indices]
1205+
sort_info[key] = image_defs[key][sorted_indices][sl1_indices]
12161206
else:
1217-
sort_info[key] = self.image_defs[key][sorted_indices]
1207+
value = image_defs[key][sorted_indices]
1208+
sort_info[key] = value.reshape(self.get_data_shape()[2:],
1209+
order='F')
12181210
return sort_info
12191211

12201212

0 commit comments

Comments
 (0)