function TabDat = spm_VOI(SPM,VOL,Dis,Num,hReg) % List of local maxima and adjusted p-values for a small Volume of Interest % FORMAT TabDat = spm_VOI(SPM,VOL,Dis,Num,hReg) % % SPM - structure containing SPM, distribution & filtering details % - required fields are: % .swd - SPM working directory - directory containing current SPM.mat % .Z - minimum of n Statistics {filtered on u and k} % .n - number of conjoint tests % .STAT - distribution {Z, T, X or F} % .df - degrees of freedom [df{interest}, df{residual}] % .u - height threshold % .k - extent threshold {resels} % .XYZ - location of voxels {voxel coords} % .XYZmm - location of voxels {mm} % % VOL - structure containing details of volume analysed % - required fields are: % .S - search Volume {voxels} % .R - search Volume {resels} % .FWHM - smoothness {voxels} % .M - voxels - > mm matrix % .VOX - voxel dimensions {mm} % .DIM - image dimensions {voxels} - column vector % .Vspm - Mapped statistic image(s) % .Msk - mask: a list of scalar indicies into image voxel space % % Dis - Minimum distance between maxima % (Passed to spm_list.m: Defaults on missing or empty) % Num - Maxiumum number of local maxima tabulated per cluster % (Passed to spm_list.m: Defaults on missing or empty) % hReg - Handle of results section XYZ registry (see spm_results_ui.m) % % TabDat - Structure containing table data % - see spm_list for definition % %_______________________________________________________________________ % % spm_VOI is called by the SPM results section and takes variables in % SPM and VOL to compute p-values corrected for a specified volume of % interest. % % The volume of interest may be defined as a box or sphere centred on % the current voxel, by the cluster in which it is embedded, or by an % external mask image. % % If the VOI is defined by the cluster in which it is embedded, this % cluster must have been defined independently of the SPM using a mask % based on an orthogonal contrast and u = -Inf (i.e. p = 1) % % External mask images should be in the same orientation as the SPM % (i.e. as the input used in stats estimation). The VOI is defined by % voxels with values greater than 0. % % See also: spm_list %_______________________________________________________________________ % @(#)spm_VOI.m 2.13 Karl Friston 01/10/18 % 02/09/30, by T. Nichols (Non SCCS fix) %-Parse arguments %----------------------------------------------------------------------- if nargin<2, error('insufficient arguments'), end if nargin<5, hReg = []; end if nargin<4, Num = []; end if isempty(Num), Num = 16; end if nargin<3, Dis = []; end if isempty(Dis), Dis = 04; end %-Title %----------------------------------------------------------------------- spm('FigName',['SPM{',SPM.STAT,'}: Small Volume Correction']); %-Get current location %----------------------------------------------------------------------- xyzmm = spm_results_ui('GetCoords'); v2mm = diag([VOL.M(1,1) VOL.M(2,2) VOL.M(3,3)]); mm2v = inv(v2mm); xyz = VOL.M \ [xyzmm; 1]; xyz = xyz(1:3); %-Specify search volume %----------------------------------------------------------------------- str = sprintf(' at [%.0f,%.0f,%.0f]',xyzmm(1),xyzmm(2),xyzmm(3)); SPACE = spm_input('Search volume...',-1,'m',... {['Sphere',str],['Box',str],'Nearest cluster',... 'Image'},['S','B','V','I']); Q = ones(1,size(SPM.XYZmm,2)); vsc = [1 1 1]; %-Voxel size scaling for FWHM DIM = VOL.DIM; switch SPACE, case 'S' % Sphere %--------------------------------------------------------------- D = spm_input('radius of spherical VOI {mm}',-2); str = sprintf('%0.1fmm sphere',D); j = find(sum((SPM.XYZmm - xyzmm*Q).^2) <= D^2); d = ceil(mm2v*[D D D]')+1; rxyz = round(xyz); [jx jy jz] = ndgrid(rxyz(1)+(-d(1):d(1)),... rxyz(2)+(-d(2):d(2)),... rxyz(3)+(-d(3):d(3))); XYZ = [jx(:) jy(:) jz(:)]'; XYZ(:,any(XYZ<=0)) = []; XYZ(:,(XYZ(1,:)>DIM(1)|XYZ(2,:)>DIM(2)|XYZ(3,:)>DIM(3))) = []; Qs = ones(1,size(XYZ,2)); js = find(sum((v2mm*(XYZ - xyz*Qs)).^2) <= D^2); XYZ = XYZ(:,js); % Convert to indicies; map T image, check image for zeros; delete % those entries from XYZ D = D./VOL.VOX; S = (4/3)*pi*prod(D); case 'B' % Box %--------------------------------------------------------------- D = spm_input('box dimensions [k l m] {mm}',-2); str = sprintf('%0.1f x %0.1f x %0.1f mm box',D(1),D(2),D(3)); j = find(all(abs(SPM.XYZmm - xyzmm*Q) <= D(:)*Q/2)); d = ceil(mm2v*D(:))+1; rxyz = round(xyz); [jx jy jz] = ndgrid(rxyz(1)+(-d(1):d(1)),... rxyz(2)+(-d(2):d(2)),... rxyz(3)+(-d(3):d(3))); XYZ = [jx(:) jy(:) jz(:)]'; XYZ(:,any(XYZ<=0)) = []; XYZ(:,(XYZ(1,:)>DIM(1)|XYZ(2,:)>DIM(2)|XYZ(3,:)>DIM(3))) = []; Qs = ones(1,size(XYZ,2)); js = find(all(abs(v2mm*(XYZ - xyz*Qs)) <= D(:)*Qs/2)); XYZ = XYZ(:,js); % Convert to indicies; map T image, check image for zeros; delete % those entries from XYZ D = D(:)./VOL.VOX; S = prod(D); case 'V' %-Voxels %--------------------------------------------------------------- if ~length(SPM.XYZ) spm('alert!','No suprathreshold clusters!',mfilename,0); spm('FigName',['SPM{',SPM.STAT,'}: Results']); return end [xyzmm,i] = spm_XYZreg('NearestXYZ',xyzmm,SPM.XYZmm); spm_results_ui('SetCoords',xyzmm); A = spm_clusters(SPM.XYZ); j = find(A == A(i)); str = sprintf('%0.0f voxel cluster',length(j)); D = SPM.XYZ(:,j); S = length(j); case 'I' % Image %--------------------------------------------------------------- %-The VOI image must be in the same (real) space as the SPM: %-Although the masking code below does take account of the % .mat file for the VOI image, the orientation of the VOI image % .mat file is *not* used in the resel calculation, % (see spm_resels_vol.c) so any mat file should contain % no rotations / shears relative to the SPM, for accuracy. im = spm_get(1,'img','Image defining volume subset'); D = spm_vol(im); tM = D.mat \ VOL.M; if any(tM([2:5,7:10,12])) spm('alert!',{ 'Mask image rotated/sheared!',... '(relative to SPM image)',... 'Can''t use for SVC.'},mfilename,0); spm('FigName',['SPM{',SPM.STAT,'}: Results']); return end mXYZ = D.mat \ [SPM.XYZmm; ones(1, size(SPM.XYZmm, 2))]; j = find(spm_sample_vol(D, mXYZ(1,:),mXYZ(2,:),mXYZ(3,:),0) > 0); str = sprintf('image mask: %s',spm_str_manip(im,'a30')); %-Compute in-mask volume S: % Correct for differences in mask and SPM voxel sizes Y = spm_read_vols(D); vsc = sqrt(sum(D.mat(1:3,1:3).^2)) ./ VOL.VOX'; S = sum(Y(:)>0) * prod(vsc); end spm('Pointer','Watch') %-Select voxels within subspace %----------------------------------------------------------------------- SPM.Z = SPM.Z(j); SPM.XYZ = SPM.XYZ(:,j); SPM.XYZmm = SPM.XYZmm(:,j); VOL.R = spm_resels(VOL.FWHM./vsc,D,SPACE); VOL.S = S; if (SPACE=='S') | (SPACE=='B') VOL.Msk = XYZ(1,:) + ... (XYZ(2,:)-1)*DIM(1) + ... (XYZ(3,:)-1)*DIM(1)*DIM(2); % Eliminate voxels with no data; assume zero masking of stat images Y = spm_read_vols(VOL.Vspm); VOL.Msk(Y(VOL.Msk)==0) = []; % Note, we get a more accurate (smaller) S, but the RFT doesn't % make use of it. VOL.S = length(VOL.Msk); elseif (SPACE=='V') VOL.Msk = SPM.XYZ(1,:) + ... (SPM.XYZ(2,:)-1)*DIM(1) + ... (SPM.XYZ(3,:)-1)*DIM(1)*DIM(2); elseif (SPACE=='I') VOL.Msk = D; end %-Tabulate p values %----------------------------------------------------------------------- str = sprintf('search volume: %s',str); if any(strcmp(SPACE,{'S','B','V'})) str = sprintf('%s at [%.0f,%.0f,%.0f]',str,xyzmm(1),xyzmm(2),xyzmm(3)); end TabDat = spm_list('List',SPM,VOL,Dis,Num,str,hReg); %-Reset title %----------------------------------------------------------------------- spm('FigName',['SPM{',SPM.STAT,'}: Results']);