MR Spectroscopy with Osprey#
Analyzing MR Spectra from the Human Brain using Single Voxel Spectroscopy (SVS)#
Author: Michal Toth
This notebook was prepared for an audience of clinicians at the 2025 PACTALS (Pan-Asian consortium of Treatment and Research in ALS) conference in Melbourne (link)
📘 How to Use This Notebook
This notebook is an interactive document. It mixes short explanations (like this box) with computer code that runs automatically.
⚡ The basics
- 👆 Click on a cell (the boxes with text or code)
- ▶️ Run it by pressing
Command + Enter
(Mac) orControl + Enter
(Windows/Linux) - ⏭️ Or use the play button in the toolbar above
- ⬇️ Move to the next cell and repeat
🧩 What happens when you run a cell?
The computer will either:
- ✏️ Show you some text or figures
- 🖥️ Run an analysis step in the background
- 📂 Save results into a folder
MR Spectroscopy (MRS)#
Quantification of concentration of various metabolites (chemical compounds)
Each metabolite has a distinct spectral (frequency-domain) signature based on its chemical structure

Introduction#
This example demonstrates the processing of MR spectra acquired from the human motor cortex using Osprey
, a standalone tool for processing and quantification of magnetic resonance spectroscopy (MRS) data.
Citation and Resources:#
Osprey:
Oeltzschner, G., Zöllner, H. J., Hui, S. C. N., Mikkelsen, M., Saleh, M. G., Tapper, S., & Edden, R. A. E. (2020). Osprey: Open-source processing, reconstruction & estimation of magnetic resonance spectroscopy data. Journal of Neuroscience Methods, 343, 108827. https://doi.org/10.1016/j.jneumeth.2020.108827
Python:
Python Software Foundation. (2023). Python (Version 3.11.6) [Software]. Available at https://www.python.org/
Dataset:
Collected at UQ as part of the BeLong study
T1 weighted MP2RAGE at 3T (healthy control)
SEMI-LASER MRS at 3T (healthy control)
Oz, G., & Tkáč, I. (2011). Short-echo, single-shot, full-intensity proton magnetic resonance spectroscopy for neurochemical profiling at 4 T: validation in the cerebellum and brainstem. Magn Reson Med, 65(4), 901-910. https://doi.org/10.1002/mrm.22708
Deelchand, D. K., Berrington, A., Noeske, R., Joers, J. M., Arani, A., Gillen, J., Schär, M., Nielsen, J. F., Peltier, S., Seraji-Bozorgzad, N., Landheer, K., Juchem, C., Soher, B. J., Noll, D. C., Kantarci, K., Ratai, E. M., Mareci, T. H., Barker, P. B., & Öz, G. (2021). Across-vendor standardization of semi-LASER for single-voxel MRS at 3T. NMR Biomed, 34(5), e4218. https://doi.org/10.1002/nbm.4218
The basis file used in this example is a placeholder, the SLaser basis set used in the acquisition (Eftekhari, Z., Shaw, T., Deelchand, D., Marjańska, M., Bogner, W., & Barth, M. (2025). Reliability and Reproducibility of Metabolite Quantification Using 1H MRS in the Human Brain at 3 T and 7 T. NMR in biomedicine, 38, e70087. https://doi.org/10.1002/nbm.70087) is not shareable at the moment.
Osprey input#
Description |
Filename |
---|---|
Raw metabolite data |
mrs/…svs_slaser_dkd_ul.dat |
Water reference data |
mrs/…svs_slaser_dkd_ul_wref.dat |
T1-weighted anatomical image |
anat/…acq-MP2RAGE_T1w.nii |
JSON file configuring the pipeline. See Osprey’s documentation for more details |
osprey-job.json |
A BASIS file containing simulated model spectra for known metabolites. Used by LCModel to fit data. |
set.BASIS |
Osprey output#
Description |
Filename |
---|---|
MRS voxel aligned with the anatomy |
osprey-output/VoxelMasks/…svs_slaser_dkd_ul_space-scanner_mask.nii |
MRS voxel segmentation masks (GM, WM, CSF) |
osprey-output/SegMaps/…svs_slaser_dkd_ul_space-scanner_Voxel-1_label-CSF.nii |
Results of spectral fitting (from LCmodel) |
osprey-output/LCMOutput/ |
Summary of results from different steps of the pipeline. It includes some QC metrics |
osprey-output/Reports/sub-002-report.html |
Tables with concentration values for metabolites using different references |
osprey-output/QuantifyResults/ |
Check CPU vendor and model for compatibility or performance considerations#
!cat /proc/cpuinfo | grep 'vendor' | uniq
!cat /proc/cpuinfo | grep 'model name' | uniq
vendor_id : GenuineIntel
model name : Intel(R) Xeon(R) Gold 6126 CPU @ 2.60GHz
Load software tools and import python libraries#
import module
await module.load('osprey/2.9.0')
await module.list()
['osprey/2.9.0']
%%capture
!pip install nibabel numpy pandas
import os
from pathlib import Path
import shutil
from IPython.display import Image as ipython_image, display
from PIL import Image, ImageDraw, ImageFont
from ipyniivue import NiiVue
import nibabel as nib
import numpy as np
import ipywidgets as widgets
import pandas as pd
import matplotlib.pyplot as plt
import json
import subprocess
Set up Osprey Job configuration#
Download and prepare data
Set up paths to the relevant MRS/MRI files
Set up the configuration JSON file
Check that all specified paths exist
NOTE: For a more detailed explanation of all the configuration options see Osprey’s documentation
%%bash
echo "Downloading data"
osf -p 3a2ws fetch osfstorage/osprey.zip ./data_osprey/osprey.zip
echo "Unzipping data"
unzip ./data_osprey/osprey.zip -d ./data_osprey/
Downloading data
Unzipping data
Archive: ./data_osprey/osprey.zip
creating: ./data_osprey/sub-002/ses-01/
creating: ./data_osprey/sub-002/ses-01/anat/
inflating: ./data_osprey/sub-002/ses-01/anat/sub-002_ses-01_acq-MP2RAGE_desc-defaced_T1w.nii
creating: ./data_osprey/sub-002/ses-01/mrs/
inflating: ./data_osprey/sub-002/ses-01/mrs/meas_MID00104_FID130221_svs_slaser_dkd_ul_wref.dat
inflating: ./data_osprey/sub-002/ses-01/mrs/meas_MID00103_FID130220_svs_slaser_dkd_ul.dat
100%|██████████| 150M/150M [00:13<00:00, 10.9Mbytes/s]
# Get path to Osprey executable.
osprey_path = subprocess.check_output("which ospreyCMD", shell=True, text=True).strip()
# Extract container base and image path.
container_base = os.path.dirname(osprey_path)
container_id = os.path.basename(container_base)
simg_path = os.path.join(container_base, f"{container_id}.simg")
# Construct the basis set path (from inside the container).
basis_path = os.path.join(
simg_path,
"opt/basissets/3T/siemens/unedited/slaser/30"
)
# Osprey will try to convert this .mat basis file into a .BASIS type
# file. It will try to write the new .BASIS file into the same folder
# where the .mat basis file is. Thus, the .mat basis file needs to be
# copied into a writeable location.
!cp {basis_path}/basis_siemens_slaser30.mat ./data_osprey/basis_siemens_slaser30.mat
base_path = os.getcwd()
data_path = f'{base_path}/data_osprey'
output_path = f'{base_path}/osprey-output'
basis_set = f'{data_path}/basis_siemens_slaser30.mat'
job_path = f'{base_path}/osprey-job.json'
sub = 'sub-002'
ses = 'ses-01'
# Metabolite data.
files = [
f'{data_path}/{sub}/{ses}/mrs/meas_MID00103_FID130220_svs_slaser_dkd_ul.dat'
]
# Water reference data.
files_ref = [
f'{data_path}/{sub}/{ses}/mrs/meas_MID00104_FID130221_svs_slaser_dkd_ul_wref.dat'
]
# T1.
files_nii = [
f'{data_path}/{sub}/{ses}/anat/{sub}_{ses}_acq-MP2RAGE_desc-defaced_T1w.nii'
]
mrs_options = {
'seqType': 'unedited',
'editTarget': ['none'],
'dataScenario': 'invivo',
'outputFolder': [output_path],
'SpecReg': 'RobSpecReg',
'SubSpecAlignment': 'none',
'EECraw': '1',
'ECCmm': '1',
'method': 'LCModel',
'saveLCM': '1',
'savejMRUI': '0',
'saveVendor': '1',
'saveNII': '1',
'savePDF': '0',
'includeMetabs': ['default'],
'style': 'Separate',
'range': ['0.5', '4.2'],
'rangeWater': ['2.0', '7.4'],
'bLineKnotSpace': '5',
'fitMM': '1',
'basisSet': basis_set,
'files': files,
'files_ref': files_ref,
'files_nii': files_nii
}
with open(job_path, 'w') as f:
json.dump(mrs_options, f)
check_paths = [data_path, output_path, basis_set, job_path]
check_paths = check_paths + files + files_ref + files_nii
# Create the results directory if it doesn't exist yet
os.makedirs(output_path, exist_ok=True)
any_warning = False
for pth in check_paths:
pth_exists = Path(pth).exists()
print(f"{'✅' if pth_exists else '❌'} '{pth}'")
✅ '/home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/data_osprey'
✅ '/home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-output'
✅ '/home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/data_osprey/basis_siemens_slaser30.mat'
✅ '/home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-job.json'
✅ '/home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/data_osprey/sub-002/ses-01/mrs/meas_MID00103_FID130220_svs_slaser_dkd_ul.dat'
✅ '/home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/data_osprey/sub-002/ses-01/mrs/meas_MID00104_FID130221_svs_slaser_dkd_ul_wref.dat'
✅ '/home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/data_osprey/sub-002/ses-01/anat/sub-002_ses-01_acq-MP2RAGE_desc-defaced_T1w.nii'
Run Osprey processing#
The following command runs the Oprey CLI software with specifications from the job file defined above.
!ospreyCMD {job_path}
------------------------------------------
Setting up environment variables
---
LD_LIBRARY_PATH is .:/opt/MCR/R2023a//runtime/glnxa64:/opt/MCR/R2023a//bin/glnxa64:/opt/MCR/R2023a//sys/os/glnxa64:/opt/MCR/R2023a//sys/opengl/lib/glnxa64
Spectral fitting parameters will not be saved (default). Please indicate otherwise in the csv-file or the GUI
/home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-job.json
Timestamp September 24, 2025 02:18:38 Osprey 2.9.0
Timestamp September 24, 2025 02:18:38 Osprey 2.9.0 OspreyLoad
Loading raw data from dataset 1 out of 1 total datasets...
Software version: VD (!?)
Reader version: 1496740353 (UTC: 06-Jun-2017 09:12:33)
Scan 1/2, read all mdhs:
35.3 MB read in 0 s
Scan 2/2, read all mdhs:
83.9 MB read in 0 s
ans =
0
multi RAID file detected.
Software version: VD (!?)
Reader version: 1496740353 (UTC: 06-Jun-2017 09:12:33)
Scan 1/2, read all mdhs:
35.3 MB read in 0 s
Scan 2/2, read all mdhs:
19.0 MB read in 0 s
ans =
0
multi RAID file detected.
... done.
Elapsed time 15.375816 seconds
Timestamp September 24, 2025 02:18:54 Osprey 2.9.0 OspreyProcess
Processing data from dataset 1 out of 1 total datasets...
... done.
Elapsed time 68.660608 seconds
Writing table to file = /home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-output/QM_processed_spectra.tsv
Writing table to file = /home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-output/QM_processed_spectra.json
Timestamp September 24, 2025 02:20:04 Osprey 2.9.0 OspreyFit
Fitting metabolite spectra from dataset 1 out of 1 total datasets...
... done.
Elapsed time 4.866655 seconds
... done.
Elapsed time 4.866655 seconds
Writing table to file = /home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-output/QM_processed_spectra.tsv
Writing table to file = /home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-output/QM_processed_spectra.json
Timestamp September 24, 2025 02:20:15 Osprey 2.9.0 OspreyCoreg
Coregistering voxel from dataset 1 out of 1 total datasets...
... done.
Elapsed time 7.306444 seconds
Timestamp September 24, 2025 02:20:23 Osprey 2.9.0 OspreySeg
Segmenting structural image from dataset 1 out of 1 total datasets...
------------------------------------------------------------------------
24-Sep-2025 02:20:31 - Running job #1
------------------------------------------------------------------------
24-Sep-2025 02:20:31 - Running 'Segment'
SPM12: spm_preproc_run (v7670) 02:20:31 - 24/09/2025
========================================================================
Segment /home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/data_osprey/sub-002/ses-01/anat/sub-002_ses-01_acq-MP2RAGE_desc-defaced_T1w.nii,1
Completed : 02:23:48 - 24/09/2025
24-Sep-2025 02:23:48 - Done 'Segment'
24-Sep-2025 02:23:48 - Done
------------------------------------------------------------------------
24-Sep-2025 02:23:51 - Running job #1
------------------------------------------------------------------------
24-Sep-2025 02:23:51 - Running 'Normalise: Write'
24-Sep-2025 02:23:56 - Done 'Normalise: Write'
24-Sep-2025 02:23:56 - Done
------------------------------------------------------------------------
24-Sep-2025 02:24:00 - Running job #1
------------------------------------------------------------------------
24-Sep-2025 02:24:00 - Running 'Image Calculator'
SPM12: spm_imcalc (v6961) 02:24:00 - 24/09/2025
========================================================================
ImCalc Image: /home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-output/VoxelMasks/MID00103_FID130220_svs_slaser_dkd_ul_space-spm152_mask_VoxelOverlap.nii
24-Sep-2025 02:24:01 - Done 'Image Calculator'
24-Sep-2025 02:24:01 - Done
... done.
Elapsed time 210.691464 seconds
Writing table to file = /home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-output/SegMaps/TissueFractions_Voxel_1.tsv
Writing table to file = /home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-output/SegMaps/TissueFractions_Voxel_1.json
Timestamp September 24, 2025 02:24:01 Osprey 2.9.0 OspreyQuantify
Quantifying dataset 1 out of 1 total datasets...
... done.
Elapsed time 0.056855 seconds
Writing table to file = /home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-output/QuantifyResults/A_amplMets_Voxel_1_Basis_1.tsv
Writing table to file = /home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-output/QuantifyResults/A_amplMets_Voxel_1_Basis_1.json
Writing table to file = /home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-output/QuantifyResults/A_tCr_Voxel_1_Basis_1.tsv
Writing table to file = /home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-output/QuantifyResults/A_tCr_Voxel_1_Basis_1.json
Writing table to file = /home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-output/QuantifyResults/A_rawWaterScaled_Voxel_1_Basis_1.tsv
Writing table to file = /home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-output/QuantifyResults/A_rawWaterScaled_Voxel_1_Basis_1.json
Writing table to file = /home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-output/QuantifyResults/A_CSFWaterScaled_Voxel_1_Basis_1.tsv
Writing table to file = /home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-output/QuantifyResults/A_CSFWaterScaled_Voxel_1_Basis_1.json
Writing table to file = /home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-output/QuantifyResults/A_TissCorrWaterScaled_Voxel_1_Basis_1.tsv
Writing table to file = /home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-output/QuantifyResults/A_TissCorrWaterScaled_Voxel_1_Basis_1.json
Writing table to file = /home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-output/QuantifyResults/A_AlphaCorrWaterScaled_Voxel_1_Basis_1.tsv
Writing table to file = /home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-output/QuantifyResults/A_AlphaCorrWaterScaled_Voxel_1_Basis_1.json
Writing table to file = /home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-output/QuantifyResults/A_AlphaCorrWaterScaledGroupNormed_Voxel_1_Basis_1.tsv
Writing table to file = /home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-output/QuantifyResults/A_AlphaCorrWaterScaledGroupNormed_Voxel_1_Basis_1.json
Writing table to file = /home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-output/QuantifyResults/A_CRLB_Voxel_1_Basis_1.tsv
Writing table to file = /home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-output/QuantifyResults/A_CRLB_Voxel_1_Basis_1.json
Writing table to file = /home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-output/QuantifyResults/A_h2oarea_Voxel_1_Basis_1.tsv
Writing table to file = /home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-output/QuantifyResults/A_h2oarea_Voxel_1_Basis_1.json
Timestamp September 24, 2025 02:24:03 Osprey 2.9.0 OspreyOverview
Gathering spectra from subspectrum 1 out of 2 total subspectra..Gathering spectra from subspectrum 1 out of 2 total subspectra..Gathering spectra from subspectrum 2 out of 2 total subspectra...
... done.
Gathering fit models from fit 1 out of 1 total fits.Gathering fit models from fit 1 out of 1 total fits...
... done.
Interpolating fit models from fit 1 out of 1 total fits.Interpolating fit models from fit 1 out of 1 total fits...
... done.
Scaling data from dataset 1 out of 1 total datasets.Scaling data from dataset 1 out of 1 total datasets.Scaling data from dataset 1 out of 1 total datasets...
... done.
Writing table to file = /home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-output/subject_names_and_excluded.tsv
Writing table to file = /home/jovyan/Git_repositories/example-notebooks/books/spectroscopy/osprey-output/subject_names_and_excluded.json
... done.
Elapsed time 0.120314 seconds
Runtime Breakdown................
OspreyLoad runtime: 15.375816 seconds
OspreyProcess runtime: 68.660608 seconds
OspreyFit runtime: 4.866655 seconds
OspreyFit metab runtime: 4.866655 seconds
OspreyCoreg runtime: 7.306444 seconds
OspreySeg runtime: 210.691464 seconds
OspreyOverview runtime: 0.120314 seconds
Full Osprey runtime: 307.078156 seconds
Results#
T1s with the MRS voxel aligned#
Osprey performs alignment with T1
Osprey also performs segmentation to determine the proportion of tissue types within the voxel (for WM and CSF correction)
# Prepare the main viewer withot the 3D render.
nv = NiiVue(
height=600,
multiplanar_layout="ROW",
multiplanar_show_render=False,
multiplanar_pad_pixels=10,
is_ruler=False,
is_colorbar=False,
is_orient_cube=False,
is_radiological_convention=False,
back_color=(0.1,0.1,0.1,1.0),
crosshair_color=[0,0,0,1]
)
# Robust intensity window (ignore zeros and outliers)
t1 = nib.load(files_nii[0]).get_fdata()
vals = t1[t1 > 0]
vmin, vmax = np.percentile(vals, [0.5, 99.5]) if vals.size else (None, None)
# T1 with contrast adjusted.
nv.add_volume({
"path": files_nii[0],
"name": "native",
"opacity": 1.0,
"colormap": "gray",
"cal_min": float(vmin) if vmin is not None else None,
"cal_max": float(vmax) if vmax is not None else None,
"ignore_zero_voxels": True,
})
# WM segmentation.
nv.add_volume({
"path": f'{output_path}/SegMaps/meas_MID00103_FID130220_svs_slaser_dkd_ul_space-scanner_Voxel-1_label-WM.nii.gz',
"name": "wm",
"opacity": 0.5,
"colormap": "red",
"ignore_zero_voxels": True,
})
# GM segmentation.
nv.add_volume({
"path": f'{output_path}/SegMaps/meas_MID00103_FID130220_svs_slaser_dkd_ul_space-scanner_Voxel-1_label-GM.nii.gz',
"name": "gm",
"opacity": 0.5,
"colormap": "blue",
"ignore_zero_voxels": True,
})
# CSF segmentation.
nv.add_volume({
"path": f'{output_path}/SegMaps/meas_MID00103_FID130220_svs_slaser_dkd_ul_space-scanner_Voxel-1_label-CSF.nii.gz',
"name": "csf",
"opacity": 0.5,
"colormap": "green",
"ignore_zero_voxels": True,
})
# Add legend (in HTML so we can style).
fontsize = 25
entry_sep = 25
text_sep = 10
cube_size = 20
capt = widgets.HTML(
f"<div style='font:{fontsize}px sans-serif; margin-left:285px;'>"
f"<div style='display:inline-block; margin-right:{entry_sep}px;'>"
f"<span style='display:inline-block; width:{cube_size}px; height:{cube_size}px; background-color:#de4743; margin-right:{text_sep}px;'></span>WM"
"</div>"
f"<div style='display:inline-block; margin-right:{entry_sep}px;'>"
f"<span style='display:inline-block; width:{cube_size}px; height:{cube_size}px; background-color:#4037a4; margin-right:{text_sep}px;'></span>GM"
"</div>"
f"<div style='display:inline-block; margin-right:{entry_sep}px;'>"
f"<span style='display:inline-block; width:{cube_size}px; height:{cube_size}px; background-color:#479a2e; margin-right:{text_sep}px;'></span>CSF"
"</div>"
"</div>"
)
# Put the legend above the NiiVue viewer.
panel = widgets.VBox([capt, nv])
display(panel)
display(ipython_image(url='https://raw.githubusercontent.com/neurodesk/example-notebooks/refs/heads/main/books/images/osprey-notebook.png'))

display(ipython_image(filename=f"{output_path}/Reports/reportFigures/sub-002/sub-002_seg_svs_space-scanner_mask.jpg"))

Spectral modelling#
Osprey runs LCModel for spectral fitting
crlb_threshold = 20
# Make sure to only include the metabolites in the LCmodel plot.
metabolites = [
'Asp', 'Asc', 'Cr', 'GABA', 'Gln', 'Glu', 'GPC', 'GSH', 'Lac',
'PCr', 'PE', 'NAA', 'NAAG', 'Tau', 'CrCH2', 'Lip13a', 'Lip13b',
'Lip09', 'MM09', 'Lip20', 'MM20', 'MM12', 'MM14', 'MM17'
]
# crlbs = pd.read_csv(f'{output_path}/QuantifyResults/A_CRLB_Voxel_1_Basis_1.tsv', delimiter='\t')[metabolites]
crlbs = pd.read_csv(f'{output_path}/QuantifyResults/A_CRLB_Voxel_1_Basis_1.tsv', delimiter='\t')[metabolites]
crlb_dict = crlbs.iloc[0, :].to_dict()
# Osprey fitting results image.
img_path = f"{output_path}/Reports/reportFigures/sub-002/sub-002_metab_A_model.jpg"
img = Image.open(img_path)
# Draw CRLBs on top of the image.
draw = ImageDraw.Draw(img)
font = ImageFont.load_default(size=15)
x, y = 1135, 253
for met, crlb in crlb_dict.items():
y += 20.67 # shift down for next line
# Only show
if crlb < crlb_threshold:
draw.text((x, y), f"({crlb:.1f})", fill="#2e4b67", font=font)
display(img)

from IPython.display import display, Markdown, HTML, Image
# Centered title
display(HTML("<h2 style='text-align: center;'>Averaged Spectra</h2>"))
# Image (keeps default alignment unless styled otherwise)
display(Image(filename=f"{output_path}/Reports/reportFigures/sub-002/sub-002_metab_A.jpg"))
Averaged Spectra

Metabolite concetrations#
Osprey calculates metabolite concentrations relative to different references
metabolites = ['Cr', 'GABA', 'Gln', 'Glu', 'GPC', 'GSH', 'Lac', 'PCr', 'PE', 'NAA', 'NAAG', 'Tau', 'CrCH2', 'tCr', 'tNAA', 'Glx']
def plot_metabolites(data, figsize=(20, 5), label='(raw values in arbitrary units)', ytick_step=0.2):
fig = plt.figure(figsize=figsize)
ax = fig.add_subplot()
ax.bar(height=data.values[0], x=data.columns, zorder=2)
ax.set_title(f'Metabolites\n{label}')
ylims = ax.get_ylim()
yticks = np.arange(ylims[0], ylims[1], ytick_step)
ax.set_yticks(yticks)
ax.grid(axis='y', zorder=1)
met_data = pd.read_csv(f'{output_path}/QuantifyResults/A_tCr_Voxel_1_Basis_1.tsv', delimiter='\t')
plot_metabolites(met_data[metabolites], label='(ratio of tCR)')

met_data = pd.read_csv(f'{output_path}/QuantifyResults/A_TissCorrWaterScaled_Voxel_1_Basis_1.tsv', delimiter='\t')
plot_metabolites(met_data[metabolites], label='(molal concentration, using tissue corrected water; mol/kg)', ytick_step=2.5)
