from CPAC.pipeline import nipype_pipeline_engine as pe
import nipype.interfaces.fsl as fsl
import nipype.interfaces.utility as util
[docs]def easy_thresh(wf_name):
"""
Workflow for carrying out cluster-based thresholding
and colour activation overlaying
Parameters
----------
wf_name : string
Workflow name
Returns
-------
easy_thresh : object
Easy thresh workflow object
Notes
-----
`Source <https://github.com/FCP-INDI/C-PAC/blob/v1.8.1/CPAC/easy_thresh/easy_thresh.py>`_
Workflow Inputs::
inputspec.z_stats : string (nifti file)
z_score stats output for t or f contrast from flameo
inputspec.merge_mask : string (nifti file)
mask generated from 4D Merged derivative file
inputspec.z_threshold : float
Z Statistic threshold value for cluster thresholding. It is used to
determine what level of activation would be statistically significant.
Increasing this will result in higher estimates of required effect.
inputspec.p_threshold : float
Probability threshold for cluster thresholding.
inputspec.paramerters : string (tuple)
tuple containing which MNI and FSLDIR path information
Workflow Outputs::
outputspec.cluster_threshold : string (nifti files)
the thresholded Z statistic image for each t contrast
outputspec.cluster_index : string (nifti files)
image of clusters for each t contrast; the values
in the clusters are the index numbers as used
in the cluster list.
outputspec.overlay_threshold : string (nifti files)
3D color rendered stats overlay image for t contrast
After reloading this image, use the Statistics Color
Rendering GUI to reload the color look-up-table
outputspec.overlay_rendered_image : string (nifti files)
2D color rendered stats overlay picture for each t contrast
outputspec.cluster_localmax_txt : string (text files)
local maxima text file, defines the coordinates of maximum value
in the cluster
Order of commands:
- Estimate smoothness of the image::
smoothest --mask= merge_mask.nii.gz --zstat=.../flameo/stats/zstat1.nii.gz
arguments
--mask : brain mask volume
--zstat : filename of zstat/zfstat image
- Create mask. For details see `fslmaths <http://www.fmrib.ox.ac.uk/fslcourse/lectures/practicals/intro/index.htm#fslutils>`_::
fslmaths ../flameo/stats/zstat1.nii.gz
-mas merge_mask.nii.gz
zstat1_mask.nii.gz
arguments
-mas : use (following image>0) to mask current image
- Copy Geometry image dimensions, voxel dimensions, voxel dimensions units string, image orientation/origin or qform/sform info) from one image to another::
fslcpgeom MNI152_T1_2mm_brain.nii.gz zstat1_mask.nii.gz
- Cluster based thresholding. For details see `FEAT <http://www.fmrib.ox.ac.uk/fsl/feat5/detail.html#poststats>`_::
cluster --dlh = 0.0023683100
--in = zstat1_mask.nii.gz
--oindex = zstat1_cluster_index.nii.gz
--olmax = zstat1_cluster_localmax.txt
--othresh = zstat1_cluster_threshold.nii.gz
--pthresh = 0.0500000000
--thresh = 2.3000000000
--volume = 197071
arguments
--in : filename of input volume
--dlh : smoothness estimate = sqrt(det(Lambda))
--oindex : filename for output of cluster index
--othresh : filename for output of thresholded image
--olmax : filename for output of local maxima text file
--volume : number of voxels in the mask
--pthresh : p-threshold for clusters
--thresh : threshold for input volume
Z statistic image is thresholded to show which voxels or clusters of voxels are activated at a particular significance level.
A Z statistic threshold is used to define contiguous clusters. Then each cluster's estimated significance level (from GRF-theory)
is compared with the cluster probability threshold. Significant clusters are then used to mask the original Z statistic image.
- Get the maximum intensity value of the output thresholded image. This used is while rendering the Z statistic image::
fslstats zstat1_cluster_threshold.nii.gz -R
arguments
-R : output <min intensity> <max intensity>
- Rendering. For details see `FEAT <http://www.fmrib.ox.ac.uk/fsl/feat5/detail.html#poststats>`_::
overlay 1 0 MNI152_T1_2mm_brain.nii.gz
-a zstat1_cluster_threshold.nii.gz
2.30 15.67
zstat1_cluster_threshold_overlay.nii.gz
slicer zstat1_cluster_threshold_overlay.nii.gz
-L -A 750
zstat1_cluster_threshold_overlay.png
The Z statistic range selected for rendering is automatically calculated by default,
to run from red (minimum Z statistic after thresholding) to yellow (maximum Z statistic, here
maximum intensity).
High Level Workflow Graph:
.. exec::
from CPAC.easy_thresh import easy_thresh
wf = easy_thresh('easy_thresh_wf')
wf.write_graph(
graph2use='orig',
dotfilename='./images/generated/easy_thresh.dot'
)
.. image:: ../../images/generated/easy_thresh.png
:width: 800
Detailed Workflow Graph:
.. image:: ../../images/generated/generated.easy_thresh_detailed.png
:width: 800
Examples
--------
>>> from CPAC.easy_thresh import easy_thresh
>>> preproc = easy_thresh("new_workflow")
>>> preproc.inputs.inputspec.z_stats= 'flameo/stats/zstat1.nii.gz'
>>> preproc.inputs.inputspec.merge_mask = 'merge_mask/alff_Z_fn2standard_merged_mask.nii.gz'
>>> preproc.inputs.inputspec.z_threshold = 2.3
>>> preproc.inputs.inputspec.p_threshold = 0.05
>>> preproc.inputs.inputspec.parameters = ('/usr/local/fsl/', 'MNI152')
>>> preporc.run() # doctest: +SKIP
"""
easy_thresh = pe.Workflow(name=wf_name)
inputnode = pe.Node(util.IdentityInterface(fields=['z_stats',
'merge_mask',
'z_threshold',
'p_threshold',
'parameters']),
name='inputspec')
outputnode = pe.Node(util.IdentityInterface(fields=['cluster_threshold',
'cluster_index',
'cluster_localmax_txt',
'overlay_threshold',
'rendered_image']),
name='outputspec')
# fsl easythresh
# estimate image smoothness
smooth_estimate = pe.MapNode(interface=fsl.SmoothEstimate(),
name='smooth_estimate',
iterfield=['zstat_file'])
# run clustering after fixing stats header for talspace
zstat_mask = pe.MapNode(interface=fsl.MultiImageMaths(),
name='zstat_mask',
iterfield=['in_file'])
# operations to perform
#-mas use (following image>0) to mask current image
zstat_mask.inputs.op_string = '-mas %s'
# fslcpgeom
#copy certain parts of the header information (image dimensions,
#voxel dimensions, voxel dimensions units string, image orientation/origin
#or qform/sform info) from one image to another
geo_imports = ['import subprocess']
copy_geometry = pe.MapNode(util.Function(input_names=['infile_a', 'infile_b'],
output_names=['out_file'],
function=copy_geom,
imports=geo_imports),
name='copy_geometry',
iterfield=['infile_a', 'infile_b'])
##cluster-based thresholding
#After carrying out the initial statistical test, the resulting
#Z statistic image is then normally thresholded to show which voxels or
#clusters of voxels are activated at a particular significance level.
#A Z statistic threshold is used to define contiguous clusters.
#Then each cluster's estimated significance level (from GRF-theory) is
#compared with the cluster probability threshold. Significant clusters
#are then used to mask the original Z statistic image for later production
#of colour blobs.This method of thresholding is an alternative to
#Voxel-based correction, and is normally more sensitive to activation.
# cluster = pe.MapNode(interface=fsl.Cluster(),
# name='cluster',
# iterfield=['in_file', 'volume', 'dlh'])
# #output of cluster index (in size order)
# cluster.inputs.out_index_file = True
# #thresholded image
# cluster.inputs.out_threshold_file = True
# #local maxima text file
# #defines the cluster cordinates
# cluster.inputs.out_localmax_txt_file = True
cluster_imports = ['import os', 'import re', 'import subprocess as sb']
cluster = pe.MapNode(util.Function(input_names=['in_file',
'volume',
'dlh',
'threshold',
'pthreshold',
'parameters'],
output_names=['index_file',
'threshold_file',
'localmax_txt_file'],
function=call_cluster,
imports=cluster_imports),
name='cluster',
iterfield=['in_file', 'volume', 'dlh'])
# max and minimum intensity values
image_stats = pe.MapNode(interface=fsl.ImageStats(),
name='image_stats',
iterfield=['in_file'])
image_stats.inputs.op_string = '-R'
# create tuple of z_threshold and max intensity value of threshold file
create_tuple = pe.MapNode(util.Function(input_names=['infile_a',
'infile_b'],
output_names=['out_file'],
function=get_tuple),
name='create_tuple',
iterfield=['infile_b'])
# colour activation overlaying
overlay = pe.MapNode(interface=fsl.Overlay(),
name='overlay',
iterfield=['stat_image', 'stat_thresh'])
overlay.inputs.transparency = True
overlay.inputs.auto_thresh_bg = True
overlay.inputs.out_type = 'float'
# colour rendering
slicer = pe.MapNode(interface=fsl.Slicer(), name='slicer',
iterfield=['in_file'])
# set max picture width
slicer.inputs.image_width = 750
# set output all axial slices into one picture
slicer.inputs.all_axial = True
# function mapnode to get the standard fsl brain image based on parameters
# as FSLDIR,MNI and voxel size
get_bg_imports = ['import os', 'from nibabel import load']
get_backgroundimage = pe.MapNode(util.Function(input_names=['in_file',
'file_parameters'],
output_names=['out_file'],
function=get_standard_background_img,
imports=get_bg_imports),
name='get_bckgrndimg1',
iterfield=['in_file'])
# function node to get the standard fsl brain image
# outputs single file
get_backgroundimage2 = pe.Node(util.Function(input_names=['in_file',
'file_parameters'],
output_names=['out_file'],
function=get_standard_background_img,
imports=get_bg_imports),
name='get_backgrndimg2')
# connections
easy_thresh.connect(inputnode, 'z_stats', smooth_estimate, 'zstat_file')
easy_thresh.connect(inputnode, 'merge_mask', smooth_estimate, 'mask_file')
easy_thresh.connect(inputnode, 'z_stats', zstat_mask, 'in_file')
easy_thresh.connect(inputnode, 'merge_mask', zstat_mask, 'operand_files')
easy_thresh.connect(zstat_mask, 'out_file', get_backgroundimage,
'in_file')
easy_thresh.connect(inputnode, 'parameters', get_backgroundimage,
'file_parameters')
easy_thresh.connect(get_backgroundimage, 'out_file', copy_geometry,
'infile_a')
easy_thresh.connect(zstat_mask, 'out_file', copy_geometry, 'infile_b')
easy_thresh.connect(copy_geometry, 'out_file', cluster, 'in_file')
easy_thresh.connect(inputnode, 'z_threshold', cluster, 'threshold')
easy_thresh.connect(inputnode, 'p_threshold', cluster, 'pthreshold')
easy_thresh.connect(smooth_estimate, 'volume', cluster, 'volume')
easy_thresh.connect(smooth_estimate, 'dlh', cluster, 'dlh')
easy_thresh.connect(inputnode, 'parameters', cluster, 'parameters')
easy_thresh.connect(cluster, 'threshold_file', image_stats, 'in_file')
easy_thresh.connect(image_stats, 'out_stat', create_tuple, 'infile_b')
easy_thresh.connect(inputnode, 'z_threshold', create_tuple, 'infile_a')
easy_thresh.connect(cluster, 'threshold_file', overlay, 'stat_image')
easy_thresh.connect(create_tuple, 'out_file', overlay, 'stat_thresh')
easy_thresh.connect(inputnode, 'merge_mask', get_backgroundimage2,
'in_file' )
easy_thresh.connect(inputnode, 'parameters', get_backgroundimage2,
'file_parameters')
easy_thresh.connect(get_backgroundimage2, 'out_file', overlay,
'background_image')
easy_thresh.connect(overlay, 'out_file', slicer, 'in_file')
easy_thresh.connect(cluster, 'threshold_file', outputnode,
'cluster_threshold')
easy_thresh.connect(cluster, 'index_file', outputnode, 'cluster_index')
easy_thresh.connect(cluster, 'localmax_txt_file', outputnode,
'cluster_localmax_txt')
easy_thresh.connect(overlay, 'out_file', outputnode, 'overlay_threshold')
easy_thresh.connect(slicer, 'out_file', outputnode, 'rendered_image')
return easy_thresh
def call_cluster(in_file, volume, dlh, threshold, pthreshold, parameters):
out_name = re.match('z(\w)*stat(\d)+', os.path.basename(in_file))
filename, ext = os.path.splitext(os.path.basename(in_file))
ext= os.path.splitext(filename)[1] + ext
filename = os.path.splitext(filename)[0]
if out_name:
out_name= out_name.group(0)
else:
out_name = filename
FSLDIR = parameters[0]
index_file = os.path.join(os.getcwd(), 'cluster_mask_' + out_name + ext)
threshold_file = os.path.join(os.getcwd(), 'thresh_' + out_name + ext)
localmax_txt_file = os.path.join(os.getcwd(), 'cluster_' + out_name + '.txt')
cmd_path = os.path.join(FSLDIR, 'bin/cluster')
f = open(localmax_txt_file,'wb')
cmd = sb.Popen([ cmd_path,
'--dlh=' + str(dlh),
'--in=' + in_file,
'--oindex=' + index_file,
'--othresh=' + threshold_file,
'--pthresh=' + str(pthreshold),
'--thresh=' + str(threshold),
'--volume=' + str(volume)],
stdout= f)
stdout_value, stderr_value = cmd.communicate()
f.close()
return index_file, threshold_file, localmax_txt_file
[docs]def copy_geom(infile_a, infile_b):
"""
Method to call fsl fslcpgeom command to copy
certain parts of the header information (image dimensions,
voxel dimensions, voxel dimensions units string, image
orientation/origin or qform/sform info) from one image to another
Parameters
-----------
infile_a : nifti file
input volume from which the geometry is copied from
infile_b : nifti file
input volume from which the geometry is copied to
Returns
-------
out_file : nifti file
Input volume infile_b with modified geometry information
in the header.
Raises
------
Exception
If fslcpgeom fails
"""
try:
out_file = infile_b
cmd = ['fslcpgeom', infile_a, out_file]
retcode = subprocess.check_output(cmd)
return out_file
except Exception:
raise Exception("Error while using fslcpgeom to copy geometry")
[docs]def get_standard_background_img(in_file, file_parameters):
"""
Method to get the standard brain image from FSL
standard data directory
Parameters
----------
in_file : nifti file
Merged 4D Zmap volume
file_parameters : tuple
Value FSLDIR and MNI from config file
Returns
-------
standard_path : string
Standard FSL Image file path
Raises
------
Exception
If nibabel cannot load the input nifti volume
"""
try:
img = load(in_file)
hdr = img.get_header()
group_mm = int(hdr.get_zooms()[2])
FSLDIR, MNI = file_parameters
standard_path = \
os.path.join(FSLDIR, 'data/standard/',
'{0}_T1_{1}mm_brain.nii.gz'.format(MNI, group_mm))
return os.path.abspath(standard_path)
except Exception:
raise Exception("Error while loading background image")
[docs]def get_tuple(infile_a, infile_b):
"""
Simple method to return tuple of z_threhsold
maximum intensity values of Zstatistic image
for input to the overlay.
Parameters
----------
z_theshold : float
z threshold value
intensity_stat : tuple of float values
minimum and maximum intensity values
Returns
-------
img_min_max : tuple (float)
tuple of zthreshold and maximum intensity
value of z statistic image
"""
out_file = (infile_a, infile_b[1])
return out_file