spm_render.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. function varargout = spm_render(dat,brt,rendfile)
  2. % Render blobs on surface of a 'standard' brain
  3. % FORMAT spm_render(dat,brt,rendfile)
  4. %
  5. % dat - a struct array of length 1 to 3
  6. % each element is a structure containing:
  7. % - XYZ - the x, y & z coordinates of the transformed SPM{.}
  8. % values in units of voxels.
  9. % - t - the SPM{.} values.
  10. % - mat - affine matrix mapping from XYZ voxels to MNI.
  11. % - dim - dimensions of volume from which XYZ is drawn.
  12. % brt - brightness control:
  13. % If NaN, then displays using the old style with hot
  14. % metal for the blobs, and grey for the brain.
  15. % Otherwise, it is used as a ``gamma correction'' to
  16. % optionally brighten the blobs up a little.
  17. % rendfile - the file containing the images to render on to (see also
  18. % spm_surf.m) or a surface mesh file.
  19. %
  20. % Without arguments, spm_render acts as its own UI.
  21. %__________________________________________________________________________
  22. %
  23. % spm_render prompts for details of up to three SPM{.}s that are then
  24. % displayed superimposed on the surface of a 'standard' brain.
  25. %
  26. % The first is shown in red, then green then blue.
  27. %
  28. % The blobs which are displayed are the integral of all transformed t
  29. % values, exponentially decayed according to their depth. Voxels that
  30. % are 10mm behind the surface have half the intensity of ones at the
  31. % surface.
  32. %__________________________________________________________________________
  33. % Copyright (C) 1996-2015 Wellcome Trust Centre for Neuroimaging
  34. % John Ashburner
  35. % $Id: spm_render.m 7381 2018-07-25 10:27:54Z guillaume $
  36. SVNrev = '$Rev: 7381 $';
  37. global prevrend
  38. if ~isstruct(prevrend)
  39. prevrend = struct('rendfile','', 'brt',[], 'col',[]);
  40. end
  41. varargout = {};
  42. %-Parse arguments, get data if not passed as parameters
  43. %==========================================================================
  44. %-Get surface
  45. %--------------------------------------------------------------------------
  46. if nargin < 3 || isempty(prevrend.rendfile)
  47. spm('FnBanner',mfilename,SVNrev);
  48. [rendfile, sts] = spm_select(1,{'mat','mesh'},'Render file');
  49. if ~sts, return; end
  50. end
  51. prevrend.rendfile = rendfile;
  52. ext = spm_file(rendfile,'ext');
  53. loadgifti = false;
  54. if strcmpi(ext,'mat')
  55. load(rendfile);
  56. if ~exist('rend','var') && ~exist('Matrixes','var')
  57. loadgifti = true;
  58. end
  59. end
  60. if ~strcmpi(ext,'mat') || loadgifti
  61. try
  62. rend = gifti(rendfile);
  63. catch
  64. error('Cannot read render file "%s".\n', rendfile);
  65. end
  66. if ~isfield(rend,'vertices')
  67. try
  68. M = rend;
  69. rend = gifti(rend.private.metadata(1).value);
  70. try, rend.cdata = M.cdata(); end
  71. catch
  72. error('Cannot find a surface mesh to be displayed.');
  73. end
  74. end
  75. rend = export(rend,'patch');
  76. end
  77. %-Get data
  78. %--------------------------------------------------------------------------
  79. if isfield(rend,'facevertexcdata')
  80. dat = rend.facevertexcdata;
  81. num = 1;
  82. elseif nargin < 1
  83. spm('FigName','Results: render');
  84. num = spm_input('Number of sets',1,'1 set|2 sets|3 sets',[1 2 3],1);
  85. for i = 1:num
  86. [SPM,xSPM] = spm_getSPM;
  87. dat(i) = struct('XYZ', xSPM.XYZ,...
  88. 't', xSPM.Z',...
  89. 'mat', xSPM.M,...
  90. 'dim', xSPM.DIM);
  91. end
  92. showbar = 1;
  93. else
  94. if isstruct(dat)
  95. num = length(dat);
  96. showbar = 0;
  97. else
  98. files = cellstr(dat);
  99. clear dat
  100. num = numel(files);
  101. for i=1:num
  102. V = spm_vol(files{i});
  103. d = spm_read_vols(V);
  104. id = find(isfinite(d));
  105. [X,Y,Z] = ind2sub(V.dim,id);
  106. dat(i).XYZ = [X Y Z]';
  107. dat(i).t = d(id);
  108. dat(i).mat = V.mat;
  109. dat(i).dim = V.dim';
  110. clear X Y Z id
  111. end
  112. showbar = 1;
  113. end
  114. end
  115. %-Get brightness & colours (mesh)
  116. %--------------------------------------------------------------------------
  117. if isfield(rend,'vertices')
  118. if num == 1
  119. col = hot(256);
  120. else
  121. col = eye(3);
  122. if spm_input('Which colours?','+1','b',{'RGB','Custom'},[0 1],1)
  123. for k = 1:num
  124. col(k,:) = uisetcolor(col(k,:),sprintf('Colour of blob set %d',k));
  125. end
  126. end
  127. end
  128. surf_rend(dat,rend,col);
  129. return
  130. end
  131. %-Get brightness & colours (image)
  132. %--------------------------------------------------------------------------
  133. if nargin < 2 || isempty(prevrend.brt)
  134. brt = 1;
  135. if num==1
  136. brt = spm_input('Style',1,'new|old',[1 NaN], 1);
  137. end
  138. if isfinite(brt)
  139. brt = spm_input('Brighten blobs','+1','none|slightly|more|lots',[1 0.75 0.5 0.25], 1);
  140. col = eye(3);
  141. % ask for custom colours & get rgb values
  142. %------------------------------------------------------------------
  143. if spm_input('Which colours?','+1','b',{'RGB','Custom'},[0 1],1)
  144. for k = 1:num
  145. col(k,:) = uisetcolor(col(k,:),sprintf('Colour of blob set %d',k));
  146. end
  147. end
  148. else
  149. col = [];
  150. end
  151. elseif isfinite(brt) && isempty(prevrend.col)
  152. col = eye(3);
  153. elseif isfinite(brt) % don't need to check prevrend.col again
  154. col = prevrend.col;
  155. else
  156. col = [];
  157. end
  158. prevrend.brt = brt;
  159. prevrend.col = col;
  160. %-Perform the rendering
  161. %==========================================================================
  162. spm('Pointer','Watch');
  163. if showbar, spm_progress_bar('Init', size(dat,1)*length(rend),...
  164. 'Formatting Renderings', 'Number completed'); end
  165. for i=1:length(rend)
  166. rend{i}.max=0;
  167. rend{i}.data = cell(size(dat,1),1);
  168. if issparse(rend{i}.ren)
  169. % Assume that images have been DCT compressed
  170. % - the SPM99 distribution was originally too big.
  171. d = size(rend{i}.ren);
  172. B1 = spm_dctmtx(d(1),d(1));
  173. B2 = spm_dctmtx(d(2),d(2));
  174. rend{i}.ren = B1*rend{i}.ren*B2';
  175. % the depths did not compress so well with
  176. % a straight DCT - therefore it was modified slightly
  177. rend{i}.dep = exp(B1*rend{i}.dep*B2')-1;
  178. end
  179. rend{i}.ren(rend{i}.ren>=1) = 1;
  180. rend{i}.ren(rend{i}.ren<=0) = 0;
  181. if showbar, spm_progress_bar('Set', i); end
  182. end
  183. if showbar, spm_progress_bar('Clear'); end
  184. if showbar, spm_progress_bar('Init', length(dat)*length(rend),...
  185. 'Making pictures', 'Number completed'); end
  186. mx = zeros(length(rend),1)+eps;
  187. mn = zeros(length(rend),1);
  188. for j=1:length(dat)
  189. XYZ = dat(j).XYZ;
  190. t = dat(j).t;
  191. dim = dat(j).dim;
  192. mat = dat(j).mat;
  193. for i=1:length(rend)
  194. % transform from Talairach space to space of the rendered image
  195. %------------------------------------------------------------------
  196. M1 = rend{i}.M*mat;
  197. zm = sum(M1(1:2,1:3).^2,2).^(-1/2);
  198. M2 = diag([zm' 1 1]);
  199. M = M2*M1;
  200. cor = [1 1 1 ; dim(1) 1 1 ; 1 dim(2) 1; dim(1) dim(2) 1 ;
  201. 1 1 dim(3) ; dim(1) 1 dim(3) ; 1 dim(2) dim(3); dim(1) dim(2) dim(3)]';
  202. tcor= M(1:3,1:3)*cor + M(1:3,4)*ones(1,8);
  203. off = min(tcor(1:2,:)');
  204. M2 = spm_matrix(-off+1)*M2;
  205. M = M2*M1;
  206. xyz = (M(1:3,1:3)*XYZ + M(1:3,4)*ones(1,size(XYZ,2)));
  207. d2 = ceil(max(xyz(1:2,:)'));
  208. % Calculate 'depth' of values
  209. %------------------------------------------------------------------
  210. if ~isempty(d2)
  211. dep = spm_slice_vol(rend{i}.dep,spm_matrix([0 0 1])*inv(M2),d2,1);
  212. z1 = dep(round(xyz(1,:))+round(xyz(2,:)-1)*size(dep,1));
  213. if ~isfinite(brt), msk = find(xyz(3,:) < (z1+20) & xyz(3,:) > (z1-5));
  214. else msk = find(xyz(3,:) < (z1+60) & xyz(3,:) > (z1-5)); end
  215. else
  216. msk = [];
  217. end
  218. if ~isempty(msk)
  219. % Generate an image of the integral of the blob values.
  220. %--------------------------------------------------------------
  221. xyz = xyz(:,msk);
  222. if ~isfinite(brt), t0 = t(msk);
  223. else
  224. dst = xyz(3,:) - z1(msk);
  225. dst = max(dst,0);
  226. t0 = t(msk).*exp((log(0.5)/10)*dst)';
  227. end
  228. X0 = full(sparse(round(xyz(1,:)), round(xyz(2,:)), t0, d2(1), d2(2)));
  229. hld = 1; if ~isfinite(brt), hld = 0; end
  230. X = spm_slice_vol(X0,spm_matrix([0 0 1])*M2,size(rend{i}.dep),hld);
  231. msk = find(X<0);
  232. X(msk) = 0;
  233. else
  234. X = zeros(size(rend{i}.dep));
  235. end
  236. % Brighten the blobs
  237. %------------------------------------------------------------------
  238. if isfinite(brt), X = X.^brt; end
  239. mx(j) = max([mx(j) max(max(X))]);
  240. mn(j) = min([mn(j) min(min(X))]);
  241. rend{i}.data{j} = X;
  242. if showbar, spm_progress_bar('Set', i+(j-1)*length(rend)); end
  243. end
  244. end
  245. mxmx = max(mx);
  246. mnmn = min(mn);
  247. if showbar, spm_progress_bar('Clear'); end
  248. %-Display
  249. %==========================================================================
  250. if ~spm('CmdLine')
  251. Fgraph = spm_figure('GetWin','Graphics');
  252. spm_results_ui('Clear',Fgraph);
  253. nrow = ceil(length(rend)/2);
  254. if showbar, hght = 0.95; else hght = 0.5; end
  255. % subplot('Position',[0, 0, 1, hght]);
  256. ax = axes('Parent',Fgraph,...
  257. 'units','normalized',...
  258. 'Position',[0, 0, 1, hght],...
  259. 'Visible','off');
  260. image(0,'Parent',ax);
  261. set(ax,'YTick',[],'XTick',[]);
  262. end
  263. rgb = cell(1,length(rend));
  264. if ~isfinite(brt)
  265. % Old style split colourmap display
  266. %----------------------------------------------------------------------
  267. split = [gray(64); hot(64)];
  268. if ~spm('CmdLine'), colormap(split); end
  269. for i=1:length(rend)
  270. ren = rend{i}.ren;
  271. X = (rend{i}.data{1}-mnmn)/(mxmx-mnmn);
  272. msk = find(X);
  273. ren(msk) = X(msk)+(1+1.51/64);
  274. rgb{i} = ind2rgb(round(ren*64),split);
  275. if ~spm('CmdLine')
  276. ax = axes('Parent',Fgraph,...
  277. 'units','normalized',...
  278. 'Position',[rem(i-1,2)*0.5, floor((i-1)/2)*hght/nrow, 0.5, hght/nrow],...
  279. 'Visible','off');
  280. image(ren*64,'Parent',ax);
  281. set(ax,'DataAspectRatio',[1 1 1],...
  282. 'PlotBoxAspectRatioMode','auto',...
  283. 'YTick',[],'XTick',[],...
  284. 'XDir','normal','YDir','normal');
  285. end
  286. end
  287. else
  288. % Combine the brain surface renderings with the blobs, and display
  289. % using 24 bit colour
  290. %----------------------------------------------------------------------
  291. for i=1:length(rend)
  292. ren = rend{i}.ren;
  293. X = cell(3,1);
  294. for j=1:length(rend{i}.data)
  295. X{j} = rend{i}.data{j}/(mxmx-mnmn)-mnmn;
  296. end
  297. for j=(length(rend{i}.data)+1):3
  298. X{j}=zeros(size(X{1}));
  299. end
  300. rgb{i} = zeros([size(ren) 3]);
  301. tmp = ren.*max(1-X{1}-X{2}-X{3},0);
  302. for k = 1:3
  303. rgb{i}(:,:,k) = tmp + X{1}*col(1,k) + X{2}*col(2,k) +X{3}*col(3,k);
  304. end
  305. rgb{i}(rgb{i}>1) = 1;
  306. if ~spm('CmdLine')
  307. ax = axes('Parent',Fgraph,...
  308. 'units','normalized',...
  309. 'Position',[rem(i-1,2)*0.5, floor((i-1)/2)*hght/nrow, 0.5, hght/nrow],...
  310. 'nextplot','add', ...
  311. 'Visible','off');
  312. image(rgb{i},'Parent',ax);
  313. set(ax,'DataAspectRatio',[1 1 1],...
  314. 'PlotBoxAspectRatioMode','auto',...
  315. 'YTick',[],'XTick',[],...
  316. 'XDir','normal','YDir','normal');
  317. end
  318. end
  319. end
  320. spm('Pointer','Arrow');
  321. if nargout
  322. for i=1:numel(rgb)
  323. for ch=1:size(rgb{i},3)
  324. rgb{i}(:,:,ch) = flipud(rgb{i}(:,:,ch));
  325. end
  326. end
  327. varargout = { rgb };
  328. end
  329. %==========================================================================
  330. % function surf_rend(dat,rend,col)
  331. %==========================================================================
  332. function surf_rend(dat,rend,col)
  333. %-Setup figure and axis
  334. %--------------------------------------------------------------------------
  335. Fgraph = spm_figure('GetWin','Graphics');
  336. spm_results_ui('Clear',Fgraph);
  337. ax0 = axes(...
  338. 'Tag', 'SPMMeshRenderBackground',...
  339. 'Parent', Fgraph,...
  340. 'Units', 'normalized',...
  341. 'Color', [1 1 1],...
  342. 'XTick', [],...
  343. 'YTick', [],...
  344. 'Position', [-0.05, -0.05, 1.05, 0.555]);
  345. ax = axes(...
  346. 'Parent', Fgraph,...
  347. 'Units', 'normalized',...
  348. 'Position', [0.05, 0.05, 0.9, 0.4],...
  349. 'Visible', 'off');
  350. H = spm_mesh_render('Disp',rend,struct('parent',ax));
  351. spm_mesh_render('Overlay',H,dat,col);
  352. camlight(H.light);
  353. try
  354. setAllowAxesRotate(H.rotate3d, setxor(findobj(Fgraph,'Type','axes'),ax), false);
  355. end
  356. %-Register with MIP
  357. %--------------------------------------------------------------------------
  358. try % meaningless when called outside spm_results_ui
  359. hReg = spm_XYZreg('FindReg',spm_figure('GetWin','Interactive'));
  360. xyz = spm_XYZreg('GetCoords',hReg);
  361. hs = mydispcursor('Create',ax,dat.mat,xyz);
  362. spm_XYZreg('Add2Reg',hReg,hs,@mydispcursor);
  363. end
  364. %==========================================================================
  365. function varargout = mydispcursor(varargin)
  366. switch lower(varargin{1})
  367. %======================================================================
  368. case 'create'
  369. %======================================================================
  370. % hMe = mydispcursor('Create',ax,M,xyz)
  371. ax = varargin{2};
  372. M = varargin{3};
  373. xyz = varargin{4};
  374. [X,Y,Z] = sphere;
  375. vx = sqrt(sum(M(1:3,1:3).^2));
  376. X = X*vx(1) + xyz(1);
  377. Y = Y*vx(2) + xyz(2);
  378. Z = Z*vx(3) + xyz(3);
  379. hold(ax,'on');
  380. hs = surf(X,Y,Z,'parent',ax,...
  381. 'EdgeColor','none','FaceColor',[1 0 0],'FaceLighting', 'gouraud');
  382. set(hs,'UserData',xyz);
  383. varargout = {hs};
  384. %======================================================================
  385. case 'setcoords' % Set co-ordinates
  386. %======================================================================
  387. % [xyz,d] = mydispcursor('SetCoords',xyz,hMe,hC)
  388. hMe = varargin{3};
  389. pxyz = get(hMe,'UserData');
  390. xyz = varargin{2};
  391. set(hMe,'XData',get(hMe,'XData') - pxyz(1) + xyz(1));
  392. set(hMe,'YData',get(hMe,'YData') - pxyz(2) + xyz(2));
  393. set(hMe,'ZData',get(hMe,'ZData') - pxyz(3) + xyz(3));
  394. set(hMe,'UserData',xyz);
  395. varargout = {xyz,[]};
  396. %======================================================================
  397. otherwise
  398. %======================================================================
  399. error('Unknown action string')
  400. end