How to build a microstructure model
This page introduces some simple steps for you to observe how tissue microstructrure parameters affect dMRI spherical mean signals using some biphysical models.
1. diffusion MRI model
Load the module
using Microstructure
Specify the acquisition parameters and make a protocol. In real case, you can read a protocol from your acquisition text files
bval = [0, 1000, 2500, 5000, 7500, 11100, 18100, 25000, 43000].*1.0e6
n = length(bval)
techo = 40.0.*ones(n,).*1e-3
tdelta = 15.192.*ones(n,).*1e-3
tsmalldel = 11.0.*ones(n,).*1e-3
prot = Protocol(bval,techo,tdelta,tsmalldel)
Specify a model containing all the tissue parameters. Here, the example ExCaliber is a model for estimating axon diameter in ex vivo tissue using the spherical mean technique
estimates = ExCaliber()
You can check how the tissue is modelled by printing the model. It will give you all the tissue compartments
print_model(estimates)
You can check the values in the tissue model by using @show macro. This will show the default values if you didn't specify parameters when declare a model
@show estimates
You can specify tissue parameters when declearing a model; fields/subfiedls that are not specified will take the default values
estimates = ExCaliber( axon = Cylinder(da = 4.0e-6, dpara = 0.7e-9))
You can change the fields/subfields of a decleared model struct by using update! funciton.
a. update a field/subfields
undate!(estimates, "axon.da" => 5.0e-6)
It's common that we need to link certain tissue parameters in some models as they may not be distinguishable under the experimental condition.
b. update a field/subfield using parameter links.
update!(estimates,"axon.d0" => "axon.dpara")
c. update multiple parameters
update!(estimates,("axon.da" => 5.0e-6, "axon.dpara" => 0.5e-9, "axon.d0" => "axon.dpara", "extra.dpara" => "axon.dpara"))
Now we can use the model and protocol to generate some mri signals
signals = model_signals(estimates,prot)
We can add some noise to the signals to make them look like real measurements
using Random, Distributions
sigma = 0.01 # SNR=100 at S(b=0,t=TEmin) (b=0 of minimal TE)
noise = Normal(0,sigma)
Add some Gaussian noise
meas = signals .+ rand(noise,size(signals))
or Rician noise
meas_rician = sqrt.((signals .+ rand(noise,size(signals))).^2.0 .+ rand(noise,size(signals)).^2.0)
You can check the predict signals and simulated measurements by ploting them
using Plots
plot(prot.bval, signals, label="predicted signals", lc=:black, lw=2)
scatter!(prot.bval, meas, label="noisy measurements", mc=:red, ms=2, ma=0.5)
xlabel!("b-values [s/m^2]")
2. Combined Diffusion-relaxometry model
Now let's look at a diffusion-relaxometry model MTE-SANDI. Similarly, declear a model object and check the values
model = MTE_SANDI()
print_model(model)
@show model
MTE_SANDI requires data acquired at multiple echo times to solve the inverse problem and we will define a different protocol for it.
Make a multi-TE protocol
nTE = 4
nb = 9
bval = repeat([0, 1000, 2500, 5000, 7500, 11100, 18100, 25000, 43000].*1.0e6, outer=nTE)
techo = repeat([32, 45, 60, 78].*1e-3, inner=9)
tdelta = 15.192.*ones(nTE*nb,).*1e-3
tsmalldel = 11.0.*ones(nTE*nb,).*1e-3
prot = Protocol(bval,techo,tdelta,tsmalldel)
Let's see how multi-TE signals look like
signals = model_signals(model, prot)
meas = signals .+ rand(noise,size(signals))
plot(signals, label="predicted signals", lc=:black, lw=2)
scatter!(meas, label="noisy measurements", mc=:red, ms=2, ma=0.5)