osl_ephys.source_recon.rhino.utils#

RHINO utilities.

Functions#

get_rhino_filenames(subjects_dir, subject)

Get paths to all RHINO files.

system_call(cmd[, verbose])

Run a system call.

get_gridstep(coords)

Get gridstep (i.e. spatial resolution of dipole grid) in mm.

niimask2indexpointcloud(nii_fname[, volindex])

Takes in a nii.gz mask file name (which equals zero for background and != zero for the mask) and returns the mask as a 3 x npoints point cloud.

niimask2mmpointcloud(nii_mask[, volindex])

Takes in a nii.gz mask (which equals zero for background and neq zero for the mask) and returns the mask as a 3 x npoints point cloud in native space in mm's.

closest_node(node, nodes)

Find nearest node in nodes to the passed in node.

get_vol_info_from_nii(mri)

Read volume info from an MRI file.

get_sform(nii_file)

sform allows mapping from simple voxel index cordinates (e.g. from 0 to 256) in scanner space to continuous coordinates (in mm)

get_orient(nii_file)

check_nii_for_nan(filename)

majority(values_ptr, len_values, result, data)

def _majority(buffer, required_majority):

binary_majority3d(img)

Set a pixel to 1 if a required majority (default=14) or more pixels in its 3x3x3 neighborhood are 1, otherwise, set the pixel to 0. img is a 3D binary image

rigid_transform_3D(B, A[, compute_scaling])

Calculate affine transform from points in A to point in B.

xform_points(xform, pnts)

Applies homogenous linear transformation to an array of 3D coordinates.

best_fit_transform(A, B)

Calculates the least-squares best-fit transform that maps corresponding points A to B in m spatial dimensions.

nearest_neighbor(src, dst)

Find the nearest (Euclidean) neighbor in dst for each point in src.

icp(A, B[, init_pose, max_iterations, tolerance])

The Iterative Closest Point method: finds best-fit transform that maps points A on to points B.

rhino_icp(smri_headshape_polhemus, ...[, n_init])

Runs Iterative Closest Point (ICP) with multiple initialisations.

get_vtk_mesh_native(vtk_mesh_file, nii_mesh_file)

Returns mesh rrs in native space in mm and the mesh tris for the passed in vtk_mesh_file

get_flirtcoords2native_xform(nii_mesh_file)

Returns xform_flirtcoords2native transform that transforms from flirtcoords space in mm into native space in mm, where the passed in nii_mesh_file specifies the native space

transform_vtk_mesh(vtk_mesh_file_in, nii_mesh_file_in, ...)

Outputs mesh to out_vtk_file, which is the result of applying the transform xform to vtk_mesh_file_in

get_mne_xform_from_flirt_xform(flirt_xform, ...)

Returns a mm coordinates to mm coordinates MNE xform that corresponds to the passed in flirt xform.

get_flirt_xform_between_axes(from_nii, target_nii)

Computes flirt xform that moves from_nii to have voxel indices on the same axis as the voxel indices for target_nii.

timeseries2nii(timeseries, timeseries_coords, ...[, times])

Maps the (ndipoles,tpts) array of timeseries to the grid defined by reference_mask_fname and outputs them as a niftii file.

recon_timeseries2niftii(subjects_dir, subject, ...[, ...])

Converts a (ndipoles,tpts) array of reconstructed timeseries (in head/polhemus space) to the corresponding dipoles in a standard brain grid in MNI space

save_or_show_renderer(renderer, filename)

Save or show a renderer.

create_freesurfer_meshes_from_bet_surfaces(filenames, ...)

create_freesurfer_mesh_from_bet_surface(infile, ...[, ...])

Creates surface mesh in .surf format and in native mri space in mm from infile.

transform_bet_surfaces(flirt_xform_file, ...)

Transforms bet surfaces into mne space.

extract_rhino_files(old_subjects_dir, new_subjects_dir)

Extract RHINO files.

save_polhemus_fif(raw, subjects_dir, subject)

Save the polhemus information (nasion, LPA, RPA, headshape) from a raw MNE file

downsample_headshape(headshape[, downsample_amount, ...])

Downsample headshape information for more accurate coregistration.

