Skip to content

Commit f476c48

Browse files
committed
new structure, names, tests
1 parent 676ac70 commit f476c48

File tree

3 files changed

+189
-67
lines changed

3 files changed

+189
-67
lines changed

nibabel/cmdline/diff.py

Lines changed: 133 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -44,95 +44,175 @@ def get_opt_parser():
4444
return p
4545

4646

47-
def diff_values(compare1, compare2):
47+
def diff_values(first_item, second_item):
4848
"""Generically compares two values, returns true if different"""
49-
if np.any(compare1 != compare2):
49+
if np.any(first_item != second_item): # comparing items that are instances of class np.ndarray
5050
return True
51-
elif type(compare1) != type(compare2):
51+
52+
elif type(first_item) != type(second_item): # comparing items that differ in data type
5253
return True
53-
else:
54-
return compare1 != compare2
5554

55+
else: # all other use cases
56+
return first_item != second_item
57+
58+
59+
def diff_headers(files, fields):
60+
"""Iterates over all header fields of all files to find those that differ
61+
62+
Parameters
63+
----------
64+
files: a given list of files to be compared
65+
fields: the fields to be compared
66+
67+
Returns
68+
-------
69+
list
70+
header fields whose values differ across files
71+
"""
72+
73+
headers = []
74+
75+
for f in range(len(files)): # for each file
76+
for h in fields: # for each header
77+
78+
# each maneuver is encased in a try block after exceptions have previously occurred
79+
# get the particular header field within the particular file
80+
81+
try:
82+
field = files[f][h]
83+
84+
except ValueError:
85+
continue
86+
87+
# filter numpy arrays with a NaN value
88+
try:
89+
if np.all(np.isnan(field)):
90+
continue
91+
92+
except TypeError:
93+
pass
94+
95+
# compare current file with other files
96+
for i in files[f+1:]:
97+
other_field = i[h]
98+
99+
# sometimes field.item doesn't work
100+
try:
101+
# converting bytes to be compared as strings
102+
if isinstance(field.item(0), bytes):
103+
field = field.item(0).decode("utf-8")
104+
105+
# converting np.ndarray to lists to remove ambiguity
106+
if isinstance(field, np.ndarray):
107+
field = field.tolist()
108+
109+
if isinstance(other_field.item(0), bytes):
110+
other_field = other_field.item(0).decode("utf-8")
111+
if isinstance(other_field, np.ndarray):
112+
other_field = other_field.tolist()
56113

57-
def diff_header_fields(key, inputs):
58-
"""Iterates over a single header field of multiple files"""
114+
except AttributeError:
115+
continue
116+
117+
# if the header values of the two files are different, append
118+
if diff_values(field, other_field):
119+
headers.append(h)
120+
121+
if headers: # return a list of headers for the files whose values differ
122+
return headers
123+
124+
125+
def diff_header_fields(header_field, files):
126+
"""Iterates over a single header field of multiple files
127+
128+
Parameters
129+
----------
130+
header_field: a given header field
131+
files: the files to be compared
132+
133+
Returns
134+
-------
135+
list
136+
str for each value corresponding to each file's given header field
137+
"""
59138

60139
keyed_inputs = []
61140

62-
for i in inputs: # stores each file's respective header files
141+
for i in files:
142+
143+
# each maneuver is encased in a try block after exceptions have previously occurred
144+
# get the particular header field within the particular file
145+
63146
try:
64-
field_value = i[key]
147+
field_value = i[header_field]
65148
except ValueError:
66149
continue
67150

68-
try: # filter numpy arrays
69-
if np.all(np.isnan(field_value)):
70-
continue
71-
except TypeError:
72-
pass
73-
74-
for x in inputs[1:]: # compare different values, print all as soon as diff is found
151+
# compare different data types, return all values as soon as diff is found
152+
for x in files[1:]:
75153
try:
76-
data_diff = diff_values(str(x[key].dtype), str(field_value.dtype))
154+
data_diff = diff_values(str(x[header_field].dtype), str(field_value.dtype))
77155

78156
if data_diff:
79157
break
80158
except ValueError:
81159
continue
82160

161+
# string formatting of responses
83162
try:
84-
if data_diff: # prints data types if they're different and not if they're not
163+
164+
# if differences are found among data types
165+
if data_diff:
166+
# accounting for how to arrange arrays
85167
if field_value.ndim < 1:
86168
keyed_inputs.append("{}@{}".format(field_value, field_value.dtype))
87169
elif field_value.ndim == 1:
88170
keyed_inputs.append("{}@{}".format(list(field_value), field_value.dtype))
171+
172+
# if no differences are found among data types
89173
else:
90174
if field_value.ndim < 1:
91-
keyed_inputs.append("{}".format(field_value))
175+
keyed_inputs.append(field_value)
92176
elif field_value.ndim == 1:
93-
keyed_inputs.append("{}".format(list(field_value)))
177+
keyed_inputs.append(list(field_value))
178+
94179
except UnboundLocalError:
95180
continue
96181

