Skip to content

Instantly share code, notes, and snippets.

@arokem
Created June 14, 2012 18:54
Show Gist options
  • Save arokem/2932187 to your computer and use it in GitHub Desktop.
Save arokem/2932187 to your computer and use it in GitHub Desktop.
Simulation
import numpy as np
import tensor as ozt
# Global constants for this module:
AD = 1.5
RD = 0.5
# This converts b values from the data, so that it matches the units of ADC we
# use in the Stejskal/Tanner equation:
SCALE_FACTOR = 1000
class ODF(object):
"""
This class represents a distribution of fiber orientations within a voxel
"""
def __init__(self,
bvecs,
weights):
self.bvecs = bvecs
# Enforce that the weights are a pdf:
if np.sum(weights)!=1:
e_s = "The ODF is a distribution function, so"
e_s += "the weights must sum to 1"
raise ValueError(e_s)
self.weights = weights
class Voxel(object):
"""
The representation of the measurement in a single voxel with some
configuration of fibers crossing through it, represented by an ODF
"""
def __init__(self,
bvecs,
bvals,
odf,
scaling_factor=SCALE_FACTOR,
axial_diffusivity=AD,
radial_diffusivity=RD):
"""
The signal in a single voxel is computed from the convolution of a
response function with the ODF
Parameters
----------
bvecs: 3 by n array
unit vectors on the sphere.
bvals: 1 by n array
The measurement parameter defining where on the curve we measure
the exponential decay of the signal
odf: an ODF class instance
The representation of the orientation distribution function. Note
that this class also has bvecs, but these bvecs don't have to be
the same bvecs as the ones of this class. bvecs In this class refer
to measurement directions, while the bvecs in the ODF class refer
to directions of fibers within the voxel (not necessarily the
same...)
S0: float (optional)
The signal measured in the non diffusion-weighted scans. Default: 1
scaling_factor: float (optional)
To get the right units on the ADC, sometimes the b value needs to
be scaled. Typically, divided by 1000 (default).
axial_diffusivity, radial_diffusivity: float
These parameters
"""
self.bvecs = bvecs
self.bvals = bvals/scaling_factor
self.odf = odf
# We assume that the response function is a cigar shaped tensor:
self.Q = np.array([[axial_diffusivity, 0, 0],
[0, radial_diffusivity, 0],
[0, 0, radial_diffusivity]])
self.response_function = ozt.Tensor(self.Q, self.bvecs, self.bvals)
def signal(self,S0=1):
"""
The signal generated by the ODF in each bvec direction
"""
# Initialize to zero:
signal = np.zeros(self.bvecs.shape[-1])
# Decompose the response function to its eigen-decomposition:
evals, evecs = self.response_function.decompose
# We generate the signal for each one of the fibers in the ODF:
for odf_idx, odf_vec in enumerate(self.odf.bvecs):
this_response = ozt.rotate_to_vector(odf_vec,
evals,
evecs,
self.bvecs,
self.bvals)
signal += (self.odf.weights[odf_idx] *
this_response.predicted_signal(S0))
return signal
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment