def fetch_rat_waxholm(template_dir="~/.samri_files/templates/rat/waxholm/", verbose=1):
	"""
	Download and load waxholm atlas for Sprague Dawley rat

	Returns
	-------
	dict
		Dictionary containing template, atlas, labels
		template - mri anatomy file; nifti file
		atlas - pixelvalues of regions are grouped together, with corresponding labels in labels.ccv; nifti file
		labels - labels annotating brain regions for pixelgroups in atlas; csv file


	Notes
	-----
	Please deprecate this function.
	We should distribute dependencies such as atlases in the form of unambiguously managed packages, and not data fetched from the web at runtime.

	References
	----------
	.. [1] 'Papp, Eszter A., et al. "Waxholm Space atlas of the Sprague Dawley rat brain." NeuroImage 97 (2014): 374-386.'
	.. [2] https://www.nitrc.org/projects/whs-sd-atlas

	"""
	from nilearn.datasets.utils import _fetch_files
	# Get template
	url_template = 'https://www.nitrc.org/frs/download.php/9423/WHS_SD_rat_T2star_v1.01.nii.gz'
	template = _fetch_files(path.abspath(path.expanduser(template_dir)), [('WHS_SD_rat_T2star_v1.01.nii.gz', url_template, {})],
			verbose=verbose)[0]

	# Get atlas
	url_atlas = 'https://www.nitrc.org/frs/download.php/9438/WHS_SD_rat_atlas_v2.nii.gz'
	atlas = _fetch_files(path.abspath(path.expanduser(template_dir)), [('WHS_SD_rat_atlas_v2.nii.gz', url_atlas, {})],
			verbose=verbose)[0]

	# Get labels
	url_labels = 'https://www.nitrc.org/frs/download.php/9439/WHS_SD_rat_atlas_v2.label'
	labels = _fetch_files(path.abspath(path.expanduser(template_dir)), [('WHS_SD_rat_atlas_v2.label', url_labels, {})],
			verbose=verbose)[0]

	# resample template
	commands = ["ResampleImage 3 WHS_SD_rat_T2star_v1.01.nii.gz _200micron_WHS_SD_rat_T2star_v1.01.nii.gz 0.2x0.2x0.2 size=1 spacing=0 4",
		"SmoothImage 3 _200micron_WHS_SD_rat_T2star_v1.01.nii.gz 0.4 200micron_WHS_SD_rat_T2star_v1.01.nii.gz",
		"rm _200micron_WHS_SD_rat_T2star_v1.01.nii.gz",
		"ResampleImage 3 WHS_SD_rat_atlas_v2.nii.gz _200micron_WHS_SD_rat_atlas_v2.nii.gz 0.2x0.2x0.2 size=1 spacing=0 4",
		"SmoothImage 3 _200micron_WHS_SD_rat_atlas_v2.nii.gz 0.4 200micron_WHS_SD_rat_atlas_v2.nii.gz",
		"rm _200micron_WHS_SD_rat_atlas_v2.nii.gz",]

	for command in commands:
		p = subprocess.Popen(command.split(), cwd=path.abspath(path.expanduser(template_dir)), stdout=subprocess.PIPE)
		p.wait()

	return dict([
			("template", path.abspath(path.expanduser(template_dir)) + "200micron_WHS_SD_rat_T2star_v1.01.nii.gz"),
			("atlas", path.abspath(path.expanduser(template_dir)) + "200micron_WHS_SD_rat_atlas_v2.nii.gz"),
			("labels", labels)]) def corresponding_physiofile(nii_path):
	"""Based on a BIDS timecourse path, get the corresponding BIDS physiology file."""

	from os import path

	nii_dir = path.dirname(nii_path)
	nii_name = path.basename(nii_path)
	stripped_name = nii_name.split('.', 1)[0].rsplit('_', 1)[0]
	physiofile = path.join(nii_dir,stripped_name+'_physio.tsv')
	meta_physiofile = path.join(nii_dir,stripped_name+'_physio.json')

	if not path.isfile(physiofile):
		physiofile = ''
	if not path.isfile(meta_physiofile):
		meta_physiofile = ''

	return physiofile, meta_physiofile def get_bids_scan(data_selection,
	bids_base="",
	ind_type="",
	selector=None,
	subject=None,
	session=None,
	extra=['acq','run'],
	):

	"""Description...

	Parameters
	----------
	bids_base : str
		Path to the bids base path.
	data_selection : pandas.DataFrame
		A `pandas.DataFrame` object as produced by `samri.preprocessing.extra_functions.get_data_selection()`.
	selector : iterable, optional
		The first method of selecting the subject and scan, this value should be a length-2 list or tuple containing the subject and sthe session to be selected.
	subject : string, optional
		This has to be defined if `selector` is not defined. The subject for which to return a scan directory.
	session : string, optional
		This has to be defined if `selector` is not defined. The session for which to return a scan directory.
	"""
	import os #for some reason the import outside the function fails
	import pandas as pd
	from samri.pipelines.utils import bids_naming

	filtered_data = []

	if selector:
		subject = selector[0]
		session = selector[1]
		filtered_data = data_selection[(data_selection["session"] == session)&(data_selection["subject"] == subject)]
	else:
		filtered_data = data_selection[data_selection.index==ind_type]

	if filtered_data.empty:
		raise Exception("SAMRIError: Does not exist: " + str(selector[0]) + str(selector[1]) + str(ind_type))
	else:
		subject = filtered_data['subject'].item()
		session = filtered_data['session'].item()
		try:
			typ = filtered_data['type'].item()
		except:
			typ = ""
		try:
			task = filtered_data['task'].item()
		except:
			task = ""
		subject_session = [subject, session]
		#scan_path = os.path.join(bids_base, 'sub-' + subject + '/', 'ses-' + session + '/', typ )

		try:
			nii_path = filtered_data['path'].item()
		except KeyError:
			nii_path = filtered_data['measurement'].item()
			nii_path += '/'+filtered_data['scan'].item()
			nii_name = bids_naming(subject_session, filtered_data,
					extra=extra,
					extension='',
					)
			scan_path = nii_path
		else:
			scan_path = os.path.dirname(nii_path)
			nii_name = os.path.basename(nii_path)

		eventfile_name = bids_naming(subject_session, filtered_data,
				extra=extra,
				extension='.tsv',
				suffix='events'
				)
		metadata_filename = bids_naming(subject_session, filtered_data,
				extra=extra,
				extension='.json',
				)

		dict_slice = filtered_data.to_dict('records')[0]

		return scan_path, typ, task, nii_path, nii_name, eventfile_name, subject_session, metadata_filename, dict_slice, ind_type def write_bids_events_file(scan_dir,
	db_path="~/syncdata/meta.db",
	metadata_file='',
	out_file="events.tsv",
	prefer_labbookdb=False,
	timecourse_file='',
	task='',
	forced_dummy_scans=0.,
	):
	"""Adjust a BIDS event file to reflect delays introduced after the trigger and before the scan onset.

	Parameters
	----------

	scan_dir : str
		ParaVision scan directory path.
	db_path : str, optional
		LabbookDB database file path from which to source the evets profile for the identifier assigned to the `task` parameter.
	metadata_file : str, optional
		Path to a BIDS metadata file.
	out_file : str, optional
		Path to which to write the adjusted events file
	prefer_labbookdb : bool, optional
		Whether to query the events file in the LabbookDB database file first (rather than look for the events file in the scan directory).
	timecourse_file : str, optional
		Path to a NIfTI file.
	task : str, optional
		Task identifier from a LabbookDB database.

	Returns
	-------

	str : Path to which the adjusted events file was saved.
	"""

	import csv
	import sys
	import json
	import os
	import pandas as pd
	import nibabel as nib
	import numpy as np
	from datetime import datetime

	out_file = os.path.abspath(os.path.expanduser(out_file))
	scan_dir = os.path.abspath(os.path.expanduser(scan_dir))
	db_path = os.path.abspath(os.path.expanduser(db_path))

	if not prefer_labbookdb:
		try:
			scan_dir_contents = os.listdir(scan_dir)
			sequence_files = [i for i in scan_dir_contents if ("sequence" in i and "tsv" in i)]
			if sequence_files:
				sequence_file = os.path.join(scan_dir, sequence_files[0])
			else:
				timecourse_dir = os.path.dirname(timecourse_file)
				timecourse_name = os.path.basename(timecourse_file)
				stripped_name = timecourse_name.split('.', 1)[0].rsplit('_', 1)[0]
				sequence_file = os.path.join(timecourse_dir,stripped_name+'_events.tsv')
			mydf = pd.read_csv(sequence_file, sep="\s", engine='python')
		except:
			if os.path.isfile(db_path):
				from labbookdb.report.tracking import bids_eventsfile
				mydf = bids_eventsfile(db_path, task)
			else:
				return '/dev/null'
	else:
		try:
			if os.path.isfile(db_path):
				from labbookdb.report.tracking import bids_eventsfile
				mydf = bids_eventsfile(db_path, task)
			else:
				return '/dev/null'
		except ImportError:
			scan_dir_contents = os.listdir(scan_dir)
			sequence_files = [i for i in scan_dir_contents if ("sequence" in i and "tsv" in i)]
			if sequence_files:
				sequence_file = os.path.join(scan_dir, sequence_files[0])
			else:
				timecourse_dir = os.path.dirname(timecourse_file)
				timecourse_name = os.path.basename(timecourse_file)
				stripped_name = timecourse_name.split('.', 1)[0].rsplit('_', 1)[0]
				sequence_file = os.path.join(timecourse_dir,stripped_name+'_events.tsv')
			mydf = pd.read_csv(sequence_file, sep="\s", engine='python')

	timecourse_file = os.path.abspath(os.path.expanduser(timecourse_file))
	timecourse = nib.load(timecourse_file)
	zooms = timecourse.header.get_zooms()
	tr = float(zooms[-1])
	delay = 0.
	if forced_dummy_scans:
		delay = forced_dummy_scans * tr
	elif metadata_file:
		metadata_file = os.path.abspath(os.path.expanduser(metadata_file))

		with open(metadata_file) as metadata:
			    metadata = json.load(metadata)
		try:
			delay += metadata['NumberOfVolumesDiscardedByScanner'] * tr
		except:
			pass
		try:
			delay += metadata['DelayAfterTrigger']
		except:
			pass
	mydf['onset'] = mydf['onset'] - delay

	# 'n/a' specifically required by BIDS, as a consequence of its infinite wisdom
	mydf.to_csv(out_file, sep=str('\t'), na_rep='n/a', index=False)

	return out_file def force_dummy_scans(in_file,
	desired_dummy_scans=10,
	out_file="forced_dummy_scans_file.nii.gz",
	):
	"""Take a scan and crop initial timepoints depending upon the number of dummy scans (determined from a Bruker scan directory) and the desired number of dummy scans.

	in_file : string
	BIDS-compliant path to the 4D NIfTI file for which to force dummy scans.

	desired_dummy_scans : int , optional
	Desired timepoints dummy scans.
	"""

	import json
	import nibabel as nib
	from os import path

	out_file = path.abspath(path.expanduser(out_file))
	in_file = path.abspath(path.expanduser(in_file))
	in_file_dir = path.dirname(in_file)
	in_file_name = path.basename(in_file)
	in_file_noext = in_file_name.split('.', 1)[0]
	metadata_file = path.join(in_file_dir, in_file_noext+'.json')

	metadata = json.load(open(metadata_file))

	dummy_scans = 0
	try:
		dummy_scans = metadata['NumberOfVolumesDiscardedByScanner']
	except:
		pass

	delete_scans = desired_dummy_scans - dummy_scans

	img = nib.load(in_file)
	if delete_scans <= 0:
		nib.save(img,out_file)
	else:
		img_ = nib.Nifti1Image(img.get_data()[...,delete_scans:], img.affine, img.header)
		nib.save(img_,out_file)
	deleted_scans = delete_scans

	return out_file, deleted_scans BIDS_METADATA_EXTRACTION_DICTS = [
	{'field_name':'EchoTime',
		'query_file':'method',
		'regex':r'^##\$EchoTime=(?P<value>.*?)$',
		'scale': 1./1000.,
		'type': float,
		},
	{'field_name':'FlipAngle',
		'query_file':'visu_pars',
		'regex':r'^##\$VisuAcqFlipAngle=(?P<value>.*?)$',
		'type': float,
		},
	{'field_name':'Manufacturer',
		'query_file':'configscan',
		'regex':r'^##ORIGIN=(?P<value>.*?)$',
		},
	{'field_name':'NumberOfVolumesDiscardedByScanner',
		'query_file':'method',
		'regex':r'^##\$PVM_DummyScans=(?P<value>.*?)$',
		'type': int,
		},
	{'field_name':'ReceiveCoilName',
		'query_file':'configscan',
		'regex':r'.*?,COILTABLE,1#\$Name,(?P<value>.*?)#\$Id.*?',
		},
	{'field_name':'RepetitionTime',
		'query_file':'method',
		'regex':r'^##\$PVM_RepetitionTime=(?P<value>.*?)$',
		'scale': 1./1000.,
		'type': float,
		},
	{'field_name':'PulseSequenceType',
		'query_file':'method',
		'regex':r'^##\$Method=<Bruker:(?P<value>.*?)>$',
		},
	] class VoxelResize(BaseInterface):
	input_spec = VoxelResizeInputSpec
	output_spec = VoxelResizeOutputSpec

	def _run_interface(self, runtime):
		import nibabel as nb
		nii_file = self.inputs.in_file
		resize_factors = self.inputs.resize_factors

		nii_img = nb.load(nii_file)
		aff = nii_img.affine
		# take original image affine, and scale the voxel size and first voxel coordinates for each dimension
		aff[0] = aff[0]*resize_factors[0]
		aff[1] = aff[1]*resize_factors[1]
		aff[2] = aff[2]*resize_factors[2]
		#apply the affine
		nii_img.set_sform(aff)
		nii_img.set_qform(aff)

		#set the sform and qform codes to "scanner" (other settings will lead to AFNI/meica.py assuming talairach space)
		nii_img.header["qform_code"] = 1
		nii_img.header["sform_code"] = 1

		out_file = self.inputs.out_file
		nii_img.to_filename(out_file)
		self.result = os.path.abspath(out_file)

		return runtime

	def _list_outputs(self):
		outputs = self._outputs().get()
		outputs['out_file'] = self.result
		return outputs class FSLOrient(FSLCommand):

	_cmd = "fslorient"
	input_spec = FSLOrientInput
	output_spec = FSLOrientOutput

	#def run(self, **inputs):
	#	shutil.copyfile(self.inputs.in_file, self.inputs.out_file)
	#	FSLCommand.run(self)

	def _list_outputs(self):
		outputs = self.output_spec().get()
		outputs["out_file"] = self.inputs.in_file
		outputs["out_file"] = os.path.abspath(outputs["out_file"])
		return outputs def bids_data_selection(base, structural_match, functional_match, subjects, sessions,
	verbose=False,
	joint_conditions=True,
	):
	"""
	Creates a Pandas Dataframe descriptor from a BIDS datapath, optionally filtering out conditions.

	Parameters
	----------

	base : str
		path specifying the root directory of the BIDS data
	structural_match : dict or bool
		Dictionary specifying a whitelist of BIDS field identifiers.
		False if no whitelist is specified.
	functional_match : dict or bool
		Dictionary specifying a whitelist of BIDS field identifiers.
		False if no whitelist is specified.
	subjects: list or bool
		A list of subjects which may be present in the 'subjects' column of the created Pandas DataFrame, 'df'.
		False if user does not want to filter DataFrame by subjects.
	sessions: list or bool
		A list of session names which may be present in the 'sessions' column of the created Pandas DataFrame, 'df'.
		False if user does not want to filter DataFrame by sessions.

	Returns
	-------

	df : pandas.DataFrame
		A Pandas DataFrame with information corresponding to the whitelisted BIDS identifiers and optionally filtered by subjects and/or sessions.
	"""
	validate = BIDSValidator()
	if verbose:
		for x in os.walk(base):
			print(x[0])
			if validate.is_bids(x[0]):
				print("Is not BIDS-formatted.")
			else:
				print("Detected!")
	#layout = BIDSLayout(base, validate=False)
	layout = BIDSLayout(base,validate=False,derivatives=True)
	try:
		df = layout.as_data_frame()
	except AttributeError:
		df = layout.to_df()

	# Not crashing if the run field is not present
	try:
		# Run is for some reason recorded as float
		df.loc[df['run'].notna(),'run'] = df.loc[df['run'].notnull(),'run'].apply(int).apply(str)
	except KeyError:
		pass

	if verbose:
		print(df)
		print(df.columns)
	# drop event files
	# PyBIDS 0.6.5 and 0.10.2 compatibility
	try:
		df = df[df.type != 'events']
	except AttributeError:
		df = df[df.suffix != 'events']

	# rm .json
	df = df.loc[df.path.str.contains('.nii')]

	# generate scan types for later
	df['scan_type'] = ""

	#print(df.path.str.startswith('task', beg=0,end=len('task')))
	beg = df.path.str.find('task-')
	end = df.path.str.find('.')
	#df.loc[df.modality == 'func', 'scan_type'] = 'acq-'+df['acq']+'_task-'+  df.path.str.partition('task-')[2].str.partition('.')[0]
	#df.loc[df.modality == 'anat', 'scan_type'] = 'acq-'+df['acq']+'_' + df['type']
	#TODO: fix task!=type
	if 'func' in df.columns:
		df.loc[df.modality == 'func', 'task'] = df.path.str.partition('task-')[2].str.partition('_')[0]
		df.loc[df.modality == 'func', 'scan_type'] = 'task-' + df['task'] + '_acq-'+ df['acq']
	if 'anat' in df.columns:
		df.loc[df.modality == 'anat', 'scan_type'] = 'acq-'+df['acq'] +'_' + df['type']

	# Unclear in current BIDS specification, we refer to BOLD/CBV as modalities and func/anat as types
	# Can be removed after Pybids 0.10.2 migration
	df = df.rename(columns={'modality': 'type', 'type': 'modality'})

	#TODO: The following should be collapsed into one criterion category
	if functional_match or structural_match:
		res_df = pd.DataFrame()
		if functional_match:
			_df = deepcopy(df)
			try:
				if joint_conditions:
					for match in functional_match.keys():
						_df = _df.loc[_df[match].isin(functional_match[match])]
					res_df = res_df.append(_df)
				else:
					for match in structural_match.keys():
						_df = filter_data(_df, match, functional_match[match])
						res_df = res_df.append(_df)
			except:
				pass
		if structural_match:
			_df = deepcopy(df)
			try:
				if joint_conditions:
					for match in structural_match.keys():
						_df = _df.loc[_df[match].isin(structural_match[match])]
					res_df = res_df.append(_df)
				else:
					for match in structural_match.keys():
						_df = filter_data(_df, match, structural_match[match])
						res_df = res_df.append(_df)
			except:
				pass
		df = res_df

	if subjects:
		df = filter_data(df, 'subject', subjects)
	if sessions:
		df = filter_data(df, 'session', sessions)

	return df def copy_bids_files(bids_in, bids_out):
	"""Copy BIDS metadata files.

	Parameters
	----------

	bids_in : str
		Input BIDS data directory.
	out_out : str
		Output BIDS data directory.
	"""

	bids_file_list = [
		'participants.tsv',
		'participants.json',
		'dataset_description.json',
		]
	for i in bids_file_list:
		in_file = os.path.join(bids_in,i)
		out_file = os.path.join(bids_out,i)
		try:
			copyfile(in_file,out_file)
		except:
			print('Copying {} to {} failed.'.format(in_file,out_file))
			pass def fslmaths_invert_values(img_path):
	"""Calculates the op_string required to make an fsl.ImageMaths() node invert an image"""
	op_string = "-sub {0} -sub {0}".format(img_path)
	return op_string def ss_to_path(subject_session):
	"""Concatenate a (subject, session) or (subject, session, scan) tuple to a BIDS-style path"""
	subject = "sub-" + subject_session[0]
	session = "ses-" + subject_session[1]
	return "/".join([subject,session]) GENERIC_PHASES = {
	"f_only_translation":{
		"transforms":"Translation",
		"transform_parameters":(0.1,),
		"number_of_iterations":[2000,1000,500],
		"metric":"MeanSquares",
		"metric_weight":1,
		"radius_or_number_of_bins":32,
		"sampling_strategy":"Regular",
		"sampling_percentage":0.8,
		"convergence_threshold":1.e-16,
		"convergence_window_size":20,
		"smoothing_sigmas":[2,1,0],
		"sigma_units":"vox",
		"shrink_factors":[4,2,1],
		"use_estimate_learning_rate_once":False,
		"use_histogram_matching":True,
		},
	"f_translation":{
		"transforms":"Translation",
		"transform_parameters":(0.1,),
		"number_of_iterations":[400,200],
		"metric":"MI",
		"metric_weight":1,
		"radius_or_number_of_bins":64,
		"sampling_strategy":"Regular",
		"sampling_percentage":0.8,
		"convergence_threshold":1.e-8,
		"convergence_window_size":20,
		"smoothing_sigmas":[1,0],
		"sigma_units":"vox",
		"shrink_factors":[2,1],
		"use_estimate_learning_rate_once":False,
		"use_histogram_matching":True,
		},
	"s_translation":{
		"transforms":"Translation",
		"transform_parameters":(0.1,),
		"number_of_iterations":[1000,500,500],
		"metric":"MeanSquares",
		"metric_weight":1,
		"radius_or_number_of_bins":32,
		"sampling_strategy":"Regular",
		"sampling_percentage":0.3,
		"convergence_threshold":1.e-16,
		"convergence_window_size":20,
		"smoothing_sigmas":[2,1,0],
		"sigma_units":"vox",
		"shrink_factors":[4,2,1],
		"use_estimate_learning_rate_once":False,
		"use_histogram_matching":False,
		},
	"similarity":{
		"transforms":"Similarity",
		"transform_parameters":(0.1,),
		"number_of_iterations":[1000],
		"metric":"MI",
		"metric_weight":1,
		"radius_or_number_of_bins":32,
		"sampling_strategy":'Regular',
		"sampling_percentage":0.75,
		"convergence_threshold":1.e-16,
		"convergence_window_size":10,
		"smoothing_sigmas":[0],
		"sigma_units":"vox",
		"shrink_factors":[1],
		"use_estimate_learning_rate_once":False,
		"use_histogram_matching":True,
		},
	"affine":{
		"transforms":"Affine",
		"transform_parameters":(0.05,),
		"number_of_iterations":[50],
		"metric":"MI",
		"metric_weight":1,
		"radius_or_number_of_bins":32,
		"sampling_strategy":'Regular',
		"sampling_percentage":0.66,
		"convergence_threshold":1.e-8,
		"convergence_window_size":10,
		"smoothing_sigmas":[0],
		"sigma_units":"vox",
		"shrink_factors":[1],
		"use_estimate_learning_rate_once":False,
		"use_histogram_matching":True,
		},
	"syn":{
		"transforms":"SyN",
		"transform_parameters":(0.05, 1.0, 15),
		"number_of_iterations":[50],
		"metric":["MI","CC"],
		#"metric":"MI",
		#"metric":"CC",
		"metric_weight":[0.25,0.75],
		#"metric_weight":1,
		"radius_or_number_of_bins":[32,5],
		#"radius_or_number_of_bins":16,
		#"radius_or_number_of_bins":4,
		"sampling_strategy":['Regular','Regular'],
		#"sampling_strategy":'Regular',
		"sampling_percentage":[0.8,0.8],
		#"sampling_percentage":0.8,
		"convergence_threshold":1.e-8,
		"convergence_window_size":10,
		"smoothing_sigmas":[0],
		"sigma_units":"vox",
		"shrink_factors":[1],
		"use_estimate_learning_rate_once":False,
		"use_histogram_matching":False,
		},
	}