rotate_pointcloud(points, angle_degrees[, axis])

Rotates the point cloud around a specified axis.

grid_average_downsample(point_cloud, voxel_size)

Downsample a point cloud using grid averaging.

replace_headshape(raw, ds_headshape)

Replace headshape points in an MNE Raw object with downsampled points and update fiducials.

Module Contents#

osl_ephys.source_recon.rhino.utils.get_rhino_filenames(subjects_dir, subject)[source]#

Get paths to all RHINO files.

Files will be in subjects_dir/subject/rhino.

Parameters:
  • subjects_dir (string) – Directory containing the subject directories.

  • subject (string) – Subject directory name to put the coregistration files in.

Returns:

files – A dict of files generated and used by RHINO. Contains three keys: - ‘surf’: containing surface extraction file paths. - ‘coreg’: containing coregistration file paths. - ‘fwd_model’: containing the forward model file path.

Return type:

dict

osl_ephys.source_recon.rhino.utils.system_call(cmd, verbose=False)[source]#

Run a system call.

Parameters:
  • cmd (string) – Command to run.

  • verbose (bool) – Print command before running.

osl_ephys.source_recon.rhino.utils.get_gridstep(coords)[source]#

Get gridstep (i.e. spatial resolution of dipole grid) in mm.

Parameters:

coords (numpy.ndarray) – Coordinates.

Returns:

gridstep – Spatial resolution of dipole grid in mm.

Return type:

int

osl_ephys.source_recon.rhino.utils.niimask2indexpointcloud(nii_fname, volindex=None)[source]#

Takes in a nii.gz mask file name (which equals zero for background and != zero for the mask) and returns the mask as a 3 x npoints point cloud.

Parameters:
  • nii_fname (string) – A nii.gz mask file name (with zero for background, and !=0 for the mask).

  • volindex (int) – Volume index, used if nii_mask is a 4D file.

Returns:

pc – 3 x npoints point cloud as voxel indices.

Return type:

numpy.ndarray

osl_ephys.source_recon.rhino.utils.niimask2mmpointcloud(nii_mask, volindex=None)[source]#

Takes in a nii.gz mask (which equals zero for background and neq zero for the mask) and returns the mask as a 3 x npoints point cloud in native space in mm’s.

Parameters:
  • nii_mask (string) – A nii.gz mask file name or the [x,y,z] volume (with zero for background, and !=0 for the mask).

  • volindex (int) – Volume index, used if nii_mask is a 4D file.

Returns:

  • pc (numpy.ndarray) – 3 x npoints point cloud as mm in native space (using sform).

  • values (numpy.ndarray) – npoints values.

osl_ephys.source_recon.rhino.utils.closest_node(node, nodes)[source]#

Find nearest node in nodes to the passed in node.

Returns:

  • index (int) – Index to the nearest node in nodes.

  • distance (float) – Distance.

osl_ephys.source_recon.rhino.utils.get_vol_info_from_nii(mri)[source]#

Read volume info from an MRI file.

Parameters:

mri (str) – Path to MRI file.

Returns:

out – Dictionary with keys ‘mri_width’, ‘mri_height’, ‘mri_depth’ and ‘mri_volume_name’.

Return type:

dict

osl_ephys.source_recon.rhino.utils.get_sform(nii_file)[source]#

sform allows mapping from simple voxel index cordinates (e.g. from 0 to 256) in scanner space to continuous coordinates (in mm)

sformcode = os.popen(‘fslorient -getsformcode {}’.format(nii_file)).read().strip()

osl_ephys.source_recon.rhino.utils.get_orient(nii_file)[source]#
osl_ephys.source_recon.rhino.utils.check_nii_for_nan(filename)[source]#
osl_ephys.source_recon.rhino.utils.majority(values_ptr, len_values, result, data)[source]#
def _majority(buffer, required_majority):

return buffer.sum() >= required_majority

See: https://ilovesymposia.com/2017/03/12/scipys-new-lowlevelcallable-is-a-game-changer/

Numba cfunc that takes in: a double pointer pointing to the values within the footprint, a pointer-sized integer that specifies the number of values in the footprint, a double pointer for the result, and a void pointer, which could point to additional parameters

