def estimate_bold_prf(bold_file, method): prf_task = Task(bold_file) stimuli = read_prf_stimuli(prf_task) region_idx = 11143 aparc = nload(str(data_aparc)) img = nload(str(prf_task.filename)) mri = img.get_data() roi = select_region(aparc, lambda x: x == region_idx) roi_idx = apply_affine(img.affine, array(where(roi.get_data() > 0)).T) roi_str = [f'{xyz[0]:.2f},{xyz[1]:.2f},{xyz[2]:.2f}' for xyz in roi_idx] dat = mri[roi.get_data() > 0, :] output = compute_prf(bold_file, dat, roi_str, stimuli, method) images = ['X', 'Y', 'SIGMA', 'BETA'] for i in range(len(output)): nii_file = replace_extension(bold_file, 'prf' + images[i] + '.nii.gz') out = zeros(mri.shape[:3]) out[roi.get_data() > 0] = output[i] x_img = Nifti1Image(out, img.affine) x_img.to_filename(str(nii_file))
def compute_percent(feat_path, normalize_to_mean): """Calculate percent change for a task. Parameters ---------- Returns ------- instance of nibabel.Nifti1Image percent change as image """ design = read_design(feat_path) pe_mri = nload(str(feat_path / 'stats' / 'pe1.nii.gz')) pe = pe_mri.get_data() pe[pe == 0] = NaN perc = pe * 100 * design.ptp() if normalize_to_mean: """I'm not sure if this is necessary, but for sure it increases the level of noise""" mean_mri = nload(str(feat_path / 'mean_func.nii.gz')) mean_func = mean_mri.get_data() array_equal(pe_mri.affine, mean_mri.affine) with errstate(invalid='ignore'): perc /= mean_func mask_mri = nload(str(feat_path / 'mask.nii.gz')) mask = mask_mri.get_data().astype(bool) perc[~mask] = NaN return Nifti1Image(perc, pe_mri.affine)
def ribbon2graymatter(ribbon_files, output_dir): graymatter_file = output_dir / 'graymatter.nii.gz' ribbon_rh_file = [x for x in ribbon_files if x.endswith('rh.ribbon.mgz')][0] ribbon_lh_file = [x for x in ribbon_files if x.endswith('lh.ribbon.mgz')][0] ribbon_rh = nload(str(ribbon_rh_file)) ribbon_lh = nload(str(ribbon_lh_file)) graymatter = ribbon_rh.get_data() + ribbon_lh.get_data() nifti = Nifti1Image(graymatter, ribbon_rh.affine) nifti.to_filename(str(graymatter_file)) return graymatter_file
def create_bold(bold_file, taskname, region_idx, timeseries): aparc = nload(str(data_aparc)) brain = select_region(aparc, lambda x: x > 0) act = select_region(aparc, lambda x: x == region_idx) t = timeseries.shape[0] random.seed(100) idx = where(brain.get_data() == 1) r = random.randn(idx[0].shape[0], t) bold = ones(brain.get_data().shape + (t, )) bold[act.get_data() == 1, :] = timeseries bold[idx] += r nifti = Nifti1Image(bold.astype('float32'), brain.affine) nifti.header['pixdim'][4] = TR nifti.header.set_xyzt_units('mm', 'sec') nifti.to_filename(str(bold_file)) d = { 'RepetitionTime': TR, 'TaskName': taskname, } json_bold = replace_extension(bold_file, '.json') with json_bold.open('w') as f: dump(d, f, ensure_ascii=False, indent=' ')
def calc_fmri_at_elec(measure_nii, electrodes_file, distance, kernels, graymatter, output_dir): """ Calculate the (weighted) average of fMRI values at electrode locations """ electrodes = Electrodes(electrodes_file) img = nload(str(measure_nii)) mri = img.get_data() mri[mri == 0] = NaN labels = electrodes.electrodes.get(map_lambda=lambda x: x['name']) chan_xyz = array(electrodes.get_xyz()) nd = array(list(ndindex(mri.shape))) ndi = from_mrifile_to_chan(img, nd) if graymatter: gm_mri = nload(str(graymatter)).get_data().astype(bool) mri[~gm_mri] = NaN lg.debug( f'Computing fMRI values for {measure_nii.name} at {len(labels)} electrodes and {len(kernels)} "{distance}" kernels' ) fmri_vals, n_voxels = compute_kernels(kernels, chan_xyz, mri, ndi, distance) fmri_vals_tsv = output_dir / replace_underscore(measure_nii.name, 'compare.tsv') n_voxels_tsv = output_dir / replace_underscore(measure_nii.name, 'nvoxels.tsv') with fmri_vals_tsv.open('w') as f: f.write('channel\t' + '\t'.join(str(one_k) for one_k in kernels) + '\n') for one_label, val_at_elec in zip(labels, fmri_vals): f.write(one_label + '\t' + '\t'.join(str(one_val) for one_val in val_at_elec) + '\n') with n_voxels_tsv.open('w') as f: f.write('channel\t' + '\t'.join(str(one_k) for one_k in kernels) + '\n') for one_label, val_at_elec in zip(labels, n_voxels): f.write(one_label + '\t' + '\t'.join(str(one_val) for one_val in val_at_elec) + '\n') return fmri_vals_tsv, n_voxels_tsv
def simulate_anat(root, task_anat, t1): mri = nload(str(t1)) x = mri.get_data() nifti = Nifti1Image(x, mri.affine) anat_path = task_anat.get_filename(root) anat_path.parent.mkdir(exist_ok=True, parents=True) nifti.to_filename(str(anat_path)) return file_Core( anat_path) # use the general file_Core (Task needs events.tsv)
def surface_ras_shift(self): """Freesurfer uses two coordinate systems: one for volumes ("RAS") and one for surfaces ("tkReg", "tkRAS", and "Surface RAS"). To get from surface to volume coordinates, add this numbers. To get from volume to surface coordinates, substract this numbers. """ T1_path = self.dir / 'mri' / 'T1.mgz' assert T1_path.exists() T1 = nload(str(T1_path)) return T1.header['Pxyz_c']
def plot_surf(img_dir, freesurfer_dir, info, surface): fs = Freesurfer(freesurfer_dir / info['subject']) surf = getattr(fs.read_brain(surface), info['hemi']) surf_img = nload(str(info['surf'])) surf_val = surf_img.get_data()[:, 0, 0].astype('float64') v = Viz3() v.add_surf(surf, values=surf_val, limits_c=(-6, 6), colorbar=True) v.save(img_dir / (info['surf'].stem + '.png')) v.close()
def test_channel_sphere(): xyz = chan.return_xyz()[0, :] fs = Freesurfer(fs_path) mask = create_sphere_around_elec(xyz, template_mri_path, distance=8, freesurfer=fs) assert mask.sum() == 4 template_mri = nload(str(template_mri_path)) xyz_volume = xyz + fs.surface_ras_shift mask = create_sphere_around_elec(xyz, template_mri, distance=16) assert mask.sum() == 35
def surface_ras_shift(self): """Freesurfer uses two coordinate systems: one for volumes ("RAS") and one for surfaces ("tkReg", "tkRAS", and "Surface RAS"). To get from surface to volume coordinates, add this numbers. To get from volume to surface coordinates, substract this numbers. """ T1_path = self.dir / 'mri' / 'T1.mgz' assert T1_path.exists() try: T1 = nload(str(T1_path)) except NameError: raise ImportError('nibabel needs to be installed for this function') return T1.header['Pxyz_c']
def get_vox2ras_tkr(filename): """This should be identical mri_info --vox2ras-tkr filename """ img = nload(str(filename)) Nc, Nr, Ns = img.shape[:3] dC, dR, dS = img.header['pixdim'][1:4] vox2ras_tkr = array([ [-dC, 0, 0, Nc / 2 * dC], [0, 0, dS, -Ns / 2 * dS], [0, -dR, 0, Nr / 2 * dR], [0, 0, 0, 1], ]) return vox2ras_tkr
def create_sphere_around_elec(xyz, template_mri, distance=8, freesurfer=None): """Create an MRI mask around an electrode location, Parameters ---------- xyz : ndarray 3x0 array template_mri : path or str (as path) or nibabel.Nifti (path to) MRI to be used as template distance : float distance in mm between electrode and selected voxels freesurfer : instance of Freesurfer to adjust RAS coordinates, see Notes Returns ------- 3d bool ndarray mask where True voxels are within selected distance to the electrode Notes ----- Freesurfer uses two coordinate systems: one for volumes ("RAS") and one for surfaces ("tkReg", "tkRAS", and "Surface RAS"), so the electrodes might be stored in one of the two systems. If the electrodes are in surface coordinates (f.e. if you can plot surface and electrodes in the same space), then you need to convert the coordinate system. This is done by passing an instance of Freesurfer. """ if freesurfer is None: shift = 0 else: shift = freesurfer.surface_ras_shift if isinstance(template_mri, str) or isinstance(template_mri, Path): try: template_mri = nload(str(template_mri)) except NameError: raise ImportError('nibabel needs to be installed for this function') mask = zeros(template_mri.shape, dtype='bool') for vox in ndindex(template_mri.shape): vox_ras = apply_affine(template_mri.affine, vox) - shift if norm(xyz - vox_ras) <= distance: mask[vox] = True return mask
def project_mri_to_surf(fmri_file, vert, kernel): img = nload(str(fmri_file)) mri = img.get_fdata() mri[mri == 0] = NaN nd = array(list(ndindex(mri.shape))) ndi = from_mrifile_to_chan(img, nd) partial_compute_chan = partial(compute_chan, KERNEL=kernel, ndi=ndi, mri=mri, distance='gaussian') with Pool() as p: fmri_vals = p.map(partial_compute_chan, vert) fmri_vals = [x[0] for x in fmri_vals] return fmri_vals
def mri_nan2zero(input_nii): """Remove NaN values and turn them into zeros (so that Freesurfer can handle them) Parameters ---------- input_nii : Path path to nii.gz containing NaN Returns ------- Path path to temporary nii.gz containing no NaN. You can remove the file with .unlink() """ img = nload(str(input_nii)) dat = img.get_data() dat[isnan(dat)] = 0 img = Nifti1Image(dat, img.affine) tmp_nii = mkstemp(suffix='.nii.gz')[1] img.to_filename(tmp_nii) return Path(tmp_nii)
def compute_zstat(feat_path): return nload(str(feat_path / 'stats' / 'zstat1.nii.gz'))
def timeseries_fmri(parameters): fmri_dir = parameters['paths']['output'] / 'workflow' / 'fmri' subject = parameters['plot']['subject'] subject = 'delft' fmri_subj_dir = fmri_dir / f'_subject_{subject}' regressor_file = next(fmri_subj_dir.rglob('design.mat')) thresh_file = next(fmri_subj_dir.rglob('thresh_zstat1.nii.gz')) timeseries_file = next(fmri_subj_dir.rglob('filtered_func_data.nii.gz')) subj_dir = parameters['paths']['input'] / f'sub-{subject}' events_fmri_file = next(subj_dir.glob('ses-*/func/*_events.tsv')) events = read_tsv(events_fmri_file) thresh = nload(thresh_file).get_fdata() i_vox = thresh >= THRESH timeseries = nload(timeseries_file).get_fdata() fmri = timeseries[i_vox] t_fmri = arange(timeseries.shape[3]) * TR regressor = genfromtxt(regressor_file, skip_header=5) traces = [ go.Scatter( x=t_fmri, y=fmri.mean(axis=0), line=dict(color='black', ), ), go.Scatter( x=t_fmri, y=regressor, yaxis='y2', ), ] layout = merge( LAYOUT, dict( height=100, width=450, showlegend=False, xaxis=dict( tick0=0, dtick=30, range=(0, 270), ), yaxis=dict(), yaxis2=dict( overlaying='y', side='right', visible=False, ), shapes=event_shapes(events), ), ) fig = go.Figure( data=traces, layout=layout, ) return fig