spm_spm_vb.m 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116
  1. function [SPM] = spm_spm_vb(SPM)
  2. % VB estimation of GLM-AR models with spatial regularisation
  3. % FORMAT [SPM] = spm_spm_vb(SPM)
  4. %
  5. % This function implements a VB estimation scheme for GLM-AR models.
  6. % Both regression coefficients and AR coefficients are spatially
  7. % regularised. The algorithm is described in a series of papers:
  8. %
  9. % Paper VB1: W. Penny, S. Kiebel and K. Friston (2003).
  10. % Variational Bayesian inference for fMRI time series.
  11. % NeuroImage 19(3), pp 727-741.
  12. %
  13. % Paper VB2: W. Penny, N. Trujillo-Barreto and K. Friston (2005).
  14. % Bayesian fMRI time series analysis with spatial priors.
  15. % NeuroImage 24(2), pp 350-362.
  16. %
  17. % Paper VB3: W. Penny and G. Flandin (2005).
  18. % Bayesian analysis of single-subject fMRI: SPM implementation.
  19. % Technical Report. WDIN, UCL.
  20. %
  21. % Paper VB4: W. Penny, G. Flandin and N. Trujillo-Barreto (2007).
  22. % Bayesian comparison of spatially regularised general linear
  23. % models. Human Brain Mapping 28(4):275-293.
  24. %
  25. % Paper VB5: W. Penny and G. Flandin (2005).
  26. % Bayesian analysis of fMRI data with spatial priors.
  27. % 2005 Proceedings of the Joint Statistical Meeting, Section on
  28. % Statistical Graphics [CDROM], Alexandria, VA: American
  29. % Statistical Association.
  30. %
  31. % The space to be analysed is a 'Volume', 'Slices' or 'Clusters'.
  32. % For 'Slices' the numbers of the slices to be analysed are then entered.
  33. % For 'Clusters' a mask specifying the volume to be analysed is entered.
  34. % ______________________________________________________________________
  35. %
  36. % Required fields of SPM:
  37. %
  38. % xY.VY - nScan x 1 struct array of mapped image volumes
  39. % Images must have the same orientation, voxel size and data type
  40. % - Any scaling should have already been applied via the image
  41. % handle scalefactors.
  42. %
  43. % xX - Structure containing design matrix information
  44. % - Required fields are:
  45. % xX.X - Design matrix (raw, not temporally smoothed)
  46. % xX.name - cellstr of parameter names corresponding to
  47. % columns of design matrix
  48. %
  49. % xM - Structure containing masking information, or a simple column
  50. % vector of thresholds corresponding to the images in VY.
  51. % - If a structure, the required fields are:
  52. % xM.TH - nVar x nScan matrix of analysis thresholds, one per image
  53. % xM.I - Implicit masking (0=>none, 1 => implicit zero/NaN mask)
  54. % xM.VM - struct array of mapped explicit mask image volumes
  55. % - (empty if no explicit masks)
  56. % - Explicit mask images are >0 for valid voxels to assess.
  57. % - Mask images can have any orientation, voxel size or
  58. % data type. They are interpolated using nearest neighbour
  59. % interpolation to the voxel locations of the data Y.
  60. % - Note that voxels with constant data (i.e. the same value
  61. % across scans) are also automatically masked out.
  62. %
  63. % ______________________________________________________________________
  64. %
  65. % spm_spm_vb adds the following fields to SPM:
  66. %
  67. % SPM.VCbeta - Handles of posterior parameter estimates
  68. % (Cbeta_????)
  69. % SPM.VPsd - Handles of SD of posterior parameter estimates
  70. % (SDbeta_????)
  71. %
  72. % SPM.PPM - Posterior Probability Map data structure
  73. %
  74. % .VB=1, tells later functions (spm_contrasts, spm_graph)
  75. % that parameters were estimated using VB
  76. %
  77. % .AR_P, assumed AR model order
  78. %
  79. % .priors, type of priors used (eg. 'Spatial-GMRF')
  80. % see spm_vb_set_priors.m
  81. %
  82. % .update_F, whether model evidence is to be computed
  83. %
  84. % .Gamma, default effect size threshold (used in spm_getSPM)
  85. %
  86. % info is stored for each "block", where a block
  87. % is either a slice or subvolume, computed using a
  88. % graph-partitioning algorithm. This is stored in
  89. % .Sess(s).block(z), further info about GLM-AR
  90. % model at block z eg. block(z).F is
  91. % evidence for block z (if computed)
  92. % where s is the session number
  93. % The following parameters are set if the space to be
  94. % analysed chosen as 'Slices'
  95. % .AN_slices, numbers of slices analysed
  96. %
  97. % For each session the following fields are also specified:
  98. %
  99. % SPM.PPM.Sess(s).VHp - Handle of standard deviation of the error
  100. % (Sess%s%_SDerror)
  101. % SPM.PPM.Sess(s).VAR - Handles of AR coefficient images
  102. % (Sess%s%_AR_????)
  103. %
  104. % If contrasts have been specified prior to estimation (this is
  105. % recommended) the following fields are also updated:
  106. %
  107. % SPM.xCon(ic).Vcon
  108. % SPM.PPM.Vcon_sd(ic)
  109. %
  110. % where ic is the contrast index.
  111. % ______________________________________________________________________
  112. %
  113. %
  114. % The following images are written to file:
  115. %
  116. % mask.{img,hdr} - analysis mask image
  117. % 8-bit (uint8) image of zero-s & one's indicating which voxels were
  118. % included in the analysis. This mask image is the intersection of the
  119. % explicit, implicit and threshold masks specified in the xM argument.
  120. % The XYZ matrix contains the voxel coordinates of all voxels in the
  121. % analysis mask. The mask image is included for reference, but is not
  122. % explicitly used by the results section.
  123. % Note mask.<ext> is only written if the selected space is 'Volume' or
  124. % 'Masked Volume' (ie not 'Slices')
  125. %
  126. % labels.<ext> - block labels
  127. % 8-bit (uint8) image of zero-s & integers from 1 to max no. of blocks,
  128. % e.g. slices or subvoumes, indicating which block a voxel belongs.
  129. % This info is also stored in SPM.xVol.labels (same order as XYZ matrix),
  130. % for all analysis space options.
  131. %
  132. % Cbeta_????.<ext>
  133. % These are 16-bit (float) images of the parameter posteriors. The image
  134. % files are numbered according to the corresponding column of the
  135. % design matrix.
  136. % Voxels outside the analysis mask (mask.<ext>) are given value NaN.
  137. %
  138. % SDbeta_????.<ext>
  139. % These are 16-bit (float) images of the standard deviation of parameter
  140. % posteriors.
  141. % The image files are numbered according to the corresponding column of
  142. % the design matrix.
  143. % Voxels outside the analysis mask (mask.<ext>) are given value NaN.
  144. %
  145. % Sess%s%_SDerror.<ext>
  146. % This is a 16-bit (float) image of the standard deviation of the error
  147. % for session s.
  148. % Voxels outside the analysis mask (mask.<ext>) are given value NaN.
  149. %
  150. % Sess%s%_AR_????.<ext>
  151. % These are 16-bit (float) images of AR coefficients for session s.
  152. % The image files are numbered according to the order of the
  153. % corresponding AR coefficient.
  154. % Voxels outside the analysis mask (mask.<ext>) are given value NaN.
  155. %_______________________________________________________________________
  156. % Copyright (C) 2005-2011 Wellcome Trust Centre for Neuroimaging
  157. % Will Penny, Nelson Trujillo-Barreto and Lee Harrison
  158. % $Id: spm_spm_vb.m 5655 2013-09-25 17:58:48Z guillaume $
  159. %-Get SPM.mat if necessary
  160. %-----------------------------------------------------------------------
  161. if ~nargin
  162. [Pf, sts] = spm_select(1,'^SPM\.mat$','Select SPM.mat');
  163. if ~sts, return; end
  164. swd = spm_file(Pf,'fpath');
  165. load(fullfile(swd,'SPM.mat'));
  166. SPM.swd = swd;
  167. end
  168. %-Change to SPM.swd if specified
  169. %-----------------------------------------------------------------------
  170. try
  171. cd(SPM.swd);
  172. catch
  173. SPM.swd = pwd;
  174. end
  175. %-Let later functions (spm_contrasts, spm_graph) know that estimation
  176. % was with Variational Bayes
  177. %-----------------------------------------------------------------------
  178. SPM.PPM.VB = 1;
  179. %-Display
  180. %-----------------------------------------------------------------------
  181. try
  182. SPM.PPM.window;
  183. catch
  184. SPM.PPM.window = 1;
  185. end
  186. if SPM.PPM.window
  187. %-Say hello
  188. %-------------------------------------------------------------------
  189. Finter = spm('FigName','Stats: Bayesian Estimation ...');
  190. spm('Pointer','Arrow');
  191. end
  192. %-Delete files from previous analyses
  193. %-----------------------------------------------------------------------
  194. if SPM.PPM.window
  195. if ~isempty(spm_select('List',SPM.swd,'^mask\..{3}$'))
  196. str = {'Current directory contains SPM estimation files:',...
  197. 'pwd = ',SPM.swd,...
  198. 'Existing results will be overwritten!'};
  199. abort = spm_input(str,1,'bd','stop|continue',[1,0],1);
  200. if abort
  201. spm('FigName','Stats: done',Finter); spm('Pointer','Arrow');
  202. return
  203. else
  204. str = sprintf('Overwriting old results\n\t (pwd = %s) ',SPM.swd);
  205. warning(str)
  206. drawnow
  207. end
  208. end
  209. end
  210. fspm = {'^mask\..{3}$','^ResMS\..{3}$','^RPV\..{3}$',...
  211. '^beta_.{4}\..{3}$','^con_.{4}\..{3}$','^ResI_.{4}\..{3}$',...
  212. '^ess_.{4}\..{3}$', '^spm\w{1}_.{4}\..{3}$'};
  213. fppm = {'^Cbeta_.{4}\..{3}$', '^LogEv\..{3}$', '^Sess.+_SDerror\..{3}$',...
  214. '^SDbeta_.{4}\..{3}$', '^Sess.+_AR_.{4}\..{3}$', '^con_sd_.{4}\..{3}$'};
  215. files = {fspm{:} fppm{:}};
  216. for i=1:length(files)
  217. j = spm_select('List',pwd,files{i});
  218. for k=1:size(j,1)
  219. spm_unlink(deblank(j(k,:)));
  220. end
  221. end
  222. %=======================================================================
  223. % - A N A L Y S I S P R E L I M I N A R I E S
  224. %=======================================================================
  225. %-Get number of sessions
  226. %-----------------------------------------------------------------------
  227. nsess = length(SPM.Sess);
  228. %-Get image dimensions and data
  229. %-----------------------------------------------------------------------
  230. VY = SPM.xY.VY;
  231. M = VY(1).mat;
  232. DIM = VY(1).dim(1:3)';
  233. xdim = DIM(1); ydim = DIM(2); zdim = DIM(3);
  234. %-Get design matrix
  235. %-----------------------------------------------------------------------
  236. xX = SPM.xX;
  237. [nScan,nBeta] = size(xX.X);
  238. nPsd = nBeta;
  239. %-Find number of pre-specified contrasts
  240. %-----------------------------------------------------------------------
  241. try
  242. ncon = length(SPM.xCon);
  243. catch
  244. ncon = 0;
  245. end
  246. %-Initialise output images
  247. %=======================================================================
  248. fprintf('%-40s: %30s','Output images','...initialising'); %-#
  249. %-Initialise XYZ matrix of in-mask voxel co-ordinates (real space)
  250. %-----------------------------------------------------------------------
  251. XYZ = zeros(3,xdim*ydim*zdim);
  252. labels = zeros(1,xdim*ydim*zdim);
  253. %-Initialise conditional estimate image files
  254. %-----------------------------------------------------------------------
  255. Vbeta(1:nBeta) = deal(struct(...
  256. 'fname', '',...
  257. 'dim', DIM',...
  258. 'dt', [spm_type('float32') spm_platform('bigend')],...
  259. 'mat', M,...
  260. 'pinfo', [1 0 0]',...
  261. 'descrip', ''));
  262. for i = 1:nBeta
  263. Vbeta(i).fname = [sprintf('Cbeta_%04d',i) spm_file_ext];
  264. Vbeta(i).descrip = sprintf('Posterior mean of beta (%04d) - %s',i,xX.name{i});
  265. end
  266. Vbeta = spm_create_vol(Vbeta);
  267. %-Initialise Posterior SD image files
  268. %-----------------------------------------------------------------------
  269. VPsd(1:nPsd) = deal(struct(...
  270. 'fname', '',...
  271. 'dim', DIM',...
  272. 'dt', [spm_type('float32') spm_platform('bigend')],...
  273. 'mat', M,...
  274. 'pinfo', [1 0 0]',...
  275. 'descrip', ''));
  276. for i = 1:nPsd
  277. VPsd(i).fname = [sprintf('SDbeta_%04d',i) spm_file_ext];
  278. VPsd(i).descrip = sprintf('Posterior SD of beta (%04d)',i);
  279. end
  280. VPsd = spm_create_vol(VPsd);
  281. %-Initialise Error SD image(s)
  282. %-----------------------------------------------------------------------
  283. for s = 1:nsess
  284. SPM.PPM.Sess(s).VHp = struct(...
  285. 'fname', [],...
  286. 'dim', DIM',...
  287. 'dt', [spm_type('float32') spm_platform('bigend')],...
  288. 'mat', M,...
  289. 'pinfo', [1 0 0]',...
  290. 'descrip', '');
  291. SPM.PPM.Sess(s).VHp.fname = [sprintf('Sess%d_SDerror',s) spm_file_ext];
  292. SPM.PPM.Sess(s).VHp.descrip = sprintf('Sess%d Error SD',s);
  293. SPM.PPM.Sess(s).VHp = spm_create_vol(SPM.PPM.Sess(s).VHp);
  294. end
  295. %-Initialise hyperparameter (AR 1..p and noise variance) image files
  296. %-----------------------------------------------------------------------
  297. %-Set number of AR coefficients
  298. try
  299. SPM.PPM.AR_P;
  300. catch
  301. SPM.PPM.AR_P = 3;
  302. end
  303. for s=1:nsess
  304. for i=1:SPM.PPM.AR_P
  305. SPM.PPM.Sess(s).VAR(i) = struct(...
  306. 'fname', '',...
  307. 'dim', DIM',...
  308. 'dt', [spm_type('float32') spm_platform('bigend')],...
  309. 'mat', M,...
  310. 'pinfo', [1 0 0]',...
  311. 'descrip', '',...
  312. 'n', 1,...
  313. 'private', []);
  314. SPM.PPM.Sess(s).VAR(i).fname = ...
  315. [sprintf('Sess%d_AR_%04d',s,i) spm_file_ext];
  316. SPM.PPM.Sess(s).VAR(i).descrip = ...
  317. sprintf('Sess%d Autoregressive coefficient (%04d)',s,i);
  318. end
  319. if SPM.PPM.AR_P > 0
  320. SPM.PPM.Sess(s).VAR = spm_create_vol(SPM.PPM.Sess(s).VAR);
  321. end
  322. end
  323. %-Initialise contribution map
  324. % (voxel-wise contribution to log evidence)
  325. %-----------------------------------------------------------------------
  326. %-Compute evidence at each iteration ?
  327. try
  328. SPM.PPM.update_F;
  329. catch
  330. SPM.PPM.update_F = 0;
  331. end
  332. if SPM.PPM.update_F
  333. SPM.PPM.LogEv = struct(...
  334. 'fname', ['LogEv' spm_file_ext],...
  335. 'dim', DIM',...
  336. 'dt', [spm_type('float32') spm_platform('bigend')],...
  337. 'mat', M,...
  338. 'pinfo', [1 0 0]',...
  339. 'descrip', 'Map of contribution to log-evidence');
  340. SPM.PPM.LogEv = spm_create_vol(SPM.PPM.LogEv);
  341. end
  342. %-Initialise contrast and contrast SD images
  343. %-----------------------------------------------------------------------
  344. for ic = 1:ncon
  345. SPM.xCon(ic).Vcon = struct(...
  346. 'fname', [sprintf('con_%04d',ic) spm_file_ext],...
  347. 'dim', DIM',...
  348. 'dt', [spm_type('float32') spm_platform('bigend')],...
  349. 'mat', M,...
  350. 'pinfo', [1,0,0]',...
  351. 'descrip', sprintf('SPM contrast - %d: %s',ic,SPM.xCon(ic).name));
  352. V = struct(...
  353. 'fname', [sprintf('con_sd_%04d',ic) spm_file_ext],...
  354. 'dim', DIM',...
  355. 'dt', [spm_type('float32') spm_platform('bigend')],...
  356. 'mat', M,...
  357. 'pinfo', [1,0,0]',...
  358. 'descrip', sprintf('PPM contrast SD - %d: %s',ic,SPM.xCon(ic).name));
  359. SPM.xCon(ic).Vcon = spm_create_vol(SPM.xCon(ic).Vcon);
  360. V = spm_create_vol(V);
  361. SPM.PPM.Vcon_sd(ic) = V;
  362. end
  363. % Set up masking details
  364. %-If xM is not a structure then assumme it's a vector of thresholds
  365. %-----------------------------------------------------------------------
  366. try
  367. xM = SPM.xM;
  368. catch
  369. xM = repmat(-Inf,nScan,1);
  370. end
  371. if ~isstruct(xM)
  372. xM = struct(...
  373. 'T', [],...
  374. 'TH', xM,...
  375. 'I', 0,...
  376. 'VM', {[]},...
  377. 'xs', struct('Masking','analysis threshold'));
  378. end
  379. %-Initialise the name of the new mask : current mask & conditions on voxels
  380. %-----------------------------------------------------------------------
  381. VM = struct(...
  382. 'fname', ['mask' spm_file_ext],...
  383. 'dim', DIM',...
  384. 'dt', [spm_type('uint8') spm_platform('bigend')],...
  385. 'mat', M,...
  386. 'pinfo', [1 0 0]',...
  387. 'descrip', 'spm_spm:resultant analysis mask');
  388. VM = spm_create_vol(VM);
  389. %=======================================================================
  390. fprintf('%s%30s\n',repmat(sprintf('\b'),1,30),'...initialised'); %-#
  391. %-Specify type of prior for regression coefficients
  392. %-----------------------------------------------------------------------
  393. try
  394. SPM.PPM.priors.W;
  395. catch
  396. SPM.PPM.priors.W = 'Spatial - UGL';
  397. end
  398. %-Specify type of prior for AR coefficients
  399. %-----------------------------------------------------------------------
  400. try
  401. SPM.PPM.priors.A;
  402. catch
  403. SPM.PPM.priors.A = 'Spatial - UGL';
  404. end
  405. if ~isdeployed && strcmp(SPM.PPM.priors.A,'Robust')
  406. addpath(fullfile(spm('Dir'),'toolbox','mixture'));
  407. end
  408. %-Get structural info if necessary
  409. %-----------------------------------------------------------------------
  410. if strcmp(SPM.PPM.priors.A,'Discrete')
  411. try
  412. SPM.PPM.priors.SY;
  413. catch
  414. SPM.PPM.priors.SY = spm_select([1 Inf],'image',...
  415. 'Select structural images eg. brain or grey/white/CSF');
  416. end
  417. SPM.PPM.priors.Sin = size(SPM.PPM.priors.SY,1);
  418. for j=1:SPM.PPM.priors.Sin
  419. xDiscrete(j) = spm_vol(deblank(SPM.PPM.priors.SY(j,:)));
  420. end
  421. end
  422. %-Analysis space (volume/slices/clusters)
  423. %-----------------------------------------------------------------------
  424. try
  425. SPM.PPM.space_type;
  426. catch
  427. SPM.PPM.space_type = 'Volume';
  428. end
  429. switch lower(SPM.PPM.space_type)
  430. case 'volume',
  431. SPM.PPM.AN_slices = [1:1:zdim];
  432. case 'slices',
  433. try
  434. SPM.PPM.AN_slices;
  435. catch
  436. SPM.PPM.AN_slices = spm_input(['Enter slice numbers eg. 3 14 2'],'+1');
  437. end
  438. case {'clusters'}
  439. %-Cluster mask
  440. %-----------------------------------------------------------------------
  441. CM = spm_vol(SPM.PPM.clustermask{1});
  442. SPM.PPM.AN_slices = [1:zdim];
  443. otherwise
  444. error('Unknown analysis space.');
  445. end
  446. %-Initialise image containing labels of each block (slice or subvolume)
  447. %-----------------------------------------------------------------------
  448. VLabel = struct(...
  449. 'fname', ['labels' spm_file_ext],...
  450. 'dim', DIM',...
  451. 'dt', [spm_type('uint8') spm_platform('bigend')],...
  452. 'mat', M,...
  453. 'pinfo', [1 0 0]',...
  454. 'descrip', 'labels used to partition a volume');
  455. VLabel = spm_create_vol(VLabel);
  456. [xords,yords] = ndgrid(1:xdim,1:ydim);
  457. xords = xords(:)'; % plane X coordinates
  458. yords = yords(:)'; % plane Y coordinates
  459. S = 0; % Number of in-mask voxels
  460. s = 0; % Volume (voxels > UF)
  461. %-Initialise aspects of block variables common to all blocks
  462. %-----------------------------------------------------------------------
  463. if nsess > 1
  464. for s=1:nsess
  465. X = SPM.xX.X(SPM.Sess(s).row,SPM.Sess(s).col);
  466. X = [X ones(length(SPM.Sess(s).row),1)]; % Add on constant
  467. block_template(s) = spm_vb_init_volume(X,SPM.PPM.AR_P);
  468. end
  469. else
  470. block_template(1) = spm_vb_init_volume(SPM.xX.X,SPM.PPM.AR_P);
  471. end
  472. %-Get matrices that will remove low-frequency drifts
  473. % if high pass filters have been specified
  474. %-----------------------------------------------------------------------
  475. for s=1:nsess
  476. sess_nScan = length(SPM.xX.K(s).row);
  477. if size(SPM.xX.K(s).X0,2) > 0
  478. X0 = SPM.xX.K(s).X0;
  479. hpf(s).R0 = eye(sess_nScan)-X0*pinv(X0);
  480. else
  481. hpf(s).R0 = eye(sess_nScan);
  482. end
  483. end
  484. %-Set maximum number of VB iterations per block
  485. %-----------------------------------------------------------------------
  486. try
  487. SPM.PPM.maxits;
  488. catch
  489. SPM.PPM.maxits=4;
  490. end
  491. try
  492. SPM.PPM.tol;
  493. catch
  494. SPM.PPM.tol=0.0001;
  495. end
  496. try
  497. SPM.PPM.compute_det_D;
  498. catch
  499. SPM.PPM.compute_det_D=0;
  500. end
  501. for s=1:nsess
  502. block_template(s).maxits = SPM.PPM.maxits;
  503. block_template(s).tol = SPM.PPM.tol;
  504. block_template(s).compute_det_D = SPM.PPM.compute_det_D;
  505. block_template(s).verbose = 0;
  506. block_template(s).update_w = 1;
  507. block_template(s).update_lambda = 1;
  508. block_template(s).update_F = SPM.PPM.update_F;
  509. end
  510. %-Compute mask volume - before analysis
  511. %-----------------------------------------------------------------------
  512. fprintf('%-40s: %30s','Calculating mask',' ') %-#
  513. for z = 1:zdim
  514. % current plane-specific parameters
  515. %-------------------------------------------------------------------
  516. zords = repmat(z,1,xdim*ydim); %-plane Z coordinates
  517. Q = []; %-in mask indices for this plane
  518. if ismember(z,SPM.PPM.AN_slices)
  519. %-Print progress information in command window
  520. %---------------------------------------------------------------
  521. fprintf('%s%30s',repmat(sprintf('\b'),1,30),sprintf('%4d/%-4d',z,zdim)) %-#
  522. %-Construct list of voxels
  523. %---------------------------------------------------------------
  524. I = [1:xdim*ydim];
  525. xyz = [xords(I); yords(I); zords(I)]; %-voxel coordinates
  526. nVox = size(xyz,2);
  527. %-Get data & construct analysis mask
  528. %---------------------------------------------------------------
  529. Cm = true(1,nVox); %-current mask
  530. %-Compute explicit mask
  531. % (note that these may not have same orientations)
  532. %---------------------------------------------------------------
  533. for i = 1:length(xM.VM)
  534. %-Coordinates in mask image
  535. %-----------------------------------------------------------
  536. j = xM.VM(i).mat\M*[xyz;ones(1,nVox)];
  537. %-Load mask image within current mask & update mask
  538. %-----------------------------------------------------------
  539. Cm(Cm) = spm_get_data(xM.VM(i),j(:,Cm)) > 0;
  540. end
  541. if strcmp(SPM.PPM.space_type,'clusters')
  542. %-Coordinates in cluster mask image
  543. %-----------------------------------------------------------
  544. j = CM.mat\M*[xyz;ones(1,nVox)];
  545. %-Load mask image within current mask & update mask
  546. %-----------------------------------------------------------
  547. Cm(Cm) = spm_get_data(CM,j(:,Cm)) > 0;
  548. end
  549. %-Get the data in mask, compute threshold & implicit masks
  550. %---------------------------------------------------------------
  551. Y = zeros(nScan,nVox);
  552. for i = 1:nScan
  553. %-Load data in mask
  554. %-----------------------------------------------------------
  555. if ~any(Cm), break, end %-Break if empty mask
  556. Y(i,Cm) = spm_get_data(VY(i),xyz(:,Cm));
  557. Cm(Cm) = Y(i,Cm) > xM.TH(i); %-Threshold (& NaN) mask
  558. if xM.I && xM.TH(i) < 0 %-Use implicit mask
  559. Cm(Cm) = abs(Y(i,Cm)) > eps;
  560. end
  561. end
  562. %-Mask out voxels where data is constant
  563. %---------------------------------------------------------------
  564. Cm(Cm) = any(diff(Y(:,Cm),1));
  565. CrS = sum(Cm);
  566. if CrS,
  567. %-Remove isolated nodes (mask is then the same for slice
  568. % and graph-partitioned analyses)
  569. %-----------------------------------------------------------
  570. vxyz = spm_vb_neighbors(xyz(:,Cm)',0);
  571. if any(sum(vxyz,2)==0)
  572. Cm(Cm) = (sum(vxyz,2)>0);
  573. end
  574. end
  575. %-Append new inmask voxel locations and volumes
  576. %---------------------------------------------------------------
  577. Q = I(Cm); %-InMask XYZ voxel indices
  578. end
  579. %-Write Mask image
  580. %-------------------------------------------------------------------
  581. j = sparse(xdim,ydim);
  582. if length(Q), j(Q) = 1; end
  583. VM = spm_write_plane(VM, j, z);
  584. end
  585. fprintf('\n')
  586. %-Remove small clusters - removes clusters containing < 16 voxels
  587. %-----------------------------------------------------------------------
  588. mask = spm_read_vols(spm_vol(VM));
  589. [Cl,nCl] = spm_bwlabel(mask,6);
  590. ncl = histc(Cl(:),[1:max(Cl(:))])';
  591. if any(ncl < 16)
  592. incl = find(ncl < 16);
  593. for j = 1:length(incl),
  594. mask(find(Cl==incl(j))) = 0;
  595. end
  596. VM = spm_write_plane(VM, mask, ':');
  597. [Cl,nCl] = spm_bwlabel(mask,6);
  598. ncl = histc(Cl(:),[1:max(Cl(:))])';
  599. end
  600. %-Compute labels
  601. %-----------------------------------------------------------------------
  602. nLb = 0;
  603. Lb = zeros(size(Cl));
  604. if strcmp(SPM.PPM.block_type,'subvolumes') % using graph partitioning
  605. vol = 1;
  606. CUTOFF = 1000; % minimal number of voxels in a block
  607. for num = 1:nCl
  608. I = find(Cl==num);
  609. if ncl(num) > CUTOFF
  610. N = ncl(num);
  611. [x,y,z] = ind2sub([DIM(1),DIM(2),DIM(3)],I);
  612. xyz = [x,y,z];
  613. vxyz = spm_vb_neighbors(xyz,1);
  614. [edges,weights] = spm_vb_edgeweights(vxyz);
  615. W = spm_vb_adjacency(edges,weights,N);
  616. lbs = zeros(N,1);
  617. ind = [1:N]';
  618. depth = 1;
  619. lbs = spm_vb_graphcut(lbs,ind,I,W,depth,'random',CUTOFF,DIM);
  620. lbs = lbs + 1;
  621. nl = max(lbs);
  622. for z = 1:nl,
  623. Lb(I(lbs==z)) = nLb + z;
  624. end
  625. else
  626. nl = 1;
  627. Lb(I) = nLb + nl;
  628. end
  629. nLb = nLb + nl;
  630. end
  631. else
  632. % label slices
  633. vol = 0;
  634. mask = spm_read_vols(spm_vol(VM));
  635. for z = 1:zdim,
  636. if any(any(mask(:,:,z)))
  637. nLb = nLb + 1;
  638. Lb(:,:,z) = mask(:,:,z)*nLb;
  639. end
  640. end
  641. end
  642. nlb = histc(Lb(:),[1:max(Lb(:))])';
  643. %-Write VLabel
  644. %-----------------------------------------------------------------------
  645. VLabel = spm_write_plane(VLabel,Lb,':');
  646. %=======================================================================
  647. % - F I T M O D E L & W R I T E P A R A M E T E R I M A G E S
  648. %=======================================================================
  649. if SPM.PPM.window
  650. spm_progress_bar('Init',100,'VB estimation','');
  651. spm('Pointer','Watch')
  652. end
  653. %-Block-wise analysis (a block is either a slice or sub-volume)
  654. %-----------------------------------------------------------------------
  655. for z = 1:nLb
  656. %-Print progress information in command window
  657. %-------------------------------------------------------------------
  658. str = sprintf('Block %3d/%-3d',z,nLb);
  659. fprintf('\r%-40s: %30s',str,' ') %-#
  660. %-Construct list of voxels
  661. %-------------------------------------------------------------------
  662. Q = find(Lb==z);
  663. [xx,yy,zz] = ind2sub(size(mask),Q);
  664. xyz = [xx,yy,zz]'; %-voxel coordinates
  665. nVox = size(xyz,2);
  666. %-Get data
  667. %-------------------------------------------------------------------
  668. fprintf('%s%30s\n',repmat(sprintf('\b'),1,30),'...read & mask data');%-#
  669. Y = zeros(nScan,nVox);
  670. for i = 1:nScan
  671. Y(i,:) = spm_get_data(VY(i),xyz);
  672. end
  673. vxyz = spm_vb_neighbors(xyz',vol);
  674. %-Conditional estimates (per partition, per voxel)
  675. %-------------------------------------------------------------------
  676. beta = zeros(nBeta,nVox);
  677. Psd = zeros(nPsd, nVox);
  678. LogEv = zeros(1,nVox);
  679. for s=1:nsess
  680. Sess(s).Hp = zeros(1, nVox);
  681. Sess(s).AR = zeros(SPM.PPM.AR_P, nVox);
  682. end
  683. if ncon > 0
  684. con = zeros(ncon,nVox);
  685. con_var = zeros(ncon,nVox);
  686. end
  687. %-Get structural info for that block
  688. %-------------------------------------------------------------------
  689. if strcmp(SPM.PPM.priors.A,'Discrete')
  690. sxyz = xDiscrete(1).mat\M*[xyz;ones(1,nVox)];
  691. gamma = [];
  692. for j=1:SPM.PPM.priors.Sin
  693. gamma(:,j) = spm_get_data(xDiscrete(j),sxyz)';
  694. end
  695. if SPM.PPM.priors.Sin==1
  696. unaccounted=find(gamma==0);
  697. if length(unaccounted) > 0
  698. % Create extra category
  699. SPM.PPM.priors.S=2;
  700. gamma(:,2)=zeros(nVox,1);
  701. gamma(unaccounted,2)=1;
  702. gamma(:,1)=ones(nVox,1)-gamma(:,2);
  703. SPM.PPM.priors.gamma=gamma;
  704. else
  705. SPM.PPM.priors.S=1;
  706. SPM.PPM.priors.gamma=ones(nVox,1);
  707. end
  708. else
  709. unaccounted=find(sum(gamma')==0);
  710. if length(unaccounted)>0
  711. % Create extra category
  712. SPM.PPM.priors.S=SPM.PPM.priors.Sin+1;
  713. gamma(:,SPM.PPM.priors.S)=zeros(1,nVox);
  714. gamma(unaccounted,SPM.PPM.priors.S)=1;
  715. else
  716. SPM.PPM.priors.S=SPM.PPM.priors.Sin;
  717. end
  718. % convert probabilities to discrete values
  719. SPM.PPM.priors.gamma=zeros(nVox,SPM.PPM.priors.S);
  720. [yy,ii]=max(gamma');
  721. for j=1:SPM.PPM.priors.S
  722. SPM.PPM.priors.gamma(find(ii==j),j)=1;
  723. end
  724. end
  725. end
  726. %-Estimate model for each session separately
  727. %-------------------------------------------------------------------
  728. for s = 1:nsess
  729. fprintf('Session %d',s); %-#
  730. block = block_template(s);
  731. block = spm_vb_set_priors(block,SPM.PPM.priors,vxyz);
  732. %-Filter data to remove low frequencies
  733. %---------------------------------------------------------------
  734. R0Y = hpf(s).R0*Y(SPM.Sess(s).row,:);
  735. %-Fit model
  736. %---------------------------------------------------------------
  737. switch SPM.PPM.priors.A
  738. case 'Robust',
  739. %k=SPM.PPM.priors.k;
  740. block = spm_vb_robust(R0Y,block);
  741. otherwise
  742. block = spm_vb_glmar(R0Y,block);
  743. end
  744. %-Report AR values
  745. %---------------------------------------------------------------
  746. if SPM.PPM.AR_P > 0
  747. % session specific
  748. Sess(s).AR(1:SPM.PPM.AR_P,:) = block.ap_mean;
  749. end
  750. if SPM.PPM.update_F
  751. switch SPM.PPM.priors.A
  752. case 'Robust',
  753. Fn=block.F;
  754. SPM.PPM.Sess(s).block(z).F=sum(Fn);
  755. otherwise
  756. SPM.PPM.Sess(s).block(z).F = block.F;
  757. % Contribution map sums over sessions
  758. Fn = spm_vb_Fn(R0Y,block);
  759. end
  760. LogEv = LogEv+Fn;
  761. end
  762. %-Update regression coefficients
  763. %---------------------------------------------------------------
  764. ncols=length(SPM.Sess(s).col);
  765. beta(SPM.Sess(s).col,:) = block.wk_mean(1:ncols,:);
  766. if ncols==0
  767. % Design matrix empty except for constant
  768. mean_col_index=s;
  769. else
  770. mean_col_index=SPM.Sess(nsess).col(end)+s;
  771. end
  772. beta(mean_col_index,:) = block.wk_mean(ncols+1,:); % Session mean
  773. %-Report session-specific noise variances
  774. %---------------------------------------------------------------
  775. Sess(s).Hp(1,:) = sqrt(1./block.mean_lambda');
  776. %-Store regression coefficient posterior standard deviations
  777. %---------------------------------------------------------------
  778. Psd (SPM.Sess(s).col,:) = block.w_dev(1:ncols,:);
  779. Psd (mean_col_index,:) = block.w_dev(ncols+1,:);
  780. %-Update contrast variance
  781. %---------------------------------------------------------------
  782. if ncon > 0
  783. for ic=1:ncon,
  784. CC=SPM.xCon(ic).c;
  785. % Get relevant columns of contrast
  786. CC=[CC(SPM.Sess(s).col) ; 0];
  787. for i=1:nVox,
  788. con_var(ic,i)=con_var(ic,i)+CC'*block.w_cov{i}*CC;
  789. end
  790. end
  791. end
  792. switch SPM.PPM.priors.A,
  793. case 'Robust',
  794. % Save voxel data where robust model is favoured
  795. outlier_voxels=find(Fn>0);
  796. N_outliers=length(outlier_voxels);
  797. Y_out=R0Y(:,outlier_voxels);
  798. gamma_out=block.gamma(:,outlier_voxels);
  799. analysed_xyz=xyz;
  800. outlier_xyz=analysed_xyz(:,outlier_voxels);
  801. SPM.PPM.Sess(s).block(z).outlier_voxels=outlier_voxels;
  802. SPM.PPM.Sess(s).block(z).N_outliers=N_outliers;
  803. SPM.PPM.Sess(s).block(z).Y_out=Y_out;
  804. SPM.PPM.Sess(s).block(z).gamma_out=gamma_out;
  805. SPM.PPM.Sess(s).block(z).outlier_xyz=outlier_xyz;
  806. block = spm_vb_taylor_R(R0Y,block);
  807. SPM.PPM.Sess(s).block(z).mean=block.mean;
  808. SPM.PPM.Sess(s).block(z).N=block.N;
  809. % Prior precision
  810. SPM.PPM.Sess(s).block(z).mean_alpha=block.mean_alpha;
  811. otherwise
  812. % Prior precision
  813. SPM.PPM.Sess(s).block(z).mean_alpha=block.mean_alpha;
  814. %-Get block-wise Taylor approximation to posterior correlation
  815. %-------------------------------------------------------
  816. block = spm_vb_taylor_R(R0Y,block);
  817. SPM.PPM.Sess(s).block(z).mean=block.mean;
  818. SPM.PPM.Sess(s).block(z).elapsed_seconds=block.elapsed_seconds;
  819. %-Save Coefficient RESELS and number of voxels
  820. %-------------------------------------------------------
  821. SPM.PPM.Sess(s).block(z).gamma_tot=block.gamma_tot;
  822. SPM.PPM.Sess(s).block(z).N=block.N;
  823. end
  824. %-Save typical structure-specific AR coeffs
  825. %---------------------------------------------------------------
  826. if strcmp(SPM.PPM.priors.A,'Discrete')
  827. SPM.PPM.Sess(s).block(z).as_mean=block.as;
  828. SPM.PPM.Sess(s).block(z).as_dev=sqrt(1./block.mean_beta);
  829. end
  830. clear block;
  831. end % loop over sessions
  832. %-Get contrasts
  833. %-------------------------------------------------------------------
  834. if ncon > 0
  835. for ic=1:ncon
  836. CC=SPM.xCon(ic).c;
  837. con(ic,:)=CC'*beta;
  838. end
  839. end
  840. %-Append new inmask voxel locations and volumes
  841. %-------------------------------------------------------------------
  842. XYZ(:,S + [1:nVox]) = xyz; %-InMask XYZ voxel coords
  843. labels(1,S + [1:nVox]) = ones(1,nVox)*z; %-InMask labels
  844. S = S + nVox; %-Volume analysed (voxels)
  845. %-Write conditional beta images
  846. %-------------------------------------------------------------------
  847. if z == 1, j = NaN(xdim,ydim,zdim); end
  848. for i = 1:nBeta
  849. if z > 1, j = spm_read_vols(spm_vol(Vbeta(i))); end
  850. j(Q) = beta(i,:);
  851. Vbeta(i) = spm_write_plane(Vbeta(i),j,':'); % this is slow.
  852. % faster to find and save relevant slices instead of whole volume
  853. end
  854. %-Write SD error images
  855. %-------------------------------------------------------------------
  856. for s=1:nsess
  857. if z == 1, j = NaN(xdim,ydim,zdim); end
  858. if z > 1, j = spm_read_vols(spm_vol(SPM.PPM.Sess(s).VHp)); end
  859. j(Q) = Sess(s).Hp(1,:);
  860. SPM.PPM.Sess(s).VHp = spm_write_plane(SPM.PPM.Sess(s).VHp,j,':');
  861. end
  862. %-Write posterior standard-deviation of beta images
  863. %-------------------------------------------------------------------
  864. if z == 1, j = NaN(xdim,ydim,zdim); end
  865. for i = 1:nPsd
  866. if z > 1, j = spm_read_vols(spm_vol(VPsd(i))); end
  867. j(Q) = Psd(i,:);
  868. VPsd(i) = spm_write_plane(VPsd(i),j,':');
  869. end
  870. %-Write AR images
  871. %-------------------------------------------------------------------
  872. for s = 1:nsess
  873. if z == 1, j = NaN(xdim,ydim,zdim); end
  874. for i = 1:SPM.PPM.AR_P
  875. if z > 1, j = spm_read_vols(spm_vol(SPM.PPM.Sess(s).VAR(i))); end
  876. j(Q) = Sess(s).AR(i,:);
  877. SPM.PPM.Sess(s).VAR(i) = spm_write_plane(SPM.PPM.Sess(s).VAR(i),j,':');
  878. end
  879. end
  880. %-Write contribution image
  881. %-------------------------------------------------------------------
  882. if SPM.PPM.update_F
  883. if z == 1, j = NaN(xdim,ydim,zdim); end
  884. if z > 1, j = spm_read_vols(spm_vol(SPM.PPM.LogEv)); end
  885. j(Q) = LogEv;
  886. SPM.PPM.LogEv = spm_write_plane(SPM.PPM.LogEv,j,':');
  887. end
  888. %-Write contrast and contrast SD images
  889. %-------------------------------------------------------------------
  890. if ncon > 0
  891. if z == 1, j = NaN(xdim,ydim,zdim); end
  892. for ic=1:ncon
  893. if z > 1, j = spm_read_vols(spm_vol(SPM.xCon(ic).Vcon)); end
  894. j(Q) = con(ic,:);
  895. SPM.xCon(ic).Vcon = spm_write_plane(SPM.xCon(ic).Vcon,j,':');
  896. end
  897. if z == 1, j = NaN(xdim,ydim,zdim); end
  898. for ic=1:ncon
  899. if z > 1, j = spm_read_vols(spm_vol(SPM.PPM.Vcon_sd(ic))); end
  900. j(Q) = sqrt(con_var(ic,:));
  901. SPM.PPM.Vcon_sd(ic) = spm_write_plane(SPM.PPM.Vcon_sd(ic),j,':');
  902. end
  903. end
  904. if SPM.PPM.window
  905. %-Report progress
  906. %---------------------------------------------------------------
  907. spm_progress_bar('Set',100*z/nLb);
  908. end
  909. end % (for z = 1:nLb)
  910. %-Done!
  911. %-----------------------------------------------------------------------
  912. fprintf('\n') %-#
  913. if SPM.PPM.window
  914. spm_progress_bar('Clear')
  915. spm('Pointer','Arrow');
  916. end
  917. %=======================================================================
  918. % - P O S T E S T I M A T I O N
  919. %=======================================================================
  920. if S == 0, warning('No inmask voxels - empty analysis!'), end
  921. %-Create 1st contrast for 'effects of interest' (all if not specified)
  922. %=======================================================================
  923. Fcname = 'effects of interest';
  924. try
  925. iX0 = [xX.iB xX.iG];
  926. catch
  927. iX0 = [];
  928. end
  929. xX.xKXs = spm_sp('Set',spm_filter(xX.K,xX.X)); % ** Not Whitened **
  930. xX.erdf = size(xX.X,1); % Just set to number of scans so, when
  931. % we assess the results, spm_getSPM is happy
  932. xX.W= eye(size(xX.X,1)); % Set whitening matrix to identity -
  933. % we must set it to keep spm_graph happy
  934. xCon = spm_FcUtil('Set',Fcname,'F','iX0',iX0,xX.xKXs);
  935. %-Compute scaled design matrix for display purposes
  936. %-----------------------------------------------------------------------
  937. xX.nKX = spm_DesMtx('sca',xX.xKXs.X,xX.name);
  938. %-Save remaining results files and analysis parameters
  939. %=======================================================================
  940. fprintf('%-40s: %30s','Saving results','...writing') %-#
  941. %-place fields in SPM
  942. %-----------------------------------------------------------------------
  943. SPM.xVol.XYZ = XYZ(:,1:S); %-InMask XYZ coords (voxels)
  944. SPM.xVol.labels= labels(:,1:S); %-InMask labels (voxels)
  945. SPM.xVol.M = M; %-voxels -> mm
  946. SPM.xVol.iM = inv(M); %-mm -> voxels
  947. SPM.xVol.DIM = DIM; %-image dimensions
  948. SPM.xVol.S = S; %-Volume (voxels)
  949. SPM.xVol.R = 100; % Set R - number of RESELS - to arbitrary value
  950. % as, if R not set, SPM will think model has not
  951. % been estimated
  952. SPM.xVol.FWHM = 10; % Set to arbitrary value so spm_getSPM is happy
  953. SPM.VCbeta = Vbeta; % Filenames - parameters
  954. SPM.VPsd = VPsd; % Filenames - hyperparameters
  955. SPM.VM = VM; %-Filehandle - Mask
  956. SPM.PPM.Gamma = 1; % Default threshold for effect size (1 per cent)
  957. SPM.xX = xX; %-design structure
  958. SPM.xM = xM; %-mask structure
  959. % Copy contrast structure
  960. SPM.PPM.xCon = SPM.xCon;
  961. for i=1:length(SPM.PPM.xCon),
  962. SPM.PPM.xCon(i).PSTAT='T';
  963. end
  964. % Add pointer to RPV image file so that spm_list works
  965. SPM.xVol.VRpv=[];
  966. %-Save analysis parameters in SPM.mat file
  967. %-----------------------------------------------------------------------
  968. save('SPM.mat', 'SPM', spm_get_defaults('mat.format'));
  969. fprintf('%s%30s\n',repmat(sprintf('\b'),1,30),'...done') %-#
  970. if SPM.PPM.window
  971. %===================================================================
  972. %- E N D: Cleanup GUI
  973. %===================================================================
  974. spm('FigName','Stats: done',Finter); spm('Pointer','Arrow')
  975. fprintf('%-40s: %30s\n','Completed',spm('time')) %-#
  976. fprintf('...use the results section for assessment\n\n') %-#
  977. end