spm_surf.m 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. function varargout = spm_surf(P,mode,thresh)
  2. % Surface extraction
  3. % FORMAT spm_surf(P,mode,thresh)
  4. %
  5. % P - char array of filenames
  6. % Usually, this will be c1xxx.<ext> & c2xxx.<ext> - grey and white
  7. % matter segments created using the segmentation routine.
  8. % mode - operation mode [1: rendering, 2: surface, 3: both]
  9. % thresh - vector or threshold values for extraction [default: 0.5]
  10. % This is only relevant for extracting surfaces, not rendering.
  11. %
  12. % Generated files (depending on 'mode'):
  13. % A "render_xxx.mat" file can be produced that can be used for
  14. % rendering activations on to, see spm_render.
  15. %
  16. % A "xxx.surf.gii" file can also be written, which is created using
  17. % Matlab's isosurface function.
  18. % This extracted brain surface can be viewed using code something like:
  19. % FV = gifti(spm_select(1,'mesh','Select surface data'));
  20. % FV = export(FV,'patch');
  21. % fg = spm_figure('GetWin','Graphics');
  22. % ax = axes('Parent',fg);
  23. % p = patch(FV, 'Parent',ax,...
  24. % 'FaceColor', [0.8 0.7 0.7], 'FaceVertexCData', [],...
  25. % 'EdgeColor', 'none',...
  26. % 'FaceLighting', 'gouraud',...
  27. % 'SpecularStrength' ,0.7, 'AmbientStrength', 0.1,...
  28. % 'DiffuseStrength', 0.7, 'SpecularExponent', 10);
  29. % set(0,'CurrentFigure',fg);
  30. % set(fg,'CurrentAxes',ax);
  31. % l = camlight(-40, 20);
  32. % axis image;
  33. % rotate3d on;
  34. %
  35. % FORMAT out = spm_surf(job)
  36. %
  37. % Input
  38. % A job structure with fields
  39. % .data - cell array of filenames
  40. % .mode - operation mode
  41. % .thresh - thresholds for extraction
  42. % Output
  43. % A struct with fields (depending on operation mode)
  44. % .rendfile - cellstring containing render filename
  45. % .surffile - cellstring containing surface filename(s)
  46. %__________________________________________________________________________
  47. %
  48. % This surface extraction is not particularly sophisticated. It simply
  49. % smooths the data slightly and extracts the surface at a threshold of
  50. % 0.5. The input segmentation images can be manually cleaned up first using
  51. % e.g., MRIcron.
  52. %__________________________________________________________________________
  53. % Copyright (C) 2002-2018 Wellcome Trust Centre for Neuroimaging
  54. % John Ashburner
  55. % $Id: spm_surf.m 7381 2018-07-25 10:27:54Z guillaume $
  56. SVNrev = '$Rev: 7381 $';
  57. spm('FnBanner',mfilename,SVNrev);
  58. spm('FigName','Surface');
  59. %-Get input: filenames 'P'
  60. %--------------------------------------------------------------------------
  61. try
  62. if isstruct(P)
  63. if all(isfield(P,{'data','mode','thresh'}))
  64. job = P;
  65. P = char(job.data);
  66. mode = job.mode;
  67. thresh = job.thresh;
  68. else
  69. % P is an spm_vol struct array
  70. end
  71. end
  72. catch
  73. [P, sts] = spm_select([1 Inf],'image','Select images');
  74. if ~sts, varargout = {}; return; end
  75. end
  76. %-Get input: operation mode 'mode'
  77. %--------------------------------------------------------------------------
  78. try
  79. mode;
  80. catch
  81. mode = spm_input('Save','+1','m',...
  82. ['Save Rendering|'...
  83. 'Save Extracted Surface|'...
  84. 'Save Rendering and Surface'],[1 2 3],3);
  85. end
  86. %-Get input: threshold for extraction 'thresh'
  87. %--------------------------------------------------------------------------
  88. try
  89. thresh;
  90. catch
  91. thresh = 0.5;
  92. end
  93. %-Surface extraction
  94. %--------------------------------------------------------------------------
  95. spm('FigName','Surface: working');
  96. spm('Pointer','Watch');
  97. out = surfext(P,mode,thresh);
  98. spm('Pointer','Arrow');
  99. spm('FigName','Surface: done');
  100. if nargout > 0
  101. varargout{1} = out;
  102. end
  103. %==========================================================================
  104. % function out = surfext(P,mode,thresh)
  105. %==========================================================================
  106. function out = surfext(P,mode,thresh)
  107. V = spm_vol(P);
  108. br = zeros(V(1).dim(1:3));
  109. for i=1:V(1).dim(3)
  110. B = spm_matrix([0 0 i]);
  111. tmp = spm_slice_vol(V(1),B,V(1).dim(1:2),1);
  112. for j=2:numel(V)
  113. M = V(j).mat\V(1).mat*B;
  114. tmp = tmp + spm_slice_vol(V(j),M,V(1).dim(1:2),1);
  115. end
  116. br(:,:,i) = tmp;
  117. end
  118. % Build a 3x3x3 seperable smoothing kernel and smooth
  119. %--------------------------------------------------------------------------
  120. kx = [0.75 1 0.75];
  121. ky = [0.75 1 0.75];
  122. kz = [0.75 1 0.75];
  123. sm = sum(kron(kron(kz,ky),kx))^(1/3);
  124. kx = kx/sm; ky = ky/sm; kz = kz/sm;
  125. spm_conv_vol(br,br,kx,ky,kz,-[1 1 1]);
  126. [pth,nam] = spm_fileparts(V(1).fname);
  127. if any(mode==[1 3])
  128. % Produce rendering
  129. %----------------------------------------------------------------------
  130. out.rendfile{1} = fullfile(pth,['render_' nam '.mat']);
  131. tmp = struct('dat',br,'dim',size(br),'mat',V(1).mat);
  132. renviews(tmp,out.rendfile{1});
  133. end
  134. if any(mode==[2 3])
  135. % Produce extracted surface
  136. %----------------------------------------------------------------------
  137. for k=1:numel(thresh)
  138. [faces,vertices] = isosurface(br,thresh(k));
  139. % Swap around x and y because isosurface does for some
  140. % weird and wonderful reason.
  141. Mat = V(1).mat(1:3,:)*[0 1 0 0;1 0 0 0;0 0 1 0; 0 0 0 1];
  142. vertices = (Mat*[vertices' ; ones(1,size(vertices,1))])';
  143. if numel(thresh)==1
  144. nam1 = nam;
  145. else
  146. nam1 = sprintf('%s-%d',nam,k);
  147. end
  148. out.surffile{k} = fullfile(pth,[nam1 '.surf.gii']);
  149. save(gifti(struct('faces',faces,'vertices',vertices)),out.surffile{k});
  150. end
  151. end
  152. %==========================================================================
  153. % function renviews(V,oname)
  154. %==========================================================================
  155. function renviews(V,oname)
  156. % Produce images for rendering activations to
  157. %
  158. % FORMAT renviews(V,oname)
  159. % V - mapped image to render, or alternatively
  160. % a structure of:
  161. % V.dat - 3D array
  162. % V.dim - size of 3D array
  163. % V.mat - affine mapping from voxels to millimeters
  164. % oname - the name of the render.mat file.
  165. %__________________________________________________________________________
  166. %
  167. % Produces a matrix file "render_xxx.mat" which contains everything that
  168. % "spm_render" is likely to need.
  169. %
  170. % Ideally, the input image should contain values in the range of zero
  171. % and one, and be smoothed slightly. A threshold of 0.5 is used to
  172. % distinguish brain from non-brain.
  173. %__________________________________________________________________________
  174. fprintf('%-40s: ','Rendering...'); %-#
  175. rend{1} = make_struct(V,[pi 0 pi/2]); % Transverse 1
  176. rend{2} = make_struct(V,[0 0 pi/2]); % Transverse 2
  177. rend{3} = make_struct(V,[0 pi/2 pi]); % Sagittal 1
  178. rend{4} = make_struct(V,[0 pi/2 0]); % Sagittal 2
  179. rend{5} = make_struct(V,[pi/2 pi/2 0]); % Coronal 1
  180. rend{6} = make_struct(V,[pi/2 pi/2 pi]); % Coronal 2
  181. fprintf('%30s\n','...done'); %-#
  182. fprintf('Writing Render file in:\n %s\n',spm_file(oname,'cpath')); %-#
  183. save(oname,'rend', spm_get_defaults('mat.format'));
  184. if ~spm('CmdLine')
  185. disp_renderings(rend);
  186. spm_print;
  187. end
  188. %==========================================================================
  189. % function str = make_struct(V,thetas)
  190. %==========================================================================
  191. function str = make_struct(V,thetas)
  192. [D,M] = matdim(V.dim(1:3),V.mat,thetas);
  193. [ren,dep] = make_pic(V,M*V.mat,D);
  194. str = struct('M',M,'ren',ren,'dep',dep);
  195. %==========================================================================
  196. % function [ren,zbuf] = make_pic(V,M,D)
  197. %==========================================================================
  198. function [ren,zbuf] = make_pic(V,M,D)
  199. % A bit of a hack to try and make spm_render_vol produce some slightly
  200. % prettier output. It kind of works...
  201. if isfield(V,'dat'), vv = V.dat; else vv = V; end;
  202. [REN, zbuf, X, Y, Z] = spm_render_vol(vv, M, D, [0.5 1]);
  203. fw = max(sqrt(sum(M(1:3,1:3).^2)));
  204. msk = find(zbuf==1024);
  205. brn = ones(size(X));
  206. brn(msk) = 0;
  207. brn = spm_conv(brn,fw);
  208. X(msk) = 0;
  209. Y(msk) = 0;
  210. Z(msk) = 0;
  211. msk = find(brn<0.5);
  212. tmp = brn;
  213. tmp(msk) = 100000;
  214. sX = spm_conv(X,fw)./tmp;
  215. sY = spm_conv(Y,fw)./tmp;
  216. sZ = spm_conv(Z,fw)./tmp;
  217. zbuf = spm_conv(zbuf,fw)./tmp;
  218. zbuf(msk) = 1024;
  219. vec = [-1 1 3]; % The direction of the lighting.
  220. vec = vec/norm(vec);
  221. [t,dx,dy,dz] = spm_sample_vol(vv,sX,sY,sZ,3);
  222. IM = inv(diag([0.5 0.5 1])*M(1:3,1:3))';
  223. ren = IM(1:3,1:3)*[dx(:)' ; dy(:)' ; dz(:)'];
  224. len = sqrt(sum(ren.^2,1))+eps;
  225. ren = [ren(1,:)./len ; ren(2,:)./len ; ren(3,:)./len];
  226. ren = reshape(vec*ren,[size(dx) 1]);
  227. ren(ren<0) = 0;
  228. ren(msk) = ren(msk)-0.2;
  229. ren = ren*0.8+0.2;
  230. mx = max(ren(:));
  231. ren = ren/mx;
  232. %==========================================================================
  233. % function disp_renderings(rend)
  234. %==========================================================================
  235. function disp_renderings(rend)
  236. Fgraph = spm_figure('GetWin','Graphics');
  237. spm_results_ui('Clear',Fgraph);
  238. hght = 0.95;
  239. nrow = ceil(length(rend)/2);
  240. ax = axes('Parent',Fgraph,'units','normalized',...
  241. 'Position',[0, 0, 1, hght],'Visible','off');
  242. image(0,'Parent',ax);
  243. set(ax,'YTick',[],'XTick',[]);
  244. for i=1:length(rend)
  245. ren = rend{i}.ren;
  246. ax = axes('Parent',Fgraph,'units','normalized',...
  247. 'Position',[rem(i-1,2)*0.5, floor((i-1)/2)*hght/nrow, 0.5, hght/nrow],...
  248. 'Visible','off');
  249. image(ren*64,'Parent',ax);
  250. set(ax,'DataAspectRatio',[1 1 1], ...
  251. 'PlotBoxAspectRatioMode','auto',...
  252. 'YTick',[],'XTick',[],'XDir','normal','YDir','normal');
  253. end
  254. %==========================================================================
  255. % function [d,M] = matdim(dim,mat,thetas)
  256. %==========================================================================
  257. function [d,M] = matdim(dim,mat,thetas)
  258. R = spm_matrix([0 0 0 thetas]);
  259. bb = [[1 1 1];dim(1:3)];
  260. c = [ bb(1,1) bb(1,2) bb(1,3) 1
  261. bb(1,1) bb(1,2) bb(2,3) 1
  262. bb(1,1) bb(2,2) bb(1,3) 1
  263. bb(1,1) bb(2,2) bb(2,3) 1
  264. bb(2,1) bb(1,2) bb(1,3) 1
  265. bb(2,1) bb(1,2) bb(2,3) 1
  266. bb(2,1) bb(2,2) bb(1,3) 1
  267. bb(2,1) bb(2,2) bb(2,3) 1]';
  268. tc = diag([2 2 1 1])*R*mat*c;
  269. tc = tc(1:3,:)';
  270. mx = max(tc);
  271. mn = min(tc);
  272. M = spm_matrix(-mn(1:2))*diag([2 2 1 1])*R;
  273. d = ceil(abs(mx(1:2)-mn(1:2)))+1;