-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Adding sensitivity maps for forward models in mne.report #13722
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 6 commits
6feddac
e5bac3f
50fa6b6
7a8308b
8451506
7a31947
80b4078
a0763e4
d6c49f8
ca976bc
4c2c13c
1066d11
577f3c4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Add ``sensitiviy`` kwarg in :meth:`mne.Report.add_forward` to get the sensitiviy map of the forward models, by `Himanshu Mahor`_. |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -40,7 +40,7 @@ | |||||||||
| from ..minimum_norm import InverseOperator, read_inverse_operator | ||||||||||
| from ..parallel import parallel_func | ||||||||||
| from ..preprocessing.ica import read_ica | ||||||||||
| from ..proj import read_proj | ||||||||||
| from ..proj import read_proj, sensitivity_map | ||||||||||
| from ..source_estimate import SourceEstimate, read_source_estimate | ||||||||||
| from ..source_space._source_space import _ensure_src | ||||||||||
| from ..surface import dig_mri_distances | ||||||||||
|
|
@@ -1546,6 +1546,7 @@ def add_forward( | |||||||||
| subject=None, | ||||||||||
| subjects_dir=None, | ||||||||||
| plot=False, | ||||||||||
| sensitivity=False, | ||||||||||
| tags=("forward-solution",), | ||||||||||
| section=None, | ||||||||||
| replace=False, | ||||||||||
|
|
@@ -1567,6 +1568,14 @@ def add_forward( | |||||||||
| If True, plot the source space of the forward solution. | ||||||||||
|
|
||||||||||
| .. versionadded:: 1.10 | ||||||||||
| sensitivity : bool | list of str | ||||||||||
| If True, compute and plot sensitivity maps for all available | ||||||||||
| channel types (MEG gradiometers, MEG magnetometers, and EEG). | ||||||||||
| If a list, compute sensitivity maps for only the specified | ||||||||||
| channel types (e.g., ``['grad', 'mag']``). | ||||||||||
| Valid channel types are ``'grad'``, ``'mag'``, and ``'eeg'``. | ||||||||||
|
|
||||||||||
| .. versionadded:: 1.12 | ||||||||||
| %(tags_report)s | ||||||||||
| %(section_report)s | ||||||||||
|
|
||||||||||
|
|
@@ -1589,6 +1598,7 @@ def add_forward( | |||||||||
| tags=tags, | ||||||||||
| replace=replace, | ||||||||||
| plot=plot, | ||||||||||
| sensitivity=sensitivity, | ||||||||||
| ) | ||||||||||
|
|
||||||||||
| @fill_doc | ||||||||||
|
|
@@ -3616,6 +3626,7 @@ def _add_forward( | |||||||||
| title, | ||||||||||
| image_format, | ||||||||||
| plot, | ||||||||||
| sensitivity, | ||||||||||
| section, | ||||||||||
| tags, | ||||||||||
| replace, | ||||||||||
|
|
@@ -3626,10 +3637,143 @@ def _add_forward( | |||||||||
|
|
||||||||||
| subject = self.subject if subject is None else subject | ||||||||||
| subject = forward["src"][0]["subject_his_id"] if subject is None else subject | ||||||||||
| subjects_dir = self.subjects_dir if subjects_dir is None else subjects_dir | ||||||||||
|
|
||||||||||
| # XXX Todo | ||||||||||
| # Render sensitivity maps | ||||||||||
| sensitivity_maps_html = "" | ||||||||||
| if sensitivity: | ||||||||||
| if subjects_dir is None: | ||||||||||
| raise ValueError( | ||||||||||
| "subjects_dir must be provided to compute sensitivity maps" | ||||||||||
| ) | ||||||||||
|
|
||||||||||
| info = forward["info"] | ||||||||||
| meg_info = info.get("meg", False) | ||||||||||
| has_grad = meg_info and ( | ||||||||||
| isinstance(meg_info, dict) | ||||||||||
| and meg_info.get("grad", False) | ||||||||||
| or meg_info is True | ||||||||||
| ) | ||||||||||
| has_mag = meg_info and ( | ||||||||||
| isinstance(meg_info, dict) | ||||||||||
| and meg_info.get("mag", False) | ||||||||||
| or meg_info is True | ||||||||||
| ) | ||||||||||
| has_eeg = info.get("eeg", False) | ||||||||||
|
|
||||||||||
| all_ch_types = [] | ||||||||||
| if has_grad: | ||||||||||
| all_ch_types.append("grad") | ||||||||||
| if has_mag: | ||||||||||
| all_ch_types.append("mag") | ||||||||||
| if has_eeg: | ||||||||||
| all_ch_types.append("eeg") | ||||||||||
|
|
||||||||||
| if not all_ch_types: | ||||||||||
| raise ValueError( | ||||||||||
| "No MEG or EEG channels found in forward solution. " | ||||||||||
| "Cannot compute sensitivity maps." | ||||||||||
| ) | ||||||||||
|
|
||||||||||
| if sensitivity is True: | ||||||||||
| ch_types = all_ch_types | ||||||||||
| else: | ||||||||||
| ch_types = list(sensitivity) | ||||||||||
| for ch_type in ch_types: | ||||||||||
| _check_option("ch_type", ch_type, ["grad", "mag", "eeg"]) | ||||||||||
| if ch_type not in all_ch_types: | ||||||||||
| raise ValueError( | ||||||||||
| f"Channel type '{ch_type}' not found in forward solution. " | ||||||||||
| f"Available types are: {all_ch_types}" | ||||||||||
| ) | ||||||||||
|
|
||||||||||
| html_parts = [] | ||||||||||
| for ch_type in ch_types: | ||||||||||
| _check_option("ch_type", ch_type, ["grad", "mag", "eeg"]) | ||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not as DRY as it could be. Making some of the checks a little redundant (they are very fast) seems worth the tradeoff Also this should be above the opening |
||||||||||
|
|
||||||||||
| html_parts = [] | ||||||||||
| for ch_type in ch_types: | ||||||||||
| stc = sensitivity_map(forward, ch_type=ch_type, mode="fixed") | ||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add this so we don't forget to allow it (or you can add support here!)
Suggested change
|
||||||||||
|
|
||||||||||
| stc_plot_kwargs = _handle_default("report_stc_plot_kwargs", dict()) | ||||||||||
| stc_plot_kwargs.update( | ||||||||||
| subject=subject, | ||||||||||
| subjects_dir=subjects_dir, | ||||||||||
| clim=dict(kind="value", lims=[0, 50, 100]), | ||||||||||
| colorbar=True, | ||||||||||
| ) | ||||||||||
|
|
||||||||||
| import matplotlib.pyplot as plt | ||||||||||
|
|
||||||||||
| if get_3d_backend() is not None: | ||||||||||
| brain = stc.plot(**stc_plot_kwargs) | ||||||||||
| brain._renderer.plotter.subplot(0, 0) | ||||||||||
| fig, ax = plt.subplots(figsize=(4.5, 4.5), layout="constrained") | ||||||||||
| ax.imshow(brain.screenshot(time_viewer=False, mode="rgb")) | ||||||||||
| ax.axis("off") | ||||||||||
| _constrain_fig_resolution( | ||||||||||
| fig, | ||||||||||
| max_width=stc_plot_kwargs.get("size", (800, 600))[0], | ||||||||||
| max_res=self.img_max_res, | ||||||||||
| ) | ||||||||||
| plt.close(fig) | ||||||||||
| brain.close() | ||||||||||
| else: | ||||||||||
| fig_lh = plt.figure(layout="constrained") | ||||||||||
| fig_rh = plt.figure(layout="constrained") | ||||||||||
| brain_lh = stc.plot( | ||||||||||
| views="lat", | ||||||||||
| hemi="lh", | ||||||||||
| initial_time=stc.times[0], | ||||||||||
| backend="matplotlib", | ||||||||||
| subject=subject, | ||||||||||
| subjects_dir=subjects_dir, | ||||||||||
| figure=fig_lh, | ||||||||||
| **stc_plot_kwargs, | ||||||||||
| ) | ||||||||||
| brain_rh = stc.plot( | ||||||||||
| views="lat", | ||||||||||
| hemi="rh", | ||||||||||
|
Comment on lines
+3696
to
+3724
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't typically do this conditional stuff. If you want 3D plots in reports you should have a 3D backend working. So just assume it is like we do in something like the alignment code, and use |
||||||||||
| initial_time=stc.times[0], | ||||||||||
| subject=subject, | ||||||||||
| subjects_dir=subjects_dir, | ||||||||||
| backend="matplotlib", | ||||||||||
| figure=fig_rh, | ||||||||||
| **stc_plot_kwargs, | ||||||||||
| ) | ||||||||||
| _constrain_fig_resolution( | ||||||||||
| fig_lh, | ||||||||||
| max_width=stc_plot_kwargs.get("size", (800, 600))[0], | ||||||||||
| max_res=self.img_max_res, | ||||||||||
| ) | ||||||||||
| _constrain_fig_resolution( | ||||||||||
| fig_rh, | ||||||||||
| max_width=stc_plot_kwargs.get("size", (800, 600))[0], | ||||||||||
| max_res=self.img_max_res, | ||||||||||
| ) | ||||||||||
| fig = fig_lh | ||||||||||
| plt.close(fig_rh) | ||||||||||
| brain_lh.close() | ||||||||||
| brain_rh.close() | ||||||||||
|
|
||||||||||
| img = self._fig_to_img(fig=fig, image_format=image_format) | ||||||||||
| plt.close(fig) | ||||||||||
|
|
||||||||||
| img_id = f"forward-sensitivity-{ch_type}" | ||||||||||
| img_html = _html_image_element( | ||||||||||
| id_=img_id, | ||||||||||
| img=img, | ||||||||||
| image_format=image_format, | ||||||||||
| caption=f"Sensitivity map ({ch_type})", | ||||||||||
| show=True, | ||||||||||
| div_klass="forward-sensitivity-map", | ||||||||||
| img_klass="forward-sensitivity-map", | ||||||||||
| title=f"Sensitivity Map - {ch_type.upper()}", | ||||||||||
| tags=(), | ||||||||||
| ) | ||||||||||
| html_parts.append(img_html) | ||||||||||
|
|
||||||||||
| sensitivity_maps_html = "\n".join(html_parts) | ||||||||||
|
|
||||||||||
| source_space_html = "" | ||||||||||
| if plot: | ||||||||||
| source_space_html = self._src_html( | ||||||||||
|
|
||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we are currently guaranteed that all our forward solutions contain some of these. If you want to be really safe you could make it do all of
grad, mag, eeg, seeg, ecogand that would be future compatible