@@ -345,6 +345,9 @@ class CompCorInputSpec(BaseInterfaceInputSpec):
345345 'unspecified' )
346346 save_pre_filter = traits .Either (
347347 traits .Bool , File , desc = 'Save pre-filter basis as text file' )
348+ ignore_initial_volumes = traits .Range (
349+ low = 0 , usedefault = True ,
350+ desc = 'Number of volumes at start of series to ignore' )
348351
349352
350353class CompCorOutputSpec (TraitedSpec ):
@@ -357,6 +360,26 @@ class CompCor(BaseInterface):
357360 """
358361 Interface with core CompCor computation, used in aCompCor and tCompCor
359362
363+ CompCor provides three pre-filter options, all of which include per-voxel
364+ mean removal:
365+ - polynomial: Legendre polynomial basis
366+ - cosine: Discrete cosine basis
367+ - False: mean-removal only
368+
369+ In the case of ``polynomial`` and ``cosine`` filters, a pre-filter file may
370+ be saved with a row for each volume/timepoint, and a column for each
371+ non-constant regressor.
372+ If no non-constant (mean-removal) columns are used, this file may be empty.
373+
374+ If ``ignore_initial_volumes`` is set, then the specified number of initial
375+ volumes are excluded both from pre-filtering and CompCor component
376+ extraction.
377+ Each column in the components and pre-filter files are prefixe with zeros
378+ for each excluded volume so that the number of rows continues to match the
379+ number of volumes in the input file.
380+ In addition, for each excluded volume, a column is added to the pre-filter
381+ file with a 1 in the corresponding row.
382+
360383 Example
361384 -------
362385
@@ -417,6 +440,12 @@ def _run_interface(self, runtime):
417440 header = imgseries .header )
418441 mask_images = [img ]
419442
443+ skip_vols = self .inputs .ignore_initial_volumes
444+ if skip_vols :
445+ imgseries = imgseries .__class__ (
446+ imgseries .get_data ()[..., skip_vols :], imgseries .affine ,
447+ imgseries .header )
448+
420449 mask_images = self ._process_masks (mask_images , imgseries .get_data ())
421450
422451 TR = 0
@@ -441,16 +470,33 @@ def _run_interface(self, runtime):
441470 imgseries .get_data (), mask_images , self .inputs .num_components ,
442471 self .inputs .pre_filter , degree , self .inputs .high_pass_cutoff , TR )
443472
473+ if skip_vols :
474+ old_comp = components
475+ nrows = skip_vols + components .shape [0 ]
476+ components = np .zeros ((nrows , components .shape [1 ]),
477+ dtype = components .dtype )
478+ components [skip_vols :] = old_comp
479+
444480 components_file = os .path .join (os .getcwd (), self .inputs .components_file )
445481 np .savetxt (components_file , components , fmt = b"%.10f" , delimiter = '\t ' ,
446482 header = self ._make_headers (components .shape [1 ]), comments = '' )
447483
448484 if self .inputs .pre_filter and self .inputs .save_pre_filter :
449485 pre_filter_file = self ._list_outputs ()['pre_filter_file' ]
450- ftype = {'polynomial' : 'poly ' ,
451- 'cosine' : 'cos ' }[self .inputs .pre_filter ]
486+ ftype = {'polynomial' : 'Legendre ' ,
487+ 'cosine' : 'Cosine ' }[self .inputs .pre_filter ]
452488 ncols = filter_basis .shape [1 ] if filter_basis .size > 0 else 0
453489 header = ['{}{:02d}' .format (ftype , i ) for i in range (ncols )]
490+ if skip_vols :
491+ old_basis = filter_basis
492+ # nrows defined above
493+ filter_basis = np .zeros ((nrows , ncols + skip_vols ),
494+ dtype = filter_basis .dtype )
495+ if old_basis .size > 0 :
496+ filter_basis [skip_vols :, :ncols ] = old_basis
497+ filter_basis [:skip_vols , - skip_vols :] = np .eye (skip_vols )
498+ header .extend (['NonSteadyStateOutlier{:02d}' .format (i )
499+ for i in range (skip_vols )])
454500 np .savetxt (pre_filter_file , filter_basis , fmt = b'%.10f' ,
455501 delimiter = '\t ' , header = '\t ' .join (header ), comments = '' )
456502
0 commit comments