97-
if keyed_inputs: # sometimes keyed_inputs is empty lol
98-
comparison_input = keyed_inputs[0]
182+
for i in range(len(keyed_inputs)):
183+
keyed_inputs[i] = str(keyed_inputs[i])
99184

100-
for i in keyed_inputs[1:]:
101-
if diff_values(comparison_input, i):
102-
return keyed_inputs
185+
return keyed_inputs
103186

104187

105-
def get_headers_diff(files, opts):
188+
def get_headers_diff(file_headers, headers):
106189
"""Get difference between headers
107190
108191
Parameters
109192
----------
110-
files: list of files
111-
opts: any options included from the command line
193+
file_headers: list of actual headers from files
194+
headers: list of header fields that differ
112195
113196
Returns
114197
-------
115198
dict
116-
str: list for each header field which differs, return list of
199+
str: list for each header field which differs, return list of
117200
values per each file
118201
"""
119-
120-
header_list = [nib.load(f).header for f in files]
121202
output = OrderedDict()
122203

123-
if opts.header_fields: # will almost always have a header field
124-
# signals "all fields"
125-
if opts.header_fields == 'all':
126-
# TODO: header fields might vary across file types, thus prior sensing would be needed
127-
header_fields = header_list[0].keys()
128-
else:
129-
header_fields = opts.header_fields.split(',')
204+
# if there are headers that differ
205+
if headers:
130206

131-
for f in header_fields:
132-
val = diff_header_fields(f, header_list)
207+
# for each header
208+
for header in headers:
133209

210+
# find the values corresponding to the files that differ
211+
val = diff_header_fields(header, file_headers)
212+
213+
# store these values in a dictionary
134214
if val:
135-
output[f] = val
215+
output[header] = val
136216

137217
return output
138218

@@ -164,8 +244,19 @@ def main():
164244
# suppress nibabel format-compliance warnings
165245
nib.imageglobals.logger.level = 50
166246

167-
diff = get_headers_diff(files, opts)
247+
file_headers = [nib.load(f).header for f in files]
248+
249+
if opts.header_fields: # will almost always have a header field
250+
# signals "all fields"
251+
if opts.header_fields == 'all':
252+
# TODO: header fields might vary across file types, thus prior sensing would be needed
253+
header_fields = file_headers[0].keys()
254+
else:
255+
header_fields = opts.header_fields.split(',')
256+
headers = diff_headers(file_headers, header_fields)
257+
diff = get_headers_diff(file_headers, headers)
168258
data_diff = get_data_md5sums(files)
259+
169260
if data_diff:
170261
diff['DATA(md5)'] = data_diff
171262

nibabel/cmdline/tests/test_utils.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,14 @@
1111
from nose.tools import (assert_true, assert_false, assert_raises,
1212
assert_equal, assert_not_equal)
1313

14+
import nibabel as nib
1415
from nibabel.cmdline.utils import *
16+
from nibabel.cmdline.diff import diff_header_fields, diff_headers
17+
from os.path import (dirname, join as pjoin, abspath, splitext, basename,
18+
exists)
19+
20+
21+
DATA_PATH = abspath(pjoin(dirname(__file__), '../../tests/data'))
1522

1623

1724
def test_table2string():
@@ -42,3 +49,26 @@ def get_test(self):
4249

4350
assert_equal(safe_get(test, "test"), 2)
4451
assert_equal(safe_get(test, "failtest"), "-")
52+
53+
54+
def test_diff_headers():
55+
fnames = [pjoin(DATA_PATH, f)
56+
for f in ('standard.nii.gz', 'example4d.nii.gz')]
57+
file_headers = [nib.load(f).header for f in fnames]
58+
headers = ['sizeof_hdr', 'data_type', 'db_name', 'extents', 'session_error', 'regular', 'dim_info', 'dim', 'intent_p1',
59+
'intent_p2', 'intent_p3', 'intent_code', 'datatype', 'bitpix', 'slice_start', 'pixdim', 'vox_offset', 'scl_slope',
60+
'scl_inter', 'slice_end', 'slice_code', 'xyzt_units', 'cal_max', 'cal_min', 'slice_duration', 'toffset', 'glmax',
61+
'glmin', 'descrip', 'aux_file', 'qform_code', 'sform_code', 'quatern_b', 'quatern_c', 'quatern_d', 'qoffset_x',
62+
'qoffset_y', 'qoffset_z', 'srow_x', 'srow_y', 'srow_z', 'intent_name', 'magic']
63+
64+
assert_equal(diff_headers(file_headers, headers), ['regular', 'dim_info', 'dim', 'datatype', 'bitpix', 'pixdim',
65+
'slice_end', 'xyzt_units', 'cal_max', 'descrip', 'qform_code',
66+
'sform_code', 'quatern_b', 'quatern_c', 'quatern_d', 'qoffset_x',
67+
'qoffset_y', 'qoffset_z', 'srow_x', 'srow_y', 'srow_z'])
68+
69+
70+
def test_diff_header_fields():
71+
fnames = [pjoin(DATA_PATH, f)
72+
for f in ('standard.nii.gz', 'example4d.nii.gz')]
73+
file_headers = [nib.load(f).header for f in fnames]
74+
assert_equal(diff_header_fields("dim_info", file_headers), ['0', '57'])

