Even after perfect realignment, Friston et. al have shown that movement related artifacts are still extant in the BOLD signal motion correction paper. The types of motion artifacts can be divided into two types:
As shown by Friston et al., this motion, while sometimes substantial and making up a significant fraction of the fMRI signal, can be effectively removed by incorporating the subject motion parameters corrected in the volume realignment step into the nuisance GLM to remove motion artifacts. Under this model, the timeseries can be written as follows:
\begin{align} Y &= WR + T \end{align}where $Y \in \mathbb{R}^{t \times n}$ for $t$ timesteps, $n$ voxels, $W \in \mathbb{R}^{t \times r}$ is our design matrix with $r$ regressors, $R \in \mathbb{R}^{r \times n}$ are our regressor coefficients. Then $WR$ is our contribution to the BOLD signal that is modellable by our identified regressors $W$, and $T \in \mathbb{R}^{t \times n}$ is the true timeseries we seek.
We can solve this system using the least-squares solution, using the assumption that our term not modellable by our regressors squared should be minimal:
\begin{align} W = (R^TR)^{-1}R^TY \end{align}We incorporate our nuisance parameters into our design matrix:
\begin{align} R = \begin{bmatrix} x_1 & f_{1} & f^2_1 & s_1 & s^2_1 & y_1 \\ \vdots & \vdots & \vdots & \vdots & \vdots & \vdots \\ y_t & f_t & f^2_t & s_t & s^2_t & y_t \end{bmatrix} \end{align}where $f$ are our first order regressors estimated by mcflirt, and $s(t) = f(t-1)$ and $s(0) = 0$ are our our regressors dependent on the previous timepoint. Then $f^2(t)$ and $s^2(t)$ are the squared regressors.
Inputs:
Outputs:
friston(mc_params):
In [2]:
import numpy as np
def friston_model(mc_params):
(t, m) = mc_params.shape
friston = np.zeros((t, 4*m))
# the motion parameters themselves
friston[:, 0:m] = mc_params
# square the motion parameters
friston[:, m:2*m] = np.square(mc_params)
# use the motion estimated at the preceding timepoint
# as a regressor
friston[1:, 2*m:3*m] = mc_params[:-1, :]
# use the motion estimated at the preceding timepoint
# squared as a regressor
friston[:, 3*m:4*m] = np.square(friston[:, 2*m:3*m])
return friston
In [54]:
t = 20
m = 2
motion = .2*np.random.rand(t, m) + np.column_stack((.05*np.array(range(0, t)), .07*np.array(range(0, t))))
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(range(0, t), motion, label='first order regs')
ax.set_ylabel('Simulated Motion')
ax.set_xlabel('Timepoint')
ax.set_title('Simulated motion parameters estimated by FLIRT')
ax.legend()
fig.tight_layout()
fig.show()
As we can see, we have 2 regressors for motion, which keeps our visualizations as simple as possible (the logic is the same with 6 instead, except we would have 6 lines for each category instead of 2). Below, we visualize the first order motion regressors.
In [48]:
friston = friston_model(motion)
In [49]:
friston.shape == (t, 4*m) # make sure that we have 4 sets of 2 regressors
Out[49]:
We will show plots of the first order regressors with the second order regressors, and the shifted regressors, separately to make visualization easier. We know from our friston code the ordering of the regressors so this should be easy:
In [51]:
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(range(0, t), motion, label='first order regs')
ax.plot(range(0, t), friston[:, m:2*m], label='second order regs')
ax.set_ylabel('Simulated Motion')
ax.set_xlabel('Timepoint')
ax.set_title('Comparison of first and second order regressors')
ax.legend()
fig.tight_layout()
fig.show()
Visually, these appear to be roughly correct; we can see that the final red timepoint, for instance, is the square of the final orange timepoint. Let's take a look at the other regressors following a similar approach:
In [53]:
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(range(0, t), motion, label='first order regs')
ax.plot(range(0, t), friston[:, 2*m:3*m], label='shifted first order')
ax.set_ylabel('Simulated Motion')
ax.set_xlabel('Timepoint')
ax.set_title('Comparison of first order and first order shifted')
ax.legend()
fig.tight_layout()
fig.show()
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(range(0, t), friston[:, m:2*m], label='second order regs')
ax.plot(range(0, t), friston[:, 3*m:4*m], label='shifted second order')
ax.set_ylabel('Simulated Motion')
ax.set_xlabel('Timepoint')
ax.set_title('Comparison of second order and second order shifted')
ax.legend()
fig.tight_layout()
fig.show()
As we can see from our shifted regressors, each timepoint's value is the preceding timepoint's estimated motion for both first and second order.
In [ ]: