@@ -1232,3 +1232,193 @@ def _run_interface(self, runtime):
12321232 if runtime .stderr :
12331233 self .raise_exception (runtime )
12341234 return runtime
1235+
1236+
1237+ class EddyQuadInputSpec (FSLCommandInputSpec ):
1238+ base_name = traits .Str (
1239+ 'eddy_corrected' ,
1240+ usedefault = True ,
1241+ argstr = '%s' ,
1242+ desc = ("Basename (including path) for EDDY output files, i.e., "
1243+ "corrected images and QC files" ),
1244+ position = 0 ,
1245+ )
1246+ idx_file = File (
1247+ exists = True ,
1248+ mandatory = True ,
1249+ argstr = "--eddyIdx=%s" ,
1250+ desc = ("File containing indices for all volumes into acquisition "
1251+ "parameters" )
1252+ )
1253+ param_file = File (
1254+ exists = True ,
1255+ mandatory = True ,
1256+ argstr = "--eddyParams=%s" ,
1257+ desc = "File containing acquisition parameters"
1258+ )
1259+ mask_file = File (
1260+ exists = True ,
1261+ mandatory = True ,
1262+ argstr = "--mask=%s" ,
1263+ desc = "Binary mask file"
1264+ )
1265+ bval_file = File (
1266+ exists = True ,
1267+ mandatory = True ,
1268+ argstr = "--bvals=%s" ,
1269+ desc = "b-values file"
1270+ )
1271+ bvec_file = File (
1272+ exists = True ,
1273+ argstr = "--bvecs=%s" ,
1274+ desc = ("b-vectors file - only used when <base_name>.eddy_residuals "
1275+ "file is present" )
1276+ )
1277+ output_dir = traits .Str (
1278+ name_template = '%s.qc' ,
1279+ name_source = ['base_name' ],
1280+ argstr = '--output-dir=%s' ,
1281+ desc = "Output directory - default = '<base_name>.qc'" ,
1282+ )
1283+ field = File (
1284+ exists = True ,
1285+ argstr = '--field=%s' ,
1286+ desc = "TOPUP estimated field (in Hz)" ,
1287+ )
1288+ slice_spec = File (
1289+ exists = True ,
1290+ argstr = '--slspec=%s' ,
1291+ desc = "Text file specifying slice/group acquisition" ,
1292+ )
1293+ verbose = traits .Bool (
1294+ argstr = '--verbose' ,
1295+ desc = "Display debug messages" ,
1296+ )
1297+
1298+
1299+ class EddyQuadOutputSpec (TraitedSpec ):
1300+ qc_json = File (
1301+ exists = True ,
1302+ desc = ("Single subject database containing quality metrics and data "
1303+ "info." )
1304+ )
1305+ qc_pdf = File (
1306+ exists = True ,
1307+ desc = "Single subject QC report."
1308+ )
1309+ avg_b_png = traits .List (
1310+ File (exists = True ),
1311+ desc = ("Image showing mid-sagittal, -coronal and -axial slices of "
1312+ "each averaged b-shell volume." )
1313+ )
1314+ avg_b0_pe_png = traits .List (
1315+ File (exists = True ),
1316+ desc = ("Image showing mid-sagittal, -coronal and -axial slices of "
1317+ "each averaged pe-direction b0 volume. Generated when using "
1318+ "the -f option." )
1319+ )
1320+ cnr_png = traits .List (
1321+ File (exists = True ),
1322+ desc = ("Image showing mid-sagittal, -coronal and -axial slices of "
1323+ "each b-shell CNR volume. Generated when CNR maps are "
1324+ "available." )
1325+ )
1326+ vdm_png = File (
1327+ exists = True ,
1328+ desc = ("Image showing mid-sagittal, -coronal and -axial slices of "
1329+ "the voxel displacement map. Generated when using the -f "
1330+ "option." )
1331+ )
1332+ residuals = File (
1333+ exists = True ,
1334+ desc = ("Text file containing the volume-wise mask-averaged squared "
1335+ "residuals. Generated when residual maps are available." )
1336+ )
1337+ clean_volumes = File (
1338+ exists = True ,
1339+ desc = ("Text file containing a list of clean volumes, based on "
1340+ "the eddy squared residuals. To generate a version of the "
1341+ "pre-processed dataset without outlier volumes, use: "
1342+ "`fslselectvols -i <eddy_corrected_data> -o "
1343+ "eddy_corrected_data_clean --vols=vols_no_outliers.txt`" )
1344+ )
1345+
1346+
1347+ class EddyQuad (FSLCommand ):
1348+ """
1349+ Interface for FSL eddy_quad, a tool for generating single subject reports
1350+ and storing the quality assessment indices for each subject.
1351+ `User guide <https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/eddyqc/UsersGuide>`_
1352+
1353+ Examples
1354+ --------
1355+
1356+ >>> from nipype.interfaces.fsl import EddyQuad
1357+ >>> quad = EddyQuad()
1358+ >>> quad.inputs.base_name = 'eddy_corrected'
1359+ >>> quad.inputs.idx_file = 'epi_index.txt'
1360+ >>> quad.inputs.param_file = 'epi_acqp.txt'
1361+ >>> quad.inputs.mask_file = 'epi_mask.nii'
1362+ >>> quad.inputs.bval_file = 'bvals.scheme'
1363+ >>> quad.inputs.bvec_file = 'bvecs.scheme'
1364+ >>> quad.inputs.output_dir = 'eddy_corrected.qc'
1365+ >>> quad.inputs.field = 'fieldmap_phase_fslprepared.nii'
1366+ >>> quad.inputs.verbose = True
1367+ >>> quad.cmdline
1368+ 'eddy_quad eddy_corrected --bvals=bvals.scheme --bvecs=bvecs.scheme \
1369+ --field=fieldmap_phase_fslprepared.nii --eddyIdx=epi_index.txt \
1370+ --mask=epi_mask.nii --output-dir=eddy_corrected.qc --eddyParams=epi_acqp.txt \
1371+ --verbose'
1372+ >>> res = quad.run() # doctest: +SKIP
1373+
1374+ """
1375+ _cmd = 'eddy_quad'
1376+ input_spec = EddyQuadInputSpec
1377+ output_spec = EddyQuadOutputSpec
1378+
1379+ def _list_outputs (self ):
1380+ from glob import glob
1381+ outputs = self .output_spec ().get ()
1382+
1383+ # If the output directory isn't defined, the interface seems to use
1384+ # the default but not set its value in `self.inputs.output_dir`
1385+ if not isdefined (self .inputs .output_dir ):
1386+ out_dir = os .path .abspath (os .path .basename (self .inputs .base_name ) + '.qc.nii.gz' )
1387+ else :
1388+ out_dir = os .path .abspath (self .inputs .output_dir )
1389+
1390+ outputs ['qc_json' ] = os .path .join (out_dir , 'qc.json' )
1391+ outputs ['qc_pdf' ] = os .path .join (out_dir , 'qc.pdf' )
1392+
1393+ # Grab all b* files here. This will also grab the b0_pe* files
1394+ # as well, but only if the field input was provided. So we'll remove
1395+ # them later in the next conditional.
1396+ outputs ['avg_b_png' ] = sorted (glob (
1397+ os .path .join (out_dir , 'avg_b*.png' )
1398+ ))
1399+
1400+ if isdefined (self .inputs .field ):
1401+ outputs ['avg_b0_pe_png' ] = sorted (glob (
1402+ os .path .join (out_dir , 'avg_b0_pe*.png' )
1403+ ))
1404+
1405+ # The previous glob for `avg_b_png` also grabbed the
1406+ # `avg_b0_pe_png` files so we have to remove them
1407+ # from `avg_b_png`.
1408+ for fname in outputs ['avg_b0_pe_png' ]:
1409+ outputs ['avg_b_png' ].remove (fname )
1410+
1411+ outputs ['vdm_png' ] = os .path .join (out_dir , 'vdm.png' )
1412+
1413+ outputs ['cnr_png' ] = sorted (glob (os .path .join (out_dir , 'cnr*.png' )))
1414+
1415+ residuals = os .path .join (out_dir , 'eddy_msr.txt' )
1416+ if os .path .isfile (residuals ):
1417+ outputs ['residuals' ] = residuals
1418+
1419+ clean_volumes = os .path .join (out_dir , 'vols_no_outliers.txt' )
1420+ if os .path .isfile (clean_volumes ):
1421+ outputs ['clean_volumes' ] = clean_volumes
1422+
1423+ return outputs
1424+
0 commit comments