From 843050a8b2686b493f59b42c2545d77d035f25d5 Mon Sep 17 00:00:00 2001 From: Arsalan Firoozi Date: Thu, 3 Jul 2025 23:39:46 -0400 Subject: [PATCH 01/10] General Space Conversionsds --- naplib/localization/coordinate_conversions.py | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/naplib/localization/coordinate_conversions.py b/naplib/localization/coordinate_conversions.py index 3f2affc..85b0b94 100644 --- a/naplib/localization/coordinate_conversions.py +++ b/naplib/localization/coordinate_conversions.py @@ -1,4 +1,6 @@ import numpy as np +import nibabel.freesurfer.io as fsio +from scipy.spatial import cKDTree def mni152_to_fsaverage(coords): """ @@ -38,4 +40,56 @@ def fsaverage_to_mni152(coords): new_coords = (xform @ old_coords).T return new_coords +def a_to_b(coords, src_pial, src_sphere, dst_pial, dst_sphere): + """ + Convert 3D coordinates from any space to another space. + Each subject comes with a bunch of MRI files; In this function these files are used: + 1. lh/rh.pial file of the source space ==> SRC_PATH/surf/lh.pial + 2. lh/rh.sphere.reg file of the source ==> SRC_PATH/surf/lh.sphere.reg + 3. lh/rh.pial file of the destination ==> DST_PATH/surf/lh.pial + 4. lh/rh.sphere.reg file of the destination ==> DST_PATH/surf/lh.sphere.reg + + Apply this function separately for left and right hemispheres. + + NOTE: In case of converting to an atlas space, the files we need are accessible + by installing freesurfer: https://surfer.nmr.mgh.harvard.edu/fswiki/DownloadAndInstall + Path of different atlas spaces: PATH_freesurfer/8.0.0/subjects/ + Parameters + ---------- + coords : np.ndarray (elecs, 3) + Coordinates in source space + src_pial : str + Path to the source pial surface file (e.g., 'lh.pial') + src_sphere : str + Path to the source sphere file (e.g., 'lh.sphere.reg') + dst_pial : str + Path to the destination pial surface file (e.g., 'lh.pial') + dst_sphere : str + Path to the destination sphere file (e.g., 'lh.sphere.reg') + + Returns + ------- + new_coords : np.ndarray (elecs, 3) + Coordinates in target space + """ + src_sphere, _ = fsio.read_geometry(src_sphere) + tgt_sphere, _ = fsio.read_geometry(dst_sphere) + + tree = cKDTree(tgt_sphere) + + if np.isnan(coords).any(): + print("WARNING: NaN values found in coordinates. Replacing with zeros.") + coords = np.nan_to_num(coords) + + lh_verts_sub, _ = fsio.read_geometry(src_pial) + lh_verts_sub_fs, _ = fsio.read_geometry(dst_pial) + + tree_elecs = cKDTree(lh_verts_sub) + _, mapping_indices_elecs = tree_elecs.query(coords, k=1) + + _, mapping_indices_elecs_warped = tree.query(src_sphere[mapping_indices_elecs], k=1) + + new_coords = lh_verts_sub_fs[mapping_indices_elecs_warped] + + return new_coords From 1b0317694071f7f7ccfa1c4aeabdfd13954ffbf4 Mon Sep 17 00:00:00 2001 From: Arsalan Firoozi Date: Thu, 3 Jul 2025 23:39:55 -0400 Subject: [PATCH 02/10] General Space Conversion --- naplib/localization/coordinate_conversions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/naplib/localization/coordinate_conversions.py b/naplib/localization/coordinate_conversions.py index 85b0b94..e6f6190 100644 --- a/naplib/localization/coordinate_conversions.py +++ b/naplib/localization/coordinate_conversions.py @@ -40,7 +40,7 @@ def fsaverage_to_mni152(coords): new_coords = (xform @ old_coords).T return new_coords -def a_to_b(coords, src_pial, src_sphere, dst_pial, dst_sphere): +def src_to_dst(coords, src_pial, src_sphere, dst_pial, dst_sphere): """ Convert 3D coordinates from any space to another space. Each subject comes with a bunch of MRI files; In this function these files are used: From a948801b30686ceaa7f09fb779a47009ab7da998 Mon Sep 17 00:00:00 2001 From: Arsalan Firoozi Date: Fri, 4 Jul 2025 11:27:03 -0400 Subject: [PATCH 03/10] General Conversion: supporting both hemispheres --- naplib/localization/coordinate_conversions.py | 59 ++++++++++++++----- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/naplib/localization/coordinate_conversions.py b/naplib/localization/coordinate_conversions.py index e6f6190..21f2c12 100644 --- a/naplib/localization/coordinate_conversions.py +++ b/naplib/localization/coordinate_conversions.py @@ -44,12 +44,12 @@ def src_to_dst(coords, src_pial, src_sphere, dst_pial, dst_sphere): """ Convert 3D coordinates from any space to another space. Each subject comes with a bunch of MRI files; In this function these files are used: - 1. lh/rh.pial file of the source space ==> SRC_PATH/surf/lh.pial - 2. lh/rh.sphere.reg file of the source ==> SRC_PATH/surf/lh.sphere.reg - 3. lh/rh.pial file of the destination ==> DST_PATH/surf/lh.pial - 4. lh/rh.sphere.reg file of the destination ==> DST_PATH/surf/lh.sphere.reg + 1. lh.pial file of the source space ==> SRC_PATH/surf/lh.pial + 2. lh.sphere.reg file of the source ==> SRC_PATH/surf/lh.sphere.reg + 3. lh.pial file of the destination ==> DST_PATH/surf/lh.pial + 4. lh.sphere.reg file of the destination ==> DST_PATH/surf/lh.sphere.reg - Apply this function separately for left and right hemispheres. + Provide LH files, the function assumes the RH ones are in the same directory. NOTE: In case of converting to an atlas space, the files we need are accessible by installing freesurfer: https://surfer.nmr.mgh.harvard.edu/fswiki/DownloadAndInstall @@ -58,9 +58,10 @@ def src_to_dst(coords, src_pial, src_sphere, dst_pial, dst_sphere): Parameters ---------- coords : np.ndarray (elecs, 3) - Coordinates in source space - src_pial : str - Path to the source pial surface file (e.g., 'lh.pial') + Coordinates in source space. Can be in both hemispheres. + src_pial : str/dict{'vert_lh', 'vert_rh'} + Path to the source pial surface file (e.g., 'lh.pial'). In case of a mat file for pial surfaces, + provide a dictionary with keys 'vert_lh' and 'vert_rh' containing the vertices for each hemisphere. src_sphere : str Path to the source sphere file (e.g., 'lh.sphere.reg') dst_pial : str @@ -73,23 +74,49 @@ def src_to_dst(coords, src_pial, src_sphere, dst_pial, dst_sphere): new_coords : np.ndarray (elecs, 3) Coordinates in target space """ - src_sphere, _ = fsio.read_geometry(src_sphere) - tgt_sphere, _ = fsio.read_geometry(dst_sphere) + src_sphere_lh, _ = fsio.read_geometry(src_sphere) + src_sphere_rh, _ = fsio.read_geometry(src_sphere.replace('lh', 'rh')) + src_sphere = np.vstack((src_sphere_lh, src_sphere_rh)) - tree = cKDTree(tgt_sphere) + tgt_sphere_lh, _ = fsio.read_geometry(dst_sphere) + tgt_sphere_rh, _ = fsio.read_geometry(dst_sphere.replace('lh', 'rh')) + + tree_lh = cKDTree(tgt_sphere_lh) + tree_rh = cKDTree(tgt_sphere_rh) if np.isnan(coords).any(): print("WARNING: NaN values found in coordinates. Replacing with zeros.") coords = np.nan_to_num(coords) - lh_verts_sub, _ = fsio.read_geometry(src_pial) + if isinstance(src_pial, str): + lh_verts_sub, _ = fsio.read_geometry(src_pial) + rh_verts_sub = fsio.read_geometry(src_pial.replace('lh', 'rh'))[0] + lh_threshold = lh_verts_sub.shape[0] + lh_verts_sub = np.vstack((lh_verts_sub, rh_verts_sub)) + else: + lh_verts_sub = src_pial['vert_lh'] + rh_verts_sub = src_pial['vert_rh'] + lh_threshold = lh_verts_sub.shape[0] + lh_verts_sub_fs, _ = fsio.read_geometry(dst_pial) - + rh_verts_sub_fs, _ = fsio.read_geometry(dst_pial.replace('lh', 'rh')) + tree_elecs = cKDTree(lh_verts_sub) _, mapping_indices_elecs = tree_elecs.query(coords, k=1) + + print(f"#Electrodes in LH: {np.sum(mapping_indices_elecs < lh_threshold)}, RH: {np.sum(mapping_indices_elecs >= lh_threshold)}") - _, mapping_indices_elecs_warped = tree.query(src_sphere[mapping_indices_elecs], k=1) + mapping_indices_elecs_lh = mapping_indices_elecs[mapping_indices_elecs < lh_threshold] + _, mapping_indices_elecs_warped_lh = tree_lh.query(src_sphere[mapping_indices_elecs_lh], k=1) - new_coords = lh_verts_sub_fs[mapping_indices_elecs_warped] + mapping_indices_elecs_rh = mapping_indices_elecs[mapping_indices_elecs >= lh_threshold] + _, mapping_indices_elecs_warped_rh = tree_rh.query(src_sphere[mapping_indices_elecs_rh - lh_threshold], k=1) - return new_coords + new_coords_lh = lh_verts_sub_fs[mapping_indices_elecs_warped_lh] + new_coords_rh = rh_verts_sub_fs[mapping_indices_elecs_warped_rh] + + new_coords = np.zeros((coords.shape[0], 3)) + new_coords[mapping_indices_elecs < lh_threshold] = new_coords_lh + new_coords[mapping_indices_elecs >= lh_threshold] = new_coords_rh + + return new_coords \ No newline at end of file From 0cc8baea7a6e214a79ebc430a40a63a5400499e9 Mon Sep 17 00:00:00 2001 From: Arsalan Firoozi Date: Sat, 5 Jul 2025 11:34:37 -0400 Subject: [PATCH 04/10] General Conversion: Add test, Fix minor things --- naplib/localization/__init__.py | 4 ++-- tests/test_coordinate_conversions.py | 25 +++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/naplib/localization/__init__.py b/naplib/localization/__init__.py index 7253b7c..537d7d5 100644 --- a/naplib/localization/__init__.py +++ b/naplib/localization/__init__.py @@ -1,4 +1,4 @@ from .freesurfer import Brain, find_closest_vertices -from .coordinate_conversions import mni152_to_fsaverage, fsaverage_to_mni152 +from .coordinate_conversions import mni152_to_fsaverage, fsaverage_to_mni152, src_to_dst -__all__ = ['Brain', 'find_closest_vertices', 'mni152_to_fsaverage', 'fsaverage_to_mni152'] +__all__ = ['Brain', 'find_closest_vertices', 'mni152_to_fsaverage', 'fsaverage_to_mni152', 'src_to_dst'] diff --git a/tests/test_coordinate_conversions.py b/tests/test_coordinate_conversions.py index aa976bb..2f6e430 100644 --- a/tests/test_coordinate_conversions.py +++ b/tests/test_coordinate_conversions.py @@ -1,8 +1,14 @@ import numpy as np -from naplib.localization import mni152_to_fsaverage, fsaverage_to_mni152 +import os +import mne +from naplib.localization import mni152_to_fsaverage, fsaverage_to_mni152, src_to_dst def test_mni152_fsaverage_conversions(): - coords_tmp = np.array([[13.987, 36.5, 10.067], [-10.54, 24.5, 15.555]]) + coords_tmp = np.array([[13.987, 36. + + , 10.067], [-10.54, 24. + + , 15.555]]) coords_tmp2 = mni152_to_fsaverage(coords_tmp) expected_2 = np.array( [[ 14.15310394, 34.73510469, 9.41733728], @@ -12,3 +18,18 @@ def test_mni152_fsaverage_conversions(): coords_tmp3 = fsaverage_to_mni152(coords_tmp2) assert np.allclose(coords_tmp3, coords_tmp, rtol=1e-3) + +def test_src_to_dst(): + coords = np.random.rand(2, 3) * 5 + + os.makedirs('./.fsaverage_tmp', exist_ok=True) + mne.datasets.fetch_fsaverage('./.fsaverage_tmp/') + + src_pial = './.fsaverage_tmp/fsaverage/surf/lh.pial' + src_sphere = './.fsaverage_tmp/fsaverage/surf/lh.sphere.reg' + dst_pial = './.fsaverage_tmp/fsaverage/surf/lh.inflated' + dst_sphere = './.fsaverage_tmp/fsaverage/surf/lh.sphere.reg' + + inflated_coords = src_to_dst(coords, src_pial, src_sphere, dst_pial, dst_sphere) + + assert inflated_coords.shape[0] == coords.shape[0] \ No newline at end of file From ec94a134035c88dcef1ad246df38ecabd7d20478 Mon Sep 17 00:00:00 2001 From: Arsalan Firoozi Date: Sat, 5 Jul 2025 17:25:30 -0400 Subject: [PATCH 05/10] General conversion: add verbose --- naplib/localization/coordinate_conversions.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/naplib/localization/coordinate_conversions.py b/naplib/localization/coordinate_conversions.py index 21f2c12..4db3dbd 100644 --- a/naplib/localization/coordinate_conversions.py +++ b/naplib/localization/coordinate_conversions.py @@ -40,7 +40,7 @@ def fsaverage_to_mni152(coords): new_coords = (xform @ old_coords).T return new_coords -def src_to_dst(coords, src_pial, src_sphere, dst_pial, dst_sphere): +def src_to_dst(coords, src_pial, src_sphere, dst_pial, dst_sphere, verbose=False): """ Convert 3D coordinates from any space to another space. Each subject comes with a bunch of MRI files; In this function these files are used: @@ -104,7 +104,8 @@ def src_to_dst(coords, src_pial, src_sphere, dst_pial, dst_sphere): tree_elecs = cKDTree(lh_verts_sub) _, mapping_indices_elecs = tree_elecs.query(coords, k=1) - print(f"#Electrodes in LH: {np.sum(mapping_indices_elecs < lh_threshold)}, RH: {np.sum(mapping_indices_elecs >= lh_threshold)}") + if verbose: + print(f"#Electrodes in LH: {np.sum(mapping_indices_elecs < lh_threshold)}, RH: {np.sum(mapping_indices_elecs >= lh_threshold)}") mapping_indices_elecs_lh = mapping_indices_elecs[mapping_indices_elecs < lh_threshold] _, mapping_indices_elecs_warped_lh = tree_lh.query(src_sphere[mapping_indices_elecs_lh], k=1) From 99b6fbf2526fe2a6d21e035abbed0ba00066db9a Mon Sep 17 00:00:00 2001 From: Arsalan Firoozi Date: Sat, 5 Jul 2025 17:44:39 -0400 Subject: [PATCH 06/10] General conversion: add docstring for verbose argument --- naplib/localization/coordinate_conversions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/naplib/localization/coordinate_conversions.py b/naplib/localization/coordinate_conversions.py index 4db3dbd..e652195 100644 --- a/naplib/localization/coordinate_conversions.py +++ b/naplib/localization/coordinate_conversions.py @@ -68,6 +68,8 @@ def src_to_dst(coords, src_pial, src_sphere, dst_pial, dst_sphere, verbose=False Path to the destination pial surface file (e.g., 'lh.pial') dst_sphere : str Path to the destination sphere file (e.g., 'lh.sphere.reg') + verbose : bool, optional + If True, prints additional information about the conversion process. Default is False. Returns ------- From 7020aa71d3e4b2a04e245735f0eb2d0a6e5d17da Mon Sep 17 00:00:00 2001 From: Arsalan Firoozi Date: Sat, 5 Jul 2025 18:02:19 -0400 Subject: [PATCH 07/10] General Conversion: update doc file --- docs/references/localization.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/references/localization.rst b/docs/references/localization.rst index 943f2a9..e2aa70c 100644 --- a/docs/references/localization.rst +++ b/docs/references/localization.rst @@ -13,6 +13,10 @@ Convert from FSAverage to MNI152 .. autofunction:: fsaverage_to_mni152 +Convert from any source space to any target space +------------------------------------------------ + +.. autofunction:: src_to_dst Localization on a Freesurfer Brain ---------------------------------- From fb8b81271735f1874be939b867ca9dcf5a980fac Mon Sep 17 00:00:00 2001 From: Arsalan Firoozi Date: Sat, 5 Jul 2025 23:19:57 -0400 Subject: [PATCH 08/10] General conversion: minor fixes --- tests/test_coordinate_conversions.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/test_coordinate_conversions.py b/tests/test_coordinate_conversions.py index 2f6e430..7d8c9a6 100644 --- a/tests/test_coordinate_conversions.py +++ b/tests/test_coordinate_conversions.py @@ -4,11 +4,7 @@ from naplib.localization import mni152_to_fsaverage, fsaverage_to_mni152, src_to_dst def test_mni152_fsaverage_conversions(): - coords_tmp = np.array([[13.987, 36. - - , 10.067], [-10.54, 24. - - , 15.555]]) + coords_tmp = np.array([[13.987, 36.5, 10.067], [-10.54, 24.5, 15.555]]) coords_tmp2 = mni152_to_fsaverage(coords_tmp) expected_2 = np.array( [[ 14.15310394, 34.73510469, 9.41733728], From 4e57655bfbef55d4e4484024f626ca043d0873e9 Mon Sep 17 00:00:00 2001 From: Arsalan Firoozi Date: Tue, 8 Jul 2025 21:20:36 -0400 Subject: [PATCH 09/10] General Conversion: Supporting fsLR and considering in depth electrodes --- naplib/localization/coordinate_conversions.py | 93 +++++++++++++++---- 1 file changed, 75 insertions(+), 18 deletions(-) diff --git a/naplib/localization/coordinate_conversions.py b/naplib/localization/coordinate_conversions.py index e652195..9f8aa1d 100644 --- a/naplib/localization/coordinate_conversions.py +++ b/naplib/localization/coordinate_conversions.py @@ -1,6 +1,7 @@ import numpy as np import nibabel.freesurfer.io as fsio from scipy.spatial import cKDTree +import nibabel as nib def mni152_to_fsaverage(coords): """ @@ -40,7 +41,7 @@ def fsaverage_to_mni152(coords): new_coords = (xform @ old_coords).T return new_coords -def src_to_dst(coords, src_pial, src_sphere, dst_pial, dst_sphere, verbose=False): +def src_to_dst(coords, src_pial, src_sphere, dst_pial, dst_sphere, require_lh_mask=False, threshold=100, distance_report=False, verbose=False): """ Convert 3D coordinates from any space to another space. Each subject comes with a bunch of MRI files; In this function these files are used: @@ -49,6 +50,8 @@ def src_to_dst(coords, src_pial, src_sphere, dst_pial, dst_sphere, verbose=False 3. lh.pial file of the destination ==> DST_PATH/surf/lh.pial 4. lh.sphere.reg file of the destination ==> DST_PATH/surf/lh.sphere.reg + fsLR is also supported: files ending with .gii + Provide LH files, the function assumes the RH ones are in the same directory. NOTE: In case of converting to an atlas space, the files we need are accessible @@ -68,6 +71,12 @@ def src_to_dst(coords, src_pial, src_sphere, dst_pial, dst_sphere, verbose=False Path to the destination pial surface file (e.g., 'lh.pial') dst_sphere : str Path to the destination sphere file (e.g., 'lh.sphere.reg') + require_lh_mask : bool, optional + If True, returns a mask indicating which coordinates are in the left hemisphere. Default is False. + threshold : float, optional + Maximum distance in mm for an electrode to be considered in cortex (not in depth). + distance_report : bool, optional + If True, returns the distance of each coordinate to the nearest vertex. Default is False. verbose : bool, optional If True, prints additional information about the conversion process. Default is False. @@ -75,37 +84,72 @@ def src_to_dst(coords, src_pial, src_sphere, dst_pial, dst_sphere, verbose=False ------- new_coords : np.ndarray (elecs, 3) Coordinates in target space + lh_mask : np.ndarray (elecs,) + Mask indicating which coordinates are in the left hemisphere (if `require_lh_mask` is True) + distance : np.ndarray (elecs,) or None + Distance of each coordinate to the nearest vertex in the source pial surface (if `distance_report` is True) """ - src_sphere_lh, _ = fsio.read_geometry(src_sphere) - src_sphere_rh, _ = fsio.read_geometry(src_sphere.replace('lh', 'rh')) - src_sphere = np.vstack((src_sphere_lh, src_sphere_rh)) - tgt_sphere_lh, _ = fsio.read_geometry(dst_sphere) - tgt_sphere_rh, _ = fsio.read_geometry(dst_sphere.replace('lh', 'rh')) + if not src_sphere.endswith('.surf.gii'): + src_sphere_lh, _ = fsio.read_geometry(src_sphere) + src_sphere_rh, _ = fsio.read_geometry(src_sphere.replace('lh', 'rh')) + src_sphere = np.vstack((src_sphere_lh, src_sphere_rh)) + else: + sphere_lh = nib.load(src_sphere) + src_sphere_lh = sphere_lh.darrays[0].data + sphere_rh = nib.load(src_sphere.replace('.L.', '.R.')) + src_sphere_rh = sphere_rh.darrays[0].data + src_sphere = np.vstack((src_sphere_lh, src_sphere_rh)) + + if not dst_sphere.endswith('.surf.gii'): + tgt_sphere_lh, _ = fsio.read_geometry(dst_sphere) + tgt_sphere_rh, _ = fsio.read_geometry(dst_sphere.replace('lh', 'rh')) + else: + sphere_lh = nib.load(dst_sphere) + tgt_sphere_lh = sphere_lh.darrays[0].data + sphere_rh = nib.load(dst_sphere.replace('.L.', '.R.')) + tgt_sphere_rh = sphere_rh.darrays[0].data tree_lh = cKDTree(tgt_sphere_lh) tree_rh = cKDTree(tgt_sphere_rh) - if np.isnan(coords).any(): - print("WARNING: NaN values found in coordinates. Replacing with zeros.") + nan_list = np.zeros(coords.shape[0], dtype=bool) + if np.isnan(coords).any() or (np.sum(np.abs(coords),axis=1)==0).any(): + print(f"WARNING: number of NaN values found in coordinates: {np.sum(np.isnan(coords))}.") + nan_list = np.isnan(coords) coords = np.nan_to_num(coords) if isinstance(src_pial, str): - lh_verts_sub, _ = fsio.read_geometry(src_pial) - rh_verts_sub = fsio.read_geometry(src_pial.replace('lh', 'rh'))[0] - lh_threshold = lh_verts_sub.shape[0] - lh_verts_sub = np.vstack((lh_verts_sub, rh_verts_sub)) + if not src_pial.endswith('.surf.gii'): + lh_verts_sub, _ = fsio.read_geometry(src_pial) + rh_verts_sub = fsio.read_geometry(src_pial.replace('lh', 'rh'))[0] + lh_threshold = lh_verts_sub.shape[0] + lh_verts_sub = np.vstack((lh_verts_sub, rh_verts_sub)) + else: + lh_verts_sub = nib.load(src_pial).darrays[0].data + rh_verts_sub = nib.load(src_pial.replace('.L.', '.R.')).darrays[0].data + lh_threshold = lh_verts_sub.shape[0] + lh_verts_sub = np.vstack((lh_verts_sub, rh_verts_sub)) else: lh_verts_sub = src_pial['vert_lh'] rh_verts_sub = src_pial['vert_rh'] lh_threshold = lh_verts_sub.shape[0] - lh_verts_sub_fs, _ = fsio.read_geometry(dst_pial) - rh_verts_sub_fs, _ = fsio.read_geometry(dst_pial.replace('lh', 'rh')) + if not dst_pial.endswith('.surf.gii'): + lh_verts_sub_fs, _ = fsio.read_geometry(dst_pial) + rh_verts_sub_fs, _ = fsio.read_geometry(dst_pial.replace('lh', 'rh')) + else: + lh_verts_sub_fs = nib.load(dst_pial).darrays[0].data + rh_verts_sub_fs = nib.load(dst_pial.replace('.L.', '.R.')).darrays[0].data tree_elecs = cKDTree(lh_verts_sub) - _, mapping_indices_elecs = tree_elecs.query(coords, k=1) - + distance, mapping_indices_elecs = tree_elecs.query(coords, k=1) + + if np.any(distance > threshold): + print(f"WARNING: Number of in depth electrodes (distance > {threshold} mm): {np.sum(distance > threshold)}") + new_nans = distance > threshold + nan_list[new_nans] = True + if verbose: print(f"#Electrodes in LH: {np.sum(mapping_indices_elecs < lh_threshold)}, RH: {np.sum(mapping_indices_elecs >= lh_threshold)}") @@ -113,7 +157,7 @@ def src_to_dst(coords, src_pial, src_sphere, dst_pial, dst_sphere, verbose=False _, mapping_indices_elecs_warped_lh = tree_lh.query(src_sphere[mapping_indices_elecs_lh], k=1) mapping_indices_elecs_rh = mapping_indices_elecs[mapping_indices_elecs >= lh_threshold] - _, mapping_indices_elecs_warped_rh = tree_rh.query(src_sphere[mapping_indices_elecs_rh - lh_threshold], k=1) + _, mapping_indices_elecs_warped_rh = tree_rh.query(src_sphere[mapping_indices_elecs_rh], k=1) new_coords_lh = lh_verts_sub_fs[mapping_indices_elecs_warped_lh] new_coords_rh = rh_verts_sub_fs[mapping_indices_elecs_warped_rh] @@ -121,5 +165,18 @@ def src_to_dst(coords, src_pial, src_sphere, dst_pial, dst_sphere, verbose=False new_coords = np.zeros((coords.shape[0], 3)) new_coords[mapping_indices_elecs < lh_threshold] = new_coords_lh new_coords[mapping_indices_elecs >= lh_threshold] = new_coords_rh + lh_mask = mapping_indices_elecs < lh_threshold - return new_coords \ No newline at end of file + new_coords[nan_list] = np.nan + + if require_lh_mask: + if distance_report: + return new_coords, lh_mask, distance + else: + return new_coords, lh_mask + else: + if distance_report: + return new_coords, distance + else: + return new_coords + \ No newline at end of file From a8da6454465ed85c01de6ce53be2c2687a03ce3d Mon Sep 17 00:00:00 2001 From: Gavin Mischler Date: Wed, 9 Jul 2025 10:11:19 -0400 Subject: [PATCH 10/10] Update tests/test_coordinate_conversions.py --- tests/test_coordinate_conversions.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_coordinate_conversions.py b/tests/test_coordinate_conversions.py index 7d8c9a6..20c4d1b 100644 --- a/tests/test_coordinate_conversions.py +++ b/tests/test_coordinate_conversions.py @@ -18,13 +18,13 @@ def test_mni152_fsaverage_conversions(): def test_src_to_dst(): coords = np.random.rand(2, 3) * 5 - os.makedirs('./.fsaverage_tmp', exist_ok=True) - mne.datasets.fetch_fsaverage('./.fsaverage_tmp/') + os.makedirs('./.fsaverage_tmp2', exist_ok=True) + mne.datasets.fetch_fsaverage('./.fsaverage_tmp2/') - src_pial = './.fsaverage_tmp/fsaverage/surf/lh.pial' - src_sphere = './.fsaverage_tmp/fsaverage/surf/lh.sphere.reg' - dst_pial = './.fsaverage_tmp/fsaverage/surf/lh.inflated' - dst_sphere = './.fsaverage_tmp/fsaverage/surf/lh.sphere.reg' + src_pial = './.fsaverage_tmp2/fsaverage/surf/lh.pial' + src_sphere = './.fsaverage_tmp2/fsaverage/surf/lh.sphere.reg' + dst_pial = './.fsaverage_tmp2/fsaverage/surf/lh.inflated' + dst_sphere = './.fsaverage_tmp2/fsaverage/surf/lh.sphere.reg' inflated_coords = src_to_dst(coords, src_pial, src_sphere, dst_pial, dst_sphere)