{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n\n# Morph volumetric source estimate\n\nThis example demonstrates how to morph an individual subject's\n:class:`mne.VolSourceEstimate` to a common reference space. We achieve this\nusing :class:`mne.SourceMorph`. Data will be morphed based on\nan affine transformation and a nonlinear registration method\nknown as Symmetric Diffeomorphic Registration (SDR) by\n:footcite:`AvantsEtAl2008`.\n\nTransformation is estimated from the subject's anatomical T1 weighted MRI\n(brain) to [FreeSurfer's 'fsaverage' T1 weighted MRI (brain)](https://surfer.nmr.mgh.harvard.edu/fswiki/FsAverage)_.\n\nAfterwards the transformation will be applied to the volumetric source estimate. The\nresult will be plotted, showing the fsaverage T1 weighted anatomical MRI, overlaid with\nthe morphed volumetric source estimate.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Author: Tommy Clausner \n#\n# License: BSD-3-Clause\n# Copyright the MNE-Python contributors." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import nibabel as nib\nfrom nilearn.plotting import plot_glass_brain\n\nimport mne\nfrom mne.datasets import fetch_fsaverage, sample\nfrom mne.minimum_norm import apply_inverse, read_inverse_operator\n\nprint(__doc__)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Setup paths\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "sample_dir_raw = sample.data_path()\nsample_dir = sample_dir_raw / \"MEG\" / \"sample\"\nsubjects_dir = sample_dir_raw / \"subjects\"\n\nfname_evoked = sample_dir / \"sample_audvis-ave.fif\"\nfname_inv = sample_dir / \"sample_audvis-meg-vol-7-meg-inv.fif\"\n\nfname_t1_fsaverage = subjects_dir / \"fsaverage\" / \"mri\" / \"brain.mgz\"\nfetch_fsaverage(subjects_dir) # ensure fsaverage src exists\nfname_src_fsaverage = subjects_dir / \"fsaverage\" / \"bem\" / \"fsaverage-vol-5-src.fif\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Compute example data. For reference see `ex-inverse-volume`.\n\nLoad data:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "evoked = mne.read_evokeds(fname_evoked, condition=0, baseline=(None, 0))\ninverse_operator = read_inverse_operator(fname_inv)\n\n# Apply inverse operator\nstc = apply_inverse(evoked, inverse_operator, 1.0 / 3.0**2, \"dSPM\")\n\n# To save time\nstc.crop(0.09, 0.09)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Get a SourceMorph object for VolSourceEstimate\n\n``subject_from`` can typically be inferred from\n:class:`src `,\nand ``subject_to`` is set to 'fsaverage' by default. ``subjects_dir`` can be\nNone when set in the environment. In that case SourceMorph can be initialized\ntaking ``src`` as only argument. See :class:`mne.SourceMorph` for more\ndetails.\n\nThe default parameter setting for *zooms* will cause the reference volumes\nto be resliced before computing the transform. A value of '5' would cause\nthe function to reslice to an isotropic voxel size of 5 mm. The higher this\nvalue the less accurate but faster the computation will be.\n\nThe recommended way to use this is to morph to a specific destination source\nspace so that different ``subject_from`` morphs will go to the same space.`\nA standard usage for volumetric data reads:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "src_fs = mne.read_source_spaces(fname_src_fsaverage)\nmorph = mne.compute_source_morph(\n inverse_operator[\"src\"],\n subject_from=\"sample\",\n subjects_dir=subjects_dir,\n niter_affine=[10, 10, 5],\n niter_sdr=[10, 10, 5], # just for speed\n src_to=src_fs,\n verbose=True,\n)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Apply morph to VolSourceEstimate\n\nThe morph can be applied to the source estimate data, by giving it as the\nfirst argument to the :meth:`morph.apply() ` method.\n\n

Note

Volumetric morphing is much slower than surface morphing because the\n volume for each time point is individually resampled and SDR morphed.\n The :meth:`mne.SourceMorph.compute_vol_morph_mat` method can be used\n to compute an equivalent sparse matrix representation by computing the\n transformation for each source point individually. This generally takes\n a few minutes to compute, but can be\n :meth:`saved ` to disk and be reused. The\n resulting sparse matrix operation is very fast (about 400\u00d7 faster) to\n :meth:`apply `. This approach is more efficient\n when the number of time points to be morphed exceeds the number of\n source space points, which is generally in the thousands. This can\n easily occur when morphing many time points and multiple conditions.

\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "stc_fsaverage = morph.apply(stc)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Convert morphed VolSourceEstimate into NIfTI\n\nWe can convert our morphed source estimate into a NIfTI volume using\n:meth:`morph.apply(..., output='nifti1') `.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Create mri-resolution volume of results\nimg_fsaverage = morph.apply(stc, mri_resolution=2, output=\"nifti1\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Plot results\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Load fsaverage anatomical image\nt1_fsaverage = nib.load(fname_t1_fsaverage)\n\n# Plot glass brain (change to plot_anat to display an overlaid anatomical T1)\ndisplay = plot_glass_brain(\n t1_fsaverage, title=\"subject results to fsaverage\", draw_cross=False, annotate=True\n)\n\n# Add functional data as overlay\ndisplay.add_overlay(img_fsaverage, alpha=0.75)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Reading and writing SourceMorph from and to disk\n\nAn instance of SourceMorph can be saved, by calling\n:meth:`morph.save `.\n\nThis methods allows for specification of a filename under which the ``morph``\nwill be save in \".h5\" format. If no file extension is provided, \"-morph.h5\"\nwill be appended to the respective defined filename::\n\n >>> morph.save('my-file-name')\n\nReading a saved source morph can be achieved by using\n:func:`mne.read_source_morph`::\n\n >>> morph = mne.read_source_morph('my-file-name-morph.h5')\n\nOnce the environment is set up correctly, no information such as\n``subject_from`` or ``subjects_dir`` must be provided, since it can be\ninferred from the data and used morph to 'fsaverage' by default, e.g.::\n\n >>> morph.apply(stc)\n\n## References\n.. footbibliography::\n\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.2" } }, "nbformat": 4, "nbformat_minor": 0 }