From d61b2626c440bfa1475fa9b4613733d02ba1fbe8 Mon Sep 17 00:00:00 2001 From: Mathieu Boudreau Date: Tue, 1 Oct 2024 11:50:30 -0300 Subject: [PATCH] Remove --- .../04-IR_BenefitsAndPitfalls.ipynb | 472 ------------------ 1 file changed, 472 deletions(-) delete mode 100644 2 T1 Mapping/Inversion Recovery T1 Mapping/04-IR_BenefitsAndPitfalls.ipynb diff --git a/2 T1 Mapping/Inversion Recovery T1 Mapping/04-IR_BenefitsAndPitfalls.ipynb b/2 T1 Mapping/Inversion Recovery T1 Mapping/04-IR_BenefitsAndPitfalls.ipynb deleted file mode 100644 index e194d2e..0000000 --- a/2 T1 Mapping/Inversion Recovery T1 Mapping/04-IR_BenefitsAndPitfalls.ipynb +++ /dev/null @@ -1,472 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "537b8c4a-fbcc-45cb-804c-4a0d299750be", - "metadata": { - "editable": true, - "slideshow": { - "slide_type": "" - }, - "tags": [] - }, - "source": [ - "---\n", - "title: Benefits and Pitfalls\n", - "subtitle: Inversion Recovery\n", - "date: 2024-07-25\n", - "authors:\n", - " - name: Mathieu Boudreau\n", - " affiliations:\n", - " - NeuroPoly Lab, Polytechnique Montreal, Quebec, Canada\n", - "numbering:\n", - " figure:\n", - " template: Fig. %s\n", - "---" - ] - }, - { - "cell_type": "markdown", - "id": "f8d1db4b-95c2-4a61-9217-1be991784701", - "metadata": { - "editable": true, - "slideshow": { - "slide_type": "" - }, - "tags": [] - }, - "source": [ - "The conventional inversion recovery experiment is considered the gold standard T1 mapping technique for several reasons: \n", - "* A typical protocol has a long TR value and a sufficient number of inversion times for stable fitting (typically 5 or more) covering the range [0, TR]. \n", - "* It offers a wide dynamic range of signals ([up to `-kM0`, `kM0`]), allowing a number of inversion times where high SNR is available to sample the signal recovery curve {cite:p}`Fukushima1981`. \n", - "* T1 maps produced by inversion recovery are largely insensitive to inaccuracies in excitation flip angles and imperfect spoiling {cite:p}`Stikov2015`, as all parameters except TI are constant for each measurement and only a single acquisition is performed (at TI) during each TR. \n", - "\n", - "One important protocol design consideration is to avoid acquiring at inversion times where the signal for T1 values of the tissue-of-interest is nulled, as the magnitude images at this TI time will be dominated by Rician noise which can negatively impact the fit under low SNR circumstances (Figure 6). Inversion recovery can also often be acquired using commonly available standard pulse sequences available on most MRI scanners by setting up a customized acquisition protocol, and does not require any additional calibration measurements. For an example, please visit the interactive preprint of the ISMRM Reproducible Research Group 2020 Challenge on inversion recovery T1 mapping {cite:p}`Boudreau2023`. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4130dad2-b8dd-43b6-96da-5a457cf29ea8", - "metadata": { - "editable": true, - "slideshow": { - "slide_type": "" - }, - "tags": [ - "hide-output", - "hide-input" - ] - }, - "outputs": [], - "source": [ - "from repo2data.repo2data import Repo2Data\n", - "import os \n", - "import pickle\n", - "import matplotlib.pyplot as plt\n", - "import chart_studio.plotly as py\n", - "import plotly.graph_objs as go\n", - "import numpy as np\n", - "from plotly import __version__\n", - "from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot\n", - "from IPython.display import display, HTML\n", - "\n", - "data_req_path = os.path.join(\"..\",\"..\", \"binder\", \"data_requirement.json\")\n", - "repo2data = Repo2Data(data_req_path)\n", - "DATA_ROOT = os.path.join(repo2data.install()[0],\"t1-book-neurolibre\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "369dd138-a820-43e3-b0fe-fdc968ca41e6", - "metadata": { - "tags": [ - "hide-input" - ] - }, - "outputs": [], - "source": [ - "filename = os.path.join(DATA_ROOT,\"01\",'figure_6.pkl')\n", - "\n", - "with open(filename, 'rb') as f:\n", - " TR_range, TI_lowres, TI_highres, T1_mean, T1_std, data_mean, data_std, data_noiseless = pickle.load(f)\n", - "\n", - "config={'showLink': False, 'displayModeBar': False}\n", - "\n", - "init_notebook_mode(connected=True)\n", - "\n", - "data1 = [dict(\n", - " visible = False,\n", - " x = np.squeeze(np.asarray(TI_lowres[ii,:])),\n", - " y = np.squeeze(np.asarray(data_mean[ii,:])),\n", - " error_y=dict(\n", - " type='data',\n", - " color = ('rgb(22, 96, 167)'),\n", - " array=np.squeeze(np.asarray(data_std[ii,:])),\n", - " visible=True\n", - " ),\n", - " line = dict(\n", - " color = ('rgb(22, 96, 167)'),\n", - " dash = 'dot'),\n", - " mode = 'markers',\n", - " name = 'Monte Carlo simulated signal',\n", - " text = 'Monte Carlo simulated signal',\n", - " hoverinfo = 'x+y+text') for ii in range(len(TR_range))]\n", - "\n", - "data1[28]['visible'] = True\n", - "\n", - "data2 = [dict(\n", - " visible = False,\n", - " x = np.squeeze(np.asarray(TI_highres[ii,:])),\n", - " y = np.squeeze(np.asarray(data_noiseless[ii,:])),\n", - " line = dict(\n", - " color = ('rgb(247, 152, 19)'),\n", - " ),\n", - " name = 'Noiseless signal',\n", - " text = 'Noiseless signal',\n", - " hoverinfo = 'x+y+text') for ii in range(len(TR_range))]\n", - "\n", - "data2[28]['visible'] = True\n", - "\n", - "data_meanT1 = [dict(\n", - " visible = False,\n", - " x = TR_range,\n", - " y = T1_mean,\n", - " name = 'Mean T1 (s)',\n", - " text = 'Mean T1 (s)',\n", - " hoverinfo = 'x+y+text',\n", - " xaxis='x2',\n", - " yaxis='y2') for ii in range(len(TR_range))]\n", - "\n", - "data_meanT1[15]['visible'] = True\n", - "\n", - "data_stdT1 = [dict(\n", - " visible = False,\n", - " x = TR_range,\n", - " y = T1_std,\n", - " line = dict(\n", - " color = ('rgb(222, 22, 22)'),\n", - " ),\n", - " name = 'STD T1 (s)',\n", - " text = 'STD T1 (s)',\n", - " hoverinfo = 'x+y+text',\n", - " xaxis='x2',\n", - " yaxis='y3') for ii in range(len(TR_range))]\n", - "\n", - "data_stdT1[28]['visible'] = True\n", - "\n", - "data = data2 + data1 + data_meanT1 + data_stdT1\n", - "\n", - "steps = []\n", - "for i in range(len(TR_range)):\n", - " step = dict(\n", - " method = 'restyle', \n", - " args = ['visible', [False] * len(data1)],\n", - " label = str(TR_range[i])\n", - " )\n", - " step['args'][1][i] = True # Toggle i'th trace to \"visible\"\n", - " steps.append(step)\n", - "\n", - "sliders = [dict(\n", - " x = 0,\n", - " y = -0.02,\n", - " active = 28,\n", - " currentvalue = {\"prefix\": \"TR value (ms): \"},\n", - " pad = {\"t\": 50, \"b\": 10},\n", - " steps = steps\n", - ")]\n", - "\n", - "layout = go.Layout(\n", - " width=540,\n", - " height=540,\n", - " margin = dict(\n", - " t=0,\n", - " r=25,\n", - " b=100,\n", - " l=75),\n", - " annotations=[\n", - " dict(\n", - " x=0.5004254919715793,\n", - " y=-0.17,\n", - " showarrow=False,\n", - " text='Inversion Time – TI (ms)',\n", - " font=dict(\n", - " family='Times New Roman',\n", - " size=26\n", - " ),\n", - " xref='paper',\n", - " yref='paper'\n", - " ),\n", - " dict(\n", - " x=-0.15,\n", - " y=0.5,\n", - " showarrow=False,\n", - " text='Signal (magnitude)',\n", - " font=dict(\n", - " family='Times New Roman',\n", - " size=26\n", - " ),\n", - " textangle=-90,\n", - " xref='paper',\n", - " yref='paper'\n", - " ),\n", - " dict(\n", - " x=0.76,\n", - " y=0.77,\n", - " showarrow=False,\n", - " text='TR (ms)',\n", - " font=dict(\n", - " family='Times New Roman',\n", - " size=14\n", - " ),\n", - " xref='paper',\n", - " yref='paper'\n", - " ),\n", - " dict(\n", - " x=0.40,\n", - " y=0.35,\n", - " showarrow=False,\n", - " text='Mean T1 (ms)',\n", - " font=dict(\n", - " family='Times New Roman',\n", - " size=14\n", - " ),\n", - " textangle=-90,\n", - " xref='paper',\n", - " yref='paper'\n", - " ),\n", - " dict(\n", - " x=1.00,\n", - " y=0.35,\n", - " showarrow=False,\n", - " text='STD T1 (ms)',\n", - " font=dict(\n", - " family='Times New Roman',\n", - " size=14\n", - " ),\n", - " textangle=-90,\n", - " xref='paper',\n", - " yref='paper'\n", - " )\n", - " ],\n", - " xaxis=dict(\n", - " autorange=False,\n", - " range=[0, 5000],\n", - " showgrid=False,\n", - " linecolor='black',\n", - " linewidth=2\n", - " ),\n", - " yaxis=dict(\n", - " autorange=False,\n", - " range=[0, 1],\n", - " showgrid=False,\n", - " linecolor='black',\n", - " linewidth=2\n", - " ),\n", - " xaxis2=dict(\n", - " domain=[0.5, 0.90],\n", - " anchor='y2',\n", - " mirror = True,\n", - " side='top',\n", - " ticks='inside',\n", - " showline=True,\n", - " ),\n", - " yaxis2=dict(\n", - " autorange=False,\n", - " range=[500, 1300],\n", - " domain=[0.05, 0.65],\n", - " anchor='x2',\n", - " mirror = True,\n", - " ticks='inside',\n", - " showline=True,\n", - " ),\n", - " yaxis3=dict(\n", - " autorange=False,\n", - " range=[0, 190],\n", - " domain=[0.05, 0.65],\n", - " anchor='x2',\n", - " overlaying='y2',\n", - " side='right',\n", - " ticks='inside',\n", - " ),\n", - " legend=dict(\n", - " x=0.3,\n", - " y=1.35,\n", - " traceorder='normal',\n", - " font=dict(\n", - " family='Times New Roman',\n", - " size=12,\n", - " color='#000'\n", - " ),\n", - " bordercolor='#000000',\n", - " borderwidth=2\n", - " ), \n", - " sliders=sliders,\n", - " plot_bgcolor='white'\n", - ")\n", - "\n", - "fig = dict(data=data, layout=layout)\n", - "\n", - "plot(fig, filename = 'ir_fig_6.html', config = config)\n", - "display(HTML('ir_fig_6.html'))" - ] - }, - { - "cell_type": "markdown", - "id": "44739c40-81bd-412b-ab14-2cf46ba03d1d", - "metadata": { - "editable": true, - "slideshow": { - "slide_type": "" - }, - "tags": [] - }, - "source": [ - "\n", - "```{figure} img/plot5.png\n", - ":label: irPlot5\n", - "\n", - "Figure 6. Monte Carlo simulations (mean and standard deviation (STD), blue markers) and fitted T1 values (mean and STD, red and green respectively) generated for a T1 value of 900 ms and 5 TI values linearly spaced across the TR (ranging from 1 to 5 s). A bump in T1 STD occurs near TR = 3000 ms, which coincides with the TR where the second TI is located near a null point for this T1 value.\n", - "```\n", - "\n", - "Despite a widely acknowledged robustness for measuring accurate T1 maps, inversion recovery is not often used in studies. An important drawback of this technique is the need for long TR values, generally on the order of a few T1 for general models (e.g. Eqs. 1 and 4), and up to 5T1 for long TR approximated models (Eq. 3). It takes about to 10-25 minutes to acquire a single-slice T1 map using the inversion recovery technique, as only one TI is acquired per TR (2-5 s) and conventional cartesian gradient readout imaging acquires one phase encode line per excitation (for a total of ~100-200 phase encode lines). The long acquisition time makes it challenging to acquire whole-organ T1 maps in clinically feasible protocol times. Nonetheless, it is useful as a reference measurement for comparisons against other T1 mapping methods, or to acquire a single-slice T1 map of a tissue to get T1 estimates for optimization of other pulse sequences." - ] - }, - { - "cell_type": "markdown", - "id": "7bf30faf-3f00-4c6e-9eed-c6b880307009", - "metadata": {}, - "source": [ - "\n", - "```{admonition} Click here to view the qMRLab (MATLAB/Octave) code that generated Figure 6.\n", - ":class: tip, dropdown\n", - "\n", - "```octave\n", - "% Verbosity level 0 overrides the disp function and supresses warnings.\n", - "% Once executed, they cannot be restored in this session\n", - "% (kernel needs to be restarted or a new notebook opened.)\n", - "VERBOSITY_LEVEL = 0;\n", - "\n", - "if VERBOSITY_LEVEL==0\n", - " % This hack was used to supress outputs from external tools\n", - " % in the Jupyter Book.\n", - " function disp(x)\n", - " end\n", - " warning('off','all')\n", - "end\n", - "\n", - "try\n", - " cd qMRLab\n", - "catch\n", - " try\n", - " cd ../../../qMRLab\n", - " catch\n", - " cd ../qMRLab\n", - " end\n", - "end\n", - "\n", - "startup\n", - "clear all\n", - "\n", - "%% Setup parameters\n", - "% All times are in seconds\n", - "% All flip angles are in degrees\n", - "\n", - "TR_range = 1000:100:5000; % in seconds\n", - "\n", - "x = struct;\n", - "x.T1 = 900; % in seconds\n", - "\n", - "Opt.SNR = 25;\n", - "Opt.M0 = 1;\n", - "Opt.FAexcite = 90; % Excitation flip angle\n", - "Opt.FAinv = 180; % Inversion flip angle\n", - "\n", - "%% Monte Carlo data simulation\n", - "% Simulate noisy signal data 1,000 time, fit the data, then calculate the means and standard deviations of the data and fitted T1\n", - "% Data is calculated by calculating the a and b values of Eq. 4 from the full analytical equations (Eq. 1)\n", - "\n", - "Model = inversion_recovery; \n", - "\n", - "for ii = 1:length(TR_range)\n", - " Opt.TR = TR_range(ii);\n", - " Opt.T1 = x.T1;\n", - " TI_lowres(ii,:) = linspace(0.05, Opt.TR, 6)';\n", - " Model.Prot.IRData.Mat = [TI_lowres(ii,:)];\n", - " [ra,rb] = Model.ComputeRaRb(x,Opt);\n", - " x.rb = rb;\n", - " x.ra = ra;\n", - " for jj = 1:1000\n", - " [FitResult{ii,jj}, noisyData{ii,jj}] = Model.Sim_Single_Voxel_Curve(x,Opt,0); \n", - " fittedT1(ii,jj) = FitResult{ii,jj}.T1;\n", - " noisyData_array(ii,jj,:) = noisyData{ii,jj}.IRData;\n", - " end\n", - " \n", - " for kk=1:length(TI_lowres(ii,:))\n", - " data_mean(ii,kk) = mean(noisyData_array(ii,:,kk));\n", - " data_std(ii,kk) = std(noisyData_array(ii,:,kk));\n", - " end\n", - " \n", - " T1_mean(ii) = mean(fittedT1(ii,:));\n", - " T1_std(ii) = std(fittedT1(ii,:));\n", - "end\n", - "\n", - "%% Calculate the noiseless data at a higher TI resolution to plot the ideal signal curve.\n", - "%\n", - "\n", - "for ii = 1:length(TR_range)\n", - " TI_highres(ii,:) = linspace(0.05, TR_range(ii), 500);\n", - " Model.Prot.IRData.Mat = [TI_highres(ii,:)];\n", - " Opt.TR = TR_range(ii);\n", - " Opt.T1 = x.T1;\n", - " [ra,rb] = Model.ComputeRaRb(x,Opt);\n", - " x.rb = rb;\n", - " x.ra = ra;\n", - "\n", - " data_noiseless(ii,:) = Model.equation(x);\n", - "end\n", - "```\n", - "\n", - "```" - ] - }, - { - "cell_type": "markdown", - "id": "7c77eab6-945c-4086-a932-1778592a606c", - "metadata": {}, - "source": [ - "```{admonition} References\n", - ":class: seealso\n", - "\n", - "```{bibliography}\n", - ":filter: docname in docnames\n", - "```\n", - "\n", - "```" - ] - } - ], - "metadata": { - "celltoolbar": "Tags", - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.18" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -}