PageRenderTime 45ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/trunk/examples/toolboxes/fieldtrip/forward/ft_compute_leadfield.m

http://brainstream.googlecode.com/
MATLAB | 497 lines | 386 code | 11 blank | 100 comment | 17 complexity | 21036e663d17ad28fd48b39a238fb0d4 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 [lf] = ft_compute_leadfield(pos, sens, vol, varargin)
  2. % FT_COMPUTE_LEADFIELD computes a forward solution for a dipole in a a volume
  3. % conductor model. The forward solution is expressed as the leadfield
  4. % matrix (Nchan*3), where each column corresponds with the potential or field
  5. % distributions on all sensors for one of the x,y,z-orientations of the
  6. % dipole.
  7. %
  8. % Use as
  9. % [lf] = ft_compute_leadfield(pos, sens, vol, ...)
  10. % with input arguments
  11. % pos position dipole (1x3 or Nx3)
  12. % sens structure with gradiometer or electrode definition
  13. % vol structure with volume conductor definition
  14. %
  15. % The vol structure represents a volume conductor model, its contents
  16. % depend on the type of model. The sens structure represents a sensor
  17. % arary, i.e. EEG electrodes or MEG gradiometers.
  18. %
  19. % It is possible to compute a simultaneous forward solution for EEG and MEG
  20. % by specifying sens and grad as two cell-arrays, e.g.
  21. % sens = {senseeg, sensmeg}
  22. % vol = {voleeg, volmeg}
  23. % This results in the computation of the leadfield of the first element of
  24. % sens and vol, followed by the second, etc. The leadfields of the
  25. % different imaging modalities are concatenated.
  26. %
  27. % Additional input arguments can be specified as key-value pairs, supported
  28. % optional arguments are
  29. % 'reducerank' = 'no' or number
  30. % 'normalize' = 'no', 'yes' or 'column'
  31. % 'normalizeparam' = parameter for depth normalization (default = 0.5)
  32. % 'weight' = number or 1xN vector, weight for each dipole position (default = 1)
  33. %
  34. % The leadfield weight may be used to specify a (normalized)
  35. % corresponding surface area for each dipole, e.g. when the dipoles
  36. % represent a folded cortical surface with varying triangle size.
  37. %
  38. % Depending on the specific input arguments for the sensor and volume, this
  39. % function will select the appropriate low-level EEG or MEG forward model.
  40. % The leadfield matrix for EEG will have an average reference over all the
  41. % electrodes.
  42. %
  43. % The supported forward solutions for MEG are
  44. % single sphere (Cuffin and Cohen, 1977)
  45. % multiple spheres with one sphere per channel (Huang et al, 1999)
  46. % realistic single shell using superposition of basis functions (Nolte, 2003)
  47. % using leadfield interpolation on precomputed grid
  48. % Boundary Element Method (BEM) using Neuromag meg_calc toolbox
  49. %
  50. % The supported forward solutions for EEG are
  51. % single sphere
  52. % multiple concentric spheres (max. 4)
  53. % using leadfield interpolation on precomputed grid
  54. % Boundary Element Method (BEM) using ASA to precompute the sytem matrix
  55. % Boundary Element Method (BEM) using Neuromag meg_calc toolbox
  56. %
  57. % References to implemented methods:
  58. % Cuffin BN, Cohen D.
  59. % Magnetic fields of a dipole in special volume conductor shapes
  60. % IEEE Trans Biomed Eng. 1977 Jul;24(4):372-81.
  61. %
  62. % Nolte G.
  63. % The magnetic lead field theorem in the quasi-static approximation and its use for magnetoencephalography forward calculation in realistic volume conductors
  64. % Phys Med Biol. 2003 Nov 21;48(22):3637-52
  65. %
  66. % Huang MX, Mosher JC, Leahy RM.
  67. % A sensor-weighted overlapping-sphere head model and exhaustive head model comparison for MEG
  68. % Phys Med Biol. 1999 Feb;44(2):423-40
  69. % Copyright (C) 2004-2010, Robert Oostenveld
  70. %
  71. % This file is part of FieldTrip, see http://www.ru.nl/neuroimaging/fieldtrip
  72. % for the documentation and details.
  73. %
  74. % FieldTrip is free software: you can redistribute it and/or modify
  75. % it under the terms of the GNU General Public License as published by
  76. % the Free Software Foundation, either version 3 of the License, or
  77. % (at your option) any later version.
  78. %
  79. % FieldTrip is distributed in the hope that it will be useful,
  80. % but WITHOUT ANY WARRANTY; without even the implied warranty of
  81. % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  82. % GNU General Public License for more details.
  83. %
  84. % You should have received a copy of the GNU General Public License
  85. % along with FieldTrip. If not, see <http://www.gnu.org/licenses/>.
  86. %
  87. % $Id: ft_compute_leadfield.m 957 2010-04-23 08:04:06Z roboos $
  88. persistent warning_issued;
  89. if iscell(sens) && iscell(vol) && numel(sens)==numel(vol)
  90. % this represents combined EEG and MEG sensors, where each modality has its own volume conduction model
  91. lf = cell(1,numel(sens));
  92. for i=1:length(sens)
  93. lf{i} = ft_compute_leadfield(pos, sens{i}, vol{i}, varargin{:});
  94. end
  95. lf = cat(1, lf{:});
  96. return;
  97. end
  98. if ~isstruct(sens) && size(sens,2)==3
  99. % definition of electrode positions only, restructure it
  100. sens = struct('pnt', sens);
  101. end
  102. % determine whether it is EEG or MEG
  103. iseeg = ft_senstype(sens, 'eeg');
  104. ismeg = ft_senstype(sens, 'meg');
  105. % get the optional input arguments
  106. reducerank = keyval('reducerank', varargin); if isempty(reducerank), reducerank = 'no'; end
  107. normalize = keyval('normalize' , varargin); if isempty(normalize ), normalize = 'no'; end
  108. normalizeparam = keyval('normalizeparam', varargin); if isempty(normalizeparam ), normalizeparam = 0.5; end
  109. weight = keyval('weight', varargin);
  110. % multiple dipoles can be represented either as a 1x(N*3) vector or as a
  111. % as a Nx3 matrix, i.e. [x1 y1 z1 x2 y2 z2] or [x1 y1 z1; x2 y2 z2]
  112. Ndipoles = numel(pos)/3;
  113. if all(size(pos)==[1 3*Ndipoles])
  114. pos = reshape(pos, 3, Ndipoles)';
  115. end
  116. if isfield(vol, 'unit') && isfield(sens, 'unit') && ~strcmp(vol.unit, sens.unit)
  117. error('inconsistency in the units of the volume conductor and the sensor array');
  118. end
  119. if ismeg && iseeg
  120. % this is something that could be implemented relatively easily
  121. error('simultaneous EEG and MEG not supported');
  122. elseif ~ismeg && ~iseeg
  123. error('the input does not look like EEG, nor like MEG');
  124. elseif ismeg
  125. switch ft_voltype(vol)
  126. case 'singlesphere'
  127. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  128. % MEG single-sphere volume conductor model
  129. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  130. pnt = sens.pnt; % position of each coil
  131. ori = sens.ori; % orientation of each coil
  132. if isfield(vol, 'o')
  133. % shift dipole and magnetometers to origin of sphere
  134. pos = pos - repmat(vol.o, Ndipoles, 1);
  135. pnt = pnt - repmat(vol.o, size(pnt,1), 1);
  136. end
  137. if Ndipoles>1
  138. % loop over multiple dipoles
  139. lf = zeros(size(pnt,1),3*Ndipoles);
  140. for i=1:Ndipoles
  141. lf(:,(3*i-2):(3*i)) = meg_leadfield1(pos(i,:), pnt, ori);
  142. end
  143. else
  144. % only single dipole
  145. lf = meg_leadfield1(pos, pnt, ori);
  146. end
  147. if isfield(sens, 'tra')
  148. % this appears to be the modern complex gradiometer definition
  149. % construct the channels from a linear combination of all magnetometers
  150. lf = sens.tra * lf;
  151. end
  152. case 'multisphere'
  153. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  154. % MEG multi-sphere volume conductor model
  155. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  156. ncoils = length(sens.pnt);
  157. if size(vol.r, 1)~=ncoils
  158. error('number of spheres is not equal to the number of coils')
  159. end
  160. if size(vol.o, 1)~=ncoils
  161. error('number of spheres is not equal to the number of coils');
  162. end
  163. lf = zeros(ncoils, 3*Ndipoles);
  164. for chan=1:ncoils
  165. for dip=1:Ndipoles
  166. % shift dipole and magnetometer coil to origin of sphere
  167. dippos = pos(dip,:) - vol.o(chan,:);
  168. chnpos = sens.pnt(chan,:) - vol.o(chan,:);
  169. tmp = meg_leadfield1(dippos, chnpos, sens.ori(chan,:));
  170. lf(chan,(3*dip-2):(3*dip)) = tmp;
  171. end
  172. end
  173. if isfield(sens, 'tra')
  174. % this appears to be the modern complex gradiometer definition
  175. % construct the channels from a linear combination of all magnetometers
  176. lf = sens.tra * lf;
  177. end
  178. case 'neuromag'
  179. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  180. % use external Neuromag toolbox for forward computation
  181. % this requires that "megmodel" is initialized, which is done in PREPARE_VOL_SENS
  182. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  183. % compute the forward model for all channels
  184. % tmp1 = ones(1, Ndipoles);
  185. % tmp2 = 0.01*pos'; %convert to cm
  186. % lf = megfield([tmp2 tmp2 tmp2],[[1 0 0]'*tmp1 [0 1 0]'*tmp1 [0 0 1]'*tmp1]);
  187. for dip=1:Ndipoles
  188. R = 0.01*pos(i,:)'; % convert from cm to m
  189. Qx = [1 0 0];
  190. Qy = [0 1 0];
  191. Qz = [0 0 1];
  192. lf(:,(3*(dip-1)+1)) = megfield(R, Qx);
  193. lf(:,(3*(dip-1)+2)) = megfield(R, Qy);
  194. lf(:,(3*(dip-1)+3)) = megfield(R, Qz);
  195. end
  196. % select only those channels from the forward model that are part of the gradiometer definition
  197. lf = lf(vol.chansel,:);
  198. case 'nolte'
  199. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  200. % use code from Guido Nolte for the forward computation
  201. % this requires that "meg_ini" is initialized, which is done in PREPARE_VOL_SENS
  202. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  203. % the dipole position and orientation should be combined in a single matrix
  204. % furthermore, here I want to compute the leadfield for each of the
  205. % orthogonzl x/y/z directions
  206. dippar = zeros(Ndipoles*3, 6);
  207. for i=1:Ndipoles
  208. dippar((i-1)*3+1,:) = [pos(i,:) 1 0 0]; % single dipole, x-orientation
  209. dippar((i-1)*3+2,:) = [pos(i,:) 0 1 0]; % single dipole, y-orientation
  210. dippar((i-1)*3+3,:) = [pos(i,:) 0 0 1]; % single dipole, z-orientation
  211. end
  212. % compute the leadfield for each individual coil
  213. lf = meg_forward(dippar,vol.forwpar);
  214. if isfield(sens, 'tra')
  215. % compute the leadfield for each gradiometer (linear combination of coils)
  216. lf = sens.tra * lf;
  217. end
  218. case 'openmeeg'
  219. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  220. % use code from OpenMEEG
  221. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  222. hastoolbox('openmeeg', 1);
  223. dsm = openmeeg_dsm(pos,vol);
  224. [h2mm,s2mm]= openmeeg_megm(pos,vol,sens);
  225. if isfield(vol,'mat')
  226. lf = s2mm+h2mm*(vol.mat*dsm);
  227. else
  228. error('No system matrix is present, BEM head model not calculated yet')
  229. end
  230. if isfield(sens, 'tra')
  231. % compute the leadfield for each gradiometer (linear combination of coils)
  232. lf = sens.tra * lf;
  233. end
  234. case 'infinite'
  235. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  236. % magnetic dipole instead of electric (current) dipole in an infinite vacuum
  237. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  238. if isempty(warning_issued)
  239. % give the warning only once
  240. warning('assuming magnetic dipole in an infinite vacuum');
  241. warning_issued = 1;
  242. end
  243. pnt = sens.pnt; % position of each coil
  244. ori = sens.ori; % orientation of each coil
  245. if Ndipoles>1
  246. % loop over multiple dipoles
  247. lf = zeros(size(pnt,1),3*Ndipoles);
  248. for i=1:Ndipoles
  249. lf(:,(3*i-2):(3*i)) = magnetic_dipole(pos(i,:), pnt, ori);
  250. end
  251. else
  252. % only single dipole
  253. lf = magnetic_dipole(pos, pnt, ori);
  254. end
  255. if isfield(sens, 'tra')
  256. % construct the channels from a linear combination of all magnetometer coils
  257. lf = sens.tra * lf;
  258. end
  259. otherwise
  260. error('unsupported volume conductor model for MEG');
  261. end % switch voltype for MEG
  262. elseif iseeg
  263. switch ft_voltype(vol)
  264. case 'multisphere'
  265. % Based on the approximation of the potential due to a single dipole in
  266. % a multishell sphere by three dipoles in a homogeneous sphere, code
  267. % contributed by Punita Christopher
  268. Nelec = size(sens.pnt,1);
  269. Nspheres = length(vol.r);
  270. % the center of the spherical volume conduction model does not have
  271. % to be in the origin, therefore shift the spheres, the electrodes
  272. % and the dipole
  273. if isfield(vol, 'o')
  274. center = vol.o;
  275. else
  276. center = [0 0 0];
  277. end
  278. % sort the spheres from the smallest to the largest
  279. % furthermore, the radius should be one (?)
  280. [radii, indx] = sort(vol.r/max(vol.r));
  281. sigma = vol.c(indx);
  282. r = (sens.pnt-repmat(center, Nelec, 1))./max(vol.r);
  283. pos = pos./max(vol.r);
  284. if Ndipoles>1
  285. % loop over multiple dipoles
  286. lf = zeros(Nelec,3*Ndipoles);
  287. for i=1:Ndipoles
  288. rq = pos(i,:) - center;
  289. % compute the potential for each dipole ortientation
  290. % it would be much more efficient to change the punita function
  291. q1 = [1 0 0]; lf(:,(3*i-2)) = multisphere(Nspheres, radii, sigma, r, rq, q1);
  292. q1 = [0 1 0]; lf(:,(3*i-1)) = multisphere(Nspheres, radii, sigma, r, rq, q1);
  293. q1 = [0 0 1]; lf(:,(3*i )) = multisphere(Nspheres, radii, sigma, r, rq, q1);
  294. end
  295. else
  296. % only single dipole
  297. lf = zeros(Nelec,3);
  298. rq = pos - center;
  299. % compute the potential for each dipole ortientation
  300. % it would be much more efficient to change the punita function
  301. q1 = [1 0 0] ; lf(:,1) = multisphere(Nspheres, radii, sigma, r, rq, q1);
  302. q1 = [0 1 0] ; lf(:,2) = multisphere(Nspheres, radii, sigma, r, rq, q1);
  303. q1 = [0 0 1] ; lf(:,3) = multisphere(Nspheres, radii, sigma, r, rq, q1);
  304. end
  305. case {'singlesphere', 'concentric'}
  306. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  307. % EEG spherical volume conductor model
  308. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  309. % FIXME, this is not consistent between spherical and BEM
  310. % sort the spheres from the smallest to the largest
  311. [vol.r, indx] = sort(vol.r);
  312. vol.c = vol.c(indx);
  313. Nspheres = length(vol.c);
  314. if length(vol.r)~=Nspheres
  315. error('the number of spheres in the volume conductor model is ambiguous');
  316. end
  317. if isfield(vol, 'o')
  318. % shift the origin of the spheres, electrodes and dipole
  319. sens.pnt = sens.pnt - repmat(vol.o, size(sens.pnt,1), 1);
  320. pos = pos - repmat(vol.o, Ndipoles, 1);
  321. end
  322. if Nspheres==1
  323. if Ndipoles>1
  324. % loop over multiple dipoles
  325. lf = zeros(size(sens.pnt,1),3*Ndipoles);
  326. for i=1:Ndipoles
  327. lf(:,(3*i-2):(3*i)) = eeg_leadfield1(pos(i,:), sens.pnt, vol);
  328. end
  329. else
  330. % only single dipole
  331. lf = eeg_leadfield1(pos, sens.pnt, vol);
  332. end
  333. elseif Nspheres==2
  334. vol.r = [vol.r(1) vol.r(2) vol.r(2) vol.r(2)];
  335. vol.c = [vol.c(1) vol.c(2) vol.c(2) vol.c(2)];
  336. if Ndipoles>1
  337. % loop over multiple dipoles
  338. lf = zeros(size(sens.pnt,1),3*Ndipoles);
  339. for i=1:Ndipoles
  340. lf(:,(3*i-2):(3*i)) = eeg_leadfield4(pos(i,:), sens.pnt, vol);
  341. end
  342. else
  343. % only single dipole
  344. lf = eeg_leadfield4(pos, sens.pnt, vol);
  345. end
  346. elseif Nspheres==3
  347. vol.r = [vol.r(1) vol.r(2) vol.r(3) vol.r(3)];
  348. vol.c = [vol.c(1) vol.c(2) vol.c(3) vol.c(3)];
  349. if Ndipoles>1
  350. % loop over multiple dipoles
  351. lf = zeros(size(sens.pnt,1),3*Ndipoles);
  352. for i=1:Ndipoles
  353. lf(:,(3*i-2):(3*i)) = eeg_leadfield4(pos(i,:), sens.pnt, vol);
  354. end
  355. else
  356. % only single dipole
  357. lf = eeg_leadfield4(pos, sens.pnt, vol);
  358. end
  359. elseif Nspheres==4
  360. if Ndipoles>1
  361. % loop over multiple dipoles
  362. lf = zeros(size(sens.pnt,1),3*Ndipoles);
  363. for i=1:Ndipoles
  364. lf(:,(3*i-2):(3*i)) = eeg_leadfield4(pos(i,:), sens.pnt, vol);
  365. end
  366. else
  367. % only single dipole
  368. lf = eeg_leadfield4(pos, sens.pnt, vol);
  369. end
  370. else
  371. error('more than 4 concentric spheres are not supported');
  372. end
  373. case {'bem', 'dipoli', 'asa', 'avo', 'bemcp'}
  374. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  375. % EEG boundary element method volume conductor model
  376. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  377. lf = eeg_leadfieldb(pos, sens.pnt, vol);
  378. case 'openmeeg'
  379. dsm = openmeeg_dsm(pos,vol);
  380. if isfield(vol,'mat')
  381. lf = vol.mat*dsm;
  382. else
  383. error('No system matrix is present, BEM head model not calculated yet')
  384. end
  385. case 'infinite'
  386. % the conductivity of the medium is not known
  387. if isempty(warning_issued)
  388. % give the warning only once
  389. warning('assuming electric dipole in an infinite medium with unit conductivity');
  390. warning_issued = 1;
  391. end
  392. lf = inf_medium_leadfield(pos, sens.pnt, 1);
  393. otherwise
  394. error('unsupported volume conductor model for EEG');
  395. end % switch voltype for EEG
  396. % compute average reference for EEG leadfield
  397. avg = mean(lf, 1);
  398. lf = lf - repmat(avg, size(lf,1), 1);
  399. end % iseeg or ismeg
  400. % optionally apply leadfield rank reduction
  401. if ~strcmp(reducerank, 'no') && reducerank<size(lf,2) && ~strcmp(ft_voltype(vol),'openmeeg')
  402. % decompose the leadfield
  403. [u, s, v] = svd(lf);
  404. r = diag(s);
  405. s(:) = 0;
  406. for j=1:reducerank
  407. s(j,j) = r(j);
  408. end
  409. % recompose the leadfield with reduced rank
  410. lf = u * s * v';
  411. end
  412. % optionally apply leadfield normaliziation
  413. switch normalize
  414. case 'yes'
  415. if normalizeparam==0.5
  416. % normalize the leadfield by the Frobenius norm of the matrix
  417. % this is the same as below in case normalizeparam is 0.5
  418. nrm = norm(lf, 'fro');
  419. else
  420. % normalize the leadfield by sum of squares of the elements of the leadfield matrix to the power "normalizeparam"
  421. % this is the same as the Frobenius norm if normalizeparam is 0.5
  422. nrm = sum(lf(:).^2)^normalizeparam;
  423. end
  424. if nrm>0
  425. lf = lf ./ nrm;
  426. end
  427. case 'column'
  428. % normalize each column of the leadfield by its norm
  429. for j=1:size(lf,2)
  430. nrm = sum(lf(:,j).^2)^normalizeparam;
  431. lf(:,j) = lf(:,j)./nrm;
  432. end
  433. end
  434. % optionally apply a weight to the leadfield for each dipole location
  435. if ~isempty(weight)
  436. for i=1:Ndipoles
  437. lf(:,3*(i-1)+1) = lf(:,3*(i-1)+1) * weight(i); % the leadfield for the x-direction
  438. lf(:,3*(i-1)+2) = lf(:,3*(i-2)+1) * weight(i); % the leadfield for the y-direction
  439. lf(:,3*(i-1)+3) = lf(:,3*(i-3)+1) * weight(i); % the leadfield for the z-direction
  440. end
  441. end