Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FIX: Check for valid qform before calculating change #466

Merged
merged 2 commits into from
Feb 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 20 additions & 13 deletions niworkflows/interfaces/images.py
Original file line number Diff line number Diff line change
Expand Up @@ -420,25 +420,28 @@ def _run_interface(self, runtime):
elif (valid_sform and sform_code > 0) and (not matching_affines or qform_code == 0):
img.set_qform(sform, sform_code)
new_qform = img.get_qform()
if np.allclose(new_qform, qform) and qform_code > 0:
# False alarm
self._results['out_file'] = self.inputs.in_file
open(out_report, 'w').close()
self._results['out_report'] = out_report
return runtime
diff = np.linalg.inv(qform) @ new_qform
trans, rot, _, _ = transforms3d.affines.decompose44(diff)
angle = transforms3d.axangles.mat2axangle(rot)[1]
total_trans = np.sqrt(np.sum(trans * trans)) # Add angle and total_trans to report
warning_txt = 'Note on orientation: qform matrix overwritten'
description = """\
if valid_qform:
# False alarm - the difference is due to precision loss of qform
if np.allclose(new_qform, qform) and qform_code > 0:
self._results['out_file'] = self.inputs.in_file
open(out_report, 'w').close()
self._results['out_report'] = out_report
return runtime
# Replacing an existing, valid qform. Report magnitude of change.
diff = np.linalg.inv(qform) @ new_qform
trans, rot, _, _ = transforms3d.affines.decompose44(diff)
angle = transforms3d.axangles.mat2axangle(rot)[1]
total_trans = np.sqrt(np.sum(trans * trans)) # Add angle and total_trans to report
warning_txt = 'Note on orientation: qform matrix overwritten'
description = """\
<p class="elem-desc">
The qform has been copied from sform.
The difference in angle is {angle:.02g}.
The difference in translation is {total_trans:.02g}.
</p>
""".format(angle=angle, total_trans=total_trans)
if not valid_qform and qform_code > 0:
elif qform_code > 0:
# qform code indicates the qform is supposed to be valid. Use more stridency.
warning_txt = 'WARNING - Invalid qform information'
description = """\
<p class="elem-desc">
Expand All @@ -448,6 +451,10 @@ def _run_interface(self, runtime):
by the scanner is advised.
</p>
"""
else: # qform_code == 0
# qform is not expected to be valids. Simple note.
warning_txt = 'Note on orientation: qform matrix overwritten'
description = '<p class="elem-desc">The qform has been copied from sform.</p>'
# Rows 5-6:
else:
affine = img.header.get_base_affine()
Expand Down
46 changes: 43 additions & 3 deletions niworkflows/interfaces/tests/test_images.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,51 @@ def test_qformsform_warning(tmp_path, qform_add, sform_add, expectation):
validate = pe.Node(im.ValidateImage(), name='validate', base_dir=str(tmp_path))
validate.inputs.in_file = fname
res = validate.run()
out_report = Path(res.outputs.out_report).read_text()
if expectation == 'warn':
assert "Note on" in Path(res.outputs.out_report).read_text()
assert len(Path(res.outputs.out_report).read_text()) > 0
assert "Note on" in out_report
elif expectation == 'no_warn':
assert len(Path(res.outputs.out_report).read_text()) == 0
assert len(out_report) == 0


@pytest.mark.parametrize('qform_code, warning_text', [
(0, "Note on orientation"),
(1, "WARNING - Invalid qform"),
])
def test_bad_qform(tmp_path, qform_code, warning_text):
fname = str(tmp_path / 'test.nii')

# make a random image
random_data = np.random.random(size=(5, 5, 5) + (5,))
img = nb.Nifti1Image(random_data, np.eye(4))

# Some magic terms from a bad qform in the wild
img.header['qform_code'] = qform_code
img.header['quatern_b'] = 0
img.header['quatern_c'] = 0.998322
img.header['quatern_d'] = -0.0579125
img.to_filename(fname)

validate = pe.Node(im.ValidateImage(), name='validate', base_dir=str(tmp_path))
validate.inputs.in_file = fname
res = validate.run()
assert warning_text in Path(res.outputs.out_report).read_text()


def test_no_good_affines(tmp_path):
fname = str(tmp_path / 'test.nii')

# make a random image
random_data = np.random.random(size=(5, 5, 5) + (5,))
img = nb.Nifti1Image(random_data, None)
img.header['qform_code'] = 0
img.header['sform_code'] = 0
img.to_filename(fname)

validate = pe.Node(im.ValidateImage(), name='validate', base_dir=str(tmp_path))
validate.inputs.in_file = fname
res = validate.run()
assert 'WARNING - Missing orientation information' in Path(res.outputs.out_report).read_text()


@pytest.mark.parametrize('nvols, nmasks, ext, factor', [
Expand Down