nilearn / nilearn

Machine learning for NeuroImaging in Python
http://nilearn.github.io
Other
1.17k stars 598 forks source link

Plot Sub Regions for given Atlas #3357

Closed DasDominus closed 2 years ago

DasDominus commented 2 years ago

I have atlas and clustering results like KMeans, Ward etc I have performed custom community detection algorithms on the clustering result and would like to plot the results of the subnetworks of the regions that I have identified.

HOWEVER, I find it currently impossible to plot such graphs.

For example, my K means have 50 regions, labes 0-49 ad I would like to plot say label [31, 34, 37] give the clustering results kmeans.gz but this is currently not supported

bthirion commented 2 years ago

Hi, I think that the discussion should take place on Neurostars, not in the issue page, because you're discussing a usage question. Could you open a discussion there to clarify the kind of plots that you would like to have ? Best, Bertrand

DasDominus commented 2 years ago

Thanks @bthirion !

Not exactly. In my thinking, the plotting.plot_roi should take a field like selected_labels/regions and only plot rois that falls into such filter.

Right now how I do this is given the roi labels, I manually extract the selected regions into a sub mask Nifti1Image and write out. Then plot using plot_roi.

This function feels like it should be a built-in

jeromedockes commented 2 years ago

Thanks for the suggestion! To make the discussion more concrete could you maybe provide a short script showing what you are currently doing (using one of the atlases provided in nilearn.datasets), and describe what would be the additional option to plot_roi and its meaning?

My first impression is that this is both quite specific and very easy to do with the approach you outline (building an image with only the selected regions before passing it to plot_roi), and therefore probably not worth cluttering the signature of plot_roi which already has many parameters.

jeromedockes commented 2 years ago

Hello @DasDominus do you plan to provide an example showing why it would be worth adding that option to plot_roi? my impression is we might not change plot_roi but it might be possible to update one of the examples to show how plotting sub-atlases can be done if that turns out to be a frequent use case.

Let us know what you think! if there is no more discussion I will close this issue in a week or so

GMerakis commented 2 years ago

oh hi! sorry I got occupied by other tasks. Yes! I can provide an example : D

from typing import Any, List
def PlotMultiRois(roi_dir: str, rois: List[str], background_image: Any, master_atlas: Any,
                  output_dir: str):
  # 3D atlas image
  atlas = nb.load(master_atlas)
  atlas_data = atlas.get_fdata()
  atlas_shape = atlas_data.shape

  mask = np.zeros(atlas_shape)
  for roi in os.listdir(roi_dir):
    roi_idx = roi.lstrip('roi_').rstrip('.nii')
    if roi_idx in rois:
      # Set value to 1
      roi_mask = nb.load(os.path.join(roi_dir, roi))
      roi_data = roi_mask.get_fdata()
      mask[np.where(roi_data==1)]=int(roi_idx)

  mask_img = nb.Nifti1Image(mask, atlas.affine)
  nb.save(mask_img, os.path.join(output_dir, 'roi_'+'_'.join(rois)+'.nii'))

  print(f'Saved ROIS: {rois}')
  return os.path.join(output_dir, 'roi_'+'_'.join(rois)+'.nii')

I actually have the original atlas split into different atlases. This is not required I believe, tho I used this step to view individual regions.

def DecompoesAtlasToRois(atlas_iamge, out_directory):
    # Init output dir
    if not file_utils.InitDir(out_directory):
        raise ValueError("Failed to Initialize Directory: {}".format(out_directory))

    # 3D atlas image
    atlas = nb.load(atlas_iamge)
    atlas_data = atlas.get_fdata()
    atlas_shape = atlas_data.shape

    num_regions = int(np.max(atlas_data))
    print ('Number of regions: {}'.format(num_regions))

    for idx in range(1, num_regions + 1):
        mask = np.zeros(atlas_shape)
        mask[np.where(atlas_data==idx)] = 1
        mask_img = nb.Nifti1Image(mask, atlas.affine)
        nb.save(mask_img, os.path.join(out_directory, 'roi_{}.nii'.format(idx)))
    print ('Rois Exported.')
jeromedockes commented 2 years ago

Thanks. IIUC you want to plot only a subset of the atlas regions. You don't need to create separate images for each region then put them back together nor to write image files. To create a new atlas image containing the selected regions and putting all other regions in the background you can for example use np.where(np.isin(atlas_data, selected_region_indices), atlas_data, 0) -- see full example below.

plot_roi simply plots the atlas we give it; if what we really want is in fact to plot a different atlas (the one containing fewer regions) it seems rather natural to first create the atlas we actually want and then pass it to the plotting function. So I would lean towards not adding the "selected_regions" parameter you suggest for filtering regions to plot_roi.

Please let me know if I misunderstood what you are trying to do!

import numpy as np
from nilearn import plotting, datasets, image

atlas_info = datasets.fetch_atlas_harvard_oxford("cort-maxprob-thr25-2mm")

selected_region_names = ["Insular Cortex", "Temporal Pole", "Frontal Pole"]
selected_region_indices = np.in1d(
    atlas_info["labels"], selected_region_names
).nonzero()

atlas_img = image.load_img(atlas_info["maps"])
atlas_data = image.get_data(atlas_img)
atlas_subset_data = np.where(
    np.isin(atlas_data, selected_region_indices), atlas_data, 0
)
atlas_subset_img = image.new_img_like(atlas_img, atlas_subset_data)

plotting.plot_roi(
    atlas_subset_img, display_mode="z", cut_coords=10, cmap="tab10"
)
plotting.show()

rois

jeromedockes commented 2 years ago

I believe the example above shows how to do this relatively easily and we want to keep the plot_roi signature simple so I will close this issue