Plot_stat_map - plot only clusters

Hi,
I plotted the following stat map using plot_stat_map.


Now, I want to threshold based on clusters.
Meaning, instead of plot value per voxel, to plot per cluster of let’s say 9 voxels, and plot based on the value of the cluster.
Is that possible?
Thanks

Hi @slab15

I’m not sure I fully understood what you want to do, but I think the function nilearn.reporting.get_clusters_table might be what you’re looking for:

https://nilearn.github.io/modules/generated/nilearn.reporting.get_clusters_table.html#nilearn.reporting.get_clusters_table

Here is an example from the gallery where it is used:

https://nilearn.github.io/auto_examples/04_glm_first_level/plot_predictions_residuals.html#sphx-glr-auto-examples-04-glm-first-level-plot-predictions-residuals-py

Hope that helps!

Nicolas

@NicolasGensollen Sorry, I will explain better:
I followed this tutorial:
https://nilearn.github.io/auto_examples/02_decoding/plot_miyawaki_encoding.html
On my data, and now I finished with section 9.3.15.3, but as you can see in the attached image - it shows each voxel seperetely and the voxels are very scatterd.
I want to plot only clusters with n neighbours voxels that are aboverthe threshold.
So, if in my case the threshold for a voxel to be shown is 0.55, and I choose n=9 , is might show only the clusters circled in red here:


Is that possible?

@slab15 below is a potential solution I came up with (assuming I understood what you want to do correctly…).
Note that there might be better ways to do this.

Let me know if this is of any help!
Nicolas

import numpy as np
from functools import reduce
from scipy.ndimage import label
from nibabel import Nifti1Image
from nilearn.image import binarize_img, get_data, new_img_like

# Creates a fake input image
# This would be equivalent to your
# thresholded stat map. I'm using
# a 2D image here because it's easier
# to see the clusters...
#
data = np.array([[0., 0., 0., 0.,  0.],
                 [1., 0., 0., 1.1, 0.],
                 [0., 0., 0., 0.,  2.],
                 [1., 0., 0., 1.2, 0.],
                 [2., 3., 0., 0.,  0.]])
input_niimg = Nifti1Image(data, affine=np.eye(4))

def get_clusters_larger_than(niimg, min_size):
    """Returns a Nifti1Image where only clusters of
    voxels of size larger than min_size were kept.
    """
    data = get_data(binarize_img(niimg))
    # structure defines the kind of connectivity
    # that you wish to use
    objs,n = label(data,
                   structure=np.ones(tuple([3] * len(data.shape)),
                                     dtype=int))
    cluster_sizes = np.bincount(objs.flatten())
    labels_above_threshold = np.argwhere(cluster_sizes > min_size).flatten()[1:]
    mask = reduce(np.logical_or, [objs == l for l in labels_above_threshold])
    input_data = get_data(niimg)
    input_data[~mask] = 0
    return new_img_like(niimg, input_data)

output_niimg = get_clusters_larger_than(input_niimg, 2)
get_data(output_niimg)
array([[0. , 0. , 0. , 0. , 0. ],
       [0. , 0. , 0. , 1.1, 0. ],
       [0. , 0. , 0. , 0. , 2. ],
       [1. , 0. , 0. , 1.2, 0. ],
       [2. , 3. , 0. , 0. , 0. ]])

@NicolasGensollen Thanks, I actually get the error:

/var/folders/39/88clnp910zlg54lrgy0d7qm40000gn/T/ipykernel_8376/1857584352.py in <module>
      3 from scipy.ndimage import label
      4 from nibabel import Nifti1Image
----> 5 from nilearn.image import binarize_img, get_data, new_img_like
      6 
      7 # Creates a fake input image
ImportError: cannot import name 'binarize_img' from 'nilearn.image' 

for Python 3.8 and nilearn 0.8

@slab15 Yes, you’re right, this function was recently added to Nilearn and we haven’t released it yet. It will be available from version 0.8.1.
What you can do in the mean time is either install the dev version of Nilearn (you git clone the repo and run pip install -e .), or you can manually define it in the example above since it is very simple:

from nilearn.image import math_img

def binarize_img(img):
    return math_img(
        "img.astype(bool).astype(int)",
        img=threshold_img(img)
    )

FYI: Here is the code of the binarize_img function: nilearn/image.py at 834d342b6cecdecf8b6f08bc075dd45e36f8e3c2 · nilearn/nilearn · GitHub

Nicolas

It is running now but I still getting even isolated voxels (not clusters) on my data, trying to understand what the problem is… Thanks!

@slab15 any luck solving the problem with the isolated voxels?

@NicolasGensollen Not yet :frowning:

We recently added cluster_threshold and two_sided parameters to nilearn.image.threshold_img. These new parameters aren’t available in a release yet, but can be accessed from the current version on GitHub.

Would cluster-extent thresholding the map prior to plotting it work for you? Something along the following lines:

from nilearn import image, plotting

thresh_img = image.threshold_img(img, cluster_threshold=9, threshold=0.55)
plotting.plot_stat_map(thresh_img)
1 Like

@tsalo Sorry for the late response.
So these two lines you wrote, should plot only clusters of at least 9 (circular?) voxels, each is above 0.55, right?

If saw then yes, this is what I need.
When should the new version should be released?

I’m not sure what you mean by circular there. The voxels should be cuboids, and the clusters are defined based on connected faces, rather than edges or corners. In AFNI parlance that would correspond to NN1 connectivity. Does that answer your question, or did I misunderstand?

The last release was about two weeks ago, so it could be a while (a month or two) before the next one. I’d recommend installing the current, unreleased version on GitHub instead of an official release.