Skip to content

Commit 66aafb4

Browse files
code and data for simulation of brain phantom
1 parent 0313b0b commit 66aafb4

File tree

8 files changed

+89
-0
lines changed

8 files changed

+89
-0
lines changed

phantoms/brain/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Code for generating ivim phantoms of the brain
2+
3+
Two sets of ground truth data are used for simulation:
4+
- Simulation in the diffusive regime (IVIM parameters D, f and D*): reference values from Ryghög et al. 2014 and b-values from Federau et al. 2012
5+
- Simulation in the ballistic regime (IVIM parameters D, f and vd): reference values and sequence parameters from Ahlgren et al. 2016
6+
7+
The segmentation used by the simulations is the ICBM 2009a nonlinear symmetric 3T atlas (https://nist.mni.mcgill.ca/icbm-152-nonlinear-atlases-2009/), the same as in e.g. ASLDRO (https://asldro.readthedocs.io/en/stable/).
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0 10 20 30 40 50 60 70 80 90 100 120 140 160 180 200 10 20 30 40 50 60 70 80 90 100 120 140 160 180 200
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.471 0.666 0.816 0.942 1.054 1.154 1.247 1.333 1.414 1.490 1.632 1.763 1.885 1.999 2.107
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import os
2+
import numpy as np
3+
4+
# Data from Ahlgren et al 2016 NMRinBiomed
5+
Delta = 7.5e-3 # 7.5 ms
6+
delta = 7.3e-3 # 7.3 ms
7+
8+
# For DDE sequence we have
9+
# b = 2y^2G^2d^2(D-d/3), c = 2yGdD => c = sqrt(b 2D^2/(D-d/3))
10+
bval_file = os.path.join(os.path.dirname(__file__),'ballistic.bval')
11+
b = np.loadtxt(bval_file)
12+
c = np.sqrt(b*2*Delta**2/(Delta-delta/3))
13+
c[1:(c.size-1)//2+1] = 0 # flow compensated => c = 0
14+
cval_file = bval_file.replace('bval','cval')
15+
np.savetxt(cval_file,c,fmt='%.3f',newline=' ')
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"D":[1.2e-3,0.98e-3,3e-3],
3+
"f":[0.0243,0.0164,0],
4+
"vd":[1.71,1.73,0],
5+
"Db":1.75e-3
6+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0 10 20 30 40 80 110 140 170 200 300 400 500 600 700 800 900
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"D":[0.81e-3,0.86e-3,3e-3],
3+
"f":[0.044,0.033,0],
4+
"Dstar":[84e-3,76e-3,0]
5+
}

phantoms/brain/sim_brain_phantom.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import os
2+
import shutil
3+
import json
4+
import numpy as np
5+
import nibabel as nib
6+
from scipy.ndimage import zoom
7+
8+
regime = 'diffusive'#'ballistic' #
9+
snr = 200
10+
resolution = [3,3,3]
11+
12+
folder = os.path.dirname(__file__)
13+
14+
# Ground truth
15+
nii = nib.load(os.path.join(folder,'ground_truth','hrgt_icbm_2009a_nls_3t.nii.gz'))
16+
segmentation = np.squeeze(nii.get_fdata()[...,-1])
17+
18+
with open(os.path.join(folder,'ground_truth',regime+'_groundtruth.json'), 'r') as f:
19+
ivim_pars = json.load(f)
20+
S0 = 1
21+
22+
# Sequence parameters
23+
bval_file = os.path.join(folder,'ground_truth',regime+'.bval')
24+
b = np.loadtxt(bval_file)
25+
if regime == 'ballistic':
26+
cval_file = bval_file.replace('bval','cval')
27+
c = np.loadtxt(cval_file)
28+
29+
# Calculate signal
30+
S = np.zeros(list(np.shape(segmentation))+[b.size])
31+
32+
if regime == 'ballistic':
33+
Db = ivim_pars["Db"]
34+
for i,(D,f,vd) in enumerate(zip(ivim_pars["D"],ivim_pars["f"],ivim_pars["vd"])):
35+
S[segmentation==i+1,:] = S0*((1-f)*np.exp(-b*D)+f*np.exp(-b*Db-c**2*vd**2))
36+
else:
37+
for i,(D,f,Dstar) in enumerate(zip(ivim_pars["D"],ivim_pars["f"],ivim_pars["Dstar"])):
38+
S[segmentation==i+1,:] = S0*((1-f)*np.exp(-b*D)+f*np.exp(-b*Dstar))
39+
40+
# Resample to suitable resolution
41+
im = zoom(S,np.append(np.diag(nii.affine)[:3]/np.array(resolution),1),order=1)
42+
sz = im.shape
43+
44+
# Add noise
45+
im_noise = np.abs(im + S0/snr*(np.random.randn(sz[0],sz[1],sz[2],sz[3])+1j*np.random.randn(sz[0],sz[1],sz[2],sz[3])))
46+
47+
# Save as image and sequence parameters
48+
nii_out = nib.Nifti1Image(im_noise,np.eye(4))
49+
base_name = os.path.join(folder,'data','{}_snr{}'.format(regime,snr))
50+
nib.save(nii_out,base_name+'.nii.gz')
51+
shutil.copyfile(bval_file,base_name+'.bval')
52+
if regime == 'ballistic':
53+
shutil.copyfile(cval_file,base_name+'.cval')

0 commit comments

Comments
 (0)