ReconPlugin

Contents

ReconPlugin#

class pulserver.recon.ReconPlugin[source]#

Bases: ABC

Base class for reconstruction plugins.

The runtime drives one MRD stream through three hooks: startup() once, before any acquisition; recon() for each reconstruction unit as it closes; and finish() once, after the last unit. Only recon() is abstract; the framework method receive() is not overridden.

A unit is the set of readouts reconstructed together: those of one branch and encoding space that share every image counter (slice, contrast, phase, repetition, set, average) not listed in axes or merge. The counters in axes are axes of the unit’s k-space instead, and those in merge are laid out along none; segment and the user counters separate no units unless segment is listed. Each accepted acquisition runs through the gadgets, then joins the unit of the branch branch_for() names, which places it by its flags (see ReconData). A unit closes when the flag triggers names for its branch has arrived for every combination of the counters in axes and merge. It leaves the plugin before recon() runs, so its buffers are freed once recon() returns; the memory a stream holds is bounded by the units open at once. Units still open at LAST_IN_MEASUREMENT or at the end of the stream are reconstructed then, in the order they opened and under their own branches.

Each stream runs on its own spawn() of the module-level PLUGIN, so state set in the hooks belongs to one stream. context.exam is shared across the series of an exam; a plugin adds to it and never clears it.

Parameters:
  • gadgets – Gadget steps applied to every accepted readout on arrival, in order, before it is placed; each receives a copy of the acquisition, which it may update to describe the readout it returns.

  • triggers – {branch: flag}: the AcquisitionFlag that closes a unit of the branch, combined with | for either of several. The first branch declared receives the readouts branch_for() does not route elsewhere. The default is {"imaging": LAST_IN_MEASUREMENT}.

  • axes – Counters of a unit placed along axes of its k-space and waited for by the closing flag, instead of separating units: any of repetition, phase, slice, contrast, set, average and segment. Their extents are the header’s encoding limits.

  • merge – Counters of a unit waited for by the closing flag and laid out along no axis, instead of separating units: any of repetition, phase, slice, contrast, set and average. A readout is placed by its other counters whatever its value of a merged one, so readouts that share them replace one another, with a warning; the echoes of one train, which fill phase encodes of their own, do not. A buffer holds one k-space however many values a merged counter takes. Their extents are the header’s encoding limits.

  • require_flags – Flags an acquisition must all carry to be accepted. A combined AcquisitionFlag counts as its members.

  • reject_flags – Flags any one of which excludes an acquisition.

  • buffered – Place readouts in ReconData.data and ReconData.ref. Disable for streams whose header does not describe their encoding spaces; the plugin then reads ReconData.acquisitions, and a unit closes at the first flag, whatever its axes.

  • chain – Deprecated alias of gadgets; warns.

  • branches – Deprecated alias of triggers, mapping {flag: branch}; warns.

Attributes:
  • gadgets (tuple of Gadget)

  • triggers (dict)

  • axes (tuple of str)

  • merge (tuple of str) – Read when a stream starts, so a subclass may assign it after super().__init__.

Examples

>>> import numpy as np
>>> import pulserver.mrd as mrd
>>> import pulserver.recon as recon
>>> class DropNoise(recon.Gadget):
...     def __call__(self, acquisition, data):
...         noise = mrd.has_acquisition_flag(acquisition, "ACQ_IS_NOISE_MEASUREMENT")
...         return None if noise else data
>>> class RootSumOfSquares(recon.ReconPlugin):
...     def __init__(self):
...         super().__init__(
...             gadgets=[DropNoise()],
...             triggers={"imaging": mrd.AcquisitionFlag.LAST_IN_SLICE},
...             axes=("average",),
...         )
...     def recon(self, context, branch, data):
...         kspace = data.data.kspace
...         return recon.ReconResult(np.sqrt(np.sum(np.abs(kspace) ** 2, axis=0)))
>>> RootSumOfSquares().triggers["imaging"]
<AcquisitionFlag.LAST_IN_SLICE: 128>

Methods

branch_for

Return the branch an acquisition belongs to, or None to drop it.

finish

Run once, after the last unit has been reconstructed.

flush

Reconstruct every unit still open under its own branch, then call finish().

gadget

Return this stream's first gadget of type kind.

receive

Take in one acquisition and reconstruct the units it completes.

receive_waveform

Hold a waveform for the next units that close; see ReconData.waveforms.

recon

Reconstruct one unit.

run

Reconstruct one ISMRMRD HDF5 file in this process, through the same hooks.

spawn

Return the instance one stream runs on.

startup

Start every gadget and lay out the units' encoding spaces from the header.