Commit 7f92d9d5 authored by William Clarke's avatar William Clarke
Browse files

Merge branch 'master' into 'master'

V1.1.3

See merge request fsl/fsl_mrs!20
parents 7d7a1bce 1f89bd3c
......@@ -88,6 +88,7 @@ pytest:
- pip install .
script:
- pytest fsl_mrs/tests
- pytest fsl_mrs/denmatsim/tests
############
# 3. doc
......
This document contains the FSL-MRS release history in reverse chronological order.
1.1.3 (Tuesday 29th June 2021)
------------------------------
- Added mrs_tools script. Replaces mrs_vis and mrs_info. Adds split/merge/reorder functionality.
- Added basis_tools script. Tools for manipulating (shifting, scaling, converting, differencing, conjugating, and adding to) basis sets.
- Improved display of basis sets using mrs_tools or basis_tools.
- Added 'default' MEGA-PRESS MM option to fsl_mrs and mrs class.
- Preprocessing tools now add processing provenance information to NIfTI-MRS files.
- Under the hood refactor of basis, MRS, and MRSI classes.
- Updated density matrix simulator. Added some automatic testing.
- Added documentation about the results_to_spectrum script.
1.1.2 (Friday 16th April 2021)
------------------------------
- Added 2H information
......
......@@ -51,13 +51,14 @@ After installation see the [quick start guide](https://open.win.ox.ac.uk/pages/f
: Pre-packaged processing for non-edited SVS.
- **fsl\_mrs\_sim**
: simulate basis spectra
- **mrs_vis**
: quick visualisation of the spectra or basis spectra
- **mrs_info**
: quick information on a NIfTI-MRS file
- **mrs_tools**
: Collection of tools for NIfTI-MRS. Includes quick visualisation and information.
- **basis_tools**
: Collection of tools for manipulating basis sets.
- **svs_segment & mrsi_segment**
: Run tissue segmentation for SVS/MRSI from T1 image.
- **results_to_spectrum**
: Generate spectrum representation of a fit from *fsl_mrs* results.
---
## Documentation
......
.. _basis_tools:
Basis Spectra Manipulation
==========================
basis_tools
-----------
The :code:`basis_tools` script provides the user with tools for manipulating basis spectra in the FSL-MRS JSON format.
Provide one of the following subcommands with :code:`basis_tools` to convert, scale, add to, difference, or shift basis spectra.
info
****
| *Example* :code:`basis_tools info path/to/my/basis`
| Provides a short summary of the contents of the basis set.
vis
***
| *Example* :code:`basis_tools vis path/to/my/basis --ppmlim 0.2 4.2`
| Provides visualisation of the basis set.
convert
*******
| *Example* :code:`basis_tools convert path/to/my/lcmbasis.BASIS path/to/my/fslbasis`
| Convert LCModel (.Basis) or JMRUI format basis sets to FSL-MRS (.json) format.
add
***
| *Example* :code:`basis_tools add --scale --name my_new_basis my_new_basis.json path/to/my/fslbasis`
| Add a json formatted basis spectrum to an existing basis set.
shift
*****
| *Example* :code:`basis_tools shift path/to/my/fslbasis NAA 1.0 path/to/my/edited_fslbasis`
| Shift a basis spectrum on the chemical shift axis.
scale
*****
| *Example* :code:`basis_tools shift path/to/my/fslbasis NAA path/to/my/scaled_fslbasis`
| Rescale a basis spectrum to the mean of all other basis spectra (or to specified target :code:`--target_scale`.
diff
****
| *Example* :code:`basis_tools diff --add_or_sub sub mega_on mega_off mega_diff`
| Form a basis set for a difference method using two other basis set. Add or subtract using :code:`--add_or_sub {'add'|'sub}`.
\ No newline at end of file
Manipulating MRS Data
=====================
Handling NIfTI-MRS
------------------
MRS data stored in NIfTI-MRS format can contain multiple higher dimensions. For example it might contain dimensions encoding multiple receive coils, multiple temporal averages, or even a spectral editing dimension.
Data might need to be manipulated within the NIfTI-MRS storage framework before, after, or during preprocessing. For this, FLS-MRS provides the :code:`mrs_tools` command line script. :code:`mrs_tools` has the ability to merge and split NIfTI-MRS files along the higher encoding dimensions. It can also reorder the higher dimensions, or create a new singleton dimension for further manipulation.
:code:`mrs_tools` also contains the :code:`mrs_tools vis` and :code:`mrs_tools info` options to provide quick visualisation and information on the command line. See the :ref:`Visualisation <visualisation>` page for more information on :code:`mrs_tools vis/info`.
:code:`mrs_tools split` takes a single file and splits it along a specified dimension e.g. :code:`--dim DIM_DYN`, at a single point (:code:`--index 8`) or extracting multiple elements into a second file (:code:`--indices 8 9 10`).
:code:`mrs_tools merge` takes two or more files and merges them along a specified dimension e.g. :code:`--dim DIM_EDIT`.
:code:`mrs_tools reorder` permutes the dimensions of an existing NIfTI-MRS file. For example, the 5th through 7th dimensions can be changed from :code:`DIM_COIL, DIM_DYN, DIM_EDIT` to :code:`DIM_DYN, DIM_EDIT, DIM_COIL` using :code:`--dim_order DIM_DYN DIM_EDIT DIM_COIL`.
\ No newline at end of file
......@@ -41,7 +41,9 @@ Results from :code:`fsl_mrs` are stored in a single folder that contains the fol
- CSV files summarising the metabolite concentrations (and uncertainties), fitted parameters, and some QC measures.
- PNG files with summary of the fitting and (optionally) voxel location.
A nifti file of the fitted spectrum, baseline, and individual metabolites can be generated using the :code:`results_to_spectrum` script. For example::
results_to_spectrum --export_baseline example_fit
MRSI
----
......@@ -115,7 +117,7 @@ Details
Modelling
~~~~~~~~~
At the core of FSL-MRS is a linear combination model. For more details on the modelling refer to [CLAR20]_.
At the core of FSL-MRS is a linear combination model. For more details on the modelling refer to [CLAR21]_.
The signal in the spectral domain :math:`\mathrm{Y}(v)` is modelled as a linear combination of (shifted and broadened) metabolite basis spectra :math:`\mathrm{M}_{l,g}` (metab = :math:`l`, metab group = :math:`g`) plus a complex polynomial baseline :math:`\mathrm{B}(v)`. The signal model is as follows:
......@@ -162,6 +164,8 @@ Below are detailed explanations of some of the optional arguments in the wrapper
Group metaboites into sub-groups that get their own lineshape parameters (shift and broadening). This can either be a list of integers (one per metabolite) from 0 to the max number of groups minus one. Or it could be a list of metabolites to be grouped. E.g. using the flag :code:`--metab_groups Mac NAA+NAAG+Cr` then the Mac spectrum will have its own group, the NAA, NAAG, and Cr will be in a different group, and all other metabolites in a 3rd group. Other possibilities are combine_all and separate_all, where metabs are combined into a single group or separated into distinct groups respectively.
:code:`--add_MM`
Add macromolecule peaks at the following frequencies: 0.9, 1.2, 1.4, 1.7 ppm and a doublet at 2.08 & 3.0 ppm
:code:`--add_MM_MEGA`
Add linked macromolecule peaks at 0.915 & 3.0 ppm (ratio of 3.75:2.0). This option is experimental!
:code:`--lorentzian`
By default the lineshape is a Voigt (lorentizian+gaussian). Use this flag to set to Lorentzian.
:code:`--ind_scale`
......@@ -203,4 +207,4 @@ The the following calls to :code:`fsl_mrs` or :code:`fsl_mrsi` are equivalent:
References
----------
.. [CLAR20] Clarke WT, Jbabdi S. FSL-MRS: An end-to-end spectroscopy analysis package. Biorxiv 2020.
\ No newline at end of file
.. [CLAR21] Clarke WT, Stagg CJ, Jbabdi S. FSL-MRS: An end-to-end spectroscopy analysis package. Magnetic Resonance in Medicine 2021;85:2950–2964 doi: 10.1002/mrm.28630.
\ No newline at end of file
......@@ -19,11 +19,13 @@ If this is your first experience with FSL-MRS, get started with the :ref:`Quick
install
quick_start
data_conversion
data_handling
processing
fitting
quantitation
macromolecules
simulation
basis_tools
visualisation
constants
troubleshooting
......
......@@ -18,4 +18,4 @@ Citing FSL-MRS
--------------
If you use FSL-MRS please cite:
Clarke WT, Stagg CJ, Jbabdi S. FSL-MRS: An end-to-end spectroscopy analysis package. MRM https://doi.org/10.1002/mrm.28630
\ No newline at end of file
Clarke WT, Stagg CJ, Jbabdi S. FSL-MRS: An end-to-end spectroscopy analysis package. Magnetic Resonance in Medicine 2021;85:2950–2964 doi: 10.1002/mrm.28630.
\ No newline at end of file
......@@ -34,9 +34,9 @@ But note that there are frequently multiple calibration scans for e.g. shimming
1.1 Take a look at your data
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can use :code:`mrs_info` and :code:`mrs_vis` on the command line to view your data at any stage of the process. First mrs_info to see the dimensionality of the data::
You can use :code:`mrs_tools info` and :code:`mrs_tools vis` on the command line to view your data at any stage of the process. First :code:`mrs_tools info` to see the dimensionality of the data::
mrs_info my_metab_file.nii.gz
mrs_tools info my_metab_file.nii.gz
Read file my_metab_file.nii.gz (/path_to_file).
NIfTI-MRS version 0.2
......@@ -47,25 +47,25 @@ You can use :code:`mrs_info` and :code:`mrs_vis` on the command line to view you
Nucleus: 1H
Field Strength: 6.98 T
Then mrs_vis to visualise the data::
Then :code:`mrs_tools vis` to visualise the data::
mrs_vis my_metab_file.nii.gz
mrs_tools vis my_metab_file.nii.gz
:code:`mrs_vis` will automatically perform coil combination and averaging down to a single spectrum for display purposes only.
:code:`mrs_tools vis` will automatically perform coil combination and averaging down to a single spectrum for display purposes only.
.. image:: data/raw_conv.png
:width: 600
You can also quickly view data across one of the NIfTI-MRS higher dimensions (those containing uncombined coils, or averages etc.) In this case we plot all the transients stored in the dimension tagged *DIM_DYN* (i.e. averages)::
mrs_vis my_metab_file.nii.gz --display_dim DIM_DYN
mrs_tools vis my_metab_file.nii.gz --display_dim DIM_DYN
.. image:: data/mrs_vis_dir.png
:width: 600
If you see a significantly different picture (no data, just noise, etc.) stop and investigate. See :ref:`Troubleshooting <TS_4>`.
Have a look at the :ref:`Visualisation <visualisation>` page for more information on :code:`mrs_vis`.
Have a look at the :ref:`Visualisation <visualisation>` page for more information on :code:`mrs_tools vis`.
2. Process your raw data
~~~~~~~~~~~~~~~~~~~~~~~~
......@@ -101,9 +101,9 @@ The fitting in FSL-MRS requires the user to provide basis spectra. Basis spectra
`my_sequence_description.json` contains a description of the sequence broken down into blocks of RF pulses and gradients. This must be created for each sequence manually once. `metabs.txt` contains a list of metabolites to simulate. Much more information on constructing a suitable sequence description JSON file can be found on the :ref:`Basis Spectra Simulation <simulation>` page.
Have a quick check of your basis set using mrs_vis::
Have a quick check of your basis set using :code:`mrs_tools vis`::
mrs_vis my_basis_spectra/
mrs_tools vis my_basis_spectra/
4. Tissue Segmentation
~~~~~~~~~~~~~~~~~~~~~~
......
......@@ -44,6 +44,11 @@ Optionally:
- Automatic phasing of the spectra can be achieved by simulating a singlet peak at a specified offset from the receiver centre (:code:`-p, --autophase`, offset in ppm).
- Different format outputs can be specified (:code:`--jmrui, --raw, --lcmIN`).
Simulating basis spectra for j-difference editing
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To simulate a basis spectrum for a j-difference edited sequence (e.g. MEGA-PRESS) simulate the basis for each condition and create the difference spectrum using the :code:`basis_tools diff` script.
Choosing metabolites
~~~~~~~~~~~~~~~~~~~~
The simulator is aware of the following metabolites. Spin systems are specified as in [SIMP17]_ and [GOVI00]_.
......
......@@ -31,7 +31,7 @@ Troubleshooting hints
.. _TS_4:
4. Data looks 'wrong' after conversion
If when using :code:`mrs_vis` you see no signal and just noise try conjugating the data using :code:`fsl_mrs_proc conj` or try expanding the ppm range plotted :code:`--ppmlim -10 10`. If you see a flat line, then conversion failed. The data might be corrupted - did the acquisition complete successfully?
If when using :code:`mrs_tools vis` you see no signal and just noise try conjugating the data using :code:`fsl_mrs_proc conj` or try expanding the ppm range plotted :code:`--ppmlim -10 10`. If you see a flat line, then conversion failed. The data might be corrupted - did the acquisition complete successfully?
.. image:: data/bad_data.png
:width: 600
\ No newline at end of file
......@@ -10,37 +10,37 @@ There are 4 ways of visualising/interacting with MRS data in FSL-MRS:
1. Quick glance (human-readable, non-interactive)
2. CSV files (human- and machine-readable)
3. HTML reports (fairly interactive)
#. FSLeyes (highly interactive)
4. FSLeyes (highly interactive)
1. Quick glance
---------------
The first thing one might want to do when given a FID file or simulated spectra is to have a quick look at the spectra to see if they look like one would expect. To get a sense of the dimensionality and basic status of the data run :code:`mrs_info` for a quick text summary. FSL-MRS then provides a light-weight script (:code:`mrs_vis`) to quickly visualise the MRS or MRSI data. For example, running :code:`mrs_vis` on the provided example SVS data:
The first thing one might want to do when given a FID file or simulated spectra is to have a quick look at the spectra to see if they look like one would expect. To get a sense of the dimensionality and basic status of the data run :code:`mrs_tools info` for a quick text summary. FSL-MRS then provides a light-weight script (:code:`mrs_tools vis`) to quickly visualise the MRS or MRSI data. For example, running :code:`mrs_tools vis` on the provided example SVS data:
::
mrs_vis example_usage/example_data/metab.nii
mrs_tools vis example_usage/example_data/metab.nii
gives the following basic plot:
.. image:: data/mrs_vis_svs.png
:width: 400
Note that the reason :code:`mrs_vis` "knows" how to scale the x-axis is that the relevant information is stored in the NIfTI-MRS MRS header extension (namely the *dwell time* and the *central frequency*).
Note that the reason :code:`mrs_tools vis` "knows" how to scale the x-axis is that the relevant information is stored in the NIfTI-MRS MRS header extension (namely the *dwell time* and the *central frequency*).
:code:`mrs_vis` can also visualise a folder of mrs data::
:code:`mrs_tools vis` can also visualise a folder of mrs data::
mrs_vis ./converted_data_dir/
mrs_tools vis ./converted_data_dir/
.. image:: data/mrs_vis_dir.png
:width: 600
We can also run :code:`mrs_vis` to visualise the metabolite basis. Again below we do so for the simulated basis provided with the example data:
We can also run :code:`mrs_tools vis` to visualise the metabolite basis. Again below we do so for the simulated basis provided with the example data:
::
mrs_vis example_usage/example_data/steam_11ms/
mrs_tools vis example_usage/example_data/steam_11ms/
.. image:: data/mrs_vis_basis.png
......
......@@ -138,7 +138,7 @@
"outputs": [],
"source": [
"%%capture\n",
"%sx mrs_vis --save basis_fig.png steam_basis"
"%sx mrs_tools vis --save basis_fig.png steam_basis"
]
},
{
......@@ -170,4 +170,4 @@
},
"nbformat": 4,
"nbformat_minor": 4
}
}
\ No newline at end of file
%% Cell type:markdown id: tags:
# Example basis spectra creation
This notebook demos the process of creating basis spectra for fitting in FSL-MRS.
### Contents:
- [1. Sequence JSON file](#1.-Sequence-JSON-file)
- [1.1. Defining MM](#1.1-Defining-MM)
- [2. Simulation](#2.-Simulation)
- [3. Visualisation](#3.-Visualisation)
Will Clarke
June 2020
University of Oxford
%% Cell type:markdown id: tags:
## 1. Sequence JSON file
%% Cell type:markdown id: tags:
The sequence to simulate and the system parameters are descibed in a json file. The file breaks the sequence into a series of RF pulses (+ slice selcect gradients) and delays with optional rephasing gradients. A coherence filter is used to crush unwanted coherences.
%% Cell type:markdown id: tags:
## 1.1 Defining MM
An experimentally measured (or otherwise derived) macromollecues basis spectrum can be included in the basis spectrum by defining an additional JSON file.
%% Cell type:markdown id: tags:
## 2. Simulation
%% Cell type:code id: tags:
``` python
%sx fsl_mrs_sim -b example_data/metabs.txt \
-o steam_basis \
-p -2.66 \
--MM example_data/macSeed.json \
--overwrite \
-e 11.0 \
example_data/steam11.json
```
%%%% Output: execute_result
["Identified spinsystems: ['Ala', 'Asc', 'Asp', 'GPC', 'PCh', 'Cr', 'PCr', 'GABA', 'Glc', 'Gln', 'Glu', 'GSH', 'Ins', 'Lac', 'NAA', 'NAAG', 'PE', 'Scyllo', 'Tau']",
'Simulation running using mode 1d. Axis order = [0 1 2].',
'Auto-phase adjustment. Phasing peak position = 1.99 ppm',
'Rx_Phase: 2.3408',
'Additional phase: 0.059',
'Final Rx_Phase: 2.400',
'Running simulation on Asc.',
'Simulation running using mode 1d. Axis order = [0 1 2].',
'Running simulation on Glu.',
'Simulation running using mode 1d. Axis order = [0 1 2].',
'Running simulation on NAA.',
'Simulation running using mode 1d. Axis order = [0 1 2].',
'Simulation running using mode 1d. Axis order = [0 1 2].',
'Running simulation on PCr.',
'Simulation running using mode 1d. Axis order = [0 1 2].',
'Simulation running using mode 1d. Axis order = [0 1 2].',
'Running simulation on GSH.',
'Simulation running using mode 1d. Axis order = [0 1 2].',
'Simulation running using mode 1d. Axis order = [0 1 2].',
'Simulation running using mode 1d. Axis order = [0 1 2].',
'Running simulation on GABA.',
'Simulation running using mode 1d. Axis order = [0 1 2].',
'Running simulation on Lac.',
'Simulation running using mode 1d. Axis order = [0 1 2].',
'Running simulation on Scyllo.',
'Simulation running using mode 1d. Axis order = [0 1 2].',
'Running simulation on Tau.',
'Simulation running using mode 1d. Axis order = [0 1 2].',
'Running simulation on Cr.',
'Simulation running using mode 1d. Axis order = [0 1 2].',
'Running simulation on Ins.',
'Simulation running using mode 1d. Axis order = [0 1 2].',
'Running simulation on Ala.',
'Simulation running using mode 1d. Axis order = [0 1 2].',
'Running simulation on Gln.',
'Simulation running using mode 1d. Axis order = [0 1 2].',
'Simulation running using mode 1d. Axis order = [0 1 2].',
'Running simulation on NAAG.',
'Simulation running using mode 1d. Axis order = [0 1 2].',
'Simulation running using mode 1d. Axis order = [0 1 2].',
'Simulation running using mode 1d. Axis order = [0 1 2].',
'Running simulation on Asp.',
'Simulation running using mode 1d. Axis order = [0 1 2].',
'Running simulation on Glc.',
'Simulation running using mode 1d. Axis order = [0 1 2].',
'Simulation running using mode 1d. Axis order = [0 1 2].',
'Running simulation on PCh.',
'Simulation running using mode 1d. Axis order = [0 1 2].',
'Simulation running using mode 1d. Axis order = [0 1 2].',
'Running simulation on PE.',
'Simulation running using mode 1d. Axis order = [0 1 2].']
%% Cell type:markdown id: tags:
## 3. Visualisation
%% Cell type:code id: tags:
``` python
%%capture
%sx mrs_vis --save basis_fig.png steam_basis
%sx mrs_tools vis --save basis_fig.png steam_basis
```
%% Cell type:markdown id: tags:
![basis spectra](basis_fig.png)
......
This source diff could not be displayed because it is stored in LFS. You can view the blob instead.
This source diff could not be displayed because it is stored in LFS. You can view the blob instead.
from .nifti_mrs import NIFTI_MRS
from .mrs import MRS
from .mrsi import MRSI
from .nifti_mrs import NIFTI_MRS
from .utility import mrs_from_files, is_nifti_mrs, mrsi_from_files
"""
Core Basis spectra handling class.
Author: Will Clarke <william.clarke@ndcn.ox.ac.uk>
Copyright Will Clarke, University of Oxford, 2021.
"""
import numpy as np
from pathlib import Path
import fsl_mrs.utils.mrs_io as mrs_io
from fsl_mrs.utils.mrs_io import fsl_io
from fsl_mrs.utils import misc
class BasisError(Exception):
pass
class IncompatibleBasisFormat(BasisError):
pass
class BasisHasInsufficentCoverage(BasisError):
pass
class Basis:
"""A Basis object is the FSL-MRS basis spectra handling class.
"""
def __init__(self, fid_array, names, headers):
"""Generate a Basis object from an array of fids, names and header information.
:param fid_array: 2D array of basis FIDs (time x metabs)
:type fid_array: numpy.ndarray
:param names: List of maetabolite names corresponding to second dimension of fid_array
:type names: List of str
:param headers: List of basis headers for each column of fid_array
:type headers: List of dict
"""
# Check all the basis headers match
def hdr_match(hdr1, hdr2):
if hdr1['dwelltime'] != hdr2['dwelltime']:
return False
if hdr1['bandwidth'] != hdr2['bandwidth']:
return False
if hdr1['centralFrequency'] != hdr2['centralFrequency']:
return False
return True
for hdr, name in zip(headers, names):
if not hdr_match(hdr, headers[0]):
raise BasisError(f'Basis headers must match, {name} does not match')
# Check for duplicate names
for name in names:
dupes = [idx for idx, n in enumerate(names) if n == name]
if len(dupes) > 0:
for idx, ddx in enumerate(dupes[1:]):
names[ddx] = names[ddx] + f'_{idx+1}'
print(f'Found duplicate basis name "{name}", renaming to "{names[ddx]}".')
# Checks on shape of fids
if fid_array.ndim == 1:
fid_array = fid_array[:, np.newaxis]
elif fid_array.ndim > 2:
raise TypeError('Basis fids must be 2D')
if fid_array.shape[0] < fid_array.shape[1]:
fid_array = fid_array.T
self._raw_fids = fid_array
self._dt = headers[0]['dwelltime']
self._cf = misc.checkCFUnits(headers[0]['centralFrequency'], units='MHz')
self._names = names
self._widths = [hdr['fwhm'] for hdr in headers]
# Assume Nucleus is 1H
# This only has bearing on the plotting currently
self._nucleus = '1H'
@classmethod
def from_file(cls, filepath):
"""Create a Basis object from a path
:param filepath: Path to basis file or folder
:type filepath: str or pathlib.Path
:return: A Basis class object
:rtype: .Basis
"""
return mrs_io.read_basis(filepath)
def __str__(self):
out = '------- Basis Object ---------\n'
out += f' BASIS.NMetabs = {self.n_metabs}\n'
out += f' BASIS.timepoints = {self.original_points}\n'
out += f' BASIS.centralFreq (MHz) = {self.cf:0.3f}\n'
out += f' BASIS.bandwidth (Hz) = {self.original_bw:0.1f}\n'
out += f' BASIS.dwelltime (s) = {self.original_dwell:0.5e}\n'
out += f' \nMetabolites: {self.names}'
return out
def __repr__(self) -> str:
return str(self)
@property
def cf(self):
"""Get the central frequency in MHz"""
return self._cf
@property
def n_metabs(self):
"""Get the number of metabolites"""
return self._raw_fids.shape[1]
@property
def names(self):
"""Get the names of all metabolites"""
return self._names
@property
def original_points(self):
"""Get the original (input) number of points"""
return self._raw_fids.shape[0]
@property
def original_dwell(self):
"""Get the original (input) dwell time in s"""
return self._dt
@property
def original_bw(self):
"""Get the original (input) bandwidth in Hz"""
return 1 / self._dt
@property
def original_basis_array(self):
"""Get the original input data"""
return self._raw_fids
@property
def basis_fwhm(self):
"""Get the original input data fwhm"""
return self._widths
@property
def original_time_axis(self):
"""Return the time axis of the raw basis set"""
return misc.calculateAxes(self.original_bw, self.cf * 1E6, self.original_points, 0.0)['time']
@property
def original_ppm_axis(self):
"""Return the ppm axis of the raw basis set"""
return misc.calculateAxes(self.original_bw, self.cf * 1E6, self.original_points, 0.0)['ppm']
@property
def original_ppm_shift_axis(self):
"""Return the ppm axis (with offset) of the raw basis set"""
from fsl_mrs.utils.constants import PPM_SHIFT
if self.nucleus in PPM_SHIFT:
shift = PPM_SHIFT[self.nucleus]
return misc.calculateAxes(self.original_bw, self.cf * 1E6, self.original_points, shift)['ppmshift']
else:
return self.original_ppm_axis
@property
def nucleus(self):
"""Return nucleus string"""
return self._nucleus
@nucleus.setter
def nucleus(self, nucleus):
"""Set the nucleus string - only affects plotting"""
self._nucleus = nucleus
def save(self, out_path, overwrite=False, info_str=''):
"""Saves basis held in memory to a directory in FSL-MRS format.
:param out_path: Directory to save files to, will be created if neccessary.
:type out_path: str or pathlib.Path
:param overwrite: Overwrite existing files, defaults to False.
:type overwrite: bool, optional
:param sim_info: Information to write to meta.SimVersion field, defaults to empy string
:type sim_info: str, optional
"""
out_path = Path(out_path)
def out_hdr(width):
return {'centralFrequency': self.cf * 1E6,
'bandwidth': self.original_bw,
'dwelltime': self.original_dwell,
'fwhm': width}
for name, basis, width in zip(self.names, self.original_basis_array.T, self.basis_fwhm):
hdr = out_hdr(width)
if not (out_path / (name + '.json')).exists()\
or ((out_path / (name + '.json')).exists() and overwrite):
fsl_io.write_fsl_basis_file(basis, name, hdr, out_path, info=info_str)
else:
continue
def get_formatted_basis(self, bandwidth, points, ignore=[], scale_factor=None, indept_scale=[]):
"""Returns basis formatted to an appropriate number of points and bandwidth.
Metabolites can be excluded based on the ignore options used.
The basis spectra will be scaled to have a certain norm (if not None), with indept_scale indicating
basis to be scaled separately.
:param bandwidth: Bandwidth of target format
:type bandwidth: float
:param points: Number of points in target format
:type points: int
:param ignore: Ignores any metabolites in this list, defaults to empty List
:type ignore: List of string, optional
:param scale_factor: Norm of basis is scaled to this value, defaults to None
:type scale_factor: float, optional
:param indept_scale: [description], defaults to empty List
:type indept_scale: List of strings, optional
:return: Formatted basis (points * N metabolites)
:rtype: numpy.ndarray
"""
# 1. Resample
formatted_basis = self._resampled_basis(1 / bandwidth, points)
# 2. Select the correct basis using the ignore syntax
ind_out = self._ignore_indicies(ignore)
formatted_basis = formatted_basis[:, ind_out]
# 3. Rescale
if scale_factor:
formatted_basis = self._rescale_basis(
formatted_basis,
self.get_formatted_names(ignore),
scale_factor,
indept_scale)[0]
return formatted_basis
def get_formatted_names(self, ignore=[]):
"""Return the names of metabolites included with any ignore options.
:param ignore: Metabolites to ignore, defaults to None
:type ignore: List of strings
:return: Retained names
:rtype: List of strings
"""
ind_out = self._ignore_indicies(ignore)
return np.asarray(self.names)[ind_out].tolist()
def get_rescale_values(self, bandwidth, points, ignore=[], scale_factor=None, indept_scale=[]):
"""Return the rescaling values usingt he same syntax as get_formatted_basis"""
# 1. Resample
formatted_basis = self._resampled_basis(1 / bandwidth, points)
# 2. Select the correct basis using the ignore syntax
ind_out = self._ignore_indicies(ignore)
formatted_basis = formatted_basis[:, ind_out]
# 3. Rescale
if scale_factor:
return self._rescale_basis(
formatted_basis,
self.get_formatted_names(ignore),
scale_factor,
indept_scale)[1]
else:
return [1.0, ]
def _ignore_indicies(self, ignore):
"""Returns indicies of metabolites that should be used given
the loaded basis set and the ignore options passed.
:param ignore: [description]
:type ignore: [type]
:return: [description]