From 6feddac64d67d7818f17e44d73dff0061897c676 Mon Sep 17 00:00:00 2001 From: 1himan Date: Thu, 5 Mar 2026 15:54:43 +0530 Subject: [PATCH 1/4] Adding sensitivity maps for forward models in mne.report --- mne/report/report.py | 115 ++++++++++++++++++++++++++++++++++- tutorials/intro/70_report.py | 4 +- 2 files changed, 114 insertions(+), 5 deletions(-) diff --git a/mne/report/report.py b/mne/report/report.py index 071d15e3cee..205173fb5ae 100644 --- a/mne/report/report.py +++ b/mne/report/report.py @@ -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.11 %(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,108 @@ 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" + ) + + ch_types = ["grad", "mag", "eeg"] + if sensitivity is not True: + ch_types = list(sensitivity) + for ch_type in ch_types: + _check_option("ch_type", ch_type, ["grad", "mag", "eeg"]) + + html_parts = [] + for ch_type in ch_types: + try: + stc = sensitivity_map(forward, ch_type=ch_type, mode="fixed") + except Exception: + continue + + 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", + 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( diff --git a/tutorials/intro/70_report.py b/tutorials/intro/70_report.py index 31133610e2c..dbeb4a63eeb 100644 --- a/tutorials/intro/70_report.py +++ b/tutorials/intro/70_report.py @@ -318,8 +318,8 @@ # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ # # Forward solutions ("leadfields") can be added by passing a `~mne.Forward` -# object or the path to a forward solution stored on disk to -# :meth:`mne.Report.add_forward`. +# object or the path to a forward solution stored on disk +# to :meth:`mne.Report.add_forward`. fwd_path = sample_dir / "sample_audvis-meg-oct-6-fwd.fif" From e5bac3f09e915297a46ccb37173897b2029b82c5 Mon Sep 17 00:00:00 2001 From: 1himan Date: Thu, 5 Mar 2026 17:13:14 +0530 Subject: [PATCH 2/4] Doc changes --- tutorials/intro/70_report.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tutorials/intro/70_report.py b/tutorials/intro/70_report.py index dbeb4a63eeb..31133610e2c 100644 --- a/tutorials/intro/70_report.py +++ b/tutorials/intro/70_report.py @@ -318,8 +318,8 @@ # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ # # Forward solutions ("leadfields") can be added by passing a `~mne.Forward` -# object or the path to a forward solution stored on disk -# to :meth:`mne.Report.add_forward`. +# object or the path to a forward solution stored on disk to +# :meth:`mne.Report.add_forward`. fwd_path = sample_dir / "sample_audvis-meg-oct-6-fwd.fif" From 50fa6b6276f3c28b921ccea60836ed35422b82d3 Mon Sep 17 00:00:00 2001 From: 1himan Date: Sat, 7 Mar 2026 16:50:46 +0530 Subject: [PATCH 3/4] adding suggested changes and doc/changes/dev/13722.newfeature.rst. --- doc/changes/dev/13722.newfeature.rst | 1 + mne/report/report.py | 49 ++++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 doc/changes/dev/13722.newfeature.rst diff --git a/doc/changes/dev/13722.newfeature.rst b/doc/changes/dev/13722.newfeature.rst new file mode 100644 index 00000000000..64b3f5caf17 --- /dev/null +++ b/doc/changes/dev/13722.newfeature.rst @@ -0,0 +1 @@ +Add `sensitiviy` kwarg in :meth:`mne.Report.add_forward` to get the sensitiviy map of the forward models, by `Himanshu Mahor`_. diff --git a/mne/report/report.py b/mne/report/report.py index 205173fb5ae..8edeb82e26d 100644 --- a/mne/report/report.py +++ b/mne/report/report.py @@ -1575,7 +1575,7 @@ def add_forward( channel types (e.g., ``['grad', 'mag']``). Valid channel types are ``'grad'``, ``'mag'``, and ``'eeg'``. - .. versionadded:: 1.11 + .. versionadded:: 1.12 %(tags_report)s %(section_report)s @@ -3646,18 +3646,53 @@ def _add_forward( "subjects_dir must be provided to compute sensitivity maps" ) - ch_types = ["grad", "mag", "eeg"] - if sensitivity is not True: + 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: - try: - stc = sensitivity_map(forward, ch_type=ch_type, mode="fixed") - except Exception: - continue + _check_option("ch_type", ch_type, ["grad", "mag", "eeg"]) + + html_parts = [] + for ch_type in ch_types: + stc = sensitivity_map(forward, ch_type=ch_type, mode="fixed") stc_plot_kwargs = _handle_default("report_stc_plot_kwargs", dict()) stc_plot_kwargs.update( From 8451506ac32ab457a327700a1bd776b0eae3e28f Mon Sep 17 00:00:00 2001 From: 1himan Date: Sat, 7 Mar 2026 17:23:23 +0530 Subject: [PATCH 4/4] Fixing CI failure --- doc/changes/dev/13722.newfeature.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/changes/dev/13722.newfeature.rst b/doc/changes/dev/13722.newfeature.rst index 64b3f5caf17..8f22cf809f1 100644 --- a/doc/changes/dev/13722.newfeature.rst +++ b/doc/changes/dev/13722.newfeature.rst @@ -1 +1 @@ -Add `sensitiviy` kwarg in :meth:`mne.Report.add_forward` to get the sensitiviy map of the forward models, by `Himanshu Mahor`_. +Add ``sensitiviy`` kwarg in :meth:`mne.Report.add_forward` to get the sensitiviy map of the forward models, by `Himanshu Mahor`_.