Operators¶
FORGE treats an MRI scan as a linear system. The image x produces the measured k-space data y through an encoding operator A:
A holds everything about the acquisition: the trajectory, the sample times, the coil sensitivities, the field map, and the shot phases. Reconstruction means finding the x that best explains y under A (see The solver and regularization). The solver never needs A as a matrix, which would be far too large. It only needs to apply A and its adjoint AH to vectors, and every operator class in FORGE provides exactly those two things.
The operator convention¶
| Expression | Meaning |
|---|---|
A * x |
Forward transform, image to k-space |
A / y |
Adjoint transform, k-space to image |
/ here is the adjoint, not a division or an inverse. Each operator has overloads for arma::Col<std::complex<T>> and for forgeCol<forgeComplex<T>>. The solver uses the forgeCol versions where they exist, which keeps the data on the GPU between iterations.
Operators wrap operators¶
Each part of the physics is a separate class, and they nest. A class that adds coils, field correction or shot phases takes the next operator in as a template parameter and calls it:
| Class | Adds |
|---|---|
Gnufft |
Non-uniform FFT by Kaiser-Bessel gridding (see The NUFFT) |
Gdft |
Exact non-uniform DFT, with field correction built in |
GdftR2 |
Gdft plus field-map gradients within each voxel |
Gfft |
Cartesian FFT |
TimeSegmentation<T, Tobj> |
Field correction around any of the above (see Field correction) |
SENSE<T, Tobj> |
Coil sensitivities: one application of Tobj per coil |
pcSENSE |
Coil sensitivities and a phase map per shot, with a Gdft per shot |
pcSenseTimeSeg |
The same, with a time-segmented Gnufft per shot |
So the command-line programs build these stacks:
| Program | Operator |
|---|---|
forgeSense -F NUFFT |
SENSE<float, TimeSegmentation<float, Gnufft<float>>> |
forgeSense -F DFT |
SENSE<float, Gdft<float>> |
forgeSense -F DFTGrads |
SENSE<float, GdftR2<float>> |
forgePcSense |
pcSENSE<float> |
forgePcSenseTimeSeg |
pcSenseTimeSeg<float> |
oscillateRecon |
LRobj around one pcSenseTimeSeg<float> per repetition (see Low-rank reconstruction) |
Because each layer only calls the one inside it, a new trajectory type or a new correction can be added as one class without touching the others.
Shot phase correction¶
In multi-shot imaging, motion during diffusion or MRE encoding gives each shot its own phase across the image. SENSE assumes all shots see the same object, so these phases become ghosts. The pcSENSE operators multiply the image by each shot's phase before encoding it:
for coil c and shot s, where Sc is the coil sensitivity, Ps the shot's phase map, and Gs the shot's Fourier operator. The phase maps come from navigators or a low-resolution reconstruction of each shot, computed before FORGE runs.
Precision and backends¶
The operators are templates on the floating-point type, with float and double instantiations. The command-line programs use float. GPU code exists only for float; the double versions always run on the CPU. GPU backends describes which parts of each operator run on the GPU.