osl_ephys.source_recon.rhino.utils.binary_majority3d(img)[source]#

Set a pixel to 1 if a required majority (default=14) or more pixels in its 3x3x3 neighborhood are 1, otherwise, set the pixel to 0. img is a 3D binary image

osl_ephys.source_recon.rhino.utils.rigid_transform_3D(B, A, compute_scaling=False)[source]#

Calculate affine transform from points in A to point in B.

Parameters:
  • A (numpy.ndarray) – 3 x num_points. Set of points to register from.

  • B (numpy.ndarray) – 3 x num_points. Set of points to register to.

  • compute_scaling (bool) – Do we compute a scaling on top of rotation and translation?

Returns:

  • xform (numpy.ndarray) – Calculated affine transform, does not include scaling.

  • scaling_xform (numpy.ndarray) – Calculated scaling transform (a diagonal 4x4 matrix), does not include rotation or translation.

  • see http (//nghiaho.com/?page_id=671)

osl_ephys.source_recon.rhino.utils.xform_points(xform, pnts)[source]#

Applies homogenous linear transformation to an array of 3D coordinates.

Parameters:
  • xform (numpy.ndarray) – 4x4 matrix containing the affine transform.

  • pnts (numpy.ndarray) – points to transform, should be 3 x num_points.

Returns:

newpnts – pnts following the xform, will be 3 x num_points.

Return type:

numpy.ndarray

osl_ephys.source_recon.rhino.utils.best_fit_transform(A, B)[source]#

Calculates the least-squares best-fit transform that maps corresponding points A to B in m spatial dimensions.

Parameters:
  • A (numpy.ndarray) – Nxm numpy array of corresponding points.

  • B (numpy.ndarray) – Nxm numpy array of corresponding points.

  • Outputs

  • -------

  • T (numpy.ndarray) – (m+1)x(m+1) homogeneous transformation matrix that maps A on to B.

osl_ephys.source_recon.rhino.utils.nearest_neighbor(src, dst)[source]#

Find the nearest (Euclidean) neighbor in dst for each point in src.

Parameters:
  • src (numpy.ndarray) – Nxm array of points.

  • dst (numpy.ndarray) – Nxm array of points.

Returns:

  • distances (numpy.ndarray) – Euclidean distances of the nearest neighbor.

  • indices (numpy.ndarray) – dst indices of the nearest neighbor.

osl_ephys.source_recon.rhino.utils.icp(A, B, init_pose=None, max_iterations=50, tolerance=0.0001)[source]#

The Iterative Closest Point method: finds best-fit transform that maps points A on to points B.

Parameters:
  • A (numpy.ndarray) – Nxm numpy array of source mD points.

  • B (numpy.ndarray) – Nxm numpy array of destination mD point.

  • init_pose (numpy.ndarray) – (m+1)x(m+1) homogeneous transformation.

  • max_iterations (int) – Exit algorithm after max_iterations.

  • tolerance (float) – Convergence criteria.

Returns:

  • T (numpy.ndarray) – (4 x 4) Final homogeneous transformation that maps A on to B.

  • distances (numpy.ndarray) – Euclidean distances (errors) of the nearest neighbor.

  • i (float) – Number of iterations to converge.

Notes

From: ClayFlannigan/icp

osl_ephys.source_recon.rhino.utils.rhino_icp(smri_headshape_polhemus, polhemus_headshape_polhemus, n_init=10)[source]#

Runs Iterative Closest Point (ICP) with multiple initialisations.

Parameters:
  • smri_headshape_polhemus (numpy.ndarray) – [3 x N] locations of the Headshape points in polehumus space (i.e. MRI scalp surface).

  • polhemus_headshape_polhemus (numpy.ndarray) – [3 x N] locations of the Polhemus headshape points in polhemus space.

  • n_init (int) – Number of random initialisations to perform.

Returns:

xform – [4 x 4] rigid transformation matrix mapping data2 to data.

Return type:

numpy.ndarray

Notes

Based on Matlab version from Adam Baker 2014.

osl_ephys.source_recon.rhino.utils.get_vtk_mesh_native(vtk_mesh_file, nii_mesh_file)[source]#

Returns mesh rrs in native space in mm and the mesh tris for the passed in vtk_mesh_file

nii_mesh_file needs to be the corresponding niftii file from bet that corresponds to the same mesh as in vtk_mesh_file

osl_ephys.source_recon.rhino.utils.get_flirtcoords2native_xform(nii_mesh_file)[source]#

Returns xform_flirtcoords2native transform that transforms from flirtcoords space in mm into native space in mm, where the passed in nii_mesh_file specifies the native space

Note that for some reason flirt outputs transforms of the form: flirt_mni2mri = mri2flirtcoords x mni2mri x flirtcoords2mni

and bet_surf outputs the .vtk file vertex values in the same flirtcoords mm coordinate system.

See the bet_surf manual: https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/BET/UserGuide#betsurf

If the image has radiological ordering (see fslorient) then the mm co-ordinates are the voxel co-ordinates scaled by the mm voxel sizes.

i.e. (x_mm = x_dim * x) where x_mm are the flirtcoords coords in mm, x is the voxel co-ordinate and x_dim is the voxel size in mm.

osl_ephys.source_recon.rhino.utils.transform_vtk_mesh(vtk_mesh_file_in, nii_mesh_file_in, out_vtk_file, nii_mesh_file_out, xform_file)[source]#

Outputs mesh to out_vtk_file, which is the result of applying the transform xform to vtk_mesh_file_in

nii_mesh_file_in needs to be the corresponding niftii file from bet that corresponds to the same mesh as in vtk_mesh_file_in

nii_mesh_file_out needs to be the corresponding niftii file from bet that corresponds to the same mesh as in out_vtk_file

osl_ephys.source_recon.rhino.utils.get_mne_xform_from_flirt_xform(flirt_xform, nii_mesh_file_in, nii_mesh_file_out)[source]#

Returns a mm coordinates to mm coordinates MNE xform that corresponds to the passed in flirt xform.

Note that we need to do this as flirt xforms include an extra xform based on the voxel dimensions (see get_flirtcoords2native_xform).

osl_ephys.source_recon.rhino.utils.get_flirt_xform_between_axes(from_nii, target_nii)[source]#

Computes flirt xform that moves from_nii to have voxel indices on the same axis as the voxel indices for target_nii.

Note that this is NOT the same as registration, i.e. the images are not aligned. In fact the actual coordinates (in mm) are unchanged. It is instead about putting from_nii onto the same axes so that the voxel INDICES are comparable. This is achieved by using a transform that sets the sform of from_nii to be the same as target_nii without changing the actual coordinates (in mm). Transform needed to do this is:

from2targetaxes = inv(targetvox2target) * fromvox2from

In more detail, we need the sform for the transformed from_nii to be the same as the sform for the target_nii, without changing the actual coordinates (in mm). In other words, we need:

fromvox2from * from_nii_vox = targetvox2target * from_nii_target_vox

where

  • fromvox2from is sform for from_nii (i.e. converts from voxel indices to voxel coords in mm)

  • targetvox2target is sform for target_nii

  • from_nii_vox are the voxel indices for from_nii

  • from_nii_target_vox are the voxel indices for from_nii when transformed onto the target axis.

=> from_nii_target_vox = from2targetaxes * from_nii_vox

where

  • from2targetaxes = inv(targetvox2target) * fromvox2from

osl_ephys.source_recon.rhino.utils.timeseries2nii(timeseries, timeseries_coords, reference_mask_fname, out_nii_fname, times=None)[source]#

Maps the (ndipoles,tpts) array of timeseries to the grid defined by reference_mask_fname and outputs them as a niftii file.

Assumes the timeseries’ dipoles correspond to those in reference_mask_fname. Both timeseries and reference_mask_fname are often output from rhino.transform_recon_timeseries.

Parameters:
  • timeseries ((ndipoles, ntpts) numpy.ndarray) – Time courses. Assumes the timeseries’ dipoles correspond to those in reference_mask_fname. Typically derives from rhino.transform_recon_timeseries

  • timeseries_coords ((ndipoles, 3) numpy.ndarray) – Coords in mm for dipoles corresponding to passed in timeseries

  • reference_mask_fname (string) – A nii.gz mask file name (with zero for background, and !=0 for the mask). Assumes the mask was used to set dipoles for timeseries, typically derived from rhino.transform_recon_timeseries

  • out_nii_fname (string) – output name of niftii file

  • times ((ntpts, ) numpy.ndarray) – Times points in seconds. Assume that times are regularly spaced. Used to set nii file up correctly.

Returns:

out_nii_fname – Name of output niftii file

Return type:

string

osl_ephys.source_recon.rhino.utils.recon_timeseries2niftii(subjects_dir, subject, recon_timeseries, out_nii_fname, spatial_resolution=None, reference_brain='mni', times=None)[source]#

Converts a (ndipoles,tpts) array of reconstructed timeseries (in head/polhemus space) to the corresponding dipoles in a standard brain grid in MNI space and outputs them as a niftii file.

Parameters:
  • subjects_dir (string) – Directory to find RHINO subject directories in.

  • subject (string) – Subject name directory to find RHINO files in.

  • recon_timeseries ((ndipoles, ntpts) np.ndarray) – Reconstructed time courses (in head (polhemus) space). Assumes that the dipoles are the same (and in the same order) as those in the forward model, rhino_files[‘fwd_model’]. Typically derive from the VolSourceEstimate’s output by MNE source recon methods, e.g. mne.beamformer.apply_lcmv, obtained using a forward model generated by RHINO.

  • spatial_resolution (int) – Resolution to use for the reference brain in mm (must be an integer, or will be cast to nearest int). If None, then the gridstep used in rhino_files[‘fwd_model’] is used.

  • reference_brain (string, 'mni' or 'mri') – ‘mni’ indicates that the reference_brain is the stdbrain in MNI space. ‘mri’ indicates that the reference_brain is the sMRI in native/mri space.

  • times ((ntpts, ) np.ndarray) – Times points in seconds. Will assume that these are regularly spaced.

Returns:

  • out_nii_fname (string) – Name of output niftii file.

  • reference_brain_fname (string) – Niftii file name of standard brain mask in MNI space at requested resolution, int(stdbrain_resolution) (with zero for background, and !=0 for the mask).

osl_ephys.source_recon.rhino.utils.save_or_show_renderer(renderer, filename)[source]#

Save or show a renderer.

Parameters:
  • renderer (mne.viz.backends._notebook._Renderer Object) – MNE renderer object.

  • filename (str) – Filename to save display to (as an interactive html). Must have extension .html. If None we display the renderer.

osl_ephys.source_recon.rhino.utils.create_freesurfer_meshes_from_bet_surfaces(filenames, xform_mri_voxel2mri)[source]#
osl_ephys.source_recon.rhino.utils.create_freesurfer_mesh_from_bet_surface(infile, surf_outfile, xform_mri_voxel2mri, nii_mesh_file=None)[source]#

Creates surface mesh in .surf format and in native mri space in mm from infile.

Parameters:
  • infile (string) –

    Either:
    1. .nii.gz file containing zero’s for background and one’s for surface

    2) .vtk file generated by bet_surf (in which case the path to the structural MRI, smri_file, must be included as an input)

  • surf_outfile (string) – Path to the .surf file generated, containing the surface mesh in mm

  • xform_mri_voxel2mri (numpy.ndarray) – 4x4 array. Transform from voxel indices to native/mri mm.

  • nii_mesh_file (string) – Path to the niftii mesh file that is the niftii equivalent of vtk file passed in as infile (only needed if infile is a vtk file).

