spm_getSPM.m 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932
  1. function [SPM,xSPM] = spm_getSPM(varargin)
  2. % Compute a specified and thresholded SPM/PPM following estimation
  3. % FORMAT [SPM,xSPM] = spm_getSPM;
  4. % Query SPM in interactive mode.
  5. %
  6. % FORMAT [SPM,xSPM] = spm_getSPM(xSPM);
  7. % Query SPM in batch mode. See below for a description of fields that may
  8. % be present in xSPM input. Values for missing fields will be queried
  9. % interactively.
  10. %
  11. % xSPM - structure containing SPM, distribution & filtering details
  12. % .swd - SPM working directory - directory containing current SPM.mat
  13. % .title - title for comparison (string)
  14. % .Z - minimum of Statistics {filtered on u and k}
  15. % .n - conjunction number <= number of contrasts
  16. % .STAT - distribution {Z, T, X, F or P}
  17. % .df - degrees of freedom [df{interest}, df{residual}]
  18. % .STATstr - description string
  19. % .Ic - indices of contrasts (in SPM.xCon)
  20. % .Im - indices of masking contrasts (in xCon)
  21. % .pm - p-value for masking (uncorrected)
  22. % .Ex - flag for exclusive or inclusive masking
  23. % .u - height threshold
  24. % .k - extent threshold {voxels}
  25. % .XYZ - location of voxels {voxel coords}
  26. % .XYZmm - location of voxels {mm}
  27. % .S - search Volume {voxels}
  28. % .R - search Volume {resels}
  29. % .FWHM - smoothness {voxels}
  30. % .M - voxels -> mm matrix
  31. % .iM - mm -> voxels matrix
  32. % .VOX - voxel dimensions {mm} - column vector
  33. % .DIM - image dimensions {voxels} - column vector
  34. % .Vspm - Mapped statistic image(s)
  35. % .Ps - uncorrected P values in searched volume (for voxel FDR)
  36. % .Pp - uncorrected P values of peaks (for peak FDR)
  37. % .Pc - uncorrected P values of cluster extents (for cluster FDR)
  38. % .uc - 0.05 critical thresholds for FWEp, FDRp, FWEc, FDRc
  39. % .thresDesc - description of height threshold (string)
  40. %
  41. % Required fields of SPM
  42. %
  43. % xVol - structure containing details of volume analysed
  44. %
  45. % xX - Design Matrix structure
  46. % - (see spm_spm.m for structure)
  47. %
  48. % xCon - Contrast definitions structure array
  49. % - (see also spm_FcUtil.m for structure, rules & handling)
  50. % .name - Contrast name
  51. % .STAT - Statistic indicator character ('T', 'F' or 'P')
  52. % .c - Contrast weights (column vector contrasts)
  53. % .X0 - Reduced design matrix data (spans design space under Ho)
  54. % Stored as coordinates in the orthogonal basis of xX.X from spm_sp
  55. % Extract using X0 = spm_FcUtil('X0',...
  56. % .iX0 - Indicates how contrast was specified:
  57. % If by columns for reduced design matrix then iX0 contains the
  58. % column indices. Otherwise, it's a string containing the
  59. % spm_FcUtil 'Set' action: Usually one of {'c','c+','X0'}
  60. % .X1o - Remaining design space data (X1o is orthogonal to X0)
  61. % Stored as coordinates in the orthogonal basis of xX.X from spm_sp
  62. % Extract using X1o = spm_FcUtil('X1o',...
  63. % .eidf - Effective interest degrees of freedom (numerator df)
  64. % - Or effect-size threshold for Posterior probability
  65. % .Vcon - Name of contrast (for 'T's) or ESS (for 'F's) image
  66. % .Vspm - Name of SPM image
  67. %
  68. % Evaluated fields in xSPM (input)
  69. %
  70. % xSPM - structure containing SPM, distribution & filtering details
  71. % .swd - SPM working directory - directory containing current SPM.mat
  72. % .title - title for comparison (string)
  73. % .Ic - indices of contrasts (in SPM.xCon)
  74. % .n - conjunction number <= number of contrasts
  75. % .Im - indices of masking contrasts (in xCon)
  76. % .pm - p-value for masking (uncorrected)
  77. % .Ex - flag for exclusive or inclusive masking
  78. % .u - height threshold
  79. % .k - extent threshold {voxels}
  80. % .thresDesc - description of height threshold (string)
  81. %
  82. % In addition, the xCon structure is updated. For newly evaluated
  83. % contrasts, SPM images (spmT_????.{img,hdr}) are written, along with
  84. % contrast (con_????.{img,hdr}) images for SPM{T}'s, or Extra
  85. % Sum-of-Squares images (ess_????.{img,hdr}) for SPM{F}'s.
  86. %
  87. % The contrast images are the weighted sum of the parameter images,
  88. % where the weights are the contrast weights, and are uniquely
  89. % estimable since contrasts are checked for estimability by the
  90. % contrast manager. These contrast images (for appropriate contrasts)
  91. % are suitable summary images of an effect at this level, and can be
  92. % used as input at a higher level when effecting a random effects
  93. % analysis. (Note that the ess_????.{img,hdr} and
  94. % SPM{T,F}_????.{img,hdr} images are not suitable input for a higher
  95. % level analysis.)
  96. %
  97. %__________________________________________________________________________
  98. %
  99. % spm_getSPM prompts for an SPM and applies thresholds {u & k}
  100. % to a point list of voxel values (specified with their locations {XYZ})
  101. % This allows the SPM be displayed and characterized in terms of regionally
  102. % significant effects by subsequent routines.
  103. %
  104. % For general linear model Y = XB + E with data Y, design matrix X,
  105. % parameter vector B, and (independent) errors E, a contrast c'B of the
  106. % parameters (with contrast weights c) is estimated by c'b, where b are
  107. % the parameter estimates given by b=pinv(X)*Y.
  108. %
  109. % Either single contrasts can be examined or conjunctions of different
  110. % contrasts. Contrasts are estimable linear combinations of the
  111. % parameters, and are specified using the SPM contrast manager
  112. % interface [spm_conman.m]. SPMs are generated for the null hypotheses
  113. % that the contrast is zero (or zero vector in the case of
  114. % F-contrasts). See the help for the contrast manager [spm_conman.m]
  115. % for a further details on contrasts and contrast specification.
  116. %
  117. % A conjunction assesses the conjoint expression of multiple effects. The
  118. % conjunction SPM is the minimum of the component SPMs defined by the
  119. % multiple contrasts. Inference on the minimum statistics can be
  120. % performed in different ways. Inference on the Conjunction Null (one or
  121. % more of the effects null) is accomplished by assessing the minimum as
  122. % if it were a single statistic; one rejects the conjunction null in
  123. % favor of the alternative that k=nc, that the number of active effects k
  124. % is equal to the number of contrasts nc. No assumptions are needed on
  125. % the dependence between the tests.
  126. %
  127. % Another approach is to make inference on the Global Null (all effects
  128. % null). Rejecting the Global Null of no (u=0) effects real implies an
  129. % alternative that k>0, that one or more effects are real. A third
  130. % Intermediate approach, is to use a null hypothesis of no more than u
  131. % effects are real. Rejecting the intermediate null that k<=u implies an
  132. % alternative that k>u, that more than u of the effects are real.
  133. %
  134. % The Global and Intermediate nulls use results for minimum fields which
  135. % require the SPMs to be identically distributed and independent. Thus,
  136. % all component SPMs must be either SPM{t}'s, or SPM{F}'s with the same
  137. % degrees of freedom. Independence is roughly guaranteed for large
  138. % degrees of freedom (and independent data) by ensuring that the
  139. % contrasts are "orthogonal". Note that it is *not* the contrast weight
  140. % vectors per se that are required to be orthogonal, but the subspaces of
  141. % the data space implied by the null hypotheses defined by the contrasts
  142. % (c'pinv(X)). Furthermore, this assumes that the errors are
  143. % i.i.d. (i.e. the estimates are maximum likelihood or Gauss-Markov. This
  144. % is the default in spm_spm).
  145. %
  146. % To ensure approximate independence of the component SPMs in the case of
  147. % the global or intermediate null, non-orthogonal contrasts are serially
  148. % orthogonalised in the order specified, possibly generating new
  149. % contrasts, such that the second is orthogonal to the first, the third
  150. % to the first two, and so on. Note that significant inference on the
  151. % global null only allows one to conclude that one or more of the effects
  152. % are real. Significant inference on the conjunction null allows one to
  153. % conclude that all of the effects are real.
  154. %
  155. % Masking simply eliminates voxels from the current contrast if they
  156. % do not survive an uncorrected p value (based on height) in one or
  157. % more further contrasts. No account is taken of this masking in the
  158. % statistical inference pertaining to the masked contrast.
  159. %
  160. % The SPM is subject to thresholding on the basis of height (u) and the
  161. % number of voxels comprising its clusters {k}. The height threshold is
  162. % specified as above in terms of an [un]corrected p value or
  163. % statistic. Clusters can also be thresholded on the basis of their
  164. % spatial extent. If you want to see all voxels simply enter 0. In this
  165. % instance the 'set-level' inference can be considered an 'omnibus test'
  166. % based on the number of clusters that obtain.
  167. %
  168. % BAYESIAN INFERENCE AND PPMS - POSTERIOR PROBABILITY MAPS
  169. %
  170. % If conditional estimates are available (and your contrast is a T
  171. % contrast) then you are asked whether the inference should be 'Bayesian'
  172. % or 'classical' (using GRF). If you choose Bayesian the contrasts are of
  173. % conditional (i.e. MAP) estimators and the inference image is a
  174. % posterior probability map (PPM). PPMs encode the probability that the
  175. % contrast exceeds a specified threshold. This threshold is stored in
  176. % the xCon.eidf. Subsequent plotting and tables will use the conditional
  177. % estimates and associated posterior or conditional probabilities.
  178. %
  179. % see spm_results_ui.m for further details of the SPM results section.
  180. % see also spm_contrasts.m
  181. %__________________________________________________________________________
  182. % Copyright (C) 1999-2017 Wellcome Trust Centre for Neuroimaging
  183. % Andrew Holmes, Karl Friston & Jean-Baptiste Poline
  184. % $Id: spm_getSPM.m 7092 2017-06-05 15:08:49Z guillaume $
  185. %-GUI setup
  186. %--------------------------------------------------------------------------
  187. spm('Pointer','Arrow')
  188. %-Select SPM.mat & note SPM results directory
  189. %--------------------------------------------------------------------------
  190. if nargin
  191. xSPM = varargin{1};
  192. end
  193. try
  194. swd = xSPM.swd;
  195. sts = 1;
  196. catch
  197. [spmmatfile, sts] = spm_select(1,'^SPM\.mat$','Select SPM.mat');
  198. swd = spm_file(spmmatfile,'fpath');
  199. end
  200. if ~sts, SPM = []; xSPM = []; return; end
  201. %-Preliminaries...
  202. %==========================================================================
  203. %-Load SPM.mat
  204. %--------------------------------------------------------------------------
  205. try
  206. load(fullfile(swd,'SPM.mat'));
  207. catch
  208. error(['Cannot read ' fullfile(swd,'SPM.mat')]);
  209. end
  210. SPM.swd = swd;
  211. %-Change directory so that relative filenames are valid
  212. %--------------------------------------------------------------------------
  213. cd(SPM.swd);
  214. %-Check the model has been estimated
  215. %--------------------------------------------------------------------------
  216. try
  217. SPM.xVol.S;
  218. catch
  219. spm('alert*',{'This model has not been estimated.','',...
  220. fullfile(swd,'SPM.mat')}, mfilename, [], ~spm('CmdLine'));
  221. SPM = []; xSPM = [];
  222. return
  223. end
  224. xX = SPM.xX; %-Design definition structure
  225. XYZ = SPM.xVol.XYZ; %-XYZ coordinates
  226. S = SPM.xVol.S; %-search Volume {voxels}
  227. R = SPM.xVol.R; %-search Volume {resels}
  228. M = SPM.xVol.M(1:3,1:3); %-voxels to mm matrix
  229. VOX = sqrt(diag(M'*M))'; %-voxel dimensions
  230. %==========================================================================
  231. % - C O N T R A S T S , S P M C O M P U T A T I O N , M A S K I N G
  232. %==========================================================================
  233. %-Get contrasts
  234. %--------------------------------------------------------------------------
  235. try, xCon = SPM.xCon; catch, xCon = {}; end
  236. try
  237. Ic = xSPM.Ic;
  238. catch
  239. [Ic,xCon] = spm_conman(SPM,'T&F',Inf,...
  240. ' Select contrasts...',' for conjunction',1);
  241. end
  242. if isempty(xCon)
  243. % figure out whether new contrasts were defined, but not selected
  244. % do this by comparing length of SPM.xCon to xCon, remember added
  245. % indices to run spm_contrasts on them as well
  246. try
  247. noxCon = numel(SPM.xCon);
  248. catch
  249. noxCon = 0;
  250. end
  251. IcAdd = (noxCon+1):numel(xCon);
  252. else
  253. IcAdd = [];
  254. end
  255. nc = length(Ic); % Number of contrasts
  256. %-Allow user to extend the null hypothesis for conjunctions
  257. %
  258. % n: conjunction number
  259. % u: Null hyp is k<=u effects real; Alt hyp is k>u effects real
  260. % (NB Here u is from Friston et al 2004 paper, not statistic thresh).
  261. % u n
  262. % Conjunction Null nc-1 1 | u = nc-n
  263. % Intermediate 1..nc-2 nc-u | #effects under null <= u
  264. % Global Null 0 nc | #effects under alt > u, >= u+1
  265. %----------------------------------+---------------------------------------
  266. if nc > 1
  267. try
  268. n = xSPM.n;
  269. catch
  270. if nc==2
  271. But = 'Conjunction|Global'; Val=[1 nc];
  272. else
  273. But = 'Conj''n|Intermed|Global'; Val=[1 NaN nc];
  274. end
  275. n = spm_input('Null hyp. to assess?','+1','b',But,Val,1);
  276. if isnan(n)
  277. if nc == 3,
  278. n = nc - 1;
  279. else
  280. n = nc - spm_input('Effects under null ','0','n1','1',nc-1);
  281. end
  282. end
  283. end
  284. else
  285. n = 1;
  286. end
  287. %-Enforce orthogonality of multiple contrasts for conjunction
  288. % (Orthogonality within subspace spanned by contrasts)
  289. %--------------------------------------------------------------------------
  290. if nc > 1 && n > 1 && ~spm_FcUtil('|_?',xCon(Ic), xX.xKXs)
  291. OrthWarn = 0;
  292. %-Successively orthogonalise
  293. %-NB: This loop is peculiarly controlled to account for the
  294. % possibility that Ic may shrink if some contrasts disappear
  295. % on orthogonalisation (i.e. if there are colinearities)
  296. %----------------------------------------------------------------------
  297. i = 1;
  298. while(i < nc), i = i + 1;
  299. %-Orthogonalise (subspace spanned by) contrast i w.r.t. previous
  300. %------------------------------------------------------------------
  301. oxCon = spm_FcUtil('|_',xCon(Ic(i)), xX.xKXs, xCon(Ic(1:i-1)));
  302. %-See if this orthogonalised contrast has already been entered
  303. % or is colinear with a previous one. Define a new contrast if
  304. % neither is the case.
  305. %------------------------------------------------------------------
  306. d = spm_FcUtil('In',oxCon,xX.xKXs,xCon);
  307. if spm_FcUtil('0|[]',oxCon,xX.xKXs)
  308. %-Contrast was colinear with a previous one - drop it
  309. %--------------------------------------------------------------
  310. Ic(i) = [];
  311. i = i - 1;
  312. elseif any(d)
  313. %-Contrast unchanged or already defined - note index
  314. %--------------------------------------------------------------
  315. Ic(i) = min(d);
  316. else
  317. %-Define orthogonalised contrast as new contrast
  318. %--------------------------------------------------------------
  319. OrthWarn = OrthWarn + 1;
  320. conlst = sprintf('%d,',Ic(1:i-1));
  321. oxCon.name = sprintf('%s (orth. w.r.t {%s})', xCon(Ic(i)).name,...
  322. conlst(1:end-1));
  323. xCon = [xCon, oxCon];
  324. Ic(i) = length(xCon);
  325. end
  326. end % while...
  327. if OrthWarn
  328. warning('SPM:ConChange','%d contrasts orthogonalized',OrthWarn)
  329. end
  330. SPM.xCon = xCon;
  331. end % if nc>1...
  332. SPM.xCon = xCon;
  333. %-Apply masking
  334. %--------------------------------------------------------------------------
  335. try
  336. Mask = ~isempty(xSPM.Im) * (isnumeric(xSPM.Im) + 2*iscellstr(xSPM.Im));
  337. catch
  338. % Mask = spm_input('mask with other contrast(s)','+1','y/n',[1,0],2);
  339. % Mask = spm_input('apply masking','+1','b','none|contrast|image',[0,1,2],1);
  340. Mask = spm_input('apply masking','+1','b','none|contrast|image|atlas',[0,1,2,3],1);
  341. end
  342. if Mask == 1
  343. %-Get contrasts for masking
  344. %----------------------------------------------------------------------
  345. try
  346. Im = xSPM.Im;
  347. catch
  348. [Im,xCon] = spm_conman(SPM,'T&F',-Inf,...
  349. 'Select contrasts for masking...',' for masking',1);
  350. end
  351. %-Threshold for mask (uncorrected p-value)
  352. %----------------------------------------------------------------------
  353. try
  354. pm = xSPM.pm;
  355. catch
  356. pm = spm_input('uncorrected mask p-value','+1','r',0.05,1,[0,1]);
  357. end
  358. %-Inclusive or exclusive masking
  359. %----------------------------------------------------------------------
  360. try
  361. Ex = xSPM.Ex;
  362. catch
  363. Ex = spm_input('nature of mask','+1','b','inclusive|exclusive',[0,1],1);
  364. end
  365. elseif Mask == 2
  366. %-Get mask images
  367. %----------------------------------------------------------------------
  368. try
  369. Im = xSPM.Im;
  370. catch
  371. [Im, sts] = spm_select([1 Inf],{'image','mesh'},'Select mask image(s)');
  372. if ~sts, Im = []; else Im = cellstr(Im); end
  373. end
  374. %-Inclusive or exclusive masking
  375. %----------------------------------------------------------------------
  376. try
  377. Ex = xSPM.Ex;
  378. catch
  379. Ex = spm_input('nature of mask','+1','b','inclusive|exclusive',[0,1],1);
  380. end
  381. pm = [];
  382. elseif Mask == 3 % unused
  383. %-Get mask from atlas
  384. %----------------------------------------------------------------------
  385. try
  386. error('Im = xSPM.Im;'); % interactive only
  387. catch
  388. VM = spm_atlas('mask'); % get atlas mask
  389. VM.fname = spm_file(VM.fname,'unique');
  390. VM = spm_write_vol(VM,VM.dat); % write mask
  391. Im = cellstr(VM.fname);
  392. end
  393. %-Inclusive or exclusive masking
  394. %----------------------------------------------------------------------
  395. try
  396. Ex = xSPM.Ex;
  397. catch
  398. Ex = spm_input('nature of mask','+1','b','inclusive|exclusive',[0,1],1);
  399. end
  400. pm = [];
  401. else
  402. Im = [];
  403. pm = [];
  404. Ex = [];
  405. end
  406. %-Create/Get title string for comparison
  407. %--------------------------------------------------------------------------
  408. if nc == 1
  409. str = xCon(Ic).name;
  410. else
  411. str = [sprintf('contrasts {%d',Ic(1)),sprintf(',%d',Ic(2:end)),'}'];
  412. if n == nc
  413. str = [str ' (global null)'];
  414. elseif n == 1
  415. str = [str ' (conj. null)'];
  416. else
  417. str = [str sprintf(' (Ha: k>=%d)',(nc-n)+1)];
  418. end
  419. end
  420. if Ex
  421. mstr = 'masked [excl.] by';
  422. else
  423. mstr = 'masked [incl.] by';
  424. end
  425. if isnumeric(Im)
  426. if length(Im) == 1
  427. str = sprintf('%s (%s %s at p=%g)',str,mstr,xCon(Im).name,pm);
  428. elseif ~isempty(Im)
  429. str = [sprintf('%s (%s {%d',str,mstr,Im(1)),...
  430. sprintf(',%d',Im(2:end)),...
  431. sprintf('} at p=%g)',pm)];
  432. end
  433. elseif iscellstr(Im) && numel(Im) > 0
  434. [pf,nf,ef] = spm_fileparts(Im{1});
  435. str = sprintf('%s (%s %s',str,mstr,[nf ef]);
  436. for i=2:numel(Im)
  437. [pf,nf,ef] = spm_fileparts(Im{i});
  438. str =[str sprintf(', %s',[nf ef])];
  439. end
  440. str = [str ')'];
  441. end
  442. try
  443. titlestr = xSPM.title;
  444. catch
  445. %titlestr = spm_input('title for comparison','+1','s',str);
  446. titlestr = '';
  447. end
  448. if isempty(titlestr), titlestr = str; end
  449. %-Bayesian or classical Inference?
  450. %==========================================================================
  451. if isfield(SPM,'PPM')
  452. % Make sure SPM.PPM.xCon field exists
  453. %----------------------------------------------------------------------
  454. if ~isfield(SPM.PPM,'xCon')
  455. SPM.PPM.xCon = [];
  456. end
  457. % Set Bayesian con type - but only if empty
  458. %----------------------------------------------------------------------
  459. if length(SPM.PPM.xCon)<Ic || ~isfield(SPM.PPM.xCon(Ic), 'PSTAT') || isempty(SPM.PPM.xCon(Ic).PSTAT)
  460. SPM.PPM.xCon(Ic).PSTAT = xCon(Ic).STAT;
  461. end
  462. if all(strcmp([SPM.PPM.xCon(Ic).PSTAT],'T'))
  463. % Simple contrast
  464. %------------------------------------------------------------------
  465. str = 'Effect size threshold for PPM';
  466. if isfield(SPM.PPM,'VB') % 1st level Bayes
  467. % For VB - set default effect size
  468. %--------------------------------------------------------------
  469. if exist('xSPM','var') && isfield(xSPM,'gamma') && ~isempty(xSPM.gamma)
  470. xCon(Ic).eidf = xSPM.gamma;
  471. else
  472. Gamma = 0.1;
  473. xCon(Ic).eidf = spm_input(str,'+1','e',sprintf('%0.2f',Gamma));
  474. end
  475. xCon(Ic).STAT='P';
  476. else % 2nd level Bayes
  477. %--------------------------------------------------------------
  478. if isempty(xCon(Ic).Vcon)
  479. % If this is first time contrast is specified then
  480. % ask user if it will be Bayesian or Classical
  481. if spm_input('Inference',1,'b',{'Bayesian','classical'},[1 0]);
  482. xCon(Ic).STAT = 'P';
  483. end
  484. end
  485. % If Bayesian then get effect size threshold (Gamma) stored in xCon(Ic).eidf
  486. % The default is one conditional s.d. of the contrast
  487. %--------------------------------------------------------------
  488. if strcmp(xCon(Ic).STAT,'P')
  489. if exist('xSPM','var') && isfield(xSPM,'gamma') && ~isempty(xSPM.gamma)
  490. xCon(Ic).eidf = xSPM.gamma;
  491. else
  492. Gamma = full(sqrt(xCon(Ic).c'*SPM.PPM.Cb*xCon(Ic).c));
  493. xCon(Ic).eidf = spm_input(str,'+1','e',sprintf('%0.2f',Gamma));
  494. end
  495. end
  496. end
  497. else
  498. if isempty(xCon(Ic).Vcon)
  499. % If this is first time contrast is specified then
  500. % ask user if it will be Bayesian or Classical
  501. %--------------------------------------------------------------
  502. if spm_input('Inference',1,'b',{'Bayesian','classical'},[1 0]);
  503. % Chi^2 statistic - 1st Level Bayes
  504. % Savage-Dickey - 2nd Level Bayes
  505. xCon(Ic).eidf = 0; % temporarily
  506. xCon(Ic).STAT='P';
  507. end
  508. end
  509. end
  510. end
  511. %-Compute & store contrast parameters, contrast/ESS images, & SPM images
  512. %==========================================================================
  513. SPM.xCon = xCon;
  514. if isnumeric(Im)
  515. SPM = spm_contrasts(SPM, unique([Ic, Im, IcAdd]));
  516. else
  517. SPM = spm_contrasts(SPM, unique([Ic, IcAdd]));
  518. end
  519. xCon = SPM.xCon;
  520. STAT = xCon(Ic(1)).STAT;
  521. VspmSv = cat(1,xCon(Ic).Vspm);
  522. %-Check conjunctions - Must be same STAT w/ same df
  523. %--------------------------------------------------------------------------
  524. if (nc > 1) && (any(diff(double(cat(1,xCon(Ic).STAT)))) || ...
  525. any(abs(diff(cat(1,xCon(Ic).eidf))) > 1))
  526. error('illegal conjunction: can only conjoin SPMs of same STAT & df');
  527. end
  528. %-Degrees of Freedom and STAT string describing marginal distribution
  529. %--------------------------------------------------------------------------
  530. df = [xCon(Ic(1)).eidf xX.erdf];
  531. if nc > 1
  532. if n > 1
  533. str = sprintf('^{%d \\{Ha:k\\geq%d\\}}',nc,(nc-n)+1);
  534. else
  535. str = sprintf('^{%d \\{Ha:k=%d\\}}',nc,(nc-n)+1);
  536. end
  537. else
  538. str = '';
  539. end
  540. switch STAT
  541. case 'T'
  542. STATstr = sprintf('%s%s_{%.0f}','T',str,df(2));
  543. case 'F'
  544. STATstr = sprintf('%s%s_{%.0f,%.0f}','F',str,df(1),df(2));
  545. case 'P'
  546. if strcmp(SPM.PPM.xCon(Ic).PSTAT,'T')
  547. STATstr = sprintf('%s^{%0.2f}','PPM',df(1));
  548. else
  549. STATstr='PPM';
  550. end
  551. end
  552. %-Compute (unfiltered) SPM pointlist for masked conjunction requested
  553. %==========================================================================
  554. fprintf('\t%-32s: %30s','SPM computation','...initialising') %-#
  555. %-Compute conjunction as minimum of SPMs
  556. %--------------------------------------------------------------------------
  557. Z = Inf;
  558. for i = Ic
  559. Z = min(Z,spm_data_read(xCon(i).Vspm,'xyz',XYZ));
  560. end
  561. %-Copy of Z and XYZ before masking, for later use with FDR
  562. %--------------------------------------------------------------------------
  563. XYZum = XYZ;
  564. Zum = Z;
  565. %-Compute mask and eliminate masked voxels
  566. %--------------------------------------------------------------------------
  567. for i = 1:numel(Im)
  568. fprintf('%s%30s',repmat(sprintf('\b'),1,30),'...masking') %-#
  569. if isnumeric(Im)
  570. Mask = spm_data_read(xCon(Im(i)).Vspm,'xyz',XYZ);
  571. um = spm_u(pm,[xCon(Im(i)).eidf,xX.erdf],xCon(Im(i)).STAT);
  572. if Ex
  573. Q = Mask <= um;
  574. else
  575. Q = Mask > um;
  576. end
  577. else
  578. v = spm_data_hdr_read(Im{i});
  579. Mask = spm_data_read(v,'xyz',v.mat\SPM.xVol.M*[XYZ; ones(1,size(XYZ,2))]);
  580. Q = Mask ~= 0 & ~isnan(Mask);
  581. if Ex, Q = ~Q; end
  582. end
  583. XYZ = XYZ(:,Q);
  584. Z = Z(Q);
  585. if isempty(Q)
  586. fprintf('\n') %-#
  587. sw = warning('off','backtrace');
  588. warning('SPM:NoVoxels','No voxels survive masking at p=%4.2f',pm);
  589. warning(sw);
  590. break
  591. end
  592. end
  593. %==========================================================================
  594. % - H E I G H T & E X T E N T T H R E S H O L D S
  595. %==========================================================================
  596. u = -Inf; % height threshold
  597. k = 0; % extent threshold {voxels}
  598. %-Get FDR mode
  599. %--------------------------------------------------------------------------
  600. try
  601. topoFDR = spm_get_defaults('stats.topoFDR');
  602. catch
  603. topoFDR = true;
  604. end
  605. if spm_mesh_detect(xCon(Ic(1)).Vspm)
  606. G = export(gifti(SPM.xVol.G),'patch');
  607. end
  608. %-Height threshold - classical inference
  609. %--------------------------------------------------------------------------
  610. if STAT ~= 'P'
  611. %-Get height threshold
  612. %----------------------------------------------------------------------
  613. fprintf('%s%30s',repmat(sprintf('\b'),1,30),'...height threshold') %-#
  614. try
  615. thresDesc = xSPM.thresDesc;
  616. catch
  617. if topoFDR
  618. str = 'FWE|none';
  619. else
  620. str = 'FWE|FDR|none';
  621. end
  622. thresDesc = spm_input('p value adjustment to control','+1','b',str,[],1);
  623. end
  624. switch thresDesc
  625. case 'FWE' % Family-wise false positive rate
  626. %--------------------------------------------------------------
  627. try
  628. u = xSPM.u;
  629. catch
  630. u = spm_input('p value (FWE)','+0','r',0.05,1,[0,1]);
  631. end
  632. thresDesc = ['p<' num2str(u) ' (' thresDesc ')'];
  633. u = spm_uc(u,df,STAT,R,n,S);
  634. case 'FDR' % False discovery rate
  635. %--------------------------------------------------------------
  636. if topoFDR
  637. fprintf('\n'); %-#
  638. error('Change defaults.stats.topoFDR to use voxel FDR');
  639. end
  640. try
  641. u = xSPM.u;
  642. catch
  643. u = spm_input('p value (FDR)','+0','r',0.05,1,[0,1]);
  644. end
  645. thresDesc = ['p<' num2str(u) ' (' thresDesc ')'];
  646. u = spm_uc_FDR(u,df,STAT,n,VspmSv,0);
  647. case 'none' % No adjustment: p for conjunctions is p of the conjunction SPM
  648. %--------------------------------------------------------------
  649. try
  650. u = xSPM.u;
  651. catch
  652. u = spm_input(['threshold {',STAT,' or p value}'],'+0','r',0.001,1);
  653. end
  654. if u <= 1
  655. thresDesc = ['p<' num2str(u) ' (unc.)'];
  656. u = spm_u(u^(1/n),df,STAT);
  657. else
  658. thresDesc = [STAT '=' num2str(u) ];
  659. end
  660. otherwise
  661. %--------------------------------------------------------------
  662. fprintf('\n'); %-#
  663. error('Unknown control method "%s".',thresDesc);
  664. end % switch thresDesc
  665. %-Compute p-values for topological and voxel-wise FDR (all search voxels)
  666. %----------------------------------------------------------------------
  667. if ~topoFDR
  668. %-Voxel-wise FDR
  669. %------------------------------------------------------------------
  670. fprintf('%s%30s',repmat(sprintf('\b'),1,30),'...for voxelFDR') %-#
  671. Ps = spm_z2p(Zum,df,STAT,n);
  672. up = spm_uc_FDR(0.05,df,STAT,n,sort(Ps(:)));
  673. Pp = [];
  674. else
  675. %-Peak FDR
  676. %------------------------------------------------------------------
  677. if ~spm_mesh_detect(xCon(Ic(1)).Vspm)
  678. [up,Pp] = spm_uc_peakFDR(0.05,df,STAT,R,n,Zum,XYZum,u);
  679. else
  680. [up,Pp] = spm_uc_peakFDR(0.05,df,STAT,R,n,Zum,XYZum,u,G);
  681. end
  682. end
  683. %-Cluster FDR
  684. %----------------------------------------------------------------------
  685. if n == 1 %% && STAT == 'T'
  686. if ~spm_mesh_detect(xCon(Ic(1)).Vspm)
  687. V2R = 1/prod(SPM.xVol.FWHM(SPM.xVol.DIM > 1));
  688. [uc,Pc,ue] = spm_uc_clusterFDR(0.05,df,STAT,R,n,Zum,XYZum,V2R,u);
  689. else
  690. V2R = 1/prod(SPM.xVol.FWHM);
  691. [uc,Pc,ue] = spm_uc_clusterFDR(0.05,df,STAT,R,n,Zum,XYZum,V2R,u,G);
  692. end
  693. else
  694. uc = NaN;
  695. ue = NaN;
  696. Pc = [];
  697. end
  698. if ~topoFDR
  699. uc = NaN;
  700. Pc = [];
  701. end
  702. %-Peak FWE
  703. %----------------------------------------------------------------------
  704. uu = spm_uc(0.05,df,STAT,R,n,S);
  705. %-Height threshold - Bayesian inference
  706. %--------------------------------------------------------------------------
  707. elseif STAT == 'P'
  708. try
  709. u = xSPM.u;
  710. catch
  711. u_default = 10;
  712. str = 'Log Odds Threshold for PPM';
  713. u = spm_input(str,'+0','r',u_default,1);
  714. end
  715. thresDesc = ['Log Odds > ' num2str(u)];
  716. end % (if STAT)
  717. %-Calculate height threshold filtering
  718. %--------------------------------------------------------------------------
  719. if spm_mesh_detect(xCon(Ic(1)).Vspm), str = 'vertices'; else str = 'voxels'; end
  720. Q = find(Z > u);
  721. %-Apply height threshold
  722. %--------------------------------------------------------------------------
  723. Z = Z(:,Q);
  724. XYZ = XYZ(:,Q);
  725. if isempty(Q)
  726. fprintf('\n'); %-#
  727. sw = warning('off','backtrace');
  728. warning('SPM:NoVoxels','No %s survive height threshold at u=%0.2g',str,u);
  729. warning(sw);
  730. end
  731. %-Extent threshold
  732. %--------------------------------------------------------------------------
  733. if ~isempty(XYZ)
  734. fprintf('%s%30s',repmat(sprintf('\b'),1,30),'...extent threshold'); %-#
  735. %-Get extent threshold [default = 0]
  736. %----------------------------------------------------------------------
  737. try
  738. k = xSPM.k;
  739. catch
  740. k = spm_input(['& extent threshold {' str '}'],'+1','r',0,1,[0,Inf]);
  741. end
  742. %-Calculate extent threshold filtering
  743. %----------------------------------------------------------------------
  744. if ~spm_mesh_detect(xCon(Ic(1)).Vspm)
  745. A = spm_clusters(XYZ);
  746. else
  747. T = false(SPM.xVol.DIM');
  748. T(XYZ(1,:)) = true;
  749. A = spm_mesh_clusters(G,T)';
  750. A = A(XYZ(1,:));
  751. end
  752. Q = [];
  753. for i = 1:max(A)
  754. j = find(A == i);
  755. if length(j) >= k, Q = [Q j]; end
  756. end
  757. % ...eliminate voxels
  758. %----------------------------------------------------------------------
  759. Z = Z(:,Q);
  760. XYZ = XYZ(:,Q);
  761. if isempty(Q)
  762. fprintf('\n'); %-#
  763. sw = warning('off','backtrace');
  764. warning('SPM:NoVoxels','No %s survive extent threshold at k=%0.2g',str,k);
  765. warning(sw);
  766. end
  767. else
  768. try
  769. k = xSPM.k;
  770. catch
  771. k = 0;
  772. end
  773. end % (if ~isempty(XYZ))
  774. %==========================================================================
  775. % - E N D
  776. %==========================================================================
  777. fprintf('%s%30s\n',repmat(sprintf('\b'),1,30),'...done') %-#
  778. spm('Pointer','Arrow')
  779. %-Assemble output structures of unfiltered data
  780. %==========================================================================
  781. xSPM = struct( ...
  782. 'swd', swd,...
  783. 'title', titlestr,...
  784. 'Z', Z,...
  785. 'n', n,...
  786. 'STAT', STAT,...
  787. 'df', df,...
  788. 'STATstr', STATstr,...
  789. 'Ic', Ic,...
  790. 'Im', {Im},...
  791. 'pm', pm,...
  792. 'Ex', Ex,...
  793. 'u', u,...
  794. 'k', k,...
  795. 'XYZ', XYZ,...
  796. 'XYZmm', SPM.xVol.M(1:3,:)*[XYZ; ones(1,size(XYZ,2))],...
  797. 'S', SPM.xVol.S,...
  798. 'R', SPM.xVol.R,...
  799. 'FWHM', SPM.xVol.FWHM,...
  800. 'M', SPM.xVol.M,...
  801. 'iM', SPM.xVol.iM,...
  802. 'DIM', SPM.xVol.DIM,...
  803. 'VOX', VOX,...
  804. 'Vspm', VspmSv,...
  805. 'thresDesc',thresDesc);
  806. %-RESELS per voxel (density) if it exists
  807. %--------------------------------------------------------------------------
  808. try, xSPM.VRpv = SPM.xVol.VRpv; end
  809. try
  810. xSPM.units = SPM.xVol.units;
  811. catch
  812. try, xSPM.units = varargin{1}.units; end
  813. end
  814. %-Topology for surface-based inference
  815. %--------------------------------------------------------------------------
  816. if spm_mesh_detect(xCon(Ic(1)).Vspm)
  817. xSPM.G = G;
  818. xSPM.XYZmm = xSPM.G.vertices(xSPM.XYZ(1,:),:)';
  819. end
  820. %-p-values for topological and voxel-wise FDR
  821. %--------------------------------------------------------------------------
  822. try, xSPM.Ps = Ps; end % voxel FDR
  823. try, xSPM.Pp = Pp; end % peak FDR
  824. try, xSPM.Pc = Pc; end % cluster FDR
  825. %-0.05 critical thresholds for FWEp, FDRp, FWEc, FDRc
  826. %--------------------------------------------------------------------------
  827. try, xSPM.uc = [uu up ue uc]; end