def load_surf_data(left_surface_list, right_surface_list): """ Load individual surface data, and generate nxp matrix, where n = number of subjects and p = number of vertices in corresponding mask. ***NOTE: this assumes that the surfaces are in fsaverage5 space, in mgh format *** *** -if downsampled to fsaverage,fsaverage6,etc then adjust n_vert *** *** -if different format: CIFTI, GIFTI, then probably best to use something else!*** Parameters ---------- left_surface_list : List of surface files (.mgh) to load, including path to surface right_surface_list : Corresponding list of right hemipshere (rh) surface files to load, including path to surface Returns ------- surf_data : numpy array (n_subjects, n_vertices) Vectorised surface data for all subjects""" n_vert = 10242 #number of vertices in fsaverage5 hemisphere surf_data = np.zeros((len(left_surface_list), n_vert * 2)) for it, filename in enumerate(left_surface_list): dat = mghformat.load(filename) surf_data[it, :n_vert] = np.squeeze(np.array(dat.get_fdata())) dat = mghformat.load(right_surface_list[it]) surf_data[it, n_vert:] = np.squeeze(np.array(dat.get_fdata())) return surf_data
def _guess_vol_file(fl): # MGH/MGZ files try: return fsmgh.load(fl) except: pass # Nifti Files try: return nib.load(fl) except: raise ValueError('Could not determine filetype for: %s' % fl)
def load_file(directory): brainmask = load(directory + '/mri/brainmask.mgz') ribbon = load(directory + '/mri/ribbon.mgz') aparc = load(directory + '/mri/aparc+aseg.mgz') segment = [] with open(directory + '/mri/segment.dat', "r") as f: segment.append(f.readlines()) content = segment[0] string_3 = content[2] wm_low = float(string_3[40:-1]) bmask_data = brainmask.get_data() ribbon_data = ribbon.get_data() aparc_data = aparc.get_data() return bmask_data, ribbon_data, aparc_data, wm_low
def _guess_surf_file(fl): # MGH/MGZ files try: return fsmgh.load(fl).get_data().flatten() except: pass # FreeSurfer Curv files try: return fsio.read_morph_data(fl) except: pass # Nifti files try: return np.squeeze(nib.load(fl).get_data()) except: raise ValueError('Could not determine filetype for: %s' % fl)
def benson14_retinotopy(sub): ''' benson14_retinotopy(subject) yields a pair of dictionaries each with three keys: polar_angle, eccentricity, and v123roi; each of these keys maps to a numpy array with one entry per vertex. The first element of the yielded pair is the left hemisphere map and the second is the right hemisphere map. The values are obtained by resampling the Benson et al. 2014 anatomically defined template of retinotopy to the given subject. Note that the subject must have been registered to the fsaverage_sym subject prior to calling this function; this requires using the surfreg command (after the xhemireg command for the RH). Additionally, you must have the fsaverage_sym template files in your fsaverage_syn/surf directory; these files are sym.template_angle.mgz, sym.template_eccen.mgz, and sym.template_areas.mgz. ''' global __benson14_templates if __benson14_templates is None: # Find a sym template that has the right data: sym_path = next((os.path.join(path0, 'fsaverage_sym') for path0 in subject_paths() for path in [os.path.join(path0, 'fsaverage_sym', 'surf')] if os.path.isfile(os.path.join(path, 'sym.template_angle.mgz')) \ and os.path.isfile(os.path.join(path, 'sym.template_eccen.mgz')) \ and os.path.isfile(os.path.join(path, 'sym.template_areas.mgz'))), None) if sym_path is None: raise ValueError('No fsaverage_sym subject found with surf/sym.template_*.mgz files!') sym = freesurfer_subject(sym_path).LH tmpl_path = os.path.join(sym_path, 'surf', 'sym.template_') # We need to load in the template data __benson14_templates = { 'angle': fsmgh.load(tmpl_path + 'angle.mgz').get_data().flatten(), 'eccen': fsmgh.load(tmpl_path + 'eccen.mgz').get_data().flatten(), 'v123r': fsmgh.load(tmpl_path + 'areas.mgz').get_data().flatten()} # Okay, we just need to interpolate over to this subject sym = freesurfer_subject('fsaverage_sym').LH return ( {'polar_angle': sub.LH.interpolate(sym, __benson14_templates['angle'], apply=False), 'eccentricity': sub.LH.interpolate(sym, __benson14_templates['eccen'], apply=False), 'v123roi': sub.LH.interpolate(sym, __benson14_templates['v123r'], apply=False, method='nearest')}, {'polar_angle': sub.RHX.interpolate(sym, __benson14_templates['angle'], apply=False), 'eccentricity': sub.RHX.interpolate(sym, __benson14_templates['eccen'], apply=False), 'v123roi': sub.RHX.interpolate(sym, __benson14_templates['v123r'], apply=False, method='nearest')})
def import_mri(filename, feature='data'): ''' import_mri(filename) yields a numpy array of the data imported from the given filename. The filename argument must be a string giving the name of a NifTI (*.nii or *.nii.gz) or MGH (*.mgh, *.mgz) file. The data is squeezed prior to being returned. import_mri(filename, feature) yields a specific feature of the object imported from filename; these features are given below. Features: * 'data' equivalent to import_mri(filename). * 'header' yields the header of the nibabel object representing the volume. * 'object' yields the nibabel object representing the volume. * 'affine' yields the affine transform of the given volume file; for an MGH file this is object.affine; for a NifTI file this is object.get_best_affine(). * 'qform' yields the qform matrix for a NifTI file; raises an exception for an MGH file. * 'sform' yields the qform matrix for a NifTI file; raises an exception for an MGH file. * 'vox2ras' yields object.get_vox2ras_tkr() for an MGH file; raises an exception for an MGH file. * 'rawdata' identical to 'data' except that the data is not squeezed. ''' import nibabel as nib, nibabel.freesurfer.mghformat as mgh if feature is None: feature = 'object' if not isinstance(feature, basestring): raise ValueError('feature must be a string or None') if feature == 'all': feature = 'object' # go ahead and get the file try: obj = nib.load(filename) except: obj = mgh.load(filename) # okay, now interpret the data feature = feature.lower() if feature == 'object': return obj elif feature == 'header': return obj.header elif feature == 'data': return np.squeeze(obj.dataobj.get_unscaled()) elif feature == 'rawdata': return obj.dataobj.get_unscaled() elif feature == 'affine': return obj.affine if isinstance( obj, mgh.MGHImage) else obj.header.get_best_affine() elif feature == 'qform': if isinstance(obj, mgh.MGHImage): raise ValueError('MGH object do not have qforms') return obj.header.get_qform() elif feature == 'sform': if isinstance(obj, mgh.MGHImage): raise ValueError('MGH object do not have sforms') return obj.header.get_sform() elif feature == 'vox2ras': if not isinstance(obj.mgh.MGHImage): raise ValueError('NifTI files do not have vox2ras matrices') return obj.header.get_vox2ras_tkr() else: raise ValueError('unrecognized feature: %s' % feature)
def load_mgh(filename, to='auto'): ''' load_mgh(filename) yields the MGHImage referened by the given filename by using the nibabel.freesurfer.mghformat.load function. The optional argument 'to' may be used to coerce the resulting data to a particular format; the following arguments are understood: * 'header' will yield just the image header * 'data' will yield the image's data-array * 'field' will yield a squeezed version of the image's data-array and will raise an error if the data object has more than 2 non-unitary dimensions (appropriate for loading surface properties stored in image files) * 'affine' will yield the image's affine transformation * 'image' will yield the raw image object * 'auto' is equivalent to 'image' unless the image has no more than 2 non-unitary dimensions, in which case it is assumed to be a surface-field and the return value is equivalent to the 'field' value. ''' img = fsmgh.load(filename) to = to.lower() if to == 'image': return img elif to == 'data': return img.get_data() elif to == 'affine': return img.affine elif to == 'header': return img.header elif to == 'field': dat = np.squeeze(img.get_data()) if len(dat.shape) > 2: raise ValueError('image requested as field has more than 2 non-unitary dimensions') return dat elif to in ['auto', 'automatic']: dims = set(img.dataobj.shape) if 1 < len(dims) < 4 and 1 in dims: return np.squeeze(img.get_data()) else: return img else: raise ValueError('unrecognized \'to\' argument \'%s\'' % to)
def nsd_write_fs(data, outputfile, fsdir): """similar to nsd_vrite_vol but for surface mgz Args: data (nd-array): the surface data outputfile (filename/path): where to save fsdir (path): we need to know where the fsdir is. Raises: ValueError: if wrong file name provided, e.g doesn't have lh or rh in filename, error is raised. """ # load template # load template if outputfile.find('lh.') != -1: hemi = 'lh' elif outputfile.find('rh.') != -1: hemi = 'rh' else: raise ValueError('wrong outpufile.') mgh0 = f'{fsdir}/surf/{hemi}.w-g.pct.mgh' if not os.path.exists(mgh0): mgh0 = f'{fsdir}/surf/{hemi}.orig.avg.area.mgh' img = fsmgh.load(mgh0) header = img.header affine = img.affine # Okay, make a new object now... vol_h = data[:, np.newaxis].astype(np.float64) v_img = fsmgh.MGHImage(vol_h, affine, header=header, extra={}) v_img.to_filename(outputfile)
def _guess_surf_file(fl): if len(fl) > 4 and (fl[-4:] == '.mgz' or fl[-4:] == '.mgh'): return np.squeeze(np.array(fsmgh.load(fl).dataobj)) else: return fsio.read_morph_data(fl)
subject_subdirs = os.listdir(subjects_dir) n_subjects = len(subject_subdirs) print('number of subjects found {}'.format(n_subjects)) for subject_subdir in subject_subdirs: surf_dir = subjects_dir + subject_subdir + '/surf' suffix = args.kernel l_surf_file = surf_dir + '/lh.thickness' + suffix r_surf_file = surf_dir + '/rh.thickness' + suffix # output CSVs l_out_file = args.output + suffix + '_lh.csv' r_out_file = args.output + suffix + '_rh.csv' subj_ID = surf_dir.rsplit('/',2)[1] try: # l_surf = list(read_morph_data(l_surf_file)) # Only works for .thickness files # r_surf = list(read_morph_data(r_surf_file)) l_surf = list(np.squeeze(load(l_surf_file).get_data())) r_surf = list(np.squeeze(load(r_surf_file).get_data())) #print('subject {}, number of vertices L: {}, R: {}'.format(subj_ID, len(l_surf),len(r_surf))) write_csv([subj_ID] + l_surf, l_out_file) write_csv([subj_ID] + r_surf, r_out_file) except: print('Unable to read thickness files from subject dir: {}'.format(subject_subdir)) print('vertex-wise CSVs saved to {}_[lh,rh].csv'.format(args.output))
def read_surf_file(flnm): if flnm.endswith(".mgh") or flnm.endswith(".mgz"): data = fsmgh.load(flnm).get_data().flatten() else: data = fsio.read_morph_data(flnm) return data
# Load conte69 c69_lh, c69_rh = load_conte69() # # Morphology # ## Thickness # ### Thickness: Inflated native surface # In[5]: # Load the data th_lh = dir_morph + subjectID + '_space-fsnative_desc-lh_thickness.mgh' th_rh = dir_morph + subjectID + '_space-fsnative_desc-rh_thickness.mgh' th_nat = np.hstack( np.concatenate( (np.array(load(th_lh).get_fdata()), np.array(load(th_rh).get_fdata())), axis=0)) # Plot the surface plot_hemispheres(inf_lh, inf_rh, array_name=th_nat, size=(900, 250), color_bar='bottom', zoom=1.25, embed_nb=True, interactive=False, share='both', nan_color=(0, 0, 0, 1), color_range=(1.5, 4), cmap="inferno",
def plot_surf_stat(lh_surf, lh_stat_map, lh_bg_map, rh_surf, rh_stat_map, rh_bg_map, out_fname, cmap='coolwarm', symmetric_cbar='auto', upper_lim=None, threshold=None): '''Use Nilearn to plot statistical surface map for L lat, med, R lat, med view. ''' import os.path as op import numpy as np import matplotlib.pyplot as plt import nibabel.freesurfer.io as fsio import nibabel.freesurfer.mghformat as fsmgh from nilearn import plotting # Get max and min value across stat maps from the two hemi lh_stat_dat = fsmgh.load(lh_stat_map).get_data() rh_stat_dat = fsmgh.load(rh_stat_map).get_data() flat_dat = np.hstack((lh_stat_dat.flatten(), rh_stat_dat.flatten())) max_val = np.maximum(np.abs(np.nanmax(flat_dat)), np.abs(np.nanmin(flat_dat))) if upper_lim is not None: vmax = upper_lim if max_val > upper_lim else max_val else: vmax = max_val fig, axs = plt.subplots(2, 2, figsize=(8, 6), subplot_kw={'projection': '3d'}) # Get threshold if txt is specified if isinstance(threshold, str): if op.exists(threshold): thresh_arr = np.loadtxt(threshold) thresh = thresh_arr if thresh_arr.shape == ( ) and thresh_arr != np.inf else None else: thresh = None elif isinstance(threshold, int) or isinstance(threshold, float): thresh = threshold else: thresh = None # Add threshold to title if not None if thresh is not None: if float(thresh) >= 1e-2 and float(thresh) < 1e2: title_txt = 'Threshold = {:.2f}'.format(float(thresh)) else: title_txt = 'Threshold = {:.2e}'.format(float(thresh)) else: title_txt = '' for i, ax in enumerate(fig.axes): if i <= 1: hemi = 'left' surf = lh_surf stat_map = lh_stat_dat bg = lh_bg_map else: hemi = 'right' surf = rh_surf stat_map = rh_stat_dat bg = rh_bg_map view = 'lateral' if i % 2 == 0 else 'medial' colorbar = True if i == 3 else False title = title_txt if i == 0 else '' plotting.plot_surf_stat_map(surf, stat_map, hemi=hemi, bg_map=bg, view=view, vmax=vmax, threshold=thresh, title=title, cmap=cmap, symmetric_cbar=symmetric_cbar, colorbar=colorbar, axes=ax, figure=fig) fig.savefig(out_fname, dpi=200, bbox_inches='tight') return op.abspath(out_fname)
def load_mgh(path): return mgh.load(path).get_data()
def plot_surf_map(lh_surf, lh_surf_map, lh_bg_map, rh_surf, rh_surf_map, rh_bg_map, out_fname, cmap='jet', vmin=None, vmax=None): '''Use Nilearn to plot non-statistical surface map for L lat, med, R lat, med view. ''' import os.path as op import numpy as np import matplotlib.pyplot as plt import nibabel.freesurfer.io as fsio import nibabel.freesurfer.mghformat as fsmgh from nilearn import plotting # Get max and min value across stat maps from the two hemi lh_surf_dat = fsmgh.load(lh_surf_map).get_data() rh_surf_dat = fsmgh.load(rh_surf_map).get_data() if vmin is None or vmax is None: flat_dat = np.hstack((lh_surf_dat.flatten(), rh_surf_dat.flatten())) if vmin is None: vmin = np.nanmin(flat_dat) if vmax is None: vmax = np.nanmax(flat_dat) fig, axs = plt.subplots(2, 2, figsize=(8, 6), subplot_kw={'projection': '3d'}) for i, ax in enumerate(fig.axes): if i <= 1: hemi = 'left' surf = lh_surf surf_map = lh_surf_dat bg = lh_bg_map else: hemi = 'right' surf = rh_surf surf_map = rh_surf_dat bg = rh_bg_map view = 'lateral' if i % 2 == 0 else 'medial' colorbar = True if i == 3 else False plotting.plot_surf(surf, surf_map, hemi=hemi, bg_map=bg, view=view, vmin=vmin, vmax=vmax, cmap=cmap, colorbar=colorbar, axes=ax, figure=fig) fig.savefig(out_fname, dpi=200, bbox_inches='tight') return op.abspath(out_fname)
# ------------------------------------------------------------------------------ # # MPC mask = np.hstack(np.where(th < 0.5, 0, 1)) for k in range(1, 15): k = str(k) k0 = k.rjust(2, '0') mpc_lh = dir_mpc + subBIDS + '_space-fsnative_desc-lh_MPC-' + k + '.mgh' mpc_rh = dir_mpc + subBIDS + '_space-fsnative_desc-rh_MPC-' + k + '.mgh' if os.path.exists(mpc_lh) and os.path.exists(mpc_rh): nom = subBIDS + "_space-fsnative_desc-surf_MPC-" + k0 + ".png" nomPng = dir_QC_png + nom print("[INFO].... Creating PNG of MPC-" + k0) mpc = np.hstack( np.concatenate((np.array( load(mpc_lh).get_fdata()), np.array(load(mpc_rh).get_fdata())), axis=0)) * mask Qt = (round(np.quantile(mpc[np.nonzero(mpc)], 0.05), 0), round(np.quantile(mpc[np.nonzero(mpc)], 0.95), 0)) plot_hemispheres(surf_lh, surf_rh, array_name=mpc, size=(900, 250), color_bar='bottom', zoom=1.25, embed_nb=True, interactive=False, share='both', nan_color=(0, 0, 0, 1), color_range=Qt, cmap="viridis",
def _load_fn(): p = fsmgh.load(flnm).get_data().flatten() p.setflags(write=False) return p
def _load_imm_mgh(flnm): img = fsmgh.load(flnm) img.get_data().setflags(write=False) return img