osl_ephys.source_recon.rhino.utils.transform_bet_surfaces(flirt_xform_file, mne_xform_file, filenames, smri_file)[source]#

Transforms bet surfaces into mne space.

Parameters:
  • flirt_xform_file (string) – Path to flirt xform file.

  • mne_xform_file (string) – Path to mne xform file.

  • filenames (dict) – Dictionary containing filenames.

  • smri_file (string) – Path to structural MRI file.

osl_ephys.source_recon.rhino.utils.extract_rhino_files(old_subjects_dir, new_subjects_dir, subjects='all', exclude=None, gen_report=True)[source]#

Extract RHINO files.

This function extracts surfaces and coregistration files calculated in a previous run.

Parameters:
  • old_subjects_dir (str) – Subjects directory created with an older version of osl-ephys.

  • new_subjects_dir (str) – New directory to create.

  • subjects (str or list) – Subjects to include. Defaults to ‘all’.

  • exclude (str or list) – Subjects to exclude.

  • bool (gen_report) – Should we generate a report?

osl_ephys.source_recon.rhino.utils.save_polhemus_fif(raw, subjects_dir, subject)[source]#

Save the polhemus information (nasion, LPA, RPA, headshape) from a raw MNE file

Files will be in subjects_dir/subject/coreg.

Parameters:
  • raw (raw MNE data structure) – Data loaded into MNE with preload=True

  • subjects_dir (string) – Directory containing the subject directories.

  • subject (string) – Subject directory name to put the coregistration files in.

osl_ephys.source_recon.rhino.utils.downsample_headshape(headshape, downsample_amount=0.015, include_facial_info=True, remove_zlim=0.02, angle=10, method='gridaverage', face_Z=[-0.08, 0.02], face_Y=[0.06, 0.15], face_X=[-0.07, 0.07], downsample_facial_info=True, downsample_facial_info_amount=0.008)[source]#

Downsample headshape information for more accurate coregistration.

Parameters:
  • headshape (-) – Nx3 array of headshape points in meters.

  • include_facial_info (-) – Include facial points if True.

  • remove_zlim (-) – Remove points above nasion on the z-axis in meters.

  • method (-) – Downsampling method. Note: only method supported is ‘gridaverage’.

  • (float) (- facial_info_above_z) – Max z-value for facial points in meters.

  • facial_info_below_z (-) – Min z-value for facial points in meters.

  • facial_info_above_y (-) – Max y-value for facial points in meters.

  • facial_info_below_y (-) – Min y-value for facial points in meters.

  • facial_info_below_x (-) – Min x-value for facial points in meters.

  • downsample_facial_info (-) – Whether to downsample facial points.

  • downsample_facial_info_amount (-) – Grid size for downsampling facial info in meters.

  • Returns

  • numpy.ndarray (-) –

osl_ephys.source_recon.rhino.utils.rotate_pointcloud(points, angle_degrees, axis='x')[source]#

Rotates the point cloud around a specified axis.

Parameters:
  • points (np.array) – Headshape points

  • angle_degrees (float) – Amount to rotate in degrees.

  • axis (str) – Axis to rotate.

osl_ephys.source_recon.rhino.utils.grid_average_downsample(point_cloud, voxel_size)[source]#

Downsample a point cloud using grid averaging.

This function divides the space into a voxel grid, computes the average position of points within each voxel, and returns a downsampled point cloud.

Parameters:
  • point_cloud (numpy.ndarray) – A numpy array of shape (N, 3) representing the point cloud, where N is the number of points, and each point has (x, y, z) coordinates.

  • voxel_size (float) – The size of the voxel grid. Points within a grid cell are averaged to compute the downsampled point.

Returns:

downsampled_cloud – A numpy array of shape (M, 3) representing the downsampled point cloud, where M is the number of voxels containing points.

Return type:

numpy.ndarray

Notes

  • This method assumes the input point cloud is dense and unstructured.

  • For very large point clouds, consider optimizing memory usage.

Examples

>>> import numpy as np
>>> point_cloud = np.random.rand(1000, 3)  # Generate random point cloud
>>> voxel_size = 0.05
>>> downsampled_cloud = grid_average_downsample(point_cloud, voxel_size)
>>> print(downsampled_cloud.shape)
osl_ephys.source_recon.rhino.utils.replace_headshape(raw, ds_headshape)[source]#

Replace headshape points in an MNE Raw object with downsampled points and update fiducials.

Parameters:
  • raw (mne.io.Raw) – The raw object containing the original digitization points.

  • ds_headshape (ndarray, shape (N, 3)) – Array of downsampled headshape points (in head coordinates).

Returns:

raw_copy – A copy of the input Raw object with the updated headshape points and montage.

Return type:

mne.io.Raw