PageRenderTime 35ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 1ms

/trunk/examples/toolboxes/fieldtrip/ft_sliceinterp.m

http://brainstream.googlecode.com/
MATLAB | 517 lines | 364 code | 32 blank | 121 comment | 90 complexity | b8765d3acf0d4923a089281b8a231cab MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, LGPL-2.0, GPL-3.0, GPL-2.0, LGPL-2.1, LGPL-3.0, BSD-2-Clause
  1. function [outim]=ft_sliceinterp(cfg, ininterp)
  2. % FT_SLICEINTERP plots a 2D-montage of source reconstruction and anatomical MRI
  3. % after these have been interpolated onto the same grid.
  4. %
  5. % Use as
  6. % ft_sliceinterp(cfg, interp)
  7. % or
  8. % [rgbimage] = ft_sliceinterp(cfg, interp), rgbimage is the monatage image
  9. %
  10. % where interp is the output of sourceinterpolate and cfg is a structure
  11. % with any of the following fields:
  12. %
  13. % cfg.funparameter string with the functional parameter of interest (default = 'source')
  14. % cfg.maskparameter parameter used as opacity mask (default = 'none')
  15. % cfg.clipmin value or 'auto' (clipping of source data)
  16. % cfg.clipmax value or 'auto' (clipping of source data)
  17. % cfg.clipsym 'yes' or 'no' (default) symmetrical clipping
  18. % cfg.colormap colormap for source overlay (default is jet(128))
  19. % cfg.colmin source value mapped to the lowest color (default = 'auto')
  20. % cfg.colmax source value mapped to the highest color (default = 'auto')
  21. % cfg.maskclipmin value or 'auto' (clipping of mask data)
  22. % cfg.maskclipmax value or 'auto' (clipping of mask data)
  23. % cfg.maskclipsym 'yes' or 'no' (default) symmetrical clipping
  24. % cfg.maskmap opacitymap for source overlay
  25. % (default is linspace(0,1,128))
  26. % cfg.maskcolmin mask value mapped to the lowest opacity, i.e.
  27. % completely transparent (default ='auto')
  28. % cfg.maskcolmin mask value mapped to the highest opacity, i.e.
  29. % non-transparent (default = 'auto')
  30. % cfg.alpha value between 0 and 1 or 'adaptive' (default)
  31. % cfg.nslices integer value, default is 20
  32. % cfg.dim integer value, default is 3 (dimension to slice)
  33. % cfg.spacemin 'auto' (default) or integer (first slice position)
  34. % cfg.spacemax 'auto' (default) or integer (last slice position)
  35. % cfg.resample integer value, default is 1 (for resolution reduction)
  36. % cfg.rotate number of ccw 90 deg slice rotations (default = 0)
  37. % cfg.title optional title (default is '')
  38. % cfg.whitebg 'yes' or 'no' (default = 'yes')
  39. % cfg.flipdim flip data along the sliced dimension, 'yes' or 'no'
  40. % (default = 'no')
  41. % cfg.marker [Nx3] array defining N marker positions to display
  42. % cfg.markersize radius of markers (default = 5);
  43. % cfg.markercolor [1x3] marker color in RGB (default = [1 1 1], i.e. white)
  44. % cfg.interactive 'yes' or 'no' (default), interactive coordinates
  45. % and source values
  46. %
  47. % if cfg.alpha is set to 'adaptive' the opacity of the source overlay
  48. % linearly follows the source value: maxima are opaque and minima are
  49. % transparent.
  50. %
  51. % if cfg.spacemin and/or cfg.spacemax are set to 'auto' the sliced
  52. % space is automatically restricted to the evaluated source-space
  53. %
  54. % if cfg.colmin and/or cfg.colmax are set to 'auto' the colormap is mapped
  55. % to source values the following way: if source values are either all
  56. % positive or all negative the colormap is mapped to from
  57. % min(source) to max(source). If source values are negative and positive
  58. % the colormap is symmetrical mapped around 0 from -max(abs(source)) to
  59. % +max(abs(source)).
  60. %
  61. % If cfg.maskparameter specifies a parameter to be used as an opacity mask
  62. % cfg.alpha is not used. Instead the mask values are maped to an opacitymap
  63. % that can be specified using cfg.maskmap. The mapping onto that
  64. % opacitymap is controlled as for the functional data using the
  65. % corresponding clipping and min/max options.
  66. %
  67. % if cfg.whitebg is set to 'yes' the function estimates the head volume and
  68. % displays a white background outside the head, which can save a lot of black
  69. % printer toner.
  70. %
  71. % if cfg.interactive is set to 'yes' a button will be displayed for
  72. % interactive data evaluation and coordinate reading. After clicking the
  73. % button named 'coords' you can click on any position in the slice montage.
  74. % After clicking these coordinates and their source value are displayed in
  75. % a text box below the button. The coordinates correspond to indeces in the
  76. % input data array:
  77. %
  78. % f = interp.source(coord_1,coord_2,coord_3)
  79. %
  80. % The coordinates are not affected by any transformations used for displaying
  81. % the data such as cfg.dim, cfg.rotate,cfg.flipdim or cfg.resample.
  82. % Copyright (C) 2004, Markus Siegel, markus.siegel@fcdonders.kun.nl
  83. %
  84. % This file is part of FieldTrip, see http://www.ru.nl/neuroimaging/fieldtrip
  85. % for the documentation and details.
  86. %
  87. % FieldTrip is free software: you can redistribute it and/or modify
  88. % it under the terms of the GNU General Public License as published by
  89. % the Free Software Foundation, either version 3 of the License, or
  90. % (at your option) any later version.
  91. %
  92. % FieldTrip is distributed in the hope that it will be useful,
  93. % but WITHOUT ANY WARRANTY; without even the implied warranty of
  94. % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  95. % GNU General Public License for more details.
  96. %
  97. % You should have received a copy of the GNU General Public License
  98. % along with FieldTrip. If not, see <http://www.gnu.org/licenses/>.
  99. %
  100. % $Id: ft_sliceinterp.m 948 2010-04-21 18:02:21Z roboos $
  101. fieldtripdefs
  102. % check if the input data is valid for this function
  103. ininterp = checkdata(ininterp, 'datatype', 'volume', 'feedback', 'yes');
  104. if ~isfield(cfg, 'clipmin'); cfg.clipmin = 'auto'; end
  105. if ~isfield(cfg, 'clipmax'); cfg.clipmax = 'auto'; end
  106. if ~isfield(cfg, 'clipsym'); cfg.clipsym = 'no'; end
  107. if ~isfield(cfg, 'alpha'); cfg.alpha = 'adaptive'; end
  108. if ~isfield(cfg, 'nslices'); cfg.nslices = 20; end
  109. if ~isfield(cfg, 'dim'); cfg.dim = 3; end
  110. if ~isfield(cfg, 'colormap'); cfg.colormap = jet(128); end
  111. if ~isfield(cfg, 'spacemin'); cfg.spacemin = 'auto'; end
  112. if ~isfield(cfg, 'spacemax'); cfg.spacemax = 'auto'; end
  113. if ~isfield(cfg, 'colmin'); cfg.colmin = 'auto'; end
  114. if ~isfield(cfg, 'colmax'); cfg.colmax = 'auto'; end
  115. if ~isfield(cfg, 'resample'); cfg.resample = 1; end
  116. if ~isfield(cfg, 'rotate'); cfg.rotate = 0; end
  117. if ~isfield(cfg, 'title'); cfg.title = ''; end
  118. if ~isfield(cfg, 'whitebg'); cfg.whitebg = 'no'; end
  119. if ~isfield(cfg, 'flipdim'); cfg.flipdim = 'no'; end
  120. if ~isfield(cfg, 'marker'); cfg.marker = []; end
  121. if ~isfield(cfg, 'markersize'); cfg.markersize = 5; end
  122. if ~isfield(cfg, 'markercolor'); cfg.markercolor = [1,1,1]; end
  123. if ~isfield(cfg, 'interactive'); cfg.interactive = 'no'; end
  124. if ~isfield(cfg, 'maskclipmin'); cfg.maskclipmin = 'auto'; end
  125. if ~isfield(cfg, 'maskclipmax'); cfg.maskclipmax = 'auto'; end
  126. if ~isfield(cfg, 'maskclipsym'); cfg.maskclipsym = 'no'; end
  127. if ~isfield(cfg, 'maskmap'); cfg.maskmap = linspace(0,1,128); end
  128. if ~isfield(cfg, 'maskcolmin'); cfg.maskcolmin = 'auto'; end
  129. if ~isfield(cfg, 'maskcolmax'); cfg.maskcolmax = 'auto'; end
  130. if ~isfield(cfg, 'maskparameter');cfg.maskparameter = []; end
  131. % perform some checks on the configuration for backward compatibility
  132. if ~isfield(cfg, 'funparameter') && isfield(ininterp, 'source')
  133. % if present, the default behavior should be to use this field for plotting
  134. cfg.funparameter = 'source';
  135. end
  136. % make the selection of functional and mask data consistent with the data
  137. cfg.funparameter = parameterselection(cfg.funparameter, ininterp);
  138. cfg.maskparameter = parameterselection(cfg.maskparameter, ininterp);
  139. % only a single parameter should be selected
  140. try, cfg.funparameter = cfg.funparameter{1}; end
  141. try, cfg.maskparameter = cfg.maskparameter{1}; end
  142. % check anatomical data
  143. if isfield(ininterp,'anatomy');
  144. interp.anatomy = reshape(ininterp.anatomy, ininterp.dim);
  145. else
  146. error('no anatomical data supplied');
  147. end
  148. % check functional data
  149. if ~isempty(cfg.funparameter)
  150. interp.source = double(reshape(getsubfield(ininterp, cfg.funparameter), ininterp.dim));
  151. else
  152. error('no functional data supplied');
  153. end
  154. % check mask data
  155. if ~isempty(cfg.maskparameter)
  156. interp.mask = double(reshape(getsubfield(ininterp,cfg.maskparameter), ininterp.dim));
  157. maskdat = 1;
  158. else
  159. fprintf('no opacity mask data supplied\n');
  160. interp.mask = [];
  161. maskdat = 0;
  162. end
  163. % only work with the copy of the relevant parameters in "interp"
  164. clear ininterp;
  165. % convert anatomy data type and optimize contrast
  166. if isa(interp.anatomy, 'uint8') || isa(interp.anatomy, 'uint16')
  167. fprintf('converting anatomy to floating point values...');
  168. interp.anatomy = double(interp.anatomy);
  169. fprintf('done\n');
  170. end
  171. fprintf('optimizing contrast of anatomical data ...');
  172. minana = min(interp.anatomy(:));
  173. maxana = max(interp.anatomy(:));
  174. interp.anatomy = (interp.anatomy-minana)./(maxana-minana);
  175. fprintf('done\n');
  176. % store original data if 'interactive' mode
  177. if strcmp(cfg.interactive,'yes')
  178. data.source = interp.source;
  179. end
  180. % place markers
  181. marker = zeros(size(interp.anatomy));
  182. if ~isempty(cfg.marker)
  183. fprintf('placing markers ...');
  184. [x,y,z] = ndgrid([1:size(interp.anatomy,1)],[1:size(interp.anatomy,2)],[1:size(interp.anatomy,3)]);
  185. for imarker = 1:size(cfg.marker,1)
  186. marker(find(sqrt((x-cfg.marker(imarker,1)).^2 + (y-cfg.marker(imarker,2)).^2 + (z-cfg.marker(imarker,3)).^2)<=cfg.markersize)) = 1;
  187. end
  188. fprintf('done\n');
  189. end
  190. % shift dimensions
  191. fprintf('sorting dimensions...');
  192. interp.anatomy = shiftdim(interp.anatomy,cfg.dim-1);
  193. interp.source = shiftdim(interp.source,cfg.dim-1);
  194. interp.mask = shiftdim(interp.mask,cfg.dim-1);
  195. marker = shiftdim(marker,cfg.dim-1);
  196. fprintf('done\n');
  197. % flip dimensions
  198. if strcmp(cfg.flipdim,'yes')
  199. fprintf('flipping dimensions...');
  200. interp.anatomy = flipdim(interp.anatomy,1);
  201. interp.source = flipdim(interp.source,1);
  202. interp.mask = flipdim(interp.mask,1);
  203. marker = flipdim(marker,1);
  204. fprintf('done\n');
  205. end
  206. % set slice space
  207. if ischar(cfg.spacemin)
  208. fprintf('setting first slice position...');
  209. spacemin = min(find(~isnan(max(max(interp.source,[],3),[],2))));
  210. fprintf('%d...done\n',spacemin);
  211. else
  212. spacemin = cfg.spacemin;
  213. end
  214. if ischar(cfg.spacemax)
  215. fprintf('setting last slice position...');
  216. spacemax = max(find(~isnan(max(max(interp.source,[],3),[],2))));
  217. fprintf('%d...done\n',spacemax);
  218. else
  219. spacemax = cfg.spacemax;
  220. end
  221. % clip funtional data
  222. if ~ischar(cfg.clipmin)
  223. fprintf('clipping functional minimum...');
  224. switch cfg.clipsym
  225. case 'no'
  226. interp.source(find(interp.source<cfg.clipmin)) = nan;
  227. case 'yes'
  228. interp.source(find(abs(interp.source)<cfg.clipmin)) = nan;
  229. end
  230. fprintf('done\n');
  231. end
  232. if ~ischar(cfg.clipmax)
  233. fprintf('clipping functional maximum...');
  234. switch cfg.clipsym
  235. case 'no'
  236. interp.source(find(interp.source>cfg.clipmax)) = nan;
  237. case 'yes'
  238. interp.source(find(abs(interp.source)>cfg.clipmax)) = nan;
  239. end
  240. fprintf('done\n');
  241. end
  242. % clip mask data
  243. if maskdat
  244. if ~ischar(cfg.maskclipmin)
  245. fprintf('clipping mask minimum...');
  246. switch cfg.maskclipsym
  247. case 'no'
  248. interp.mask(find(interp.mask<cfg.maskclipmin)) = nan;
  249. case 'yes'
  250. interp.mask(find(abs(interp.mask)<cfg.maskclipmin)) = nan;
  251. end
  252. fprintf('done\n');
  253. end
  254. if ~ischar(cfg.maskclipmax)
  255. fprintf('clipping mask maximum...');
  256. switch cfg.maskclipsym
  257. case 'no'
  258. interp.mask(find(interp.mask>cfg.maskclipmax)) = nan;
  259. case 'yes'
  260. interp.mask(find(abs(interp.mask)>cfg.maskclipmax)) = nan;
  261. end
  262. fprintf('done\n');
  263. end
  264. end
  265. % scale functional data
  266. fprintf('scaling functional data...');
  267. fmin = min(interp.source(:));
  268. fmax = max(interp.source(:));
  269. if ~ischar(cfg.colmin)
  270. fcolmin = cfg.colmin;
  271. else
  272. if sign(fmin)==sign(fmax)
  273. fcolmin = fmin;
  274. else
  275. fcolmin = -max(abs([fmin,fmax]));
  276. end
  277. end
  278. if ~ischar(cfg.colmax)
  279. fcolmax = cfg.colmax;
  280. else
  281. if sign(fmin)==sign(fmax)
  282. fcolmax = fmax;
  283. else
  284. fcolmax = max(abs([fmin,fmax]));
  285. end
  286. end
  287. interp.source = (interp.source-fcolmin)./(fcolmax-fcolmin);
  288. if ~ischar(cfg.colmax)
  289. interp.source(find(interp.source>1)) = 1;
  290. end
  291. if ~ischar(cfg.colmin)
  292. interp.source(find(interp.source<0)) = 0;
  293. end
  294. fprintf('done\n');
  295. % scale mask data
  296. if maskdat
  297. fprintf('scaling mask data...');
  298. fmin = min(interp.mask(:));
  299. fmax = max(interp.mask(:));
  300. if ~ischar(cfg.maskcolmin)
  301. mcolmin = cfg.maskcolmin;
  302. else
  303. if sign(fmin)==sign(fmax)
  304. mcolmin = fmin;
  305. else
  306. mcolmin = -max(abs([fmin,fmax]));
  307. end
  308. end
  309. if ~ischar(cfg.maskcolmax)
  310. mcolmax = cfg.maskcolmax;
  311. else
  312. if sign(fmin)==sign(fmax)
  313. mcolmax = fmax;
  314. else
  315. mcolmax = max(abs([fmin,fmax]));
  316. end
  317. end
  318. interp.mask = (interp.mask-mcolmin)./(mcolmax-mcolmin);
  319. if ~ischar(cfg.maskcolmax)
  320. interp.mask(find(interp.mask>1)) = 1;
  321. end
  322. if ~ischar(cfg.maskcolmin)
  323. interp.mask(find(interp.mask<0)) = 0;
  324. end
  325. fprintf('done\n');
  326. end
  327. % merge anatomy, functional data and mask
  328. fprintf('constructing overlay...');
  329. if ischar(cfg.colormap)
  330. % replace string by colormap using standard Matlab function
  331. cfg.colormap = colormap(cfg.colormap);
  332. end
  333. cmap = cfg.colormap;
  334. cmaplength = size(cmap,1);
  335. maskmap = cfg.maskmap(:);
  336. maskmaplength = size(maskmap,1);
  337. indslice = round(linspace(spacemin,spacemax,cfg.nslices));
  338. nvox1 = length(1:cfg.resample:size(interp.anatomy,2));
  339. nvox2 = length(1:cfg.resample:size(interp.anatomy,3));
  340. if mod(cfg.rotate,2)
  341. dummy = nvox1;
  342. nvox1 = nvox2;
  343. nvox2 = dummy;
  344. end
  345. out = zeros(nvox1,nvox2,3,cfg.nslices);
  346. for islice = 1:cfg.nslices
  347. dummy1 = squeeze(interp.anatomy(indslice(islice),1:cfg.resample:end,1:cfg.resample:end));
  348. dummy2 = squeeze(interp.source(indslice(islice),1:cfg.resample:end,1:cfg.resample:end));
  349. indmarker = find(squeeze(marker(indslice(islice),1:cfg.resample:end,1:cfg.resample:end)));
  350. indsource = find(~isnan(dummy2));
  351. if maskdat
  352. dummymask = squeeze(interp.mask(indslice(islice),1:cfg.resample:end,1:cfg.resample:end));
  353. indsource = find(~isnan(dummy2) & ~isnan(dummymask));
  354. end
  355. for icol = 1:3
  356. dummy3 = dummy1;
  357. if not(maskdat)
  358. if ~ischar(cfg.alpha)
  359. try
  360. dummy3(indsource) = ...
  361. (1-cfg.alpha) * dummy3(indsource) + ...
  362. cfg.alpha * cmap(round(dummy2(indsource)*(cmaplength-1))+1,icol);
  363. end
  364. else
  365. try
  366. dummy3(indsource) = ...
  367. (1-dummy2(indsource)) .* dummy3(indsource) + ...
  368. dummy2(indsource) .* cmap(round(dummy2(indsource)*(cmaplength-1))+1,icol);
  369. end
  370. end
  371. else
  372. dummy3(indsource) = ...
  373. (1-maskmap(round(dummymask(indsource)*(maskmaplength-1))+1)).* ...
  374. dummy3(indsource) + ...
  375. maskmap(round(dummymask(indsource)*(maskmaplength-1))+1) .* ...
  376. cmap(round(dummy2(indsource)*(cmaplength-1))+1,icol);
  377. end
  378. dummy3(indmarker) = cfg.markercolor(icol);
  379. out(:,:,icol,islice) = rot90(dummy3,cfg.rotate);
  380. end
  381. if strcmp(cfg.whitebg,'yes')
  382. bgmask = zeros(nvox1,nvox2);
  383. bgmask(find(conv2(mean(out(:,:,:,islice),3),ones(round((nvox1+nvox2)/8))/(round((nvox1+nvox2)/8).^2),'same')<0.1)) = 1;
  384. for icol = 1:3
  385. out(:,:,icol,islice) = bgmask.*ones(nvox1,nvox2) + (1-bgmask).* out(:,:,icol,islice);
  386. end
  387. end
  388. end
  389. fprintf('done\n');
  390. clf;
  391. fprintf('plotting...');
  392. axes('position',[0.9 0.3 0.02 0.4]);
  393. image(permute(cmap,[1 3 2]));
  394. set(gca,'YAxisLocation','right');
  395. set(gca,'XTick',[]);
  396. set(gca,'YDir','normal');
  397. set(gca,'YTick',linspace(1,cmaplength,5));
  398. set(gca,'YTickLabel',linspace(fcolmin,fcolmax,5));
  399. set(gca,'Box','on');
  400. axes('position',[0.01 0.01 0.88 0.90]);
  401. [h,nrows,ncols]=slicemon(out);
  402. xlim=get(gca,'XLim');
  403. ylim=get(gca,'YLim');
  404. text(diff(xlim)/2,-diff(ylim)/100,cfg.title,'HorizontalAlignment','center','Interpreter','none');
  405. drawnow;
  406. fprintf('done\n');
  407. if nargout > 0
  408. outim=get(h,'CData');
  409. end
  410. if strcmp(cfg.interactive,'yes')
  411. data.sin = size(interp.source);
  412. data.nrows = nrows;
  413. data.ncols = ncols;
  414. data.out = out;
  415. data.indslice = indslice;
  416. data.cfg = cfg;
  417. data.hfig = gcf;
  418. uicontrol('Units','norm', 'Position', [0.9 0.2 0.08 0.05], 'Style','pushbutton', 'String','coords',...
  419. 'Callback',@getcoords,'FontSize',7);
  420. data.hcoords = uicontrol('Units','norm', 'Position', [0.9 0.05 0.08 0.13], 'Style','text', 'String','','HorizontalAlign','left','FontSize',7);
  421. guidata(data.hfig,data);
  422. end
  423. % ---------------- subfunctions ----------------
  424. function getcoords(h,eventdata,handles,varargin)
  425. data = guidata(gcf);
  426. [xi,yi] = ginput(1);
  427. co(2,1) = round(mod(yi,size(data.out,1)));
  428. co(3,1) = round(mod(xi,size(data.out,2)));
  429. switch mod(data.cfg.rotate,4)
  430. case 1,
  431. t1 = co(2);
  432. co(2) = co(3);
  433. co(3) = data.sin(3)-t1;
  434. case 2,
  435. co(2) = data.sin(2)-co(2);
  436. co(3) = data.sin(3)-co(3);
  437. case 3,
  438. t1 = co(3);
  439. co(3) = co(2);
  440. co(2) = data.sin(2)-t1;
  441. end
  442. try
  443. co(1) = data.indslice(fix(xi/size(data.out,2)) + fix(yi/size(data.out,1))*data.ncols + 1);
  444. catch
  445. co(1) = NaN;
  446. end
  447. if strcmp(data.cfg.flipdim, 'yes')
  448. co(1) = data.sin(1) - co(1) + 1;
  449. end
  450. co = co(:);
  451. co(2:3) = round(co(2:3)*data.cfg.resample);
  452. for ishift = 1:data.cfg.dim-1
  453. co = [co(3);co(1);co(2)];
  454. end
  455. set(data.hcoords,'String',sprintf('1: %d\n2: %d\n3: %d\nf: %0.4f',co(1),co(2),co(3),data.source(co(1),co(2),co(3))));
  456. function [h,nrows,ncols] = slicemon(a) % display the montage w/o image_toolbox
  457. siz = [size(a,1) size(a,2) size(a,4)];
  458. nn = sqrt(prod(siz))/siz(2);
  459. mm = siz(3)/nn;
  460. if (ceil(nn)-nn) < (ceil(mm)-mm),
  461. nn = ceil(nn); mm = ceil(siz(3)/nn);
  462. else
  463. mm = ceil(mm); nn = ceil(siz(3)/mm);
  464. end
  465. b = a(1,1);
  466. b(1,1) = 0;
  467. b = repmat(b, [mm*siz(1), nn*siz(2), size(a,3), 1]);
  468. rows = 1:siz(1); cols = 1:siz(2);
  469. for i=0:mm-1,
  470. for j=0:nn-1,
  471. k = j+i*nn+1;
  472. if k<=siz(3),
  473. b(rows+i*siz(1),cols+j*siz(2),:) = a(:,:,:,k);
  474. end
  475. end
  476. end
  477. hh = image(b);
  478. axis image;
  479. box off;
  480. set(gca,'XTick',[],'YTick',[],'Visible','off');
  481. if nargout > 0
  482. h = hh;
  483. nrows = mm;
  484. ncols = nn;
  485. end