Commit 71bbb316 authored by William Clarke's avatar William Clarke
Browse files

Add results_to_spectrum script and tests.

parent 73a4430c
This document contains the FSL-MRS release history in reverse chronological order.
1.1.0 (WIP)
---------------------------------
- Support for NIfTI-MRS format.
- Preprocessing scripts reoriented around NIfTI-MRS framework
- New script results_to_spectrum for generating full fits in NIfTI-MRS format from fsl_mrs results.
1.0.6 (Tuesday 12th January 2021)
---------------------------------
- Internal changes to core MRS class.
......@@ -10,7 +16,7 @@ This document contains the FSL-MRS release history in reverse chronological orde
- Synthetic spectra now use fitting model directly.
- Bug fixes in the fsl_Mrs commandline interface. Thanks to Alex Craig-Craven.
- WIP: Dynamic fitting model and dynamic experiment simulation.
- spec2nii requirement pinned to 0.2.11 during NIFTI-MRS development.
- spec2nii requirement pinned to 0.2.11 during NIfTI-MRS development.
1.0.5 (Friday 9th October 2020)
-------------------------------
......
......@@ -138,6 +138,7 @@ def main():
import time
import os
import shutil
import json
import warnings
import matplotlib
matplotlib.use('agg')
......@@ -170,7 +171,7 @@ def main():
# Save chosen arguments
with open(os.path.join(args.output, "options.txt"), "w") as f:
f.write(str(args))
f.write(json.dumps(vars(args)))
f.write("\n--------\n")
f.write(p.format_values())
......
#!/usr/bin/env python
# results_to_spectrum - script to convert result to a full spectrum
#
# Author: Saad Jbabdi <saad@fmrib.ox.ac.uk>
# William Carke <william.clarke@ndcn.ox.ac.uk>
#
# Copyright (C) 2021 University of Oxford
# SHBASECOPYRIGHT
import argparse
from pathlib import Path
def main():
# Parse command-line arguments
parser = argparse.ArgumentParser(
description="FSL Magnetic Resonance Spectroscopy - convert fsl_mrs results to spectrum.")
parser.add_argument('results_dir', type=Path,
help='Directory containing fsl_mrs results.')
parser.add_argument('--export_baseline', action="store_true",
help="Output just baseline")
parser.add_argument('--export_no_baseline', action="store_true",
help="Output fit without baseline")
parser.add_argument('--export_separate', action="store_true",
help="Output individual metabolites")
parser.add_argument('-o', '--output', type=Path,
help='Output directory', default='.')
parser.add_argument('-f', '--filename', type=str,
help='Output file name', default='fit')
args = parser.parse_args()
import pandas as pd
import json
from fsl_mrs.utils import results, mrs_io, misc
from fsl_mrs.utils.baseline import prepare_baseline_regressor
from fsl_mrs.core.nifti_mrs import gen_new_nifti_mrs
# Read all_parameters.csv into pandas DF
param_df = pd.read_csv(args.results_dir / 'all_parameters.csv')
# Read options.txt
with open(args.results_dir / 'options.txt', "r") as f:
orig_args = json.loads(f.readline())
# Load data into mrs object
FID = mrs_io.read_FID(orig_args['data'])
basis, names, basisheader = mrs_io.read_basis(orig_args['basis'])
# Instantiate MRS object
mrs = FID.mrs(basis=basis,
names=names,
basis_hdr=basisheader[0])
if orig_args['conjfid'] is not None:
if orig_args['conjfid']:
mrs.conj_FID()
else:
mrs.check_FID(repair=True)
if orig_args['conjbasis'] is not None:
if orig_args['conjbasis']:
mrs.conj_Basis()
else:
mrs.check_Basis(repair=True)
if not orig_args['no_rescale']:
mrs.rescaleForFitting(ind_scaling=orig_args['ind_scale'])
mrs.keep(orig_args['keep'])
mrs.ignore(orig_args['ignore'])
if orig_args['lorentzian']:
model = 'lorentzian'
else:
model = 'voigt'
method = orig_args['algo']
# Generate metabolite groups
metab_groups = misc.parse_metab_groups(mrs, orig_args['metab_groups'])
baseline_order = orig_args['baseline_order']
ppmlim = orig_args['ppmlim']
# Generate baseline polynomials (B)
B = prepare_baseline_regressor(mrs, baseline_order, ppmlim)
# Add any basisc MM
if orig_args['add_MM']:
nMM = mrs.add_MM_peaks(gamma=10, sigma=20)
G = [i + max(metab_groups) + 1 for i in range(nMM)]
metab_groups += G
# Generate results object
res = results.FitRes(model, method, mrs.names, metab_groups, baseline_order, B, ppmlim)
res.loadResults(mrs, param_df['mean'].to_numpy())
# res.params = param_df['mean'].to_numpy()
if orig_args['combine'] is not None:
res.combine(orig_args['combine'])
data_out = res.predictedFID(mrs, mode='Full')
data_out /= mrs.scaling['FID']
data_out = data_out.reshape((1, 1, 1) + data_out.shape)
out = gen_new_nifti_mrs(data_out,
mrs.dwellTime,
mrs.centralFrequency,
nucleus=mrs.nucleus,
affine=FID.voxToWorldMat)
out.save(args.output / args.filename)
if args.export_no_baseline:
data_out = res.predictedFID(mrs, mode='Full', noBaseline=True)
data_out /= mrs.scaling['FID']
data_out = data_out.reshape((1, 1, 1) + data_out.shape)
out = gen_new_nifti_mrs(data_out,
mrs.dwellTime,
mrs.centralFrequency,
nucleus=mrs.nucleus,
affine=FID.voxToWorldMat)
out.save(args.output / (args.filename + '_no_baseline'))
if args.export_baseline:
data_out = res.predictedFID(mrs, mode='baseline')
data_out /= mrs.scaling['FID']
data_out = data_out.reshape((1, 1, 1) + data_out.shape)
out = gen_new_nifti_mrs(data_out,
mrs.dwellTime,
mrs.centralFrequency,
nucleus=mrs.nucleus,
affine=FID.voxToWorldMat)
out.save(args.output / (args.filename + '_baseline'))
if args.export_separate:
for metab in res.original_metabs:
data_out = res.predictedFID(mrs, mode=metab)
data_out /= mrs.scaling['FID']
data_out = data_out.reshape((1, 1, 1) + data_out.shape)
out = gen_new_nifti_mrs(data_out,
mrs.dwellTime,
mrs.centralFrequency,
nucleus=mrs.nucleus,
affine=FID.voxToWorldMat)
out.save(args.output / (args.filename + f'_{metab}'))
if __name__ == '__main__':
main()
# Test the generation of spectra from fsl_mrs output
# Imports
import subprocess
import os.path as op
# Files
testsPath = op.dirname(__file__)
data = {'metab': op.join(testsPath, 'testdata/fsl_mrs/metab.nii.gz'),
'water': op.join(testsPath, 'testdata/fsl_mrs/wref.nii.gz'),
'basis': op.join(testsPath, 'testdata/fsl_mrs/steam_basis'),
'seg': op.join(testsPath, 'testdata/fsl_mrs/segmentation.json')}
def test_results_to_spectrum(tmp_path):
subprocess.check_call(['fsl_mrs',
'--data', data['metab'],
'--basis', data['basis'],
'--output', tmp_path,
'--h2o', data['water'],
'--TE', '11',
'--metab_groups', 'Mac',
'--tissue_frac', '0.45', '0.45', '0.1',
'--overwrite',
'--combine', 'Cr', 'PCr'])
subprocess.check_call(['results_to_spectrum',
str(tmp_path),
'--output', str(tmp_path),
'--filename', 'test',
'--export_baseline',
'--export_no_baseline',
'--export_separate'])
assert op.exists(op.join(tmp_path, 'test.nii.gz'))
assert op.exists(op.join(tmp_path, 'test_baseline.nii.gz'))
assert op.exists(op.join(tmp_path, 'test_no_baseline.nii.gz'))
assert op.exists(op.join(tmp_path, 'test_NAA.nii.gz'))
assert op.exists(op.join(tmp_path, 'test_Cr.nii.gz'))
......@@ -43,5 +43,6 @@ setup(name='fsl_mrs',
'fsl_mrs/scripts/mrs_vis',
'fsl_mrs/scripts/merge_mrs_reports',
'fsl_mrs/scripts/svs_segment',
'fsl_mrs/scripts/mrsi_segment']
'fsl_mrs/scripts/mrsi_segment',
'fsl_mrs/scripts/results_to_spectrum']
)
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment