@@ -1232,3 +1232,193 @@ def _run_interface(self, runtime):
1232
1232
if runtime .stderr :
1233
1233
self .raise_exception (runtime )
1234
1234
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