Populse_MIA’s pipeline processes¶
This page explains how to create a process that can be used in Populse_MIA’s Pipeline Manager and how to install it to Populse_MIA.
Populse_MIA uses Capsul to handle Pipeline processing. During Populse_MIA installation, Nipype’s interfaces are stored in the package library and are directly available in the Process Library. However, any user can import its own processes in Populse_MIA following these next steps:
Creating a MIA process¶
MIA processes are Capsul processes made specific for Populse_MIA. They need at least three methods to work properly: __init__, list_outputs and _run_process.
__init__()
Definition of the inputs/outputs of the process. Each input/output is typed using the Enthought’s Traits library and is either mandatory or optional.
list_outputs()
This method is called during a pipeline initialization to generate the process outputs and their inheritances (from which input they depends). The return value of this method must at least be a dictionary of this type {‘name_of_the_output_plug’: ‘value’, …}. To improve the file tracking, antoher dictionary can be added to the return value. This dictionary called inheritance dictionary specifies for each output which input generated it: {‘output_value’: ‘input_value’, …}.
_run_process()
This method is called during a pipeline run. It has to contain the desired processing and need no return value.
Note: if you are using a Nipype interface that need to use Matlab script. Make sure to use the “manage_matlab_launch_parameters” method in the _run_process method to set the Matlab’s parameters of your Populse_MIA’s config to the Nipype interface.
self.process = spm.Smooth()
self.manage_matlab_launch_parameters()
# Then set the several inputs of the interface
self.process.inputs.in_files = self.in_files
self.process.inputs.fwhm = self.fwhm # etc.
Example: creating a smooth process using SPM Smooth (from Nipype’s interfaces) or Scipy’s gaussian filtering function.
import os
import traits.api as traits # used to declare the inputs/outputs of the process
import nibabel as nib # used to read and save Nifti images
from nipype.interfaces import spm # used to use SPM's Smooth
from scipy.ndimage.filters import gaussian_filter # used to apply the smoothing on an array
from populse_mia.user_interface.pipeline_manager.process_mia import ProcessMIA # base class that the created process has to inherit from
class SmoothSpmScipy(ProcessMIA):
def __init__(self):
super(SmoothSpmScipy, self).__init__()
# Inputs
self.add_trait("in_file", traits.File(output=False, desc='3D input file')) # Mandatory plug
# For inputs/outputs that are lists, it is possible to specify which the type of the list element (here
# traits.Float(). The second value ([1.0, 1.0, 1.0]) corresponds to the default value
self.add_trait("fwhm", traits.List(traits.Float(), [1.0, 1.0, 1.0], output=False, optional=True,
desc='List of fwhm for each dimension (in mm)'))
self.add_trait("out_prefix", traits.String('s', output=False, optional=True, desc='Output file prefix'))
self.add_trait("method", traits.Enum('SPM', 'Scipy', output=False, optional=True,
desc='Method used (either "SPM" or "Scipy")'))
# Output
self.add_trait("smoothed_file", traits.File(output=True, desc='Output file')) # Mandatory plug
self.process = spm.Smooth()
def list_outputs(self):
# Depending on the chosen method, the output dictionary will be generated differently
if self.method in ['SPM', 'Scipy']:
if self.method == 'SPM':
# Nipype interfaces have already a _list_outputs method that generates the output dictionary
if not self.in_file:
print('"in_file" plug is mandatory for a Smooth process')
return {}
else:
self.process.inputs.in_files = self.in_file # The input for a SPM Smooth is "in_files"
self.process.inputs.out_prefix = self.out_prefix
nipype_dict = self.process._list_outputs() # Generates: {'smoothed_files' : [out_filename]}
output_dict = {'smoothed_file': nipype_dict['smoothed_files'][0]}
else:
# Generating the filename by hand
if not self.in_file:
print('"in_file" plug is mandatory for a Smooth process')
return {}
else:
path, filename = os.path.split(self.in_file)
out_filename = self.out_prefix + filename
output_dict = {'smoothed_file': os.path.join(path, out_filename)}
# Generating the inheritance dictionary
inheritance_dict = {output_dict['smoothed_file']: self.in_file}
return output_dict, inheritance_dict
else:
print('"method" input has to be "SPM" or "Scipy" for a Smooth process')
return {}
def _run_process(self):
# Depending on the chosen method, the output file will be generated differently
if self.method in ['SPM', 'Scipy']:
if self.method == 'SPM':
# Make sure to call the manage_matlab_launch_parameters method to set the config parameters
self.manage_matlab_launch_parameters()
if not self.in_file:
print('"in_file" plug is mandatory for a Smooth process')
return
else:
self.process.inputs.in_files = self.in_file # The input for a SPM Smooth is "in_files"
self.process.inputs.fwhm = self.fwhm
self.process.inputs.out_prefix = self.out_prefix
self.process.run() # Running the interface
else:
if not self.in_file:
print('"in_file" plug is mandatory for a Smooth process')
return
else:
input_image = nib.load(self.in_file) # Loading the nibabel image
input_image_header = input_image.header
input_array = input_image.get_fdata() # Getting the 3D volume as a numpy array
# Getting the image resolution in x, y and z
x_resolution = abs(input_image_header['pixdim'][1])
y_resolution = abs(input_image_header['pixdim'][2])
z_resolution = abs(input_image_header['pixdim'][3])
# Convert the fwhm for each dimension from mm to pixel
x_fwhm = self.fwhm[0] / x_resolution
y_fwhm = self.fwhm[1] / y_resolution
z_fwhm = self.fwhm[2] / z_resolution
pixel_fwhm = [x_fwhm, y_fwhm, z_fwhm]
sigma = [pixel_fwhm_dim / 2.355 for pixel_fwhm_dim in pixel_fwhm] # Converting fwmh to sigma
output_array = gaussian_filter(input_array, sigma) # Filtering the array
# Creating a new Nifti image with the affine/header of the input_image
output_image = nib.Nifti1Image(output_array, input_image.affine, input_image.header)
# Saving the image
path, filename = os.path.split(self.in_file)
out_filename = self.out_prefix + filename
nib.save(output_image, os.path.join(path, out_filename))
else:
print('"method" input has to be "SPM" or "Scipy" for a Smooth process')
return {}
Creating a Python package containing the process¶
Make sure that the file containing the Smooth class is contained in a Python package and add this to the __init__.py file:
from .name_of_file import SmoothSpmScipy
Installing the package in Populse_MIA¶
In the software menu bar, go to More > Install processes > From folder and browse to the package. Click on “Install package”. The package is now stored in the process library and the Smooth process can be used to create pipelines.