-
Notifications
You must be signed in to change notification settings - Fork 768
Expand file tree
/
Copy pathft_headmodel_openmeeg.m
More file actions
231 lines (199 loc) · 7.35 KB
/
Copy pathft_headmodel_openmeeg.m
File metadata and controls
231 lines (199 loc) · 7.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
function headmodel = ft_headmodel_openmeeg(mesh, varargin)
% FT_HEADMODEL_OPENMEEG creates a volume conduction model of the head using the
% boundary element method (BEM). This function takes as input the triangulated
% surfaces that describe the boundaries and returns as output a volume conduction
% model which can be used to compute leadfields.
%
% This function implements
% Gramfort et al. OpenMEEG: opensource software for quasistatic
% bioelectromagnetics. Biomedical engineering online (2010) vol. 9 (1) pp. 45
% http://www.biomedical-engineering-online.com/content/9/1/45
% doi:10.1186/1475-925X-9-45
% and
% Kybic et al. Generalized head models for MEG/EEG: boundary element method
% beyond nested volumes. Phys. Med. Biol. (2006) vol. 51 pp. 1333-1346
% doi:10.1088/0031-9155/51/5/021
%
% This link with FieldTrip is derived from the OpenMEEG project with contributions
% from Daniel Wong and Sarang Dalal, and uses external command-line executables.
% See http://openmeeg.github.io/
%
% Use as
% headmodel = ft_headmodel_openmeeg(bnd, ...)
%
% Optional input arguments should be specified in key-value pairs and can
% include
% conductivity = vector, conductivity of each compartment
% tissue = cell-array with the tissue labels for each compartment
% checkmesh = 'yes' or 'no'
%
% See also FT_PREPARE_HEADMODEL, FT_COMPUTE_LEADFIELD
% Copyright (C) 2010-2024, Robert Oostenveld
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
% FieldTrip is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% FieldTrip is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$
ft_hastoolbox('openmeeg', 1); % add to path (if not yet on path)
openmeeg_license; % show the license (only once)
prefix = om_checkombin; % check the installation of the binaries
if(~ispc) % if Linux/Mac, set number of threads
omp_num_threads = feature('numCores');
prefix = ['export OMP_NUM_THREADS=' num2str(omp_num_threads) ' && ' prefix];
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% the first part is largely shared with the dipoli and bemcp implementation
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% get the optional arguments
conductivity = ft_getopt(varargin, 'conductivity');
tissue = ft_getopt(varargin, 'tissue');
checkmesh = ft_getopt(varargin, 'checkmesh', 'yes');
% convert to Boolean value
checkmesh = istrue(checkmesh);
% copy the boundaries from the mesh into the volume conduction model
if isfield(mesh, 'bnd')
mesh = mesh.bnd;
end
if ischar(tissue)
% it should be a cell-array
tissue = {tissue};
end
% rename pnt into pos
mesh = fixpos(mesh);
% determine the number of compartments
numboundaries = length(mesh);
% OpenMEEG v2.3 and up internally adjusts the convention for surface
% normals, but OpenMEEG v2.2 expects surface normals to point inwards;
% this checks and corrects if needed
for i=1:numboundaries
switch surface_orientation(mesh(i).pos, mesh(i).tri)
case 'outward'
ft_warning('flipping mesh %d', i);
mesh(i).tri = fliplr(mesh(i).tri);
case 'inward'
% this is ok
case 'otherwise'
ft_error('incorrect mesh %d', i)
end
end
% determine the desired nesting of the compartments
order = surface_nesting(mesh, 'outsidefirst');
% reorder the boundaries
if numel(mesh)>1
fprintf('reordering the boundaries to: ');
fprintf('%d ', order);
fprintf('\n');
% update the order of the compartments
mesh = mesh(order);
end
% start with an empty volume conductor
headmodel = [];
if isempty(conductivity)
ft_warning('no conductivity specified, using default values')
if numboundaries == 1
% uniform
conductivity = 1;
elseif numboundaries == 3
% skin/skull/brain
conductivity = [0.33 0.0042 0.33];
elseif numboundaries == 4
% skin/skull/csf/brain
conductivity = [0.33 0.0042 1.7900 0.33];
else
ft_error('conductivity values must be specified')
end
headmodel.cond = conductivity;
else
if numel(conductivity)~=numboundaries
ft_error('each compartment should have a conductivity value');
end
% reorder the user-specified conductivities
headmodel.cond = conductivity(order);
end
% assign default tissue labels if none provided
% the tissue is used for the filename
if isempty(tissue)
switch(numboundaries)
case 1
tissue = {'scalp'};
case 2
tissue = {'skull', 'brain'};
case 3
tissue = {'scalp','skull','brain'};
case 4
tissue = {'scalp','skull','csf','brain'};
otherwise
ft_error('tissue types must be specified')
end
else
tissue = tissue(order);
end
% do some sanity checks on the meshes
if checkmesh
for i=1:numboundaries
ntri = size(mesh(i).tri,1);
npos = size(mesh(i).pos,1);
assert(2*(npos-2)==ntri, 'the number of triangles does not match the number of vertices')
end
% check for each of the vertices that it falls inside the previous surface
for i=2:numboundaries
for j=1:size(mesh(i).pos,1)
inside = surface_inside(mesh(i).pos(j,:), mesh(i-1).pos, mesh(i-1).tri);
assert(inside==true, 'vertex %d of surface %d is outside surface %d', j, i, i-1);
end
end
ft_info('the meshes are closed and properly nested')
end % if checkmesh
headmodel.tissue = tissue;
headmodel.bnd = mesh;
headmodel.skin_surface = 1;
headmodel.source = numboundaries;
% just to be sure
clear mesh
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% this uses an implementation that was contributed by INRIA Odyssee Team
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
workdir = fullfile(tempdir,['ft_om_' datestr(now,'ddmmyyHHMMSSFFF')]);
mkdir(workdir);
try
% Write the triangulations to file, named after tissue type.
bndfile = fullfile(workdir,strcat(tissue,'.tri'));
for ii=1:length(headmodel.bnd)
om_save_tri(bndfile{ii}, headmodel.bnd(ii).pos, headmodel.bnd(ii).tri);
end
condfile = fullfile(workdir, 'om.cond');
geomfile = fullfile(workdir, 'om.geom');
hmfile = fullfile(workdir, 'hm.bin');
hminvfile = fullfile(workdir, 'hminv.bin');
% write conductivity and mesh files
bndlabel = {};
for i=1:length(headmodel.bnd)
[dum, bndlabel{i}] = fileparts(bndfile{i});
end
om_write_geom(geomfile, bndfile, bndlabel);
om_write_cond(condfile, headmodel.cond, bndlabel);
om_status = system([prefix 'om_assemble -HM ' geomfile ' ' condfile ' ' hmfile]);
if(om_status ~= 0) % status = 0 if successful
ft_error('Aborting OpenMEEG pipeline due to above error.');
end
headmodel.mat = inv(om_load_sym(hmfile,'binary'));
rmdir(workdir,'s'); % remove workdir with intermediate files
catch me
rmdir(workdir,'s'); % remove workdir with intermediate files
rethrow(me);
end
% remember the type of volume conduction model
headmodel.type = 'openmeeg';