I try to create a 10mm sphere ROI mask around a given coordinate,
I thought the nilearn.maskers.NiftiSpheresMasker() function would work,
but it seems more like to extract the signal from the ROI, instead of creating a binary mask.
Or I just blind myself somewhere…
I firstly defined a coordinate from the peak of a first-level contrast t-map,
and I want to use this coordinate to create a 10mm sphere ROI mask,
and apply this mask on another unsmoothed first-level contrast t-map for further classification.
Question:
Should I use the (fMRIprep) preprocessed T1w.nii.gz image to create the mask,
or I can directly use the t-map of the subject?
Is NiftiSpheresMasker() the right function for my case? If so, dose the script below make sense?
I tried to create the mask by NiftiSpheresMasker() and mask the t-map as below:
TypeError: ("Input images cannot be compared, you provided 'dict_values([<nibabel.nifti1.Nifti1Image object at 0x000001E32DF41050>, array([[196.29353]], dtype=float32)])',", 'Data given cannot be loaded because it is not compatible with nibabel format:\n196.29353')
Probably easier to use the BOLD image (or derivatives of BOLD images like a statmap) so you don’t have to worry about resampling if voxel sizes differ between anatomical and BOLD.
That function is most helpful for extracting signals within a spherical mask. I do not know the full nature of your analysis, so hard to say one way or the other.
If you want to create a binary mask image object, here is a ChatGPT-generated script.
Here is a ChatGPT generated script.
import numpy as np
import nibabel as nib
def create_sphere_mask(coordinate, radius, voxel_size=1, affine=None, output_filename=None):
# Generate a 3D grid of coordinates
x_range = np.arange(-radius, radius + voxel_size, voxel_size)
y_range = np.arange(-radius, radius + voxel_size, voxel_size)
z_range = np.arange(-radius, radius + voxel_size, voxel_size)
xx, yy, zz = np.meshgrid(x_range, y_range, z_range)
# Compute the distance of each voxel to the center
distances = np.sqrt((xx - coordinate[0])**2 + (yy - coordinate[1])**2 + (zz - coordinate[2])**2)
# Create a binary mask where voxels inside the sphere are 1 and outside are 0
binary_mask = np.where(distances <= radius, 1, 0)
# If affine is not provided, use identity matrix
if affine is None:
affine = np.eye(4) # Identity affine matrix for MNI space
# Convert the binary mask to a Nifti image
binary_nifti = nib.Nifti1Image(binary_mask, affine)
# Save the binary mask to a file if filename is provided
if output_filename:
nib.save(binary_nifti, output_filename)
return binary_nifti
# Example usage:
coordinate = (0, 0, 0) # Example coordinate
radius = 10 # Example radius in mm
voxel_size = 1 # Example voxel size in mm
affine = np.eye(4) # Example affine matrix for MNI space
output_filename = 'sphere_mask.nii.gz' # Example output filename
binary_nifti = create_sphere_mask(coordinate, radius, voxel_size, affine, output_filename)
OK so I am adapting things from @emdupre in this post.
from nilearn import datasets, plotting
from nilearn.masking import _unmask_3d
from nilearn.maskers import nifti_spheres_masker
import nibabel as nib
from nibabel import Nifti1Image
# let's assume we are in MNI space
brain_mask = datasets.load_mni152_brain_mask()
_, A = nifti_spheres_masker._apply_mask_and_get_affinity(
seeds=[(-42, -36, 16)],
niimg=None,
radius=10,
allow_overlap=False,
mask_img=brain_mask)
sphere_mask = _unmask_3d(
X=A.toarray().flatten(),
mask=brain_mask.get_fdata().astype(bool))
sphere_mask = Nifti1Image(sphere_mask, brain_mask.affine)
nib.save(sphere_mask, "sphere.nii.gz")
# plot the result to make sure it makes sense
plotting.plot_roi("sphere.nii.gz")
plotting.show()
Yes, very grateful for people who are willing to put time and efforts in this community!
I have a follow-up question:
Since the mask is based on .astype(bool), if I feed a stat image, the sphere ROIs vary a lot between subject based on the activation. To include all the voxels in the sphere ROI, I tried to use MNI template to get the mask, and make it binary before resampling it with the stat t-map, so that I can apply the mask later.
Does this make sense?