DSC parameter estimation (CBV, CBF)#

Hide code cell source
# import statements
import os
import numpy as np
from matplotlib import pyplot as plt
import csv
import pandas as pd
import seaborn as sns
from plotting_results_nb import plot_bland_altman, bland_altman_statistics
import json
from pathlib import Path

Test data#

Data summary: Simulated signal time curves representing perfusion scenarios typical of grey and white matter.

Detailed info:

  • Transit time distribution: gamma variate distribution with shape parameter lambda = 1 (negative exponential)

  • AIF modelled using a gamma variate function with shape parameter alfa = 1.5 and scale parameter beta = 3

  • Arterial delay not included

  • Arterial dispersion not included

  • Tissue and Arterial SNR = 200

  • TR = 1.243 s

  • TE = 29 ms

Reference values: Reference values are the parameters used to generate the data. The following combinations of CBV and CBF are included for

  • white matter CBV 2 ml/100ml and CBF 5, 10, 15, 20, 25, 30, 35 ml/100ml/min

  • grey matter CBV 4 ml/100ml and CBF 10, 20, 30, 40, 50, 60, 70 ml/100ml/min.

Source: arthur-chakwizira/BezierCurveDeconvolution

Ref: Non-parametric deconvolution using Bézier curves for quantification of cerebral perfusion in dynamic susceptibility contrast MRI (https://doi.org/10.1007/s10334-021-00995-0)

Equivalent simulations have previously been used to evaluate perfusion estimation techniques for DSC-MRI:

  • Wu et al. 2003 (DOI 10.1002/mrm.10522)

  • Mouridsen et al. 2006 (DOI 10.1016/j.neuroimage.2006.06.015)

  • Chappell et al. 2015 (10.1002/mrm.25390)

Tolerances

  • CBV: a_tol=1 ml/100ml, r_tol=0.1

  • CBF: a_tol=15 ml/100ml/min, r_tol=0.1

Visualize test data#

To get an impression of the test data, the concentration time curves of the test data are plotted below.

#plot test data
filename = ('../test/DSCmodels/data/dsc_data.csv')
# read from CSV to pandas
df1 = pd.read_csv(filename)

no_voxels = len(df1.label)
fig, ax = plt.subplots(1, 1, sharex='col', sharey='row', figsize=(6,6))
for currentvoxel in range(no_voxels):
    a = df1['C_tis'][currentvoxel]
    c = np.fromstring(a, dtype=float, sep=' ')
    ax.plot(c)
    
ax.set_ylabel('C (mM)', fontsize=14)
ax.set_xlabel('index', fontsize=14)
plt.xticks(fontsize=14)
plt.yticks(fontsize=14);

    
_images/c037c892bfd6469de2160289f6d14158618fcc3bf0ad462f1747f95766ccbe38.png

Import data#

# Load the meta data
meta = json.load(open("../test/results-meta.json"))
# Loop over each entry and collect the dataframe
df = []
for entry in meta:
    if (entry['category'] == 'DSCmodels'):
        fpath, fname, category, method, author = entry.values()
        df_entry = pd.read_csv(Path(fpath, fname)).assign(author=author)
        df.append(df_entry)
    
# Concat all entries
df = pd.concat(df)
author_list = df.author.unique()
no_authors = len(author_list)

# calculate error between measured and reference values
df['error_cbv'] = df['cbv_meas'] - df['cbv_ref']
df['error_cbf'] = df['cbf_meas'] - df['cbf_ref']

# tolerances
tolerances = { 'cbv': {'atol' : 1, 'rtol': 0.1 }, 'cbf': {'atol':15, 'rtol':0.1}}

Results#

If the tolerance lines are not shown, it means that they are outside the limits of the y-axis.

The contribution by SR_TBG_BNI_USAPhoenix uses the L-curve criterion (for more information see doi:10.1088/0031-9155/52/2/009). Additional tag was added to the contribution to indicate the method (LCC SFTR)

# set-up styling for seaborn plots
sns.set(font_scale=1.5)
sns.set_style("ticks")
fig, ax = plt.subplots(1,2, sharey='none', figsize=(12,5))
plot_bland_altman(ax[0], df, tolerances, 'cbv', ylim=(-2,2),label_xaxis='$CBV_{ref}$ (mL/100mL)',label_yaxis='$\Delta$ $CBV$ (mL/100mL)')
plot_bland_altman(ax[1], df, tolerances, 'cbf', ylim=(-20,20),label_xaxis='$CBF_{ref}$ (mL/100mL/min)',label_yaxis='$\Delta$ $CBF$ (mL/100mL/min)')
fig.tight_layout()
fig.subplots_adjust(left=0.15, top=0.95)

# Hide the legend for the left subplot
ax[0].get_legend().set_visible(False)
# Set the position of the legend
plt.legend(bbox_to_anchor=(1.05, 1), title='ContributionID', loc='upper left', borderaxespad=0, fontsize=14);
_images/a74d405ff0526347799da3850b0a679c082dac6eeeac2e49ed1e7c641ccf9133.png

Bias results estimated CBV values combined for all voxels

resultsBA = bland_altman_statistics(data=df,par='error_cbv',grouptag='author')
print(resultsBA)
                                           bias     stdev  LoA lower  \
author                                                                 
SR_LB_TBG_BNI_USAPhoenix_USA_LCC SFTR  0.312242  0.236365  -0.151033   

                                       LoA upper  
author                                            
SR_LB_TBG_BNI_USAPhoenix_USA_LCC SFTR   0.775517  

Bias results estimated CBF values combined for all voxels

resultsBA = bland_altman_statistics(data=df,par='error_cbf',grouptag='author')
print(resultsBA)
                                          bias     stdev  LoA lower  LoA upper
author                                                                        
SR_LB_TBG_BNI_USAPhoenix_USA_LCC SFTR -2.93965  4.107339 -10.990034   5.110734

Notes#

Additional notes/remarks

References#

Sourbron et al. Pixel-by-pixel deconvolution of bolus-tracking data: optimization and implementation, Phys Med Biol (2007), 52:429 doi:10.1088/0031-9155/52/2/009