From b4d4bf4a903d2f7ea72f615d742575d9e127e79b Mon Sep 17 00:00:00 2001 From: pmolfese Date: Mon, 18 Dec 2023 18:37:31 -0500 Subject: [PATCH 01/24] add functions to export STC as GIFTI --- mne/export/gifti.py | 111 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 mne/export/gifti.py diff --git a/mne/export/gifti.py b/mne/export/gifti.py new file mode 100644 index 00000000000..189a3982fa1 --- /dev/null +++ b/mne/export/gifti.py @@ -0,0 +1,111 @@ +# Authors: MNE Developers +# +# License: BSD-3-Clause +# Copyright the MNE-Python contributors. + +import os +import nibabel as nib +import numpy as np + +def _get_decimated_surfaces(src): + """Helper function to make exporting GIFTI surfaces easier + Parameters + ----------- + src : instance of SourceSpaces + Returns + ----------- + surfaces : list of dict + The decimated surfaces present in the source space. Each dict + which contains 'rr' and 'tris' keys for vertices positions and + triangle indices. + Notes + .. versionadded:: 1.7 + """ + surfaces = [] + for s in src: + if s['type'] != 'surf': + continue + rr = s['rr'] + use_tris = s['use_tris'] + vertno = s['vertno'] + ss = {} + ss['rr'] = rr[vertno] + reindex = np.full(len(rr), -1, int) + reindex[vertno] = np.arange(len(vertno)) + ss['tris'] = reindex[use_tris] + assert (ss['tris'] >= 0).all() + surfaces.append(ss) + return surfaces + + +def export_gifti(fname, stc, src, scale=1, scale_rr=1e3): + """Function for exporting STC to GIFTI file + Parameters + ------------ + fname : string + filename basename to save files as + stc : stc object + instance of stc to export + src : source solution (surface) object + the source space of the forward solution + scale : int + scale of functional values + scale_rr : float + Value to scale source solution + Notes + ------------ + Creates gifti files for source solution and time courses of STC + .. versionadded:: 1.7 + """ + + ss = _get_decimated_surfaces(src) + + # Create lists to put DataArrays into + lh = [] + rh = [] + + # Coerce rr to be in mm (MNE uses meters) + ss[0]['rr'] *= scale_rr + ss[1]['rr'] *= scale_rr + + lh.append(nib.gifti.gifti.GiftiDataArray(data=ss[0]['rr'], intent='NIFTI_INTENT_POINTSET', datatype='NIFTI_TYPE_FLOAT32')) + rh.append(nib.gifti.gifti.GiftiDataArray(data=ss[1]['rr'], intent='NIFTI_INTENT_POINTSET', datatype='NIFTI_TYPE_FLOAT32')) + + # Make the topology DataArray + lh.append(nib.gifti.gifti.GiftiDataArray(data=ss[0]['tris'], intent='NIFTI_INTENT_TRIANGLE', datatype='NIFTI_TYPE_INT32')) + rh.append(nib.gifti.gifti.GiftiDataArray(data=ss[1]['tris'], intent='NIFTI_INTENT_TRIANGLE', datatype='NIFTI_TYPE_INT32')) + + # Make the output GIFTI for anatomicals + topo_gi_lh = nib.gifti.gifti.GiftiImage(darrays=lh) + topo_gi_rh = nib.gifti.gifti.GiftiImage(darrays=rh) + + #actually save the files + nib.save(topo_gi_lh, f"{fname}-lh.gii") + nib.save(topo_gi_rh, f"{fname}-rh.gii") + + # Make the Time Series data arrays + lh_ts = [] + rh_ts = [] + + for t in range(stc.shape[1]): + lh_ts.append( + nib.gifti.gifti.GiftiDataArray( + data=stc.lh_data[:, t] * scale, + intent='NIFTI_INTENT_POINTSET', + datatype='NIFTI_TYPE_FLOAT32' + ) + ) + + rh_ts.append( + nib.gifti.gifti.GiftiDataArray( + data=stc.rh_data[:, t] * scale, + intent='NIFTI_INTENT_POINTSET', + datatype='NIFTI_TYPE_FLOAT32' + ) + ) + + #save the time series + ts_gi_lh = nib.gifti.gifti.GiftiImage(darrays=lh_ts) + ts_gi_rh = nib.gifti.gifti.GiftiImage(darrays=rh_ts) + nib.save(ts_gi_lh, f"{fname}-lh.time.gii") + nib.save(ts_gi_rh, f"{fname}-rh.time.gii") \ No newline at end of file From 0e4a0a80585e22e344c507fe08d0c4cacc56316f Mon Sep 17 00:00:00 2001 From: pmolfese Date: Mon, 18 Dec 2023 18:40:28 -0500 Subject: [PATCH 02/24] added more attribution --- mne/export/gifti.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mne/export/gifti.py b/mne/export/gifti.py index 189a3982fa1..1aa907875c5 100644 --- a/mne/export/gifti.py +++ b/mne/export/gifti.py @@ -1,4 +1,6 @@ -# Authors: MNE Developers +# Authors: Joshua Teves & Peter Molfese +# Affiliation: National Institutes of Health (NIH) +# Added: December 2023 ~ Version 1.7 # # License: BSD-3-Clause # Copyright the MNE-Python contributors. From 7c5b675bcd75434be0c69e508d855977a79a810e Mon Sep 17 00:00:00 2001 From: pmolfese Date: Tue, 19 Dec 2023 12:09:52 -0500 Subject: [PATCH 03/24] move gifti export functionality to source_estimate.py --- mne/export/gifti.py | 113 ----------------------------------------- mne/source_estimate.py | 106 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 113 deletions(-) delete mode 100644 mne/export/gifti.py diff --git a/mne/export/gifti.py b/mne/export/gifti.py deleted file mode 100644 index 1aa907875c5..00000000000 --- a/mne/export/gifti.py +++ /dev/null @@ -1,113 +0,0 @@ -# Authors: Joshua Teves & Peter Molfese -# Affiliation: National Institutes of Health (NIH) -# Added: December 2023 ~ Version 1.7 -# -# License: BSD-3-Clause -# Copyright the MNE-Python contributors. - -import os -import nibabel as nib -import numpy as np - -def _get_decimated_surfaces(src): - """Helper function to make exporting GIFTI surfaces easier - Parameters - ----------- - src : instance of SourceSpaces - Returns - ----------- - surfaces : list of dict - The decimated surfaces present in the source space. Each dict - which contains 'rr' and 'tris' keys for vertices positions and - triangle indices. - Notes - .. versionadded:: 1.7 - """ - surfaces = [] - for s in src: - if s['type'] != 'surf': - continue - rr = s['rr'] - use_tris = s['use_tris'] - vertno = s['vertno'] - ss = {} - ss['rr'] = rr[vertno] - reindex = np.full(len(rr), -1, int) - reindex[vertno] = np.arange(len(vertno)) - ss['tris'] = reindex[use_tris] - assert (ss['tris'] >= 0).all() - surfaces.append(ss) - return surfaces - - -def export_gifti(fname, stc, src, scale=1, scale_rr=1e3): - """Function for exporting STC to GIFTI file - Parameters - ------------ - fname : string - filename basename to save files as - stc : stc object - instance of stc to export - src : source solution (surface) object - the source space of the forward solution - scale : int - scale of functional values - scale_rr : float - Value to scale source solution - Notes - ------------ - Creates gifti files for source solution and time courses of STC - .. versionadded:: 1.7 - """ - - ss = _get_decimated_surfaces(src) - - # Create lists to put DataArrays into - lh = [] - rh = [] - - # Coerce rr to be in mm (MNE uses meters) - ss[0]['rr'] *= scale_rr - ss[1]['rr'] *= scale_rr - - lh.append(nib.gifti.gifti.GiftiDataArray(data=ss[0]['rr'], intent='NIFTI_INTENT_POINTSET', datatype='NIFTI_TYPE_FLOAT32')) - rh.append(nib.gifti.gifti.GiftiDataArray(data=ss[1]['rr'], intent='NIFTI_INTENT_POINTSET', datatype='NIFTI_TYPE_FLOAT32')) - - # Make the topology DataArray - lh.append(nib.gifti.gifti.GiftiDataArray(data=ss[0]['tris'], intent='NIFTI_INTENT_TRIANGLE', datatype='NIFTI_TYPE_INT32')) - rh.append(nib.gifti.gifti.GiftiDataArray(data=ss[1]['tris'], intent='NIFTI_INTENT_TRIANGLE', datatype='NIFTI_TYPE_INT32')) - - # Make the output GIFTI for anatomicals - topo_gi_lh = nib.gifti.gifti.GiftiImage(darrays=lh) - topo_gi_rh = nib.gifti.gifti.GiftiImage(darrays=rh) - - #actually save the files - nib.save(topo_gi_lh, f"{fname}-lh.gii") - nib.save(topo_gi_rh, f"{fname}-rh.gii") - - # Make the Time Series data arrays - lh_ts = [] - rh_ts = [] - - for t in range(stc.shape[1]): - lh_ts.append( - nib.gifti.gifti.GiftiDataArray( - data=stc.lh_data[:, t] * scale, - intent='NIFTI_INTENT_POINTSET', - datatype='NIFTI_TYPE_FLOAT32' - ) - ) - - rh_ts.append( - nib.gifti.gifti.GiftiDataArray( - data=stc.rh_data[:, t] * scale, - intent='NIFTI_INTENT_POINTSET', - datatype='NIFTI_TYPE_FLOAT32' - ) - ) - - #save the time series - ts_gi_lh = nib.gifti.gifti.GiftiImage(darrays=lh_ts) - ts_gi_rh = nib.gifti.gifti.GiftiImage(darrays=rh_ts) - nib.save(ts_gi_lh, f"{fname}-lh.time.gii") - nib.save(ts_gi_rh, f"{fname}-rh.time.gii") \ No newline at end of file diff --git a/mne/source_estimate.py b/mne/source_estimate.py index efc5a06515a..f81f9e992cf 100644 --- a/mne/source_estimate.py +++ b/mne/source_estimate.py @@ -1566,6 +1566,112 @@ def in_label(self, label): subject=self.subject, ) return label_stc + + def _get_decimated_surfaces(self, src): + """Helper function to make exporting GIFTI surfaces easier + Parameters + ----------- + src : instance of SourceSpaces + Returns + ----------- + surfaces : list of dict + The decimated surfaces present in the source space. Each dict + which contains 'rr' and 'tris' keys for vertices positions and + triangle indices. + Notes + .. versionadded:: 1.7 + """ + surfaces = [] + for s in src: + if s['type'] != 'surf': + continue + rr = s['rr'] + use_tris = s['use_tris'] + vertno = s['vertno'] + ss = {} + ss['rr'] = rr[vertno] + reindex = np.full(len(rr), -1, int) + reindex[vertno] = np.arange(len(vertno)) + ss['tris'] = reindex[use_tris] + assert (ss['tris'] >= 0).all() + surfaces.append(ss) + return surfaces + + def save_as_surface(self, fname, src, scale=1, scale_rr=1e3): + """Function for exporting STC to GIFTI file + Parameters + ------------ + self : stc object + instance of stc to export + fname : string + filename basename to save files as + Will write anatomical gifti plus time series gifti for lh/rh + src : source solution (surface) object + the source space of the forward solution + scale : int + scale of functional values + scale_rr : float + Value to scale source solution + Notes + ------------ + Creates gifti files for source solution and time courses of STC + .. versionadded:: 1.7 + """ + nib = _import_nibabel() + + ss = self._get_decimated_surfaces(src) + stc = self + + # Create lists to put DataArrays into + lh = [] + rh = [] + + # Coerce rr to be in mm (MNE uses meters) + ss[0]['rr'] *= scale_rr + ss[1]['rr'] *= scale_rr + + lh.append(nib.gifti.gifti.GiftiDataArray(data=ss[0]['rr'], intent='NIFTI_INTENT_POINTSET', datatype='NIFTI_TYPE_FLOAT32')) + rh.append(nib.gifti.gifti.GiftiDataArray(data=ss[1]['rr'], intent='NIFTI_INTENT_POINTSET', datatype='NIFTI_TYPE_FLOAT32')) + + # Make the topology DataArray + lh.append(nib.gifti.gifti.GiftiDataArray(data=ss[0]['tris'], intent='NIFTI_INTENT_TRIANGLE', datatype='NIFTI_TYPE_INT32')) + rh.append(nib.gifti.gifti.GiftiDataArray(data=ss[1]['tris'], intent='NIFTI_INTENT_TRIANGLE', datatype='NIFTI_TYPE_INT32')) + + # Make the output GIFTI for anatomicals + topo_gi_lh = nib.gifti.gifti.GiftiImage(darrays=lh) + topo_gi_rh = nib.gifti.gifti.GiftiImage(darrays=rh) + + #actually save the files + nib.save(topo_gi_lh, f"{fname}-lh.gii") + nib.save(topo_gi_rh, f"{fname}-rh.gii") + + # Make the Time Series data arrays + lh_ts = [] + rh_ts = [] + + for t in range(stc.shape[1]): + lh_ts.append( + nib.gifti.gifti.GiftiDataArray( + data=stc.lh_data[:, t] * scale, + intent='NIFTI_INTENT_POINTSET', + datatype='NIFTI_TYPE_FLOAT32' + ) + ) + + rh_ts.append( + nib.gifti.gifti.GiftiDataArray( + data=stc.rh_data[:, t] * scale, + intent='NIFTI_INTENT_POINTSET', + datatype='NIFTI_TYPE_FLOAT32' + ) + ) + + #save the time series + ts_gi_lh = nib.gifti.gifti.GiftiImage(darrays=lh_ts) + ts_gi_rh = nib.gifti.gifti.GiftiImage(darrays=rh_ts) + nib.save(ts_gi_lh, f"{fname}-lh.time.gii") + nib.save(ts_gi_rh, f"{fname}-rh.time.gii") + def expand(self, vertices): """Expand SourceEstimate to include more vertices. From dab0639861b4741b2d1a8497a60d92363f878ac5 Mon Sep 17 00:00:00 2001 From: pmolfese Date: Tue, 19 Dec 2023 13:12:23 -0500 Subject: [PATCH 04/24] devel doc update --- doc/changes/devel.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/changes/devel.rst b/doc/changes/devel.rst index fdf307bbbd1..940ed039d3a 100644 --- a/doc/changes/devel.rst +++ b/doc/changes/devel.rst @@ -41,6 +41,7 @@ Enhancements - Add ``method="polyphase"`` to :meth:`mne.io.Raw.resample` and related functions to allow resampling using :func:`scipy.signal.upfirdn` (:gh:`12268` by `Eric Larson`_) - The package build backend was switched from ``setuptools`` to ``hatchling``. This will only affect users who build and install MNE-Python from source. (:gh:`12269`, :gh:`12281` by `Richard Höchenberger`_) - :meth:`mne.Annotations.to_data_frame` can now output different formats for the ``onset`` column: seconds, milliseconds, datetime objects, and timedelta objects. (:gh:`12289` by `Daniel McCloy`_) +- Add ``save_as_surface`` method to allow saving GIFTI files from STC SourceEstimates in :class:`~mne._BaseSurfaceSourceEstimate` (:gh:`12309` by `Peter Molfese`_) Bugs ~~~~ From 1be89f143d5eee78985521a12cec64b56d2479df Mon Sep 17 00:00:00 2001 From: pmolfese Date: Tue, 19 Dec 2023 14:54:45 -0500 Subject: [PATCH 05/24] add test to write and check save_as_surface --- mne/tests/test_source_estimate.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/mne/tests/test_source_estimate.py b/mne/tests/test_source_estimate.py index be31fd1501b..96a766dcd20 100644 --- a/mne/tests/test_source_estimate.py +++ b/mne/tests/test_source_estimate.py @@ -247,6 +247,34 @@ def test_volume_stc(tmp_path): assert_array_equal(stc.vertices[0], stc_new.vertices[0]) assert_array_almost_equal(stc.data, stc_new.data) +@testing.requires_testing_data +def test_save_stc_as_gifti(tmp_path): + """Save the stc as a gifti file and export.""" + nib = pytest.importorskip("nibabel") + surfpath_src = bem_path / "sample-oct-6-src.fif" + surfpath_stc = data_path / "MEG" / "sample" / "sample_audvis_trunc-meg" + src = read_source_spaces(surfpath_src) #need source space + stc = read_source_estimate(surfpath_stc) #need stc + assert isinstance(src, SourceSpaces) + assert isinstance(stc, SourceEstimate) + + surf_fname = tmp_path / "stc_write" + + stc.save_as_surface(surf_fname, src) + + #did structural get written? + img_lh = nib.load(f'{surf_fname}-lh.gii') + img_rh = nib.load(f'{surf_fname}-rh.gii') + assert isinstance(img_lh, nib.gifti.gifti.GiftiImage) + assert isinstance(img_rh, nib.gifti.gifti.GiftiImage) + + #did time series get written? + img_timelh = nib.load(f'{surf_fname}-lh.time.gii') + img_timerh = nib.load(f'{surf_fname}-rh.time.gii') + assert isinstance(img_timelh, nib.gifti.gifti.GiftiImage) + assert isinstance(img_timerh, nib.gifti.gifti.GiftiImage) + + @testing.requires_testing_data def test_stc_as_volume(): From 0e8960ebdd35b92947bd9bfd36016b3fb51ce026 Mon Sep 17 00:00:00 2001 From: pmolfese Date: Tue, 19 Dec 2023 15:17:00 -0500 Subject: [PATCH 06/24] Reusing existing get_decimated_surfaces from source_space, missed that it existing since 1.0 --- mne/source_estimate.py | 32 ++------------------------------ 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/mne/source_estimate.py b/mne/source_estimate.py index b75e8073dd6..2eaa617c09c 100644 --- a/mne/source_estimate.py +++ b/mne/source_estimate.py @@ -1584,35 +1584,6 @@ def in_label(self, label): ) return label_stc - def _get_decimated_surfaces(self, src): - """Helper function to make exporting GIFTI surfaces easier - Parameters - ----------- - src : instance of SourceSpaces - Returns - ----------- - surfaces : list of dict - The decimated surfaces present in the source space. Each dict - which contains 'rr' and 'tris' keys for vertices positions and - triangle indices. - Notes - .. versionadded:: 1.7 - """ - surfaces = [] - for s in src: - if s['type'] != 'surf': - continue - rr = s['rr'] - use_tris = s['use_tris'] - vertno = s['vertno'] - ss = {} - ss['rr'] = rr[vertno] - reindex = np.full(len(rr), -1, int) - reindex[vertno] = np.arange(len(vertno)) - ss['tris'] = reindex[use_tris] - assert (ss['tris'] >= 0).all() - surfaces.append(ss) - return surfaces def save_as_surface(self, fname, src, scale=1, scale_rr=1e3): """Function for exporting STC to GIFTI file @@ -1635,8 +1606,9 @@ def save_as_surface(self, fname, src, scale=1, scale_rr=1e3): .. versionadded:: 1.7 """ nib = _import_nibabel() + from .source_space import get_decimated_surfaces - ss = self._get_decimated_surfaces(src) + ss = get_decimated_surfaces(src) stc = self # Create lists to put DataArrays into From 1784e66d18f1f8fa22c907fa53cad6dfc6001e9c Mon Sep 17 00:00:00 2001 From: pmolfese Date: Tue, 19 Dec 2023 16:17:21 -0500 Subject: [PATCH 07/24] styling fixes --- mne/source_estimate.py | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/mne/source_estimate.py b/mne/source_estimate.py index 2eaa617c09c..579f85bdd69 100644 --- a/mne/source_estimate.py +++ b/mne/source_estimate.py @@ -1584,24 +1584,27 @@ def in_label(self, label): ) return label_stc - + @verbose def save_as_surface(self, fname, src, scale=1, scale_rr=1e3): - """Function for exporting STC to GIFTI file + """Save a surface source estimate (stc) as a GIFTI file. + Parameters - ------------ - self : stc object - instance of stc to export - fname : string + ---------- + fname : path-like filename basename to save files as - Will write anatomical gifti plus time series gifti for lh/rh + Will write anatomical gifti plus time series gifti for both lh/rh + ex: + "basename" --> "basename.lh.gii" and "basename.lh.time.gii" + --> "basename.rh.gii" and "basename.rh.time.gii" src : source solution (surface) object the source space of the forward solution scale : int scale of functional values scale_rr : float Value to scale source solution + Notes - ------------ + ---------- Creates gifti files for source solution and time courses of STC .. versionadded:: 1.7 """ @@ -1619,12 +1622,24 @@ def save_as_surface(self, fname, src, scale=1, scale_rr=1e3): ss[0]['rr'] *= scale_rr ss[1]['rr'] *= scale_rr - lh.append(nib.gifti.gifti.GiftiDataArray(data=ss[0]['rr'], intent='NIFTI_INTENT_POINTSET', datatype='NIFTI_TYPE_FLOAT32')) - rh.append(nib.gifti.gifti.GiftiDataArray(data=ss[1]['rr'], intent='NIFTI_INTENT_POINTSET', datatype='NIFTI_TYPE_FLOAT32')) + lh.append(nib.gifti.gifti.GiftiDataArray( + data=ss[0]['rr'], + intent='NIFTI_INTENT_POINTSET', + datatype='NIFTI_TYPE_FLOAT32')) + rh.append(nib.gifti.gifti.GiftiDataArray( + data=ss[1]['rr'], + intent='NIFTI_INTENT_POINTSET', + datatype='NIFTI_TYPE_FLOAT32')) # Make the topology DataArray - lh.append(nib.gifti.gifti.GiftiDataArray(data=ss[0]['tris'], intent='NIFTI_INTENT_TRIANGLE', datatype='NIFTI_TYPE_INT32')) - rh.append(nib.gifti.gifti.GiftiDataArray(data=ss[1]['tris'], intent='NIFTI_INTENT_TRIANGLE', datatype='NIFTI_TYPE_INT32')) + lh.append(nib.gifti.gifti.GiftiDataArray( + data=ss[0]['tris'], + intent='NIFTI_INTENT_TRIANGLE', + datatype='NIFTI_TYPE_INT32')) + rh.append(nib.gifti.gifti.GiftiDataArray( + data=ss[1]['tris'], + intent='NIFTI_INTENT_TRIANGLE', + datatype='NIFTI_TYPE_INT32')) # Make the output GIFTI for anatomicals topo_gi_lh = nib.gifti.gifti.GiftiImage(darrays=lh) From 7eaafcc973590e500587348c523b215c540d5164 Mon Sep 17 00:00:00 2001 From: pmolfese Date: Tue, 19 Dec 2023 16:24:48 -0500 Subject: [PATCH 08/24] verbose breakage --- mne/source_estimate.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mne/source_estimate.py b/mne/source_estimate.py index 579f85bdd69..e14934eec86 100644 --- a/mne/source_estimate.py +++ b/mne/source_estimate.py @@ -1584,7 +1584,6 @@ def in_label(self, label): ) return label_stc - @verbose def save_as_surface(self, fname, src, scale=1, scale_rr=1e3): """Save a surface source estimate (stc) as a GIFTI file. From 070f9bbac92ceda8bedb15beb10d068ce34cfb49 Mon Sep 17 00:00:00 2001 From: "Peter J. Molfese" Date: Wed, 20 Dec 2023 13:11:23 -0500 Subject: [PATCH 09/24] Update doc/changes/devel.rst Co-authored-by: Eric Larson --- doc/changes/devel.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/changes/devel.rst b/doc/changes/devel.rst index 940ed039d3a..89a9c9a1c58 100644 --- a/doc/changes/devel.rst +++ b/doc/changes/devel.rst @@ -41,7 +41,7 @@ Enhancements - Add ``method="polyphase"`` to :meth:`mne.io.Raw.resample` and related functions to allow resampling using :func:`scipy.signal.upfirdn` (:gh:`12268` by `Eric Larson`_) - The package build backend was switched from ``setuptools`` to ``hatchling``. This will only affect users who build and install MNE-Python from source. (:gh:`12269`, :gh:`12281` by `Richard Höchenberger`_) - :meth:`mne.Annotations.to_data_frame` can now output different formats for the ``onset`` column: seconds, milliseconds, datetime objects, and timedelta objects. (:gh:`12289` by `Daniel McCloy`_) -- Add ``save_as_surface`` method to allow saving GIFTI files from STC SourceEstimates in :class:`~mne._BaseSurfaceSourceEstimate` (:gh:`12309` by `Peter Molfese`_) +- Add ``save_as_surface`` method to allow saving GIFTI files from :class:`~mne.SourceEstimate` and related classes (:gh:`12309` by `Peter Molfese`_) Bugs ~~~~ From 8b8f7666dae33ad1237ee68901358503d76b6cac Mon Sep 17 00:00:00 2001 From: "Peter J. Molfese" Date: Wed, 20 Dec 2023 13:11:40 -0500 Subject: [PATCH 10/24] Update mne/source_estimate.py Co-authored-by: Eric Larson --- mne/source_estimate.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mne/source_estimate.py b/mne/source_estimate.py index e14934eec86..567d465d155 100644 --- a/mne/source_estimate.py +++ b/mne/source_estimate.py @@ -1591,10 +1591,10 @@ def save_as_surface(self, fname, src, scale=1, scale_rr=1e3): ---------- fname : path-like filename basename to save files as - Will write anatomical gifti plus time series gifti for both lh/rh - ex: - "basename" --> "basename.lh.gii" and "basename.lh.time.gii" - --> "basename.rh.gii" and "basename.rh.time.gii" + Will write anatomical gifti plus time series gifti for both lh/rh, + for example ``"basename"`` will write ``"basename.lh.gii"``, + ``"basename.lh.time.gii"``, ``"basename.rh.gii"``, and + ``"basename.rh.time.gii"``. src : source solution (surface) object the source space of the forward solution scale : int From 861371965f1f4e504faa43f3d631b79178e32cc9 Mon Sep 17 00:00:00 2001 From: "Peter J. Molfese" Date: Wed, 20 Dec 2023 13:12:05 -0500 Subject: [PATCH 11/24] Update mne/source_estimate.py version add Co-authored-by: Eric Larson --- mne/source_estimate.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mne/source_estimate.py b/mne/source_estimate.py index 567d465d155..2fdc650aa74 100644 --- a/mne/source_estimate.py +++ b/mne/source_estimate.py @@ -1605,6 +1605,7 @@ def save_as_surface(self, fname, src, scale=1, scale_rr=1e3): Notes ---------- Creates gifti files for source solution and time courses of STC + .. versionadded:: 1.7 """ nib = _import_nibabel() From f21209e198d73d5a804daa8ba0374077d7bd019d Mon Sep 17 00:00:00 2001 From: "Peter J. Molfese" Date: Wed, 20 Dec 2023 13:12:27 -0500 Subject: [PATCH 12/24] Update mne/tests/test_source_estimate.py Co-authored-by: Eric Larson --- mne/tests/test_source_estimate.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mne/tests/test_source_estimate.py b/mne/tests/test_source_estimate.py index 96a766dcd20..0574c5f215f 100644 --- a/mne/tests/test_source_estimate.py +++ b/mne/tests/test_source_estimate.py @@ -247,6 +247,7 @@ def test_volume_stc(tmp_path): assert_array_equal(stc.vertices[0], stc_new.vertices[0]) assert_array_almost_equal(stc.data, stc_new.data) + @testing.requires_testing_data def test_save_stc_as_gifti(tmp_path): """Save the stc as a gifti file and export.""" From e10f5d74bfb4fc591571acdf5586e2a34cfc8f10 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 20 Dec 2023 13:50:52 -0500 Subject: [PATCH 13/24] TST: Ping From 1587da12e9ea47e0d7feb9026fa5ccbb6f275aa0 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 20 Dec 2023 13:53:52 -0500 Subject: [PATCH 14/24] STY: pre-commit --- mne/source_estimate.py | 87 +++++++++++++++++-------------- mne/tests/test_source_estimate.py | 17 +++--- 2 files changed, 57 insertions(+), 47 deletions(-) diff --git a/mne/source_estimate.py b/mne/source_estimate.py index 2fdc650aa74..4feddd3c0a1 100644 --- a/mne/source_estimate.py +++ b/mne/source_estimate.py @@ -1583,7 +1583,7 @@ def in_label(self, label): subject=self.subject, ) return label_stc - + def save_as_surface(self, fname, src, scale=1, scale_rr=1e3): """Save a surface source estimate (stc) as a GIFTI file. @@ -1592,7 +1592,7 @@ def save_as_surface(self, fname, src, scale=1, scale_rr=1e3): fname : path-like filename basename to save files as Will write anatomical gifti plus time series gifti for both lh/rh, - for example ``"basename"`` will write ``"basename.lh.gii"``, + for example ``"basename"`` will write ``"basename.lh.gii"``, ``"basename.lh.time.gii"``, ``"basename.rh.gii"``, and ``"basename.rh.time.gii"``. src : source solution (surface) object @@ -1601,9 +1601,9 @@ def save_as_surface(self, fname, src, scale=1, scale_rr=1e3): scale of functional values scale_rr : float Value to scale source solution - + Notes - ---------- + ----- Creates gifti files for source solution and time courses of STC .. versionadded:: 1.7 @@ -1617,66 +1617,77 @@ def save_as_surface(self, fname, src, scale=1, scale_rr=1e3): # Create lists to put DataArrays into lh = [] rh = [] - + # Coerce rr to be in mm (MNE uses meters) - ss[0]['rr'] *= scale_rr - ss[1]['rr'] *= scale_rr - - lh.append(nib.gifti.gifti.GiftiDataArray( - data=ss[0]['rr'], - intent='NIFTI_INTENT_POINTSET', - datatype='NIFTI_TYPE_FLOAT32')) - rh.append(nib.gifti.gifti.GiftiDataArray( - data=ss[1]['rr'], - intent='NIFTI_INTENT_POINTSET', - datatype='NIFTI_TYPE_FLOAT32')) - + ss[0]["rr"] *= scale_rr + ss[1]["rr"] *= scale_rr + + lh.append( + nib.gifti.gifti.GiftiDataArray( + data=ss[0]["rr"], + intent="NIFTI_INTENT_POINTSET", + datatype="NIFTI_TYPE_FLOAT32", + ) + ) + rh.append( + nib.gifti.gifti.GiftiDataArray( + data=ss[1]["rr"], + intent="NIFTI_INTENT_POINTSET", + datatype="NIFTI_TYPE_FLOAT32", + ) + ) + # Make the topology DataArray - lh.append(nib.gifti.gifti.GiftiDataArray( - data=ss[0]['tris'], - intent='NIFTI_INTENT_TRIANGLE', - datatype='NIFTI_TYPE_INT32')) - rh.append(nib.gifti.gifti.GiftiDataArray( - data=ss[1]['tris'], - intent='NIFTI_INTENT_TRIANGLE', - datatype='NIFTI_TYPE_INT32')) - + lh.append( + nib.gifti.gifti.GiftiDataArray( + data=ss[0]["tris"], + intent="NIFTI_INTENT_TRIANGLE", + datatype="NIFTI_TYPE_INT32", + ) + ) + rh.append( + nib.gifti.gifti.GiftiDataArray( + data=ss[1]["tris"], + intent="NIFTI_INTENT_TRIANGLE", + datatype="NIFTI_TYPE_INT32", + ) + ) + # Make the output GIFTI for anatomicals topo_gi_lh = nib.gifti.gifti.GiftiImage(darrays=lh) topo_gi_rh = nib.gifti.gifti.GiftiImage(darrays=rh) - - #actually save the files + + # actually save the files nib.save(topo_gi_lh, f"{fname}-lh.gii") nib.save(topo_gi_rh, f"{fname}-rh.gii") - + # Make the Time Series data arrays lh_ts = [] rh_ts = [] - + for t in range(stc.shape[1]): lh_ts.append( nib.gifti.gifti.GiftiDataArray( data=stc.lh_data[:, t] * scale, - intent='NIFTI_INTENT_POINTSET', - datatype='NIFTI_TYPE_FLOAT32' + intent="NIFTI_INTENT_POINTSET", + datatype="NIFTI_TYPE_FLOAT32", ) ) - + rh_ts.append( nib.gifti.gifti.GiftiDataArray( data=stc.rh_data[:, t] * scale, - intent='NIFTI_INTENT_POINTSET', - datatype='NIFTI_TYPE_FLOAT32' + intent="NIFTI_INTENT_POINTSET", + datatype="NIFTI_TYPE_FLOAT32", ) ) - - #save the time series + + # save the time series ts_gi_lh = nib.gifti.gifti.GiftiImage(darrays=lh_ts) ts_gi_rh = nib.gifti.gifti.GiftiImage(darrays=rh_ts) nib.save(ts_gi_lh, f"{fname}-lh.time.gii") nib.save(ts_gi_rh, f"{fname}-rh.time.gii") - def expand(self, vertices): """Expand SourceEstimate to include more vertices. diff --git a/mne/tests/test_source_estimate.py b/mne/tests/test_source_estimate.py index 0574c5f215f..f351581b60b 100644 --- a/mne/tests/test_source_estimate.py +++ b/mne/tests/test_source_estimate.py @@ -254,8 +254,8 @@ def test_save_stc_as_gifti(tmp_path): nib = pytest.importorskip("nibabel") surfpath_src = bem_path / "sample-oct-6-src.fif" surfpath_stc = data_path / "MEG" / "sample" / "sample_audvis_trunc-meg" - src = read_source_spaces(surfpath_src) #need source space - stc = read_source_estimate(surfpath_stc) #need stc + src = read_source_spaces(surfpath_src) # need source space + stc = read_source_estimate(surfpath_stc) # need stc assert isinstance(src, SourceSpaces) assert isinstance(stc, SourceEstimate) @@ -263,19 +263,18 @@ def test_save_stc_as_gifti(tmp_path): stc.save_as_surface(surf_fname, src) - #did structural get written? - img_lh = nib.load(f'{surf_fname}-lh.gii') - img_rh = nib.load(f'{surf_fname}-rh.gii') + # did structural get written? + img_lh = nib.load(f"{surf_fname}-lh.gii") + img_rh = nib.load(f"{surf_fname}-rh.gii") assert isinstance(img_lh, nib.gifti.gifti.GiftiImage) assert isinstance(img_rh, nib.gifti.gifti.GiftiImage) - #did time series get written? - img_timelh = nib.load(f'{surf_fname}-lh.time.gii') - img_timerh = nib.load(f'{surf_fname}-rh.time.gii') + # did time series get written? + img_timelh = nib.load(f"{surf_fname}-lh.time.gii") + img_timerh = nib.load(f"{surf_fname}-rh.time.gii") assert isinstance(img_timelh, nib.gifti.gifti.GiftiImage) assert isinstance(img_timerh, nib.gifti.gifti.GiftiImage) - @testing.requires_testing_data def test_stc_as_volume(): From f205937e1902afb8d195a3fef0ba47937ad8e404 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 20 Dec 2023 14:06:25 -0500 Subject: [PATCH 15/24] STY: Caps and dots --- mne/source_estimate.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mne/source_estimate.py b/mne/source_estimate.py index 4feddd3c0a1..a31373ed788 100644 --- a/mne/source_estimate.py +++ b/mne/source_estimate.py @@ -1590,21 +1590,21 @@ def save_as_surface(self, fname, src, scale=1, scale_rr=1e3): Parameters ---------- fname : path-like - filename basename to save files as + Filename basename to save files as. Will write anatomical gifti plus time series gifti for both lh/rh, for example ``"basename"`` will write ``"basename.lh.gii"``, ``"basename.lh.time.gii"``, ``"basename.rh.gii"``, and ``"basename.rh.time.gii"``. src : source solution (surface) object - the source space of the forward solution + The source space of the forward solution. scale : int - scale of functional values + Scale of functional values. scale_rr : float - Value to scale source solution + Value to scale source solution. Notes ----- - Creates gifti files for source solution and time courses of STC + Creates gifti files for source solution and time courses of STC. .. versionadded:: 1.7 """ From bca27735d08786a4edd72b4755844533c36ff1bf Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 20 Dec 2023 14:06:57 -0500 Subject: [PATCH 16/24] FIX: Name --- mne/source_estimate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mne/source_estimate.py b/mne/source_estimate.py index a31373ed788..b2be0447384 100644 --- a/mne/source_estimate.py +++ b/mne/source_estimate.py @@ -1595,7 +1595,7 @@ def save_as_surface(self, fname, src, scale=1, scale_rr=1e3): for example ``"basename"`` will write ``"basename.lh.gii"``, ``"basename.lh.time.gii"``, ``"basename.rh.gii"``, and ``"basename.rh.time.gii"``. - src : source solution (surface) object + src : instance of SourceSpaces The source space of the forward solution. scale : int Scale of functional values. From dffd5a93a6c5294912ff64a69a5014360e77d4a1 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 20 Dec 2023 14:07:46 -0500 Subject: [PATCH 17/24] FIX: No nest --- mne/source_estimate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mne/source_estimate.py b/mne/source_estimate.py index b2be0447384..7bd8c4ab67f 100644 --- a/mne/source_estimate.py +++ b/mne/source_estimate.py @@ -31,6 +31,7 @@ _ensure_src_subject, _get_morph_src_reordering, _get_src_nn, + get_decimated_surfaces, ) from .surface import _get_ico_surface, _project_onto_surface, mesh_edges, read_surface from .transforms import _get_trans, apply_trans @@ -1609,7 +1610,6 @@ def save_as_surface(self, fname, src, scale=1, scale_rr=1e3): .. versionadded:: 1.7 """ nib = _import_nibabel() - from .source_space import get_decimated_surfaces ss = get_decimated_surfaces(src) stc = self From 11d77dfaf04c934e8314b554ae43f97e5a43af68 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 20 Dec 2023 14:59:35 -0500 Subject: [PATCH 18/24] MAINT: DRY code --- mne/source_estimate.py | 104 +++++++++++++++-------------------------- 1 file changed, 37 insertions(+), 67 deletions(-) diff --git a/mne/source_estimate.py b/mne/source_estimate.py index 7bd8c4ab67f..a6a6ecbe05d 100644 --- a/mne/source_estimate.py +++ b/mne/source_estimate.py @@ -1585,7 +1585,7 @@ def in_label(self, label): ) return label_stc - def save_as_surface(self, fname, src, scale=1, scale_rr=1e3): + def save_as_surface(self, fname, src, *, scale=1, scale_rr=1e3): """Save a surface source estimate (stc) as a GIFTI file. Parameters @@ -1598,10 +1598,11 @@ def save_as_surface(self, fname, src, scale=1, scale_rr=1e3): ``"basename.rh.time.gii"``. src : instance of SourceSpaces The source space of the forward solution. - scale : int - Scale of functional values. + scale : float + Scale factor to apply to the data (functional) values. scale_rr : float - Value to scale source solution. + Scale factor for the source vertex positions. The default (1e3) will + scale from meters to millimeters, which is more standard for GIFTI files. Notes ----- @@ -1610,83 +1611,52 @@ def save_as_surface(self, fname, src, scale=1, scale_rr=1e3): .. versionadded:: 1.7 """ nib = _import_nibabel() - + _check_option("src.kind", src.kind, ("surface", "mixed")) ss = get_decimated_surfaces(src) - stc = self + assert len(ss) == 2 # should be guaranteed by _check_option above # Create lists to put DataArrays into - lh = [] - rh = [] - - # Coerce rr to be in mm (MNE uses meters) - ss[0]["rr"] *= scale_rr - ss[1]["rr"] *= scale_rr - - lh.append( - nib.gifti.gifti.GiftiDataArray( - data=ss[0]["rr"], - intent="NIFTI_INTENT_POINTSET", - datatype="NIFTI_TYPE_FLOAT32", - ) - ) - rh.append( - nib.gifti.gifti.GiftiDataArray( - data=ss[1]["rr"], - intent="NIFTI_INTENT_POINTSET", - datatype="NIFTI_TYPE_FLOAT32", - ) - ) - - # Make the topology DataArray - lh.append( - nib.gifti.gifti.GiftiDataArray( - data=ss[0]["tris"], - intent="NIFTI_INTENT_TRIANGLE", - datatype="NIFTI_TYPE_INT32", - ) - ) - rh.append( - nib.gifti.gifti.GiftiDataArray( - data=ss[1]["tris"], - intent="NIFTI_INTENT_TRIANGLE", - datatype="NIFTI_TYPE_INT32", - ) - ) - - # Make the output GIFTI for anatomicals - topo_gi_lh = nib.gifti.gifti.GiftiImage(darrays=lh) - topo_gi_rh = nib.gifti.gifti.GiftiImage(darrays=rh) - - # actually save the files - nib.save(topo_gi_lh, f"{fname}-lh.gii") - nib.save(topo_gi_rh, f"{fname}-rh.gii") - - # Make the Time Series data arrays - lh_ts = [] - rh_ts = [] - - for t in range(stc.shape[1]): - lh_ts.append( + hemis = ("lh", "rh") + for s, hemi in zip(ss, hemis): + darrays = list() + darrays.append( nib.gifti.gifti.GiftiDataArray( - data=stc.lh_data[:, t] * scale, + data=s["rr"] * scale_rr, intent="NIFTI_INTENT_POINTSET", datatype="NIFTI_TYPE_FLOAT32", ) ) - rh_ts.append( + # Make the topology DataArray + darrays.append( nib.gifti.gifti.GiftiDataArray( - data=stc.rh_data[:, t] * scale, + data=s["tris"], + intent="NIFTI_INTENT_TRIANGLE", + datatype="NIFTI_TYPE_INT32", + ) + ) + + # Make the output GIFTI for anatomicals + topo_gi_hemi = nib.gifti.gifti.GiftiImage(darrays=darrays) + + # actually save the file + nib.save(topo_gi_hemi, f"{fname}-{hemi}.gii") + + # Make the Time Series data arrays + ts = [] + data = getattr(self, f"{hemi}_data") * scale + ts = [ + nib.gifti.gifti.GiftiDataArray( + data=data[:, idx], intent="NIFTI_INTENT_POINTSET", datatype="NIFTI_TYPE_FLOAT32", ) - ) + for idx in range(data.shape[1]) + ] - # save the time series - ts_gi_lh = nib.gifti.gifti.GiftiImage(darrays=lh_ts) - ts_gi_rh = nib.gifti.gifti.GiftiImage(darrays=rh_ts) - nib.save(ts_gi_lh, f"{fname}-lh.time.gii") - nib.save(ts_gi_rh, f"{fname}-rh.time.gii") + # save the time series + ts_gi = nib.gifti.gifti.GiftiImage(darrays=ts) + nib.save(ts_gi, f"{fname}-{hemi}.time.gii") def expand(self, vertices): """Expand SourceEstimate to include more vertices. From e6b7b0a43fb789fe86dc23de07abc7079e775702 Mon Sep 17 00:00:00 2001 From: "Peter J. Molfese" Date: Wed, 20 Dec 2023 17:29:45 -0500 Subject: [PATCH 19/24] Update mne/tests/test_source_estimate.py caps Co-authored-by: Daniel McCloy --- mne/tests/test_source_estimate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mne/tests/test_source_estimate.py b/mne/tests/test_source_estimate.py index f351581b60b..ebe1a369e4d 100644 --- a/mne/tests/test_source_estimate.py +++ b/mne/tests/test_source_estimate.py @@ -250,7 +250,7 @@ def test_volume_stc(tmp_path): @testing.requires_testing_data def test_save_stc_as_gifti(tmp_path): - """Save the stc as a gifti file and export.""" + """Save the stc as a GIFTI file and export.""" nib = pytest.importorskip("nibabel") surfpath_src = bem_path / "sample-oct-6-src.fif" surfpath_stc = data_path / "MEG" / "sample" / "sample_audvis_trunc-meg" From 86a34299b4f646ad1bc6d3645384a6bef564f45d Mon Sep 17 00:00:00 2001 From: "Peter J. Molfese" Date: Wed, 20 Dec 2023 17:30:09 -0500 Subject: [PATCH 20/24] Update doc/changes/devel/12309.newfeature.rst meth instead of class in diff doc Co-authored-by: Daniel McCloy --- doc/changes/devel/12309.newfeature.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/changes/devel/12309.newfeature.rst b/doc/changes/devel/12309.newfeature.rst index d4a9b5972b0..8e732044a8e 100644 --- a/doc/changes/devel/12309.newfeature.rst +++ b/doc/changes/devel/12309.newfeature.rst @@ -1 +1 @@ -Add method :class:`mne.SourceEstimate.save_as_surface` to allow saving GIFTI files from surface source estimates, by `Peter Molfese`_. +Add method :meth:`mne.SourceEstimate.save_as_surface` to allow saving GIFTI files from surface source estimates, by `Peter Molfese`_. From a0df09d77860c2103a453a477dd6b32f97c4359e Mon Sep 17 00:00:00 2001 From: "Peter J. Molfese" Date: Wed, 20 Dec 2023 17:30:22 -0500 Subject: [PATCH 21/24] Update mne/source_estimate.py caps in GIFTI Co-authored-by: Daniel McCloy --- mne/source_estimate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mne/source_estimate.py b/mne/source_estimate.py index a6a6ecbe05d..4a0b7ca2cf9 100644 --- a/mne/source_estimate.py +++ b/mne/source_estimate.py @@ -1592,7 +1592,7 @@ def save_as_surface(self, fname, src, *, scale=1, scale_rr=1e3): ---------- fname : path-like Filename basename to save files as. - Will write anatomical gifti plus time series gifti for both lh/rh, + Will write anatomical GIFTI plus time series GIFTI for both lh/rh, for example ``"basename"`` will write ``"basename.lh.gii"``, ``"basename.lh.time.gii"``, ``"basename.rh.gii"``, and ``"basename.rh.time.gii"``. From 8a0b922e7c2802820e4a526e5abaf2efa8ae7ca8 Mon Sep 17 00:00:00 2001 From: "Peter J. Molfese" Date: Wed, 20 Dec 2023 17:30:36 -0500 Subject: [PATCH 22/24] Update mne/source_estimate.py Repetitive text Co-authored-by: Daniel McCloy --- mne/source_estimate.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/mne/source_estimate.py b/mne/source_estimate.py index 4a0b7ca2cf9..eb3ae898aa5 100644 --- a/mne/source_estimate.py +++ b/mne/source_estimate.py @@ -1606,8 +1606,6 @@ def save_as_surface(self, fname, src, *, scale=1, scale_rr=1e3): Notes ----- - Creates gifti files for source solution and time courses of STC. - .. versionadded:: 1.7 """ nib = _import_nibabel() From d78b12107f726a66e631f1d0a225d021750df0f9 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Thu, 21 Dec 2023 10:37:02 -0500 Subject: [PATCH 23/24] Apply suggestions from code review --- mne/source_estimate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mne/source_estimate.py b/mne/source_estimate.py index eb3ae898aa5..b5e5e0b7f73 100644 --- a/mne/source_estimate.py +++ b/mne/source_estimate.py @@ -1619,7 +1619,7 @@ def save_as_surface(self, fname, src, *, scale=1, scale_rr=1e3): darrays = list() darrays.append( nib.gifti.gifti.GiftiDataArray( - data=s["rr"] * scale_rr, + data=(s["rr"] * scale_rr).astype(np.float32), intent="NIFTI_INTENT_POINTSET", datatype="NIFTI_TYPE_FLOAT32", ) @@ -1628,7 +1628,7 @@ def save_as_surface(self, fname, src, *, scale=1, scale_rr=1e3): # Make the topology DataArray darrays.append( nib.gifti.gifti.GiftiDataArray( - data=s["tris"], + data=s["tris"].astype(np.int32), intent="NIFTI_INTENT_TRIANGLE", datatype="NIFTI_TYPE_INT32", ) From c3d0075d6c7290fc9132430ce6f7fd13c8e6721b Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Thu, 21 Dec 2023 10:37:30 -0500 Subject: [PATCH 24/24] Update mne/source_estimate.py --- mne/source_estimate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mne/source_estimate.py b/mne/source_estimate.py index b5e5e0b7f73..19b23da7d60 100644 --- a/mne/source_estimate.py +++ b/mne/source_estimate.py @@ -1645,7 +1645,7 @@ def save_as_surface(self, fname, src, *, scale=1, scale_rr=1e3): data = getattr(self, f"{hemi}_data") * scale ts = [ nib.gifti.gifti.GiftiDataArray( - data=data[:, idx], + data=data[:, idx].astype(np.float32), intent="NIFTI_INTENT_POINTSET", datatype="NIFTI_TYPE_FLOAT32", )