spm_searchlight.m 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. function R = spm_searchlight(SPM,searchopt,fun,varargin)
  2. % Local mass-multivariate (c.f., searchlight) facility
  3. % FORMAT R = spm_searchlight(SPM,searchopt,fun,varargin)
  4. % SPM - structure with fields:
  5. % .xY.VY - filenames char array or spm_vol struct array of images
  6. % .VM - filename or spm_vol structure to a mask (binary) image
  7. % Mask image can have any orientation, voxel size or data type.
  8. % It is interpolated using nearest neighbour interpolation to
  9. % the voxel locations of the data.
  10. % If empty, all voxels are used.
  11. % searchopt - searchlight options using VOI structure (xY) from spm_ROI
  12. % .def - searchlight definition {['sphere'] 'box'}
  13. % .spec - searchlight parameters [sphere radius {mm}]
  14. % fun - function handle to a function that takes three input arguments:
  15. % a [n x v] matrix (nb images x nb voxels within searchlight)
  16. % a [3 x v] matrix of voxels location within searchlight {vox}
  17. % a list of parameters provided in varargin
  18. % and returns a vector value [1 x N]
  19. % varargin - list of parameters sent to fun
  20. %
  21. % R - a [N x 1] cell array with each output (fun nargout) reshaped
  22. % to a volume or directly a volume if N == 1
  23. % Values outside the mask are attributed NaN.
  24. %__________________________________________________________________________
  25. %
  26. % References:
  27. %
  28. % [1] Adaptive Analysis of fMRI Data. Friman O, Borga M, Lundberg P and
  29. % Knutsson H. (2003) NeuroImage 19(3):837-845.
  30. %
  31. % [2] Information-based functional brain mapping. Kriegeskorte N, Goebel R,
  32. % Bandettini P. (2006) PNAS 103: 3863-3868.
  33. %__________________________________________________________________________
  34. % Copyright (C) 2010 Wellcome Trust Centre for Neuroimaging
  35. % Guillaume Flandin
  36. % $Id: spm_searchlight.m 4475 2011-09-09 17:53:14Z guillaume $
  37. spm('FnBanner',mfilename); %-#
  38. %-Get input images
  39. %--------------------------------------------------------------------------
  40. try
  41. VY = SPM.xY.VY;
  42. catch
  43. [VY, sts] = spm_select([1 Inf],'image','Select images');
  44. if ~sts, R = {}; return; end
  45. end
  46. if iscellstr(VY), VY = char(VY); end
  47. if ~isstruct(VY)
  48. VY = spm_vol(VY);
  49. end
  50. %-Check dimensions and orientations of all images
  51. %--------------------------------------------------------------------------
  52. spm_check_orientations(VY);
  53. %-Get mask image
  54. %--------------------------------------------------------------------------
  55. try
  56. VM = SPM.VM;
  57. catch
  58. [VM, sts] = spm_select([0 1],'image','Select mask');
  59. if ~sts, VM = []; end
  60. end
  61. if ~isstruct(VM) && ~isempty(VM)
  62. VM = spm_vol(VM);
  63. end
  64. %-Get space details
  65. %--------------------------------------------------------------------------
  66. N = numel(VY); %-number of images
  67. M = VY(1).mat; %-voxels to mm matrix
  68. iM = inv(M); %-mm to voxels matrix
  69. DIM = VY(1).dim; %-image dimensions
  70. NDIM = prod([DIM N]); %-overall dimension
  71. [x,y,z] = ndgrid(1:DIM(1),1:DIM(2),1:DIM(3));
  72. XYZ = [x(:)';y(:)';z(:)']; clear x y z %-voxel coordinates {vx}
  73. XYZmm = M(1:3,:)*[XYZ; ones(1,size(XYZ,2))];%-voxel coordinates {mm}
  74. XYZmm_cpy = XYZmm; %-copy without masking
  75. %-Strategy for reading data: memory [1] / block [2] / disk [3]
  76. %--------------------------------------------------------------------------
  77. fprintf('%-40s: ','Read data'); %-#
  78. try
  79. ds = NDIM*8; %-data size {bytes}
  80. MAXMEM = spm('memory'); %-max amnt usable memory
  81. if ds > MAXMEM
  82. YY = struct('y',[], 'i',[Inf Inf]);
  83. blk = floor(MAXMEM/(prod([DIM(1:2) N])*8));%-blk size {# slices}
  84. if blk == 0, error('revert to disk.'); end
  85. Ystrtg = 2;
  86. fprintf('%30s\n','...per block'); %-#
  87. else
  88. YY = spm_read_vols(VY);
  89. Ystrtg = 1;
  90. fprintf('%30s\n','...in memory'); %-#
  91. end
  92. catch
  93. Ystrtg = 3;
  94. fprintf('%30s\n','...from disk'); %-#
  95. end
  96. %-Search volume (from mask)
  97. %--------------------------------------------------------------------------
  98. fprintf('%-40s: ','Read mask'); %-#
  99. if ~isempty(VM)
  100. if any(DIM-VM.dim) || any(any(abs(VM.mat-M)>1e-4))
  101. MM = spm_get_data(VM,VM.mat\[XYZmm;ones(1,size(XYZmm,2))],false);
  102. else
  103. MM = spm_read_vols(VM);
  104. end
  105. MM = logical(MM);
  106. XYZmm = XYZmm(:,MM(:));
  107. XYZ = XYZ(:,MM(:));
  108. fprintf('%30s\n','...done'); %-#
  109. else
  110. MM = true(DIM);
  111. fprintf('%30s\n', '...none'); %-#
  112. end
  113. %-Searchlight options (clique definition)
  114. %--------------------------------------------------------------------------
  115. try, xY = searchopt; end
  116. xY.xyz = [NaN NaN NaN];
  117. xY.rej = {'cluster','mask'};
  118. xY = spm_ROI(xY);
  119. %-Evaluated function
  120. %--------------------------------------------------------------------------
  121. if ischar(fun)
  122. fun = str2func(fun);
  123. end
  124. if ~isa(fun, 'function_handle')
  125. error('''fun'' must be a function handle with two input parameters.');
  126. end
  127. %-Get local clique and perform searchlight over voxels
  128. %==========================================================================
  129. %-Build local clique
  130. %--------------------------------------------------------------------------
  131. fprintf('%-40s: ','Construct clique'); %-#
  132. c = round(DIM(:)/2);
  133. xY.xyz = M(1:3,:) * [c;1];
  134. [xY, clique] = spm_ROI(xY,XYZmm_cpy);
  135. clique = round(iM(1:3,:) * [clique;ones(1,size(clique,2))]);
  136. clique = bsxfun(@minus, clique, c);
  137. dc = (max(clique,[],2) - min(clique,[],2) + 1)';
  138. fprintf('%30s\n',sprintf('%d voxels - [%dx%dx%d]',size(clique,2),dc)); %-#
  139. if Ystrtg == 2 && blk < dc(3)
  140. fprintf('%-40s: %30s\n','Read data','revert to disk'); %-#
  141. Ystrtg = 3;
  142. end
  143. %-Initialise progress bar
  144. %--------------------------------------------------------------------------
  145. spm_figure('GetWin','Interactive');
  146. spm_progress_bar('Init',size(XYZ,2));
  147. Ibar = floor(linspace(1,size(XYZ,2), 100));
  148. fprintf('%-40s: %30s','Searchlight','...computing'); %-#
  149. SLR = [];
  150. %-Searchlight
  151. %--------------------------------------------------------------------------
  152. for i=1:size(XYZ,2)
  153. %-Local clique (handle image boundaries and mask)
  154. %----------------------------------------------------------------------
  155. xyz = bsxfun(@plus,XYZ(:,i),clique);
  156. xyz(:,any(bsxfun(@lt,xyz,[1 1 1]') | bsxfun(@gt,xyz,DIM'))) = [];
  157. idx = sub2ind(DIM,xyz(1,:),xyz(2,:),xyz(3,:));
  158. j = MM(idx);
  159. idx = idx(j);
  160. xyz = xyz(:,j);
  161. %-Read data
  162. %----------------------------------------------------------------------
  163. if Ystrtg == 3
  164. Y = spm_get_data(VY,xyz,false);
  165. elseif Ystrtg == 2
  166. if min(xyz(3,:)) < min(YY.i) || max(xyz(3,:)) > max(YY.i)
  167. YY.i = min(xyz(3,:)):min(xyz(3,:))+blk;
  168. YY.i(YY.i>DIM(3)) = [];
  169. YY.y = zeros(DIM(1),DIM(2),numel(YY.i),N);
  170. for v=1:N
  171. for p=1:numel(YY.i)
  172. YY.y(:,:,p,v) = ...
  173. spm_slice_vol(VY(v), ...
  174. spm_matrix([0 0 YY.i(p)]), ...
  175. VY(v).dim(1:2),0);
  176. end
  177. end
  178. end
  179. idx = idx - (min(YY.i)-1) * prod(DIM(1:2));
  180. k = prod(DIM(1:2))*numel(YY.i);
  181. idx = bsxfun(@plus, idx(:), 0:k:k*N-1);
  182. Y = YY.y(idx)';
  183. elseif Ystrtg == 1
  184. idx = bsxfun(@plus, idx(:), 0:prod(DIM):NDIM-1);
  185. Y = YY(idx)';
  186. end
  187. %-Call user-specified function
  188. %----------------------------------------------------------------------
  189. if isempty(SLR)
  190. t = fun(Y,xyz,varargin{:});
  191. SLR = zeros(size(XYZ,2),length(t));
  192. SLR(1,:) = t;
  193. else
  194. SLR(i,:) = fun(Y,xyz,varargin{:});
  195. end
  196. %-Update progress bar
  197. %----------------------------------------------------------------------
  198. if any(Ibar == i), spm_progress_bar('Set', i); end
  199. end
  200. %-Clear progress bar
  201. %--------------------------------------------------------------------------
  202. fprintf('%s%30s\n',repmat(sprintf('\b'),1,30),'...done'); %-#
  203. spm_progress_bar('Clear');
  204. %-Return computations - reshaped as volumes in a cell array
  205. %--------------------------------------------------------------------------
  206. R = cell(size(SLR,2),1);
  207. for i=1:size(SLR,2)
  208. MV = NaN(DIM);
  209. MV(sub2ind(DIM,XYZ(1,:),XYZ(2,:),XYZ(3,:))) = SLR(:,i);
  210. R{i} = MV;
  211. end
  212. %-Write images if required
  213. %--------------------------------------------------------------------------
  214. fprintf('%-40s: %30s','Output images','...writing'); %-#
  215. VO(1:size(SLR,2)) = deal(struct(...
  216. 'fname', [],...
  217. 'dim', DIM,...
  218. 'dt', [spm_type('float64') spm_platform('bigend')],...
  219. 'mat', M,...
  220. 'pinfo', [1 0 0]',...
  221. 'descrip', ['spm_searchlight: ' func2str(fun)]));
  222. for i=1:size(SLR,2)
  223. VO(i).fname = sprintf('%s_%04d%s', ...
  224. 'searchlight',i,spm_file_ext);
  225. VO(i).descrip = sprintf('%s (%04d)',VO(i).descrip,i);
  226. end
  227. for i=1:size(SLR,2)
  228. spm_write_vol(VO(i),R{i});
  229. end
  230. fprintf('%s%30s\n',repmat(sprintf('\b'),1,30),'...done'); %-#
  231. %-And exit
  232. %--------------------------------------------------------------------------
  233. if size(SLR,2) == 1, R = R{1}; end
  234. fprintf('%-40s: %30s\n','Completed',spm('time')); %-#