def load_or_generate_components(hemi, out_dir='.', plot_dir=None, force=False, *args, **kwargs): """Load an image and return if it exists, otherwise compute via ICA""" # Only re-run if image doesn't exist. img_path = op.join(out_dir, '%s_ica_components.nii.gz' % hemi) if not force and op.exists(img_path): img = NiftiImageWithTerms.from_filename(img_path) else: img = generate_components(hemi=hemi, out_dir=out_dir, *args, **kwargs) png_dir = op.join(out_dir, 'png') plot_components(img, hemi=hemi, out_dir=png_dir) plot_components_summary(img, hemi=hemi, out_dir=png_dir) return img
def generate_components( images, hemi, term_scores=None, n_components=20, random_state=42, out_dir=None, memory=Memory(cachedir="nilearn_cache"), ): """ images: list Can be nibabel images, can be file paths. """ # Create grey matter mask from mni template target_img = datasets.load_mni152_template() # Reshape & mask images print("%s: Reshaping and masking images; may take time." % hemi) if hemi == "wb": masker = GreyMatterNiftiMasker(target_affine=target_img.affine, target_shape=target_img.shape, memory=memory) else: # R and L maskers masker = HemisphereMasker( target_affine=target_img.affine, target_shape=target_img.shape, memory=memory, hemisphere=hemi ) masker = masker.fit() # Images may fail to be transformed, and are of different shapes, # so we need to trasnform one-by-one and keep track of failures. X = [] # noqa xformable_idx = np.ones((len(images),), dtype=bool) for ii, im in enumerate(images): img = cast_img(im, dtype=np.float32) img = clean_img(img) try: X.append(masker.transform(img)) except Exception as e: print("Failed to mask/reshape image %d/%s: %s" % (im.get("collection_id", 0), op.basename(im), e)) xformable_idx[ii] = False # Now reshape list into 2D matrix X = np.vstack(X) # noqa # Run ICA and map components to terms print("%s: Running ICA; may take time..." % hemi) fast_ica = FastICA(n_components=n_components, random_state=random_state) fast_ica = memory.cache(fast_ica.fit)(X.T) ica_maps = memory.cache(fast_ica.transform)(X.T).T # Tomoki's suggestion to normalize components_ # X ~ ica_maps * fast_ica.components_ # = (ica_maps * f) * (fast_ica.components_ / f) # = new_ica_map * new_components_ C = fast_ica.components_ factor = np.sqrt(np.multiply(C, C).sum(axis=1, keepdims=True)) # (n_components x 1) ica_maps = np.multiply(ica_maps, factor) fast_ica.components_ = np.multiply(C, 1.0 / (factor + 1e-12)) if term_scores is not None: terms = term_scores.keys() term_matrix = np.asarray(term_scores.values()) term_matrix[term_matrix < 0] = 0 term_matrix = term_matrix[:, xformable_idx] # terms x images # Don't use the transform method as it centers the data ica_terms = np.dot(term_matrix, fast_ica.components_.T).T # 2015/12/26 - sign matters for comparison, so don't do this! # 2016/02/01 - sign flipping is ok for R-L comparison, but RL concat # may break this. # Pretty up the results for idx, ic in enumerate(ica_maps): if -ic.min() > ic.max(): # Flip the map's sign for prettiness ica_maps[idx] = -ic if term_scores: ica_terms[idx] = -ica_terms[idx] # Create image from maps, save terms to the image directly ica_image = NiftiImageWithTerms.from_image(masker.inverse_transform(ica_maps)) if term_scores: ica_image.terms = dict(zip(terms, ica_terms.T)) # Write to disk if out_dir is not None: out_path = op.join(out_dir, "%s_ica_components.nii.gz" % hemi) if not op.exists(op.dirname(out_path)): os.makedirs(op.dirname(out_path)) ica_image.to_filename(out_path) return ica_image
def image_analyses(components, dataset, memory=Memory(cachedir='nilearn_cache'), **kwargs): """ 1) Plot sparsity of ICA images for wb, R, and L. 2) Plot Hemispheric Participation Index (HPI) for wb ICA images """ out_dir = op.join('ica_imgs', dataset) images_key = ["R", "L", "wb"] sparsity_levels = ['pos_005', 'neg_005', 'abs_005'] # For calculating hemispheric participation index (HPI) from wb components, # prepare hemisphere maskers hemi_maskers = [HemisphereMasker(hemisphere=hemi, memory=memory).fit() for hemi in ['R', 'L']] # Store sparsity (and hpi for wb) vals in a DF columns = ["n_comp"] + sparsity_levels wb_columns = columns + ["pos_hpi", "neg_hpi"] hemi_dfs = {hemi: pd.DataFrame(columns=wb_columns if hemi == "wb" else columns) for hemi in images_key} # Loop over components for c in components: print("Simply loading component images for n_component = %s" % c) nii_dir = op.join('ica_nii', dataset, str(c)) for hemi in images_key: img_path = op.join(nii_dir, '%s_ica_components.nii.gz' % (hemi)) img = NiftiImageWithTerms.from_filename(img_path) data = pd.DataFrame({"n_comp": [c] * c}, columns=columns) # get mean sparsity for the ica iamge and store in sparsity dict for s in sparsity_levels: thresh = float('0.%s' % (re.findall('\d+', s)[0])) # sparsity is # of voxels above the given sparsity level for each component if 'pos' in s: data[s] = (img.get_data() > thresh).sum(axis=0).sum(axis=0).sum(axis=0) elif 'neg' in s: data[s] = (img.get_data() < -thresh).sum(axis=0).sum(axis=0).sum(axis=0) elif 'abs' in s: data[s] = (abs(img.get_data()) > thresh).sum(axis=0).sum(axis=0).sum(axis=0) # get hpi values for wb components if hemi == "wb": hemi_vectors = [masker.transform(img) for masker in hemi_maskers] # transform back so that values for each component can be calculated hemi_imgs = [masker.inverse_transform(vec) for masker, vec in zip(hemi_maskers, hemi_vectors)] # pos/neg_vals[0] = # voxels in R, pos/neg_vals[1] = # voxels in L pos_vals = [(hemi_img.get_data() > 0.005).sum(axis=0).sum(axis=0).sum(axis=0) for hemi_img in hemi_imgs] neg_vals = [(hemi_img.get_data() < -0.005).sum(axis=0).sum(axis=0).sum(axis=0) for hemi_img in hemi_imgs] for sign, val in zip(['pos', 'neg'], [pos_vals, neg_vals]): with np.errstate(divide="ignore", invalid="ignore"): # pos/neg HPI vals, calculated as (R-L)/(R+L) for num. of voxels above # the given threshold hpi = (val[0].astype(float) - val[1]) / (val[0] + val[1]) data["%s_hpi" % (sign)] = hpi hemi_dfs[hemi] = hemi_dfs[hemi].append(data) # Now plot: # 1) Sparsity for wb, R and L ICA images fh, axes = plt.subplots(1, 3, sharex=True, sharey=True, figsize=(18, 6)) sparsity_styles = {'pos_005': ['b', 'lightblue'], 'neg_005': ['r', 'lightpink'], 'abs_005': ['g', 'lightgreen']} for ax, hemi in zip(axes, images_key): df = hemi_dfs[hemi] by_comp = df.groupby('n_comp') for s in sparsity_levels: mean, sd = by_comp.mean()[s], by_comp.std()[s] ax.fill_between(components, mean + sd, mean - sd, linewidth=0, facecolor=sparsity_styles[s][1], alpha=0.5) ax.plot(components, mean, color=sparsity_styles[s][0], label=s) # Overlay individual points for absolute threshold ax.scatter(df.n_comp, df.abs_005, c=sparsity_styles['abs_005'][0]) ax.set_title("Sparsity of the %s components" % (hemi)) ax.set_xlim(xmin=components[0] - 1, xmax=components[-1] + 1) ax.set_xticks(components) plt.legend() fh.text(0.5, 0.04, "# of components", ha="center") fh.text(0.04, 0.5, "# of voxels above the threshold", va='center', rotation='vertical') out_path = op.join(out_dir, 'sparsity.png') save_and_close(out_path, fh=fh) # 2) HPI plot for wb components fh, axes = plt.subplots(1, 2, sharex=True, sharey=True, figsize=(12, 6)) fh.suptitle("Hemispheric Participation Index for each component", fontsize=16) hpi_styles = {'pos': ['b', 'lightblue', 'above 0.005'], 'neg': ['r', 'lightpink', 'below -0.005']} df = hemi_dfs["wb"] by_comp = df.groupby("n_comp") for ax, sign in zip(axes, ['pos', 'neg']): mean, sd = by_comp.mean()["%s_hpi" % sign], by_comp.std()["%s_hpi" % sign] ax.fill_between(components, mean + sd, mean - sd, linewidth=0, facecolor=hpi_styles[sign][1], alpha=0.5) size = df['%s_005' % (sign)] ax.scatter(df.n_comp, df["%s_hpi" % sign], label=sign, c=hpi_styles[sign][0], s=size / 20) ax.plot(components, mean, c=hpi_styles[sign][0]) ax.set_title("%s" % (sign)) ax.set_xlim((0, components[-1] + 5)) ax.set_ylim((-1, 1)) ax.set_xticks(components) ax.set_ylabel("HPI((R-L)/(R+L) for # of voxels %s" % (hpi_styles[sign][2])) fh.text(0.5, 0.04, "# of components", ha="center") out_path = op.join(out_dir, 'wb_HPI.png') save_and_close(out_path, fh=fh) # Save sparsity and HPI vals for all the components for hemi in images_key: hemi_dfs[hemi].index.name = "idx" hemi_dfs[hemi].to_csv(op.join(out_dir, "%s_summary.csv" % (hemi)))