def resection_area_computation(img: Any, _subject_tree=None, _priority=None): """turns given `resection map`_ into mni_ coordinate system, uses :func:`nodestimation.project.read_or_write` decorator :param img: `resection map`_ :type img: Any :param _subject_tree: representation of patient`s files structure, default None :type _subject_tree: *look for SubjectTree in* :mod:`nodestimation.project.annotations` *, optional* :param _priority: if several files are read, which one to choose, if None, read all of them, default None :type _priority: int, optional :return: resection area :rtype: np.ndarray_ .. _mni: https://brainmap.org/training/BrettTransform.html """ res = np.array(img.get_data().tolist()) img_coordinates = list() for i in range(res.shape[0]): for j in range(res.shape[1]): for k in range(res.shape[2]): if res[i, j, k] != 0: img_coordinates.append(np.array([i, j, k])) img_coordinates = np.array(img_coordinates) mni_coordinates = list() for coordinate in img_coordinates: mni_coordinates.append( np.array( image.coord_transform(coordinate[0], coordinate[1], coordinate[2], img.affine))) print(np.array(mni_coordinates).shape) return np.array(mni_coordinates)
def test_get_mask_measures(): # Create ellipsoid image one_mask_data = np.ones((60, 60, 80)) affine = np.eye(4) affine[0, 0] = .7 affine[2, 2] = .5 affine[:3, 3] = np.array([-2, 7, 3]) one_mask_img = nibabel.Nifti1Image(one_mask_data, affine) i, j, k = np.where(one_mask_data <2) voxels_coords = np.array(coord_transform(i, j, k, affine)).T center_coords = np.array([ 20., 31., 20.]) positions = voxels_coords - center_coords angle = np.pi / 3 c = np.cos(angle) s = np.sin(angle) rotation = np.array([[c, -s, 0], [s, c, 0.], [0, 0, 1]]) lambd = np.eye(3) a = 6. b = 15. c = 16. lambd[0, 0] = 1. / a ** 2 lambd[2, 2] = 1. / b ** 2 lambd[1, 1] = 1. / c ** 2 trans_sqrt = np.sqrt(lambd).dot(rotation) transformed_positions = trans_sqrt.dot(positions.T).T new_voxels = np.sum(transformed_positions ** 2, axis=1) <= 1 mask_file = os.path.join(tst.tmpdir, 'ellipsoid.nii.gz') masking.unmask(new_voxels, one_mask_img).to_filename(mask_file) length_ap, length_rl, length_is, symmetry, volume = brain_mask._get_mask_measures(mask_file) np.testing.assert_array_almost_equal(length_ap, 2 * c) np.testing.assert_array_almost_equal(length_rl, 2 * b, decimal=0) np.testing.assert_array_almost_equal(length_is, 2 * a, decimal=0) assert_greater(symmetry, .99)
def plot_2D_crosscuts(bm_data, coord, fdir): # Get the locations of the mni template XX, YY, ZZ = np.where(bm_data > 0.1) # Transform a few of them in MNI space nskip = 29 xmni, ymni, zmni = coord_transform(XX.ravel()[::nskip], YY.ravel()[::nskip], ZZ.ravel()[::nskip], bm.affine) # Plot the template and the fieldtrip locations together in a 2D crosscuts way fig, ax = plt.subplots(ncols=3, figsize=(15, 5)) ax[0].scatter(xmni, ymni, s=1) ax[1].scatter(xmni, zmni, s=1) ax[2].scatter(ymni, zmni, s=1) # ax[0].scatter(XX.ravel()[::nskip],YY.ravel()[::nskip], s = bm_data.ravel()[::nskip]) # ax[1].scatter(XX.ravel()[::nskip],ZZ.ravel()[::nskip], s = bm_data.ravel()[::nskip]) # ax[2].scatter(YY.ravel()[::nskip],ZZ.ravel()[::nskip], s = bm_data.ravel()[::nskip]) crosscuts = [[0, 1], [0, 2], [1, 2]] for i, cc in enumerate(crosscuts): ax[i].scatter(coord[:, cc[0]], coord[:, cc[1]], c='C1', alpha=0.5, s=1) ax[i].set_title('{} crosscut'.format(cc)) ax[i].set_xticks([]) ax[i].set_yticks([]) plt.savefig(out_dir + '2Dcrosscut.png', fmt='png', dpi=300) plt.close('all') return
def transform_to_2d(self, data, affine): """Cut the 3D volume into a 2D slice. Parameters ---------- data : 3D :class:`~numpy.ndarray` The 3D volume to cut. affine : 4x4 :class:`~numpy.ndarray` The affine of the volume. """ coords = [0, 0, 0] if self.direction not in ['x', 'y', 'z']: raise ValueError('Invalid value for direction %s' % self.direction) coords['xyz'.index(self.direction)] = self.coord x_map, y_map, z_map = [ int(np.round(c)) for c in coord_transform(coords[0], coords[1], coords[2], np.linalg.inv(affine)) ] if self.direction == 'y': cut = np.rot90(data[:, y_map, :]) elif self.direction == 'x': cut = np.rot90(data[x_map, :, :]) elif self.direction == 'z': cut = np.rot90(data[:, :, z_map]) return cut
def get_coords_ball(r2): vox_coords = np.unravel_index(r2.get_fdata().argmax(), r2.shape) coords = image.coord_transform(*vox_coords, thr_r2.affine) _, ball =_apply_mask_and_get_affinity([list(coords)], image.concat_imgs([r2]), 3., True) ball = ball.toarray().reshape(r2.shape)* 100. ball = image.new_img_like(r2, ball.astype(int)) return coords, ball
def _run_interface(self, runtime): import os.path as op import nibabel.gifti as ng import numpy as np import skimage.measure as sm import nilearn.image as nimg import slam.io as sio import slam.differential_geometry as sdg from nipype.utils.filemanip import split_filename # Generate output mesh filename from the input image name _, fname, _ = split_filename(self.inputs.image_file) gii_file = op.abspath(op.join(runtime.cwd, fname + ".gii")) # Load the largest connected component of the input image img = nimg.largest_connected_component_img(self.inputs.image_file) # TODO: check if the input image is correct (binary) # Run the marching cube algorithm verts, faces, normals, values = sm.marching_cubes_lewiner( img.get_data(), self.inputs.level) # Convert vertices coordinates to image space # TODO: check that is correct by plotting the mesh on the image x, y, z = nimg.coord_transform(verts[:, 0], verts[:, 1], verts[:, 2], img.affine) mm_verts = np.array([x, y, z]).T # Save the mesh as Gifti # FIXME: FreeView can not open the mesh (but anatomist do) gii = ng.GiftiImage(darrays=[ ng.GiftiDataArray(mm_verts, intent='NIFTI_INTENT_POINTSET'), ng.GiftiDataArray(faces, intent='NIFTI_INTENT_TRIANGLE') ]) gii.meta = ng.GiftiMetaData().from_dict({ "volume_file": self.inputs.image_file, "marching_cube_level": str(self.inputs.level), "smoothing_iterations": str(self.inputs.smoothing_iter), "smoothing_dt": str(self.inputs.smoothing_dt) }) ng.write(gii, gii_file) # Optional: Smooth the marching cube output with SLAM if self.inputs.smoothing_iter > 0: mesh = sdg.laplacian_mesh_smoothing( sio.load_mesh(gii_file), nb_iter=self.inputs.smoothing_iter, dt=self.inputs.smoothing_dt) sio.write_mesh(mesh, gii_file) return runtime
def coords_to_img(coords, fwhm=9.0): mask = load_mni152_brain_mask() masker = NiftiMasker(mask).fit() voxels = np.asarray( image.coord_transform(*coords.T, np.linalg.pinv(mask.affine)), dtype=int, ).T peaks = np.zeros(mask.shape) np.add.at(peaks, tuple(voxels.T), 1.0) peaks_img = image.new_img_like(mask, peaks) img = image.smooth_img(peaks_img, fwhm=fwhm) img = masker.inverse_transform(masker.transform(img).squeeze()) return img
def roi_map_scatter(roi, beta_map, ref_vol_img): # get coordinates of roi, calculate bounds coords = np.argwhere(load_img(roi).get_fdata() != 0) roi_max_bounds = np.max(coords, 0) roi_min_bounds = np.min(coords, 0) roi_center_native = np.mean(coords, 0) roi_center_ras = coord_transform(*roi_center_native, ref_vol_img.affine) print( f"****\n{roi} extends from {roi_max_bounds} to {roi_min_bounds} and is centered at:\n{roi_center_native} (native) = {roi_center_ras} (RAS)", sep='\n') # mask the beta map with the roi mask beta_masker = NiftiMasker(mask_img=roi) roi_betas = beta_masker.fit_transform(beta_map)[0] roi_beta_min = np.min(roi_betas) roi_beta_max = np.max(roi_betas) print(coords.shape, roi_betas.shape) coords_ras = np.array( coord_transform(coords[:, 0], coords[:, 1], coords[:, 2], ref_vol_img.affine)) plt.scatter(coords_ras[0, :], roi_betas) plt.xlabel("Left - Right") plt.ylabel(f"{op.basename(beta_map)}") plt.show() plt.close() plt.scatter(coords_ras[1, :], roi_betas) plt.xlabel("Posterior - Anterior") plt.ylabel(f"{op.basename(beta_map)}") plt.show() plt.close() # plot histogram of beta values within roi mask plt.scatter(coords_ras[2, :], roi_betas) plt.xlabel("Inferior - Superior") plt.ylabel(f"{op.basename(beta_map)}") plt.show() plt.close()
def find_atlas(volume): #atlas = fetch_atlas_destrieux_2009(lateralized=True, data_dir=None, url=None, resume=True, verbose=1) atlas = fetch_atlas_aal(version='SPM12', data_dir=None, url=None, resume=True, verbose=1) dictionary = dict(zip([int(i) for i in atlas.indices], atlas.labels)) A = load_img(atlas.maps).affine trA = np.linalg.inv(A) atlas_map = load_img(atlas.maps).get_fdata() affine = [[-3, 0, 0, 78], [0, 3, 0, -112], [0, 0, 6, -50], [0, 0, 0, 1]] def cast(x, y, z, affine, trA): coord = coord_transform(x, y, z, affine) return np.dot(trA, [coord[0], coord[1], coord[2], 1]) def find(x, y, z, affine, trA): vox_coord = cast(x, y, z, affine, trA) label = atlas_map[int(vox_coord[0]), int(vox_coord[1]), int(vox_coord[2])] if int(label) == 0: return return dictionary[int(label)] index = np.where(volume > 0.8) for i in range(np.sum(volume > 0.8)): x, y, z = index[0][i], index[1][i], index[2][i] print(x, y, z, coord_transform(x, y, z, affine), find(x, y, z, affine, trA), volume[x, y, z]) index = np.where(volume < -0.8) for i in range(np.sum(volume < -0.8)): x, y, z = index[0][i], index[1][i], index[2][i] print(x, y, z, coord_transform(x, y, z, affine), find(x, y, z, affine, trA), volume[x, y, z])
def extract_img_value_for_mni_coords(mni_coords, img): '''Extract image value for specific mni coordinates. Args: mni_coords (tuple): MNI coordinates x, y, z. img (Nifti1Image): 3D Nifti image. Can be atlas image, statistical map, T1, etc. Returns: Image value for voxel closest to specified MNI coordinates. ''' array_coords = image.coord_transform(*mni_coords, np.linalg.inv(img.affine)) array_coords = tuple(round(array_coord) for array_coord in array_coords) return img.get_fdata()[array_coords]
def get_seeds(self, nifti_img_path, connect_diag=True, min_size=100): """ Get the seeds from regions in a group parcellation Nifti image :param nifti_img_path: str Path to the group parcellation Nifti image :param connect_diag: See the documentation of nilearn.regions.connected_label_regions :param min_size: See the documentation of nilearn.regions.connected_label_regions :return: l_medoids: list of tuple of shape (x,y,z) Each tuple represents the coordinates of the seed voxel of a region l_medoids_coord_mniL list of tuple of shape (x,y,z) Each tuple represents the coordinates of the seed voxel of a region in MNI coordinates """ mist_img = nb.load(nifti_img_path) regions = connected_label_regions(nifti_img_path, connect_diag=connect_diag, min_size=min_size).get_data() labels_regions = np.unique(regions) # Apply the kmedoids clustering for each region l_medoids = [] for label in range(1, len(labels_regions)): label_coords = np.argwhere(regions == label) kmedoids_instance = kmedoids(label_coords, [0]) kmedoids_instance.process() medoid = kmedoids_instance.get_medoids() elem = tuple(label_coords[medoid[0]]) l_medoids.append(elem) l_medoids_coord_mni = [coord_transform(medoid[0], medoid[1], medoid[2], mist_img.get_affine()) for medoid in l_medoids] return l_medoids, l_medoids_coord_mni
def nifti_to_txt(filename_nii, filename_txt="auto"): """ Extracts possible coordinates from a NiFTI template and saves them as a .txt file. """ # Read template image img = image.load_img(filename_nii) # Extract gray matter voxels dat = img.get_fdata() x, y, z = np.where(dat == 1.0) # Convert to MNI space affine = img.affine within_mni = image.coord_transform(x=x, y=y, z=z, affine=affine) within_mni = np.array(within_mni).T # Create automatic output filename if filename_txt == "auto": basename_nii = path.basename(filename_nii) basename_txt = "within_" + basename_txt filename_txt = filename_nii.replace(basename_nii, basename_txt) # Save possible MNI coordinates _ = np.savetxt(filename_txt, within_mni, fmt="%i") return within_mni
def convert_coordinates(self, x, y, z): niimg = datasets.load_mni152_template() converted = image.coord_transform((x, y, z), niimg.affine) print("CONVERTED COORDINATES: ", converted)
bm = load_mni152_brain_mask() bm_data = bm.get_fdata() Lx, Ly, Lz = bm_data.shape # Apply shift and scaling to the data and reorder the axis in pos shift = np.array([0, 40, 40]) scale = np.array([1, 1, 1]) ix, iy, iz = [1, 0, 2] pos_ref = np.array([pos[:, ix], pos[:, iy], pos[:, iz]]).T transformed_pos = pos_ref * scale[None, :] - shift[None, :] # Plot a 2d crosscut overview to check that the scaling makes sense plot_2D_crosscuts(bm_data, transformed_pos, fdir) # Send the position to volume space pos_volume = coord_transform(transformed_pos[:, 0], transformed_pos[:, 1], transformed_pos[:, 2], inv(bm.affine)) loc_volume = np.round(pos_volume).astype(int).T # Plot the location of source detection on a brain template # plot_sources_on_brain(loc_volume,bm,bm_data) # Define the window for temporal averaging dt = (time[1] - time[0]) * av_win time_av = np.arange(time[0], time[-1], dt) t_size = len(time_av) print( 'The software is currently averaging the time traces over a time window of {} s' .format(dt)) print('This will result in a nifti file with {} volumes.'.format(t_size)) print(fun.shape)
bg_img = "BG.nii.gz" img = "vol0000.nii.gz" sl = int(100) outpng = "test" lthresh = float(4) useatlas = "empty.nii.gz" annotate = "0" alpha = 0.4 alpha = 0.3 #bgrange = sys.argv[2] #ulthresh = sys.argv[5] img = image.load_img(img) useatlas = '1' img.get_fdata()[img.get_fdata() < 0] = 0 (x, y, z) = image.coord_transform(0, 0, sl, img.affine) print(sl, x, y, z) smith = datasets.fetch_atlas_smith_2009() if useatlas == '0': atlas = image.load_img(smith.bm10) atlas = image.index_img(atlas, (7, 8, 9)) print(useatlas) display = plotting.plot_prob_atlas( atlas, bg_img=bg_img, #colorbar=True, black_bg=True, display_mode='z', cut_coords=(z, ), # display_mode="z", cut_coords = 3,
def index_to_xy_coord(x, y, z=10): '''Transforms data index to coordinates of the background + offset''' coords = coord_transform(x, y, z, affine=thresholded_score_map_img.affine) return np.array(coords)[np.newaxis, :] + np.array([0, 1, 0])
from nilearn import datasets, image import numpy as np niimg = datasets.load_mni152_template() print(niimg.get_data().shape) result = image.coord_transform(54, 10, 38, niimg.affine) print(np.asarray(result) / 1000)
def plot_scores(scores_path, save_path, glassbrain_save, mask_path, select_voxels_by_pval=False, perm_path=None, roi_mask=None): print('Plotting', save_path) from nilearn import plotting mask = joblib.load(mask_path)[0] mean_scores = mean_img(scores_path) img = load_img(scores_path) if roi_mask: roi_mask = resample_img(roi_mask, mask._affine, mask.shape, interpolation='nearest') roi_mask = intersect_masks([mask, roi_mask]) if select_voxels_by_pval: perm_all = joblib.load(perm_path.format('s')) pval = joblib.load(perm_path.format('pval')) selection_mask = mask if roi_mask: pval = unmask(pval, mask) pval = apply_mask(pval, roi_mask) selection_mask = roi_mask n_permutations = perm_all.shape[1] thresh = 1.0 / n_permutations pval_mask = pval > thresh mean_scores = apply_mask(mean_scores, selection_mask) mean_scores[pval_mask] = 0 mean_scores = unmask(mean_scores, selection_mask) img = apply_mask(img, selection_mask) img[np.broadcast_to(pval_mask, (img.shape[0], pval_mask.shape[0]))] = 0 img = unmask(img, selection_mask) elif roi_mask: mean_scores = apply_mask(mean_scores, roi_mask) mean_scores = unmask(mean_scores, roi_mask) img = apply_mask(img, roi_mask) img = unmask(img, roi_mask) data = img.dataobj avg_max = np.max(mean_scores.dataobj) avg_argmax = np.unravel_index(np.argmax(mean_scores.dataobj), mean_scores.shape) total_max = np.max(data) total_argmax = np.unravel_index(np.argmax(data), data.shape) fold0_max = np.max(data[..., 0]) fold0_argmax = np.unravel_index(np.argmax(data[..., 0]), data.shape[:-1]) avg_argmax_mni = coord_transform(*avg_argmax, mask.affine) fold0_argmax_mni = coord_transform(*fold0_argmax, mask.affine) total_argmax_mni = coord_transform(*total_argmax[:3], mask.affine) def _plot_helper(scores, markers, save_suffix, title): thresh = 0.0 # 0.05 display = plotting.plot_stat_map(scores, threshold=thresh, title=title) display.add_markers([markers], marker_color='w', marker_size=50) # display.add_contours(temporal_lobe_mask,filled=False,colors='m') # display.add_contours(heschl_mask,filled=False,colors='g') plt.gcf().savefig(save_path + save_suffix) plt.close() display = plotting.plot_glass_brain(scores, threshold=thresh, colorbar=True, display_mode='lzry', plot_abs=False) display.add_contours(temporal_lobe_mask, filled=False, colors='m') display.add_contours(heschl_mask, filled=False, colors='g') display.add_markers([markers], marker_color='w', marker_size=50) # proxy artist trick to make legend from matplotlib.patches import Rectangle cont1 = Rectangle((0, 0), 1, 1, fc="magenta") cont2 = Rectangle((0, 0), 1, 1, fc="green") plt.legend([cont1, cont2], ['Temporal lobe', 'Heschl gyrus']) plt.gcf().savefig(glassbrain_save + save_suffix) plt.close() title = 'Avg %.3f %s' % (avg_max, str(avg_argmax_mni)) _plot_helper(mean_scores, avg_argmax_mni, '_avg.png', title) title = 'Fold 0 %.3f %s' % (fold0_max, str(fold0_argmax_mni)) _plot_helper(img.slicer[..., 0], fold0_argmax_mni, '_fold0.png', title) title = 'Total %.3f %s fold %d' % (total_max, str(total_argmax_mni), total_argmax[3]) _plot_helper(img.slicer[..., total_argmax[3]], total_argmax_mni, '_total.png', title)
filters = make_lcmv(evoked.info, fwd, cov, reg=0.05, pick_ori='max-power', weight_norm='unit-noise-gain', reduce_rank=True) stc = apply_lcmv(evoked, filters, max_ori_out='signed') max_vetrs_across_time = np.argmax(stc.rh_data, axis=0) verts = stc.vertices[0][max_vetrs_across_time] tbas = [] for v in verts: mni = mne.vertex_to_mni(v, 0, 'fsaverage')[0] tal_coord = coord_transform(mni[0], mni[1], mni[2], affine) ba = tal_to_ba[int(tal_coord[0]), int(tal_coord[1]), int(tal_coord[2])] tbas.append(ba) if yi in tbas: ypred.append(yi) else: k = classes_to_keep.copy() k.remove(yi) ti = random.randint(0, 13) ypred.append(k[ti]) print(ypred[i]) np.save('y.npy', y) np.save('ypred.npy', ypred) from sklearn.metrics import f1_score print(f1_score(y, ypred, average='macro'))
def driver(image_file, output_directory, mask_file=None, erode=3, clean=False, mode='single', apply_noise=None, no_scale=False, intensity=0.01, location=[], force=False, verbose=False, **kwargs): scale = not no_scale if apply_noise: output_file = op.splitext(apply_noise)[0] # Handle special case: apply_noise and clean means delete noisy image. if clean: # TODO: generalize to other output image formats if op.isfile(output_file + ".nii.gz"): os.remove(output_file + ".nii.gz") return 0 else: # Create output filename for noise data bname = op.basename(image_file).split(".")[0] modifier = "_1vox-" + str(uuid.uuid1())[0:8] output_file = op.join(output_directory, bname + modifier) # Load nifti images and extract their data image_loaded = nib.load(image_file) image_data = image_loaded.get_data() # If a noise file is provided, use it to grab noise features if apply_noise: with open(apply_noise) as fhandle: noise_data = json.loads(fhandle.read()) scale = noise_data['scale'] intensity = noise_data['intensity'] loc = [tuple(vl) for vl in noise_data["voxel_location"]] mm_loc = noise_data["mm_location"] original_hash = noise_data['matrix_hash'] # If not, generate noise based on parameters from the command-line else: original_hash = None if not mask_file: raise ValueError("Must provide a mask for generating noise.") mask_loaded = nib.load(mask_file) mask_data = mask_loaded.get_data() # Generate noise based on input params loc = generate_noise_params(image_data, mask=mask_data, erode=erode, location=location, force=force, mode=mode) # Apply noise to image output_data, output_hash = apply_noise_params(image_data, loc, scale=scale, intensity=intensity) # Verify that the hashes match for our noisy images. if original_hash and output_hash != original_hash: print("WARNING: Noisy image hash is different from expected hash.") # Only create noise JSON if there wasn't one provided if not apply_noise: # Get noise locations in mm (useful for visualizing) mm_loc = [] for l in loc: tmp_mm = nilimage.coord_transform(l[0], l[1], l[2], image_loaded.affine) mm_loc += [tuple(float(tmm) for tmm in tmp_mm)] # Save noise information to a JSON file with open(output_file + ".json", 'w') as fhandle: noisedict = {"voxel_location": loc, "mm_location": mm_loc, "base_image": image_file, "matrix_hash": output_hash, "scale": scale, "intensity": intensity} fhandle.write(json.dumps(noisedict, indent=4, sort_keys=True)) if verbose: print("Noise added in matrix coordinates at: {0}".format(loc)) print("Noise added in mm coordinates at: {0}".format(mm_loc)) print("Image stored in: {0}".format(output_file)) # If we're being clean, return without saving an image. if not clean: image_writer(output_file + ".nii.gz", image_loaded, output_data)
# VISUALIZATION # T1 weighted image for background anatDir = os.path.join(dataDir,'derivatives_selected/fmriprep/sub-09/anat/') imageT1 = os.path.join(anatDir, 'sub-09_space-MNI152NLin2009cAsym_desc-preproc_T1w.nii.gz') # Thresholded zstat image fThZStat=os.path.join(statsDir,'threshold_file/zstat' + contInd + '_threshold.nii.gz') thImageStat=nib.load(fThZStat) # global maximum cooridnates X_zstat = nib.load(imgZStat).get_data() # loading the zstat image globalMax = np.unravel_index(np.argmax(X_zstat), X_zstat.shape) # voxel space globalMaxMNI = coord_transform(globalMax[0], globalMax[1], globalMax[2], thImageStat.affine) # MNI space # blob overlay at global max plot_stat_map(thImageStat, bg_img=imageT1, colorbar=True, threshold=2.3, black_bg=True, draw_cross=True, cut_coords=globalMaxMNI) # interactive visualization view_img(thImageStat, bg_img=imageT1, cmap='black_red', symmetric_cmap=False, annotate=True, colorbar=True, threshold=2.3, black_bg=True, cut_coords=globalMaxMNI)
def extract_roi_info(statfile, stat_name=None, roi_type='unilateral', per_cluster=True, cluster_engine='scipy', min_clust_size=20, stat_threshold=None, mask_threshold=20, save_indices=True, verbose=True): """ Extracts information per ROI for a given statistics-file. Reads in a thresholded (!) statistics-file (such as a thresholded z- or t-stat from a FSL first-level directory) and calculates for a set of ROIs the number of significant voxels included and its maximum value (+ coordinates). Saves a csv-file in the same directory as the statistics-file. Assumes that the statistics file is in MNI152 2mm space. Parameters ---------- statfile : str Absolute path to statistics-file (nifti) that needs to be evaluated. stat_name : str Name for the contrast/stat-file that is being analyzed. roi_type : str Whether to use unilateral or bilateral masks (thus far, only Harvard- Oxford atlas masks are supported.) per_cluster : bool Whether to evaluate the statistics-file as a whole (per_cluster=False) or per cluster separately (per_cluster=True). cluster_engine : str Which 'engine' to use for clustering; can be 'scipy' (default), using scipy.ndimage.measurements.label, or 'fsl' (using FSL's cluster commmand). min_clust_size : int Minimum cluster size (i.e. clusters with fewer voxels than this number are discarded; also, ROIs containing fewer voxels than this will not be listed on the CSV. stat_threshold : int or float If the stat-file contains uncorrected data, stat_threshold can be used to set a lower bound. mask_threshold : bool Threshold for probabilistics masks, such as the Harvard-Oxford masks. Default of 25 is chosen as this minimizes overlap between adjacent masks while still covering most of the entire brain. save_indices : bool Whether to save the indices (coordinates) of peaks of clusters. verbose : bool Whether to print some output regarding the parsing process. Returns ------- df : Dataframe Dataframe corresponding to the written csv-file. """ if isinstance(statfile, str): data = nib.load(statfile).get_data() else: data = statfile sign_mask = np.ones(shape=data.shape) sign_mask[data < 0] = -1 mni_affine = load_mni152_template().affine if stat_threshold: data[data < stat_threshold] = 0 if roi_type == 'unilateral': cort_rois = fetch_atlas_harvard_oxford('cort-maxprob-thr0-2mm', symmetric_split=True) subc_rois = fetch_atlas_harvard_oxford('sub-maxprob-thr0-2mm', symmetric_split=True) else: cort_rois = fetch_atlas_harvard_oxford('cort-maxprob-thr0-2mm', symmetric_split=True) subc_rois = fetch_atlas_harvard_oxford('sub-maxprob-thr0-2mm', symmetric_split=True) cort_rois['idx'] = np.arange(len(cort_rois['labels'])) subc_rois['idx'] = np.arange(len(subc_rois['labels'])) df_list = [] # Start clustering of data clustered, _ = label(data > 0) values, counts = np.unique(clustered.ravel(), return_counts=True) n_clust = np.argmax(np.sort(counts)[::-1] < min_clust_size) if n_clust == 0: print('No (sufficiently large) clusters!') return 0 # Sort and trim cluster_nrs = values[counts.argsort()[::-1][:n_clust]] cluster_nrs = np.delete(cluster_nrs, 0) print('Analyzing %i clusters for %s' % (len(cluster_nrs), stat_name)) cluster_list = [] # Looping over clusters for i, cluster_id in enumerate(cluster_nrs): if verbose: print('Processing cluster %i' % (i + 1)) cl_mask = clustered == cluster_id k = cl_mask.sum() mx = data[cl_mask].max() tmp = np.zeros(data.shape) tmp[cl_mask] = data[cl_mask] == mx # in case of multiple voxels with same stat / weight if np.sum(tmp == 1) > 1: X, Y, Z = [coord[0] for coord in np.where(tmp == 1)] else: X, Y, Z = np.where(tmp == 1) # if weight / stat is negative, change sign of mx if sign_mask[X, Y, Z] < 0: mx = -mx # convert to MNI-coordinates X, Y, Z = coord_transform(X, Y, Z, mni_affine) # This is purely for formatting issues if i == 0: c = stat_name else: c = '' to_append = { 'Contrast': c, 'cluster': (i + 1), 'k cluster': str(k), 'max cluster': mx, 'x': str(X[0]), 'y': str(Y[0]), 'z': str(Z[0]), 'Region': '', 'k region': '', 'max region': '' } cluster_list.append(to_append) roi_list = [] # Loop over ROIs for i_atlas, atlas in enumerate([cort_rois, subc_rois]): maps = atlas['maps'].get_data() for ii_roi, mask_name in enumerate(atlas['labels']): roi_mask = maps == atlas['idx'][ii_roi] overlap = (cl_mask.astype(int) + roi_mask.astype(int)) == 2 k = overlap.sum() if k > 0: mx = data[overlap].max() tmp = np.zeros(data.shape) tmp[overlap] = data[overlap] == mx # in case of multiple voxels with same stat / weight if np.sum(tmp == 1) > 1: X, Y, Z = [coord[0] for coord in np.where(tmp == 1)] else: X, Y, Z = np.where(tmp == 1) # if sign of weight / stat is negative, change sign of mx if sign_mask[X, Y, Z] < 0: mx = -mx # convert to MNI-coordinates X, Y, Z = coord_transform(X, Y, Z, mni_affine) else: # If no voxels, write some default values mx = 0 X, Y, Z = 0, 0, 0 if ii_roi == 0: cluster_list[-1]['Region'] = mask_name cluster_list[-1]['k region'] = k cluster_list[-1]['max region'] = mx else: to_append = { 'Contrast': '', 'cluster': (i + 1 + 0.1), 'k cluster': '', 'max cluster': '', 'x': '', 'y': '', 'z': '', 'Region': mask_name, 'k region': k, 'max region': mx } if k > cluster_list[-1]['k region']: to_append['Region'] = cluster_list[-1]['Region'] to_append['k region'] = cluster_list[-1]['k region'] maxr = cluster_list[-1]['max region'] to_append['max region'] = maxr cluster_list[-1]['Region'] = mask_name cluster_list[-1]['k region'] = k cluster_list[-1]['max region'] = mx roi_list.append(to_append) roi_dfs = pd.concat([pd.DataFrame(tmp, [0]) for tmp in roi_list]) roi_dfs = roi_dfs[roi_dfs['k region'] > min_clust_size] roi_dfs = roi_dfs.sort_values(by=['cluster', 'k region'], ascending=[True, False]) whiteline = { key: '' if key != 'cluster' else (i + 1 + 0.1) for key in roi_dfs.keys() } roi_dfs = roi_dfs.append(whiteline, ignore_index=True) df_list.append(pd.DataFrame(cluster_list[-1], [0])) df_list.append(roi_dfs) # Concatenate dataframes! df = pd.concat(df_list) # Some cleaning / processing cols_ordered = [ 'Contrast', 'cluster', 'k cluster', 'max cluster', 'x', 'y', 'z', 'Region', 'k region', 'max region' ] df = df[cols_ordered] df['cluster'] = [ '' if (val % 1) != 0 else str(val) for val in df['cluster'] ] filename = op.join(op.dirname(statfile), 'roi_info_%s.csv' % stat_name) df.to_csv(filename, index=False, header=True, sep='\t') return df
def cast(x, y, z, affine, trA): coord = coord_transform(x, y, z, affine) return np.dot(trA, [coord[0], coord[1], coord[2], 1])
_, fname, _ = split_filename(self.inputs.image_file) gii_file = op.abspath(op.join(runtime.cwd, fname + ".gii")) # Load the largest connected component of the input image img = nimg.largest_connected_component_img(self.inputs.image_file) # TODO: check if the input image is correct (binary) # Run the marching cube algorithm verts, faces, normals, values = sm.marching_cubes_lewiner( img.get_data(), self.inputs.level) >>>>>>> 02924efd7fd9ff93e10f0e0030a43b878812e288 # Convert vertices coordinates to image space # TODO: check that is correct by plotting the mesh on the image x, y, z = nimg.coord_transform( verts[:, 0], verts[:, 1], verts[:, 2], img.affine) mm_verts = np.array([x, y, z]).T # Save the mesh as Gifti # FIXME: FreeView can not open the mesh (but anatomist do) gii = ng.GiftiImage(darrays=[ ng.GiftiDataArray(mm_verts, intent='NIFTI_INTENT_POINTSET'), ng.GiftiDataArray(faces, intent='NIFTI_INTENT_TRIANGLE')]) gii.meta = ng.GiftiMetaData().from_dict({ <<<<<<< HEAD "volume_file": self.input_spec.image_file, "marching_cube_level": self.input_spec.level, "smoothing_iterations": self.input_spec.smoothing_iter, "smoothing_dt": self.input_spec.smoothing_dt ======= "volume_file": self.inputs.image_file,
def index_to_xy_coord(x, y, z=10): '''Transforms data index to coordinates of the background + offset''' coords = coord_transform(x, y, z, affine=thresholded_score_map_img.affine) return np.array(coords)[np.newaxis, :] + np.array([0, 1, 0])
def create_screenshot( slice, t1_img, seg_img, affine, view, cmap, output_dir, w=256, h=256, dpi=1024, alpha=0.15, dim=-1, quality=95, optimize=True, ): with Xvfb() as xvfb: # TODO: Moved this config here for now, maybe move out of the function later? view_info = { "coronal": ("y", False, True), "axial": ("z", False, True), "sagittal": ("x", False, True), } mode, reverse, flip = view_info[view] output_dir = Path(f"{output_dir}/{view}") output_dir.mkdir(parents=True, exist_ok=True) image_buffer = BytesIO() fig = plt.figure(figsize=(w, h), dpi=dpi) # TODO- cleanup, may be a better way but not bothering for now... # coords are (y, z, x) if view == "sagittal": val = image.coord_transform(128, 128, slice, affine)[0] elif view == "coronal": val = image.coord_transform(slice, 128, 128, affine)[1] elif view == "axial": val = image.coord_transform(128, slice, 128, affine)[2] else: raise ValueError(f"view {view} not supported") if reverse: flip_list = range(256, 0, -1) output_file = str(output_dir / f"{view}_{flip_list[slice]:03d}.jpg" ) # reverse ordering of numberings else: output_file = str(output_dir / f"{view}_{slice+1:03d}.jpg") nilearn.plotting.plot_anat( seg_img, bg_img=t1_img, display_mode=mode, cut_coords=[(val)], annotate=False, black_bg=True, figure=fig, output_file=image_buffer, cmap=cmap, vmin=100, vmax=115, alpha=alpha, dim=dim, ) image_buffer.seek(0) img = Image.open(image_buffer) if flip: out = img.transpose(Image.FLIP_LEFT_RIGHT).convert("RGB") else: out = img.convert("RGB") out.save(output_file, quality=quality, optimize=optimize) image_buffer.close() plt.close("all")
import matplotlib.pyplot as plt from nilearn.plotting import plot_stat_map, view_img from nilearn.image import math_img, coord_transform # Directory and file business dataDir = '/tmp/Data/ds114' # Data directory anatDir = os.path.join(dataDir,'derivatives_selected/fmriprep/sub-09/anat/') imageT1 = os.path.join(anatDir, 'sub-09_space-MNI152NLin2009cAsym_desc-preproc_T1w.nii.gz') featDir = os.path.join(dataDir, 'WorkflowOutput/feat_dir/run0.feat/') statDir = os.path.join(featDir,'stats') imageStat = os.path.join(statDir, 'zstat6.nii.gz') # thresholding the stat image to positive values only for visualization thImageStat = math_img("np.ma.masked_less(img, 0)", img=imageStat) # stat map at the maximum plot_stat_map(thImageStat, bg_img=imageT1, colorbar=True, threshold=2.3, black_bg=True, draw_cross=True, cut_coords=coord_transform(23,27,36,thImageStat.affine)) # interactive visualization view_img(thImageStat, bg_img=imageT1, cmap='black_red', symmetric_cmap=False, annotate=True, colorbar=True, threshold=2.3, black_bg=True, cut_coords=coord_transform(23,27,36,thImageStat.affine))
def vol_ba(): cor_vols = list( filter(None, os.popen('find processed_data -name bold_mcf_brainCorNorm_*.nii.gz').read().split('\n'))) # MNI to TAL Transform affine = np.zeros((4, 4)) affine[0] = [0.88, 0, 0, -0.8] affine[1] = [0, 0.97, 0, -3.32] affine[2] = [0, 0.05, 0.88, -0.44] affine[3] = [0, 0, 0, 1] with open('/gpfs/hpchome/gagand87/project/tal_to_BA.pkl', 'rb') as handle: tal_to_ba = pickle.load(handle) for ind, vol in enumerate(cor_vols): ba_intensity = {'Amygdala': [], 'Anterior Commissure': [], 'Anterior Nucleus': [], 'Background': [], 'Brodmann area 1': [], 'Brodmann area 10': [], 'Brodmann area 11': [], 'Brodmann area 13': [], 'Brodmann area 17': [], 'Brodmann area 18': [], 'Brodmann area 19': [], 'Brodmann area 2': [], 'Brodmann area 20': [], 'Brodmann area 21': [], 'Brodmann area 22': [], 'Brodmann area 23': [], 'Brodmann area 24': [], 'Brodmann area 25': [], 'Brodmann area 27': [], 'Brodmann area 28': [], 'Brodmann area 29': [], 'Brodmann area 3': [], 'Brodmann area 30': [], 'Brodmann area 31': [], 'Brodmann area 32': [], 'Brodmann area 33': [], 'Brodmann area 34': [], 'Brodmann area 35': [], 'Brodmann area 36': [], 'Brodmann area 37': [], 'Brodmann area 38': [], 'Brodmann area 39': [], 'Brodmann area 4': [], 'Brodmann area 40': [], 'Brodmann area 41': [], 'Brodmann area 42': [], 'Brodmann area 43': [], 'Brodmann area 44': [], 'Brodmann area 45': [], 'Brodmann area 46': [], 'Brodmann area 47': [], 'Brodmann area 5': [], 'Brodmann area 6': [], 'Brodmann area 7': [], 'Brodmann area 8': [], 'Brodmann area 9': [], 'Caudate Body': [], 'Caudate Head': [], 'Caudate Tail': [], 'Corpus Callosum': [], 'Dentate': [], 'Hippocampus': [], 'Hypothalamus': [], 'Lateral Dorsal Nucleus': [], 'Lateral Geniculum Body': [], 'Lateral Globus Pallidus': [], 'Lateral Posterior Nucleus': [], 'Mammillary Body': [], 'Medial Dorsal Nucleus': [], 'Medial Geniculum Body': [], 'Medial Globus Pallidus': [], 'Midline Nucleus': [], 'Optic Tract': [], 'Pulvinar': [], 'Putamen': [], 'Red Nucleus': [], 'Substania Nigra': [], 'Subthalamic Nucleus': [], 'Ventral Anterior Nucleus': [], 'Ventral Lateral Nucleus': [], 'Ventral Posterior Lateral Nucleus': [], 'Ventral Posterior Medial Nucleus': []} vol_name_parts = vol.split('/') BA_intensity_file_name = vol_name_parts[0] + '/' + vol_name_parts[1] + '/' + vol_name_parts[ 2] + '/' + 'BA_Intensity/bold_mcf_brain_BA_Intensity' + str(ind) + '.pkl' fmri = image.load_img(vol) fmri_data = fmri.get_data() for i in range(fmri.shape[0]): for j in range(fmri.shape[1]): for k in range(fmri.shape[2]): mni_coord = coord_transform(i, j, k, fmri.affine) tal_coord = coord_transform(mni_coord[0], mni_coord[1], mni_coord[2], affine) # Since tal coordinates range is (-70.0, -102.0, -42.0) to (70.0, 69.0, 67.0), we ignore the rest of the coordinates if -70 <= tal_coord[0] <= 70 and -102 <= tal_coord[1] <= 69 and -42 <= tal_coord[2] <= 67: t_coord = [0] * 3 t_coord[0] = round(tal_coord[0]) t_coord[1] = round(tal_coord[1]) t_coord[2] = round(tal_coord[2]) ba_intensity[tal_to_ba[(t_coord[0], t_coord[1], t_coord[2])]].append(fmri_data[i, j, k]) with bz2.BZ2File(BA_intensity_file_name, 'wb') as handle: pickle.dump(ba_intensity, handle)