An open framework for parameter mapping and image processing Apr 8, 2013 Jason Su Motivation • Mapping methods share many common tasks and features – Fitting of a tissue model to data with a known signal equation – Multi-subject processing – Residuals and statistics analysis to evaluate fit – Pause and resume, recovery from crash • We should promote code sharing in the community, so groups around the world can experiment with promising new mapping methods Goals • There are 2 audiences, coders and users • For developers of novel methods: – Provide a framework that fosters readability and code re-use – Easy to convert a bare-bones implementation into production-level functionality, avoid code re-writes – Provide easy ways to speed up code: embarrassing parallelization (across slices or subjects) and caching – What else do developers need? • Data/error visualization • ? Goals • For clinicians and users: – A consistent workflow and usage pattern for producing a multitude of mapping methods – Support for all platforms – Scalability to large-subject studies and diverse computing resources • Amazon EC2 has built-in support for Python • A community – Open source repository to share and improve The Framework: Protocol class • Like when we sit down at the scanner, we start with a protocol • Input is readable JSON, not an inscrutable stream of command-line switches • Instantiates a bunch of Series and manages access to their image data – I’ve assumed voxel-wise fits { "Mask": { "filename": “brainMask_mask.nii.gz" }, "SPGR": [ { "filename”: "spgr_fa3000.nii.gz", "flip_angle": 3.0, "repetition_time": 6.74, "weight": 2 }, ... { "filename": "spgr_fa8000.nii.gz", "flip_angle": 8.0, "repetition_time": 6.74 } ], "SSFP": [ { "filename": "ssfp_fa11000_ph0.nii.gz", "flip_angle": 11.0, "repetition_time": 3.6, "rf_phase_cycle": 0.0, "weight": 2 }, ... ] Series class • Defines important scan parameters, can pull from DICOM header • Loads image from disk • SPGR and SSFP are implemented – Flexible for any combination of phase cycles, TRs, flip angles • B0, B1/Kappa, ROI? Utilities module • Memoization/caching of expensive results • pandas data structures inspired by R – Vectors and table data frames with named access • Notification of other functions when data changes >> @memo def stats(x): return (mean(x), std(x)) >> x = DataFrame(d, index=['a', 'b', 'c', 'd']) P001 P002 Types 'RRMS' 'SPMS' EDSS 3.5 6.0 >> x['Bob'] >> y = notify_container_factory( DataFrame)(stats, data=x) >> y.ix['EDSS'] = [4.0 7.0] -> stats() function is called and recomputes mean and std. of EDSS Implementing a signal equation • Define a TissueModel – RelaxationTissueModel • Take parameters from TissueModel and compute expensive intermediate parts of the signal equation: – SPGRSystemArray and SSFPSystemArray • Finally, incorporate sequence parameters from a Series and compute the intensity at a voxel – SPGRSignal and SSFPSignal • Series are able to discover the modules that model their signal behavior – Automatically looks for the module based on its name – SPGRSeries is tied to the sequence.spgr module TissueModel class • A bare-bones base class that provides a flexible dictionary hierarchy and notification > model.parameters['myelin']['fraction'] = 0.2 • Methods: – – – – add_notifiee del_notifiee desync – do something when the model is changed sync – call notifiees, propagate the changes • Notifiees are called in the order they are added, duplicates are ignored • Acts like a state machine • Classes group/organize functions and the data they manipulate under common umbrellas RelaxationTissueModel • Converts parameters to pandas DataFrame for vectorized operations • N-component relaxation model – mcDESPOT is usually 2component – Pure tissue, does not include terms like RF phase cycle • desync enforces model assumptions – Chemical equilibrium and unit total volume component_list = [ { 'name': 'intraextra', 'T1': 1000, 'T2': 70, 'off-resonance': 0.1, 'fraction': 0.75, 'exchange_rate': {'myelin': .1, 'csf': 0 } }, { 'name': 'myelin', 'T1': 800.0, 'T2': 30.0, 'off-resonance': 0.2, 'fraction': 0.2, 'exchange_rate': {'intraextra': .1, 'csf': 0 } }, { 'name': 'csf', 'T1': 5000.0, 'T2': 300.0, 'off-resonance': 0.3, 'fraction': 0.05, 'exchange_rate': {'intraextra': 0, 'myelin': 0 } } ] model = RelaxationTissueModel(component_list) Simplifying Assumptions • 2 component model f S 1 f F – Only need to find fF (the fast volume fraction) • Chemical equilibrium f F kFS f S kSF – Allows us to eliminate finding k SF=1/τS • Both components are on the same resonance F S SPGR/SSFPSystemArray • Subclassing promotes code re-use – SPGR is the simpler form, so SPGR extends it • Computes shared intermediate quantities • A shared cache between different objects • Notified by TissueModel 1 k SF RF F T k FS 2,F 1 kFS k SF 0 T2,S 1 0 k FS RF F T2,F ASSFP 0 RF S k FS 0 0 0 0 0 0 0 0 RF S 0 kSF 0 1 k SF T2,S 0 0 0 1 kFS T1,F k FS 0 0 0 k SF 1 k FS T1,F 0 SPGR/SSFPSignal • With phase cycle separated out of the tissue model matrix, matrix multiply instead of expm – Adding more phase cycles has minimal impact on processing time • Current C implementation calculates all quantities for every image Solvers • Input a TissueModel, constraints, and initial guess to fit – Each of these are like TissueModel • Stochastic Region Contraction, in progress – Store summary about each contraction step for later viewing – Idea: chain a solver like Newton’s or gradient descent once SRC has narrowed in What is the advantage of all this structure? • Thinking about code re-use and class structure helps to expose similarities in signal equations – Can lead to speed-ups as with SPGR/SSFP • Improved debugging via unit testing and exceptions – e.g. Series raises exceptions giving valuable feedback about what sequence parameters are missing in the input JSON • Dynamic typing can allow for unexpected clever use by others Open Source: Python vs. Matlab • Matlab Central is a nice central repository for interesting code – But for the most part it is single contributor – Limits the possibility of large scale improvements to MATLAB functionality • Python Package Index (PyPI) – Has single line install for most everything – Just in the category of nearly drop-in acceleration of Python: • • • • Theano – GPU and generated C Numba(Pro) – LLVM PyPy/NumPyPy – total rewrite of CPython core interpreter Numexpr – optimizes evaluation of math expressions including considerations like CPU cache Disadvantages • Need to learn a new language • Loss of existing tools in MATLAB, including: – read_pfile – Extended phase graph analysis • Development environment is different – Spyder tries to imitate MATLAB IDE • Too many packages to install? – There are bundles that have all of the best packages for Python with a double-click Possible Future Features • Access to super-large files with HDF5 and PyTables, important for high-res 32ch data • Extending to handle more exotic cases – QSM –? • P-files and raw data? – Reconstruction does share some similar desired functions: • Multi-subject processing, pause and resume • Auto-suggest of possible fits given a Protocol • GUI for users?