function [FWHM,VRpv,R] = spm_est_smoothness(V,VM,ndf) % Estimation of smoothness based on [residual] images % FORMAT [FWHM,VRpv,R] = spm_est_smoothness(V,VM,[ndf]) % % V - Filenames or mapped standardized residual images % VM - Filename of mapped mask image % ndf - A 2-vector, [n df], the original n & dof of the linear model % % FWHM - estimated FWHM in all image directions % VRpv - handle of Resels per Voxel image % R - vector of resel counts %__________________________________________________________________________ % % spm_est_smoothness returns a spatial smoothness estimator based on the % variances of the normalized spatial derivatives as described in K. % Worsley, (1996). Inputs are a mask image and a number of standardized % residual images, or any set of mean zero, unit variance images. Output % is a global estimate of the smoothness expressed as the FWHM of an % equivalent Gaussian point spread function. An estimate of resels per % voxels (see spm_spm) is written as an image file ('RPV.') to the % current directory. % % To improve the accuracy of the smoothness estimation the error degrees % of freedom can be supplied. Since it is not assumed that all residual % images are passed to this function, the full, original sample size n % must be supplied as well. % % The mask image specifies voxels, used in smoothness estimation, by % assigning them non-zero values. The dimensions, voxel sizes, orientation % of all images must be the same. The dimensions of the images can be of % dimensions 0, 1, 2 and 3. % % Note that 1-dim images (lines) must exist in the 1st dimension and % 2-dim images (slices) in the first two dimensions. The estimated fwhm % for any non-existing dimension is infinity. %__________________________________________________________________________ % % Refs: % % K.J. Worsley (1996). An unbiased estimator for the roughness of a % multivariate Gaussian random field. Technical Report, Department of % Mathematics and Statistics, McGill University % % S.J. Kiebel, J.B. Poline, K.J. Friston, A.P. Holmes, and K.J. Worsley. % Robust Smoothness Estimation in Statistical Parametric Maps Using % Standardized Residuals from the General Linear Model. NeuroImage, % 10:756-766, 1999. % % S. Hayasaka, K. Phan, I. Liberzon, K.J. Worsley, T.E. Nichols (2004). % Nonstationary cluster-size inference with random field and permutation % methods. NeuroImage, 22:676-687, 2004. %__________________________________________________________________________ % Copyright (C) 2002-2015 Wellcome Trust Centre for Neuroimaging % Stefan Kiebel, Tom Nichols % $Id: spm_est_smoothness.m 6894 2016-09-30 16:48:46Z spm $ %-Assign input arguments %-------------------------------------------------------------------------- if nargin < 1 V = spm_select(Inf,'image','Select residual images',{},pwd,'^ResI.*.{3}$'); end if nargin < 2 VM = spm_select(1,'image','Select mask image',{},pwd,'^mask\..{3}$'); end if nargin < 3, ndf = [NaN NaN]; end if numel(ndf) ~= 2 error('ndf argument must be of length 2 ([n df]).') end %-Initialise %-------------------------------------------------------------------------- V = spm_vol(V); VM = spm_vol(VM); if any(isnan(ndf)) ndf = [numel(V) numel(V)]; % Assume full df end n_full = ndf(1); edf = ndf(2); %-Initialise RESELS per voxel image %-------------------------------------------------------------------------- VRpv = struct( 'fname',['RPV' spm_file_ext],... 'dim', VM.dim(1:3),... 'dt', [spm_type('float64') spm_platform('bigend')],... 'mat', VM.mat,... 'pinfo', [1 0 0]',... 'descrip', 'spm_spm: resels per voxel'); VRpv = spm_create_vol(VRpv); %-Dimensionality of image %-------------------------------------------------------------------------- D = 3 - sum(VM.dim(1:3) == 1); if D == 0 FWHM = [Inf Inf Inf]; R = [0 0 0]; return; end %-Find voxels within mask %-------------------------------------------------------------------------- d = spm_read_vols(VM); [Ix,Iy,Iz] = ndgrid(1:VM.dim(1),1:VM.dim(2),1:VM.dim(3)); Ix = Ix(d~=0); Iy = Iy(d~=0); Iz = Iz(d~=0); %-Compute covariance of derivatives %-------------------------------------------------------------------------- str = 'Spatial non-sphericity (over scans)'; fprintf('%-40s: %30s',str,'...estimating derivatives'); %-# spm_progress_bar('Init',100,'smoothness estimation',''); L = zeros(size(Ix,1),D,D); ssq = zeros(size(Ix,1),1); for i = 1:numel(V) [d,dx,dy,dz] = spm_sample_vol(V(i),Ix,Iy,Iz,1); % sum of squares %---------------------------------------------------------------------- ssq = ssq + d.^2; % covariance of finite differences %---------------------------------------------------------------------- if D >= 1 L(:,1,1) = L(:,1,1) + dx.*dx; end if D >= 2 L(:,1,2) = L(:,1,2) + dx.*dy; L(:,2,2) = L(:,2,2) + dy.*dy; end if D >= 3 L(:,1,3) = L(:,1,3) + dx.*dz; L(:,2,3) = L(:,2,3) + dy.*dz; L(:,3,3) = L(:,3,3) + dz.*dz; end spm_progress_bar('Set',100*i/numel(V)); end spm_progress_bar('Clear') %-Scale sum into an average (and account for DF) % The standard result uses normalised residuals e/sqrt(RSS) and % % \hat\Lambda = grad(e/sqrt(RSS))' * grad(e/sqrt(RSS)) % % In terms of standardized residuals e/sqrt(RMS) this is % % \hat\Lambda = (1/DF) * grad(e/sqrt(RMS))' * grad(e/sqrt(RMS)) % % but both of these expressions assume that the RSS or RMS correspond to % the full set of residuals considered. However, spm_spm only considers % upto MAXRES residual images. To adapt, re-write the above as a scaled % average over n scans % % \hat\Lambda = (n/DF) * ( (1/n) * grad(e/sqrt(RMS))' * grad(e/sqrt(RMS)) ) % % I.e. the roughness estimate \hat\Lambda is an average of outer products % of standardized residuals (where the average is over scans), scaled by % n/DF. Hence, we can use only a subset of scans simply by replacing this % last average term with an average over the subset. % % See Hayasaka et al, p. 678, for more on estimating roughness with % standardized residuals (e/sqrt(RMS)) instead of normalised residuals % (e/sqrt(RSS)). Note that the names arise from the fact that % sqrt(RSS) = sqrt(r'*r) is norm(r), while sqrt(RMS) = sqrt(r'*r/edf) % is the unbiased (ReML) estimate of the standard deviation. %-------------------------------------------------------------------------- L = L/numel(V); % Average L = L*(n_full/edf); % Scale %-Evaluate determinant (and xyz components for FWHM) %-------------------------------------------------------------------------- if D == 1 resel_xyz = L; resel_img = L; end if D == 2 resel_xyz = [L(:,1,1) L(:,2,2)]; resel_img = L(:,1,1).*L(:,2,2) - ... L(:,1,2).*L(:,1,2); end if D == 3 resel_xyz = [L(:,1,1) L(:,2,2) L(:,3,3)]; resel_img = L(:,1,1).*L(:,2,2).*L(:,3,3) + ... L(:,1,2).*L(:,2,3).*L(:,1,3)*2 - ... L(:,1,1).*L(:,2,3).*L(:,2,3) - ... L(:,1,2).*L(:,1,2).*L(:,3,3) - ... L(:,1,3).*L(:,2,2).*L(:,1,3); end resel_img(resel_img<0) = 0; % Convert det(Lambda) and diag(Lambda) to units of resels resel_img = sqrt(resel_img/(4*log(2))^D); resel_xyz = sqrt(resel_xyz/(4*log(2))); %-Optional mask-weighted smoothing of RPV image %-------------------------------------------------------------------------- if spm_get_defaults('stats.rft.nonstat') fwhm_vox = 3; else fwhm_vox = 0; end if any(fwhm_vox) if length(fwhm_vox) == 1, fwhm_vox = fwhm_vox([1 1 1]); end % Convert resel_img at in-mask voxels to resel volume mask = spm_read_vols(VM) > 0; RPV = zeros(size(mask)); RPV(mask) = resel_img; % Remove invalid mask voxels, i.e. edge voxels with missing derivatives smask = double(mask & isfinite(RPV)); % leaves mask for resel_img below % Smooth RPV volume (note that NaNs are treated as zeros in spm_smooth) spm_smooth(RPV, RPV, fwhm_vox); % Smooth mask and decide how far to trust smoothing-based extrapolation spm_smooth(smask, smask, fwhm_vox); infer = smask > 1e-3; % require sum of voxel's in-mask weights > 1e-3 % Normalise smoothed RPV by smoothed mask RPV( infer) = RPV(infer) ./ smask(infer); RPV(~infer) = NaN; % spm_list handles remaining (unlikely) in-mask NaNs % Get data at in-mask voxels; smoothed resel_img conforms with original resel_img = RPV(mask); end %-Write Resels Per Voxel image %-------------------------------------------------------------------------- fprintf('%s%30s',repmat(sprintf('\b'),1,30),'...writing resels/voxel image');%-# for i = 1:VM.dim(3) d = NaN(VM.dim(1:2)); I = find(Iz == i); if ~isempty(I) d(sub2ind(VM.dim(1:2), Ix(I), Iy(I))) = resel_img(I); end VRpv = spm_write_plane(VRpv, d, i); end %-(unbiased) RESEL estimator and Global equivalent FWHM % where we desire FWHM with components proportional to 1./mean(resel_xyz), % but scaled so prod(1./FWHM) agrees with (the unbiased) mean(resel_img). %-------------------------------------------------------------------------- i = isnan(ssq) | ssq < sqrt(eps); resel_img = mean(resel_img(~i,:)); resel_xyz = mean(resel_xyz(~i,:)); RESEL = resel_img^(1/D)*(resel_xyz/prod(resel_xyz)^(1/D)); FWHM = full(sparse(1,1:D,1./RESEL,1,3)); FWHM(isnan(FWHM)) = 0; FWHM(~FWHM) = 1; %-resel counts for search volume (defined by mask) %-------------------------------------------------------------------------- % R0 = spm_resels_vol(VM,[1 1 1])'; % R = R0.*(resel.^([0:3]/3)); % OR R = spm_resels_vol(VM,FWHM)'; fprintf('%s%30s\n',repmat(sprintf('\b'),1,30),'...done'); %-#