[Neuroimaging] [nipype] updated external mask handling in SPM

Dimitri Papadopoulos Orfanos dimitri.papadopoulos at cea.fr
Wed Dec 9 06:43:25 EST 2015


Dear all,

I looked into masking in SPM and with some help I came up with the
following findings:


* The SPM user interface only sets "analysis threshold masking", see
file spm_fmri_spm_ui.m around line 375:

%-Masking
%==========================================================================

%-Masking threshold, as proportion of globals
%--------------------------------------------------------------------------
try
    gMT = SPM.xM.gMT;
catch
    gMT = spm_get_defaults('mask.thresh');
end
TH = g.*gSF*gMT;

%-Place masking structure in xM
%--------------------------------------------------------------------------
SPM.xM = struct(...
    'T',   ones(q,1),...
    'TH',  TH,...
    'gMT', gMT,...
    'I',   0,...
    'VM',  {[]},...
    'xs',  struct('Masking','analysis threshold'));


* When specifying the model in batch jobs, SPM itself sets "explicit
masking" in the following way in file /spm_run_fmri_spec.m around line 385:

%-Explicit mask
%--------------------------------------------------------------------------
if ~design_only
    if ~isempty(job.mask{1})
        SPM.xM.VM         = spm_data_hdr_read(job.mask{1});
        SPM.xM.xs.Masking = [SPM.xM.xs.Masking, '+explicit mask'];
    end
end


* The intent of SPM seems to be that the "explicit mask" is a superset
of the final mask, *not* that it will be the exact mask used during
estimation. From spm_spm.m around line 50:

%         xM.VM - struct array of explicit mask image handles
%       - (empty if no explicit masks)
%               - Explicit mask images are >0 for valid voxels to assess.
%               - Mask images can have any orientation, voxel size or data
%                 type. They are interpolated using nearest neighbour
%                 interpolation to the voxel locations of the data Y.
%       - Note that voxels with constant data (i.e. the same value across
%         scans) are also automatically masked out.



* We find different results with Nipype than when running vanilla SPM
batch jobs.


I would therefore suggest removing this Nipype hack by default:
https://github.com/nipy/nipype/commit/fe9d0e07a288afefb34e99f488ef194a443d6089
It could be added as an option, for different "explicit masking" than
initially.
I intend to propose a patch to remove the current hack. Does anyone have
more insight on this Nipype hack that modifies default SPM behavior and
would be against the patch?

Best,
Dimitri

Le 04/12/2015 16:20, Dimitri Papadopoulos Orfanos a écrit :
> Hi Satra,
> 
> I understand the whole purpose of this piece of code is to get SPM to
> use an explicit mask. This is done by manipulating the masking structure
> "SPM.xM".
> 
> I'm not yet entirely convinced this still needs to be done "behind the
> back of SPM" by modifying SPM.mat outside of the SPM code, but here is
> one of "John's Gems" that recommends this Nipype hack (it refers to SPM2
> though):
>   http://blogs.warwick.ac.uk/nichols/entry/spm2_gem_12/
> 
>   With fMRI data/models, SPM2 is fully capable of doing explicit
>   masking, but the user interface for fMRI doesn't ask for it.
>   One way to do this type of masking anyway is to change the
>   SPM.mat file *after* you specify your model, but *before*
>   clicking 'Estimate'.
>   Specifically:
>      1. Load the SPM.mat file,
>                load SPM
>         set the SPM.xM.TH values all to -Inf,
>                SPM.xM.TH = -Inf*SPM.xM.TH;
>         and, in case that you have an image format not allowing
>         NaNs, set SPM.xM.I to 0
>                SPM.xM.I = 0;
>      2. If using a mask image, set SPM.xM.VM to a vector of
>         structures, where each structure element is the output
>         of spm_vol. For instance:
>                SPM.xM.VM = spm_vol('Maskimage');
>      3. Finally, save by
>                save SPM SPM
> 
> 
> Most importantly, I am puzzled by the fact that we don't find the same
> results when running SPM8 via Nipype than when running SPM8 back in
> 2010-2012 using batches created from the SPM user interface. At this
> point I am not sure if this is an issue with Nipype, an issue with our
> own scripts created in 2009-2010, or perhaps related to changes in SPM8.
> I would like to the bottom of it - time permitting... I don't feel
> comfortable with the idea that the default SPM workflow could have been
> modified and I'd like to understand the cause of the discrepancy in the
> results.
> 
> Best,
> Dimitri
> 
> Le 02/12/2015 23:17, Satrajit Ghosh a écrit :
>> hi dimitri,
>>
>> it's been a long while since those lines were written. but i believe
>> this was written as noted in the comments to support the case where the
>> user simply wanted spm to use the explicit mask. the other reason for it
>> was that there were several places that could control spm options (e.g.,
>> config file). we did not want to rely on the config file.
>>
>> i believe it was more of a perspective on what an explicit mask meant. i
>> can't remember if this was discussed on the spm list or came from best
>> practices in our lab at that time.
>>
>> cheers,
>>
>> satra
>>
>> On Wed, Dec 2, 2015 at 9:45 AM, Dimitri Papadopoulos Orfanos
>> <dimitri.papadopoulos at cea.fr <mailto:dimitri.papadopoulos at cea.fr>> wrote:
>>
>>     Hi,
>>
>>     I have a question on the following commit:
>>     https://github.com/nipy/nipype/commit/fe9d0e07a288afefb34e99f488ef194a443d6089
>>
>>     Could someone explain the rationale behind the addition of this piece of
>>     code in nipype/interfaces/spm/model.py? We came across this code while
>>     trying to reproduce the results obtained with an old version of SPM8 run
>>     manually vs. the latest version of SPM8 run from Nipype.
>>
>>             if isdefined(self.inputs.mask_image):
>>                 # SPM doesn't handle explicit masking properly, especially
>>                 # when you want to use the entire mask image
>>                 postscript = "load SPM;\n"
>>                 postscript += "SPM.xM.VM = spm_vol('%s');\n" %
>>     list_to_filename(self.inputs.mask_image)
>>                 postscript += "SPM.xM.I = 0;\n"
>>                 postscript += "SPM.xM.T = [];\n"
>>                 postscript += "SPM.xM.TH <http://SPM.xM.TH> =
>>     ones(size(SPM.xM.TH <http://SPM.xM.TH>))*(%s);\n" %
>>     self.inputs.mask_threshold
>>                 postscript += "SPM.xM.xs = struct('Masking', 'explicit
>>     masking only');\n"
>>                 postscript += "save SPM SPM;\n"
>>
>>     We have understood almost all causes for differences in results. The
>>     only cause that remains to be explained is this Nipype hack. Has perhaps
>>     this issue been discussed on the SPM mailing list?
>>
>>     Best,
>>     --
>>     Dimitri Papadopoulos
>>     CEA/Saclay
>>     I2BM, NeuroSpin
>>     F-91191 Gif-sur-Yvette cedex, France
>>     _______________________________________________
>>     Neuroimaging mailing list
>>     Neuroimaging at python.org <mailto:Neuroimaging at python.org>
>>     https://mail.python.org/mailman/listinfo/neuroimaging
>>
>>
>>
>>
>> _______________________________________________
>> Neuroimaging mailing list
>> Neuroimaging at python.org
>> https://mail.python.org/mailman/listinfo/neuroimaging


More information about the Neuroimaging mailing list