import argh
import inspect
import json
import os
import re
import shutil
import multiprocessing as mp
import nipype.interfaces.io as nio
import nipype.interfaces.utility as util
import nipype.pipeline.engine as pe
import pandas as pd
		import nipype.interfaces.ants.legacy as antslegacy
from copy import deepcopy
from itertools import product
from os import path
from nipype.interfaces import ants, afni, bru2nii, fsl, nipy
from samri.fetch.templates import fetch_rat_waxholm
from samri.pipelines.extra_functions import corresponding_physiofile, get_bids_scan, write_bids_events_file, force_dummy_scans, BIDS_METADATA_EXTRACTION_DICTS
from samri.pipelines.extra_interfaces import VoxelResize, FSLOrient
from samri.pipelines.nodes import *
from samri.pipelines.utils import bids_data_selection, copy_bids_files, fslmaths_invert_values, ss_to_path, GENERIC_PHASES
			from samri.fetch.templates import fetch_rat_waxholm
		(get_f_scan, datasink, [(('subject_session',ss_to_path), 'container')]),
		(temporal_mean, f_percentile, [('out_file', 'in_file')]),
		# here we divide by 10 assuming 10 percent noise
		(f_percentile, f_threshold, [(('out_stat', divideby_10), 'thresh')]),
		(temporal_mean, f_threshold, [('out_file', 'in_file')]),
		(f_threshold, f_fast, [('out_file', 'in_files')]),
		(f_fast, f_bet, [('restored_image', 'in_file')]),
		(f_resize, f_deleteorient, [('out_file', 'in_file')]),
		(f_deleteorient, f_swapdim, [('out_file', 'in_file')]),
		]

	if realign == "space":
		realigner = pe.Node(interface=spm.Realign(), name="realigner")
		realigner.inputs.register_to_mean = True
		workflow_connections.extend([
			(f_swapdim, realigner, [('out_file', 'in_file')]),
			])

	elif realign == "spacetime":
		realigner = pe.Node(interface=nipy.SpaceTimeRealigner(), name="realigner")
		realigner.inputs.slice_times = "asc_alt_2"
		realigner.inputs.tr = tr
		realigner.inputs.slice_info = 3 #3 for coronal slices (2 for horizontal, 1 for sagittal)
		workflow_connections.extend([
			(f_swapdim, realigner, [('out_file', 'in_file')]),
			])

	elif realign == "time":
		realigner = pe.Node(interface=fsl.SliceTimer(), name="slicetimer")
		realigner.inputs.time_repetition = tr
		workflow_connections.extend([
			(f_swapdim, realigner, [('out_file', 'in_file')]),
			])

	f_antsintroduction = pe.Node(interface=antslegacy.antsIntroduction(), name='ants_introduction')
	f_antsintroduction.inputs.dimension = 3
	f_antsintroduction.inputs.reference_image = template
	#will need updating to `1`
	f_antsintroduction.inputs.bias_field_correction = True
	f_antsintroduction.inputs.transformation_model = 'GR'
	f_antsintroduction.inputs.max_iterations = [8,15,8]

	f_warp = pe.Node(interface=ants.WarpTimeSeriesImageMultiTransform(), name='f_warp')
	f_warp.inputs.reference_image = template
	f_warp.inputs.dimension = 4

	f_copysform2qform = pe.Node(interface=FSLOrient(), name='f_copysform2qform')
	f_copysform2qform.inputs.main_option = 'copysform2qform'

	warp_merge = pe.Node(util.Merge(2), name='warp_merge')

	workflow_connections.extend([
		(f_bet, f_antsintroduction, [('out_file', 'input_image')]),
		(f_antsintroduction, warp_merge, [('warp_field', 'in1')]),
		(f_antsintroduction, warp_merge, [('affine_transformation', 'in2')]),
		(warp_merge, f_warp, [('out', 'transformation_series')]),
		(f_warp, f_copysform2qform, [('output_image', 'in_file')]),
		])
	if realign == "space":
		workflow_connections.extend([
			(realigner, temporal_mean, [('realigned_files', 'in_file')]),
			(realigner, f_warp, [('realigned_files', 'input_image')]),
			])
	elif realign == "spacetime":
		workflow_connections.extend([
			(realigner, temporal_mean, [('out_file', 'in_file')]),
			(realigner, f_warp, [('out_file', 'input_image')]),
			])
	elif realign == "time":
		workflow_connections.extend([
			(realigner, temporal_mean, [('slice_time_corrected_file', 'in_file')]),
			(realigner, f_warp, [('slice_time_corrected_file', 'input_image')]),
			])
	else:
		workflow_connections.extend([
			(f_resize, temporal_mean, [('out_file', 'in_file')]),
			(f_swapdim, f_warp, [('out_file', 'input_image')]),
			])

	if functional_blur_xy:
		blur = pe.Node(interface=afni.preprocess.BlurToFWHM(), name="blur")
		blur.inputs.fwhmxy = functional_blur_xy
		workflow_connections.extend([
			(get_f_scan, blur, [('nii_name','out_file')]),
			(f_copysform2qform, blur, [('out_file', 'in_file')]),
			(blur, datasink, [('out_file', 'func')]),
			])
	else:

		f_rename = pe.Node(util.Rename(), name='f_rename')

		workflow_connections.extend([
			(get_f_scan, f_rename, [('nii_name','format_string')]),
			(f_copysform2qform, f_rename, [('out_file', 'in_file')]),
			(f_rename, datasink, [('out_file', 'func')]),
			])


	workflow_config = {'execution': {'crashdump_dir': path.join(out_base,'crashdump'),}}
	if debug:
		workflow_config['logging'] = {
			'workflow_level':'DEBUG',
			'utils_level':'DEBUG',
			'interface_level':'DEBUG',
			'filemanip_level':'DEBUG',
			'log_to_file':'true',
			}

	workdir_name = workflow_name+"_work"
	#this gives the name of the workdir, the output name is passed to the datasink
	workflow = pe.Workflow(name=workdir_name)
	workflow.connect(workflow_connections)
	workflow.base_dir = out_base
	workflow.config = workflow_config
	try:
		workflow.write_graph(dotfilename=path.join(workflow.base_dir,workdir_name,"graph.dot"), graph2use="hierarchical", format="png")
	except OSError:
		print('We could not write the DOT file for visualization (`dot` function from the graphviz package). This is non-critical to the processing, but you should get this fixed.')

	workflow.run(plugin="MultiProc", plugin_args={'n_procs' : n_jobs})

	copy_bids_files(bids_base, os.path.join(out_base,workflow_name))