nibabel/tests/test_scripts.py

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import difflib
1919

2020
import nibabel as nib
21+
from nibabel.cmdline.diff import diff_headers
2122
from ..tmpdirs import InTemporaryDirectory
2223
from ..loadsave import load
2324
from ..orientations import flip_axis, aff2axcodes, inv_ornt_aff
@@ -69,35 +70,35 @@ def check_nib_ls_example4d(opts=[], hdrs_str="", other_str=""):
6970
assert_re_in(expected_re, stdout[len(fname):])
7071

7172

72-
def check_nib_diff_examples(opts=[], hdrs_str="", other_str=""):
73+
def check_nib_diff_examples():
7374
# test nib-diff script
7475
fnames = [pjoin(DATA_PATH, f)
75-
for f in ('example4d.nii.gz', 'standard.nii.gz')]
76+
for f in ('standard.nii.gz', 'example4d.nii.gz')]
7677
target_output = """\
7778
These files are different.
78-
Field example4d.nii.gz standard.nii.gz
79-
regular r
80-
dim_info 57 0
81-
dim [4, 128, 96, 24, 2, 1, 1, 1] [3, 4, 5, 7, 1, 1, 1, 1]
82-
datatype 4 2
83-
bitpix 16 8
84-
pixdim [-1.0, 2.0, 2.0, 2.199999, 2000.0, 1.0, 1.0, 1.0][1.0, 1.0, 3.0, 2.0, 1.0, 1.0, 1.0, 1.0]
85-
slice_end 23 0
86-
xyzt_units 10 0
87-
cal_max 1162.0 0.0
88-
descrip FSL3.3? v2.25 NIfTI-1 Single file format
89-
qform_code 1 0
90-
sform_code 1 2
91-
quatern_b -1.94510681403e-26 0.0
92-
quatern_c -0.996708512306 0.0
93-
quatern_d -0.081068739295 0.0
94-
qoffset_x 117.855102539 0.0
95-
qoffset_y -35.7229423523 0.0
96-
qoffset_z -7.24879837036 0.0
97-
srow_x [-2.0, 6.7147157e-19, 9.0810245e-18, 117.8551][1.0, 0.0, 0.0, 0.0]
98-
srow_y [-6.7147157e-19, 1.9737115, -0.35552824, -35.722942][0.0, 3.0, 0.0, 0.0]
99-
srow_z [8.255481e-18, 0.32320762, 2.1710818, -7.2487984][0.0, 0.0, 2.0, 0.0]
100-
DATA(md5) b0abbc492b4fd533b2c80d82570062cf 0a2576dd6badbb25bfb3b12076df986b"""
79+
Field standard.nii.gz example4d.nii.gz
80+
regular b'' b'r'
81+
dim_info 0 57
82+
dim [3, 4, 5, 7, 1, 1, 1, 1] [4, 128, 96, 24, 2, 1, 1, 1]
83+
datatype 2 4
84+
bitpix 8 16
85+
pixdim [1.0, 1.0, 3.0, 2.0, 1.0, 1.0, 1.0, 1.0] [-1.0, 2.0, 2.0, 2.1999991, 2000.0, 1.0, 1.0, 1.0]
86+
slice_end 0 23
87+
xyzt_units 0 10
88+
cal_max 0.0 1162.0
89+
descrip b'' b'FSL3.3? v2.25 NIfTI-1 Single file format'
90+
qform_code 0 1
91+
sform_code 2 1
92+
quatern_b 0.0 -1.9451068140294884e-26
93+
quatern_c 0.0 -0.9967085123062134
94+
quatern_d 0.0 -0.0810687392950058
95+
qoffset_x 0.0 117.8551025390625
96+
qoffset_y 0.0 -35.72294235229492
97+
qoffset_z 0.0 -7.248798370361328
98+
srow_x [1.0, 0.0, 0.0, 0.0] [-2.0, 6.7147157e-19, 9.0810245e-18, 117.8551]
99+
srow_y [0.0, 3.0, 0.0, 0.0] [-6.7147157e-19, 1.9737115, -0.35552824, -35.722942]
100+
srow_z [0.0, 0.0, 2.0, 0.0] [8.2554809e-18, 0.32320762, 2.1710818, -7.2487984]
101+
DATA(md5) 0a2576dd6badbb25bfb3b12076df986b b0abbc492b4fd533b2c80d82570062cf"""
101102
fnames2 = [pjoin(DATA_PATH, f)
102103
for f in ('example4d.nii.gz', 'example4d.nii.gz')]
103104
code, stdout, stderr = run_command(['nib-diff'] + fnames, check_code=False)

0 commit comments

Comments
 (0)