v2.0.0
Loading...
Searching...
No Matches
inv_dipole_fit.cpp
Go to the documentation of this file.
1//=============================================================================================================
18
19//=============================================================================================================
20// INCLUDES
21//=============================================================================================================
22
23#include "inv_dipole_fit.h"
25#include "inv_guess_data.h"
26
27#include <memory>
28#include <vector>
29
30//=============================================================================================================
31// USED NAMESPACES
32//=============================================================================================================
33
34using namespace INVLIB;
35using namespace MNELIB;
36using namespace FWDLIB;
37
38//=============================================================================================================
39// CONSTANTS
40//=============================================================================================================
41
42static constexpr float SEG_LEN = 10.0f;
43
44//=============================================================================================================
45// STATIC DEFINITIONS
46//=============================================================================================================
47
59static mneChSelection mne_ch_selection_these(const QString& selname, const QStringList& names, int nch)
60{
61 auto sel = new MNEChSelection();
62 sel->name = selname;
63 sel->ndef = nch;
64 sel->kind = MNE_CH_SELECTION_USER;
65
66 for (int c = 0; c < nch; c++)
67 sel->chdef.append(names[c]);
68
69 return sel;
70}
71
86static int mne_ch_selection_assign_chs(mneChSelection sel,
87 MNERawData* data)
88{
89 if (!sel || !data)
90 return 0;
91
92 auto info = data->info.get();
93 sel->chspick = sel->chdef;
94 sel->chspick_nospace = sel->chdef;
95 for (auto& name : sel->chspick_nospace)
96 name = name.trimmed();
97 sel->nchan = sel->ndef;
98
99 sel->pick.setConstant(sel->nchan, -1);
100 sel->pick_deriv.setConstant(sel->nchan, -1);
101 sel->ch_kind.setConstant(sel->nchan, -1);
102
103 for (int c = 0; c < sel->nchan; c++) {
104 for (int rc = 0; rc < info->nchan; rc++) {
105 if (QString::compare(sel->chspick[c],info->chInfo[rc].ch_name,Qt::CaseInsensitive) == 0 ||
106 QString::compare(sel->chspick_nospace[c],info->chInfo[rc].ch_name,Qt::CaseInsensitive) == 0) {
107 sel->pick[c] = rc;
108 sel->ch_kind[c] = info->chInfo[rc].kind;
109 break;
110 }
111 }
112 }
113 /*
114 * Maybe the derivations will help
115 */
116 sel->nderiv = 0;
117 if (data->deriv_matched) {
118 QStringList deriv_names = data->deriv_matched->deriv_data->rowlist;
119 int nderiv = data->deriv_matched->deriv_data->nrow;
120
121 for (int c = 0; c < sel->nchan; c++) {
122 if (sel->pick[c] == -1) {
123 for (int d = 0; d < nderiv; d++) {
124 if (QString::compare(sel->chspick[c],deriv_names[d],Qt::CaseInsensitive) == 0 &&
125 data->deriv_matched->valid.size() > 0 && data->deriv_matched->valid[d]) {
126 sel->pick_deriv[c] = d;
127 sel->ch_kind[c] = data->deriv_matched->chs[d].kind;
128 sel->nderiv++;
129 break;
130 }
131 }
132 }
133 }
134 }
135 /*
136 * Try simple channels again without the part after dashes
137 */
138 for (int c = 0; c < sel->nchan; c++) {
139 if (sel->pick[c] == -1 && sel->pick_deriv[c] == -1) {
140 for (int rc = 0; rc < info->nchan; rc++) {
141 QString dash = QString(info->chInfo[rc].ch_name).mid(QString(info->chInfo[rc].ch_name).indexOf("-")+1);
142 if (!dash.isNull()) {
143 if (QString::compare(sel->chspick[c],info->chInfo[rc].ch_name,Qt::CaseInsensitive) == 0 ||
144 QString::compare(sel->chspick_nospace[c],info->chInfo[rc].ch_name,Qt::CaseInsensitive) == 0) {
145 sel->pick[c] = rc;
146 sel->ch_kind[c] = info->chInfo[rc].kind;
147 break;
148 }
149 }
150 }
151 }
152 }
153 int nch = 0;
154 for (int c = 0; c < sel->nchan; c++) {
155 if (sel->pick[c] >= 0)
156 nch++;
157 }
158 if (sel->nderiv > 0)
159 qInfo("Selection \"%s\" has %d matched derived channels.",sel->name.toUtf8().constData(),sel->nderiv);
160 return nch;
161}
162
163//=============================================================================================================
164// DEFINE MEMBER METHODS
165//=============================================================================================================
166
168: settings(p_settings)
169{
170}
171
172//=============================================================================================================
174{
175 InvEcdSet set;
176
177 qInfo("---- Setting up...\n");
178 std::unique_ptr<FwdEegSphereModel> eeg_model;
179 if (settings->include_eeg) {
180 eeg_model = FwdEegSphereModel::setup_eeg_sphere_model(settings->eeg_model_file,settings->eeg_model_name,settings->eeg_sphere_rad);
181 if (!eeg_model)
182 return set;
183 }
184
185 std::unique_ptr<InvDipoleFitData> fit_data(InvDipoleFitData::setup_dipole_fit_data(
186 settings->mriname,
187 settings->measname,
188 settings->bemname,
189 &settings->r0,
190 eeg_model.get(),
191 settings->accurate,
192 settings->badname,
193 settings->noisename,
194 settings->grad_std,
195 settings->mag_std,
196 settings->eeg_std,
197 settings->mag_reg,
198 settings->grad_reg,
199 settings->eeg_reg,
200 settings->diagnoise,
201 settings->projnames,
202 settings->include_meg,
203 settings->include_eeg));
204 if (!fit_data)
205 return set;
206
207 eeg_model.release(); // ownership transferred to fit_data->eeg_model
208
209 fit_data->fit_mag_dipoles = settings->fit_mag_dipoles;
210
211 std::unique_ptr<MNERawData> raw;
212 std::unique_ptr<MNEMeasData> data;
213 std::unique_ptr<MNEChSelection> sel;
214
215 if (settings->is_raw) {
216 qInfo("\n---- Opening a raw data file...\n");
217 raw.reset(MNERawData::open_file(settings->measname, true, false, settings->filter));
218 if (!raw)
219 return set;
220 /*
221 * Set up a channel selection to pick data from the raw file
222 */
223 sel.reset(mne_ch_selection_these("fit",fit_data->ch_names,fit_data->nmeg+fit_data->neeg));
224 mne_ch_selection_assign_chs(sel.get(),raw.get());
225 for (int c = 0; c < sel->nchan; c++)
226 if (sel->pick[c] < 0) {
227 qCritical("All desired channels were not available");
228 return set;
229 }
230 qInfo("\tChannel selection created.");
231 /*
232 * Let's be a little generous here
233 */
234 float t1 = raw->first_samp/raw->info->sfreq;
235 float t2 = (raw->first_samp+raw->nsamp-1)/raw->info->sfreq;
236 if (settings->tmin < t1 + settings->integ)
237 settings->tmin = t1 + settings->integ;
238 if (settings->tmax > t2 - settings->integ)
239 settings->tmax = t2 - settings->integ;
240 if (settings->tstep < 0)
241 settings->tstep = 1.0f/raw->info->sfreq;
242
243 qInfo("\tOpened raw data file %s : %d MEG and %d EEG",
244 settings->measname.toUtf8().constData(),fit_data->nmeg,fit_data->neeg);
245 }
246 else {
247 qInfo("\n---- Reading data...\n");
248 data.reset(MNEMeasData::mne_read_meas_data(settings->measname,
249 settings->setno,
250 nullptr,
251 nullptr,
252 fit_data->ch_names,
253 fit_data->nmeg+fit_data->neeg));
254 if (!data)
255 return set;
256 if (settings->do_baseline)
257 data->adjust_baselines(settings->bmin,settings->bmax);
258 else
259 qInfo("\tNo baseline setting in effect.");
260 if (settings->tmin < data->current->tmin + settings->integ/2.0f)
261 settings->tmin = data->current->tmin + settings->integ/2.0f;
262 if (settings->tmax > data->current->tmin + (data->current->np-1)*data->current->tstep - settings->integ/2.0f)
263 settings->tmax = data->current->tmin + (data->current->np-1)*data->current->tstep - settings->integ/2.0f;
264 if (settings->tstep < 0)
265 settings->tstep = data->current->tstep;
266
267 qInfo("\tRead data set %d from %s : %d MEG and %d EEG",
268 settings->setno,settings->measname.toUtf8().constData(),fit_data->nmeg,fit_data->neeg);
269 if (!settings->noisename.isEmpty()) {
270 qInfo("Scaling the noise covariance...");
271 if (InvDipoleFitData::scale_noise_cov(fit_data.get(),data->current->nave) < 0)
272 return set;
273 }
274 }
275
276 /*
277 * Proceed to computing the fits
278 */
279 qInfo("\n---- Computing the forward solution for the guesses...\n");
280 auto guess = std::make_unique<InvGuessData>(settings->guessname,
281 settings->guess_surfname,
282 settings->guess_mindist, settings->guess_exclude, settings->guess_grid, fit_data.get());
283 if (!guess)
284 return set;
285
286 qInfo("\n---- Fitting : %7.1f ... %7.1f ms (step: %6.1f ms integ: %6.1f ms)\n",
287 1000*settings->tmin,1000*settings->tmax,1000*settings->tstep,1000*settings->integ);
288
289 if (raw) {
290 if (!fit_dipoles_raw(settings->measname,raw.get(),sel.get(),fit_data.get(),guess.get(),settings->tmin,settings->tmax,settings->tstep,settings->integ,settings->verbose,set))
291 return set;
292 }
293 else {
294 if (!fit_dipoles(settings->measname,data.get(),fit_data.get(),guess.get(),settings->tmin,settings->tmax,settings->tstep,settings->integ,settings->verbose,set))
295 return set;
296 }
297 qInfo("%d dipoles fitted",set.size());
298
299 return set;
300}
301
302//=============================================================================================================
303
304bool InvDipoleFit::fit_dipoles( const QString& dataname, MNEMeasData* data, InvDipoleFitData* fit, InvGuessData* guess, float tmin, float tmax, float tstep, float integ, int verbose, InvEcdSet& p_set)
305{
306 Eigen::VectorXf one(data->nchan);
307 InvEcdSet set;
308 InvEcd dip;
309 constexpr int report_interval = 10;
310
311 set.dataname = dataname;
312
313 if (verbose)
314 qInfo("Fitting...");
315 for (int s = 0; tmin + s*tstep < tmax; s++) {
316 float time = tmin + s*tstep;
317 if (data->current->getValuesAtTime(time, integ, data->nchan, false, one.data()) < 0) {
318 qWarning("Cannot pick time: %7.1f ms",1000.0f*time);
319 continue;
320 }
321
322 if (!InvDipoleFitData::fit_one(fit,guess,time,one,verbose,dip))
323 qWarning("t = %7.1f ms : fit error",1000.0f*time);
324 else {
325 set.addEcd(dip);
326 if (verbose)
327 dip.print();
328 else {
329 if (set.size() % report_interval == 0)
330 qInfo("%d..",set.size());
331 }
332 }
333 }
334 if (!verbose)
335 qInfo("[done]");
336 p_set = set;
337 return true;
338}
339
340//=============================================================================================================
341
342bool InvDipoleFit::fit_dipoles_raw(const QString& dataname, MNERawData* raw, mneChSelection sel, InvDipoleFitData* fit, InvGuessData* guess, float tmin, float tmax, float tstep, float integ, int verbose, InvEcdSet& p_set)
343{
344 const int nchan = sel->nchan;
345 const float sfreq = raw->info->sfreq;
346 const float myinteg = integ > 0.0f ? 2*integ : 0.1f;
347 const int overlap = static_cast<int>(std::ceil(myinteg*sfreq));
348 const int length = static_cast<int>(SEG_LEN*sfreq);
349 const int step = length - overlap;
350 const int stepo = step + overlap/2;
351 int start = raw->first_samp;
352 constexpr int report_interval = 10;
353
354 Eigen::VectorXf one(nchan);
355
356 // Row-major storage compatible with float** interface
357 std::vector<float> storage(static_cast<std::size_t>(nchan) * length);
358 std::vector<float*> rows(nchan);
359 for (int i = 0; i < nchan; ++i)
360 rows[i] = storage.data() + i * length;
361 float** data = rows.data();
362
363 InvEcd dip;
364 InvEcdSet set;
365 set.dataname = dataname;
366
367 /*
368 * Load the initial data segment
369 */
370 float stime = start/sfreq;
371 if (raw->pick_data_filt(sel,start,length,data) < 0)
372 return false;
373 if (verbose)
374 qInfo("Fitting...");
375 for (int s = 0; tmin + s*tstep < tmax; s++) {
376 float time = tmin + s*tstep;
377 int picks = time*sfreq - start;
378 if (picks > stepo) {
379 start = start + step;
380 if (raw->pick_data_filt(sel,start,length,data) < 0)
381 return false;
382 picks = time*sfreq - start;
383 stime = start/sfreq;
384 }
385 if (MNEMeasDataSet::getValuesFromChannelData(time, integ, data, length, nchan, stime, sfreq, false, one.data()) < 0) {
386 qWarning("Cannot pick time: %8.3f s",time);
387 continue;
388 }
389 if (!InvDipoleFitData::fit_one(fit,guess,time,one,verbose,dip))
390 qWarning("t = %8.3f s : fit error",time);
391 else {
392 set.addEcd(dip);
393 if (verbose)
394 dip.print();
395 else {
396 if (set.size() % report_interval == 0)
397 qInfo("%d..",set.size());
398 }
399 }
400 }
401 if (!verbose)
402 qInfo("[done]");
403 p_set = set;
404 return true;
405}
406
407//=============================================================================================================
408
409bool InvDipoleFit::fit_dipoles_raw(const QString& dataname, MNERawData* raw, mneChSelection sel, InvDipoleFitData* fit, InvGuessData* guess, float tmin, float tmax, float tstep, float integ, int verbose)
410{
411 InvEcdSet set;
412 return fit_dipoles_raw(dataname, raw, sel, fit, guess, tmin, tmax, tstep, integ, verbose, set);
413}
One condition / averaging slice within a legacy MNELIB::MNEMeasData.
#define MNE_CH_SELECTION_USER
Definition mne_types.h:96
Initial-guess grid for the dipole-fit optimiser, with per-guess forward fields pre-computed.
High-level driver for sequential equivalent-current-dipole (ECD) fitting at every time point of an ev...
Core MNE data structures (source spaces, source estimates, hemispheres).
MNEChSelection * mneChSelection
Inverse source estimation (MNE, dSPM, sLORETA, dipole fitting).
Forward modelling — BEM solver, spherical models, sensor/coil definitions and the lead-field assembly...
Definition compute_fwd.h:83
static FwdEegSphereModel::UPtr setup_eeg_sphere_model(const QString &eeg_model_file, QString eeg_model_name, float eeg_sphere_rad)
InvEcdSet calculateFit() const
static bool fit_dipoles(const QString &dataname, MNELIB::MNEMeasData *data, InvDipoleFitData *fit, InvGuessData *guess, float tmin, float tmax, float tstep, float integ, int verbose, InvEcdSet &p_set)
InvDipoleFit(InvDipoleFitSettings *p_settings)
static bool fit_dipoles_raw(const QString &dataname, MNELIB::MNERawData *raw, MNELIB::mneChSelection sel, InvDipoleFitData *fit, InvGuessData *guess, float tmin, float tmax, float tstep, float integ, int verbose, InvEcdSet &p_set)
Dipole fit workspace holding sensor geometry, forward model, noise covariance, and projection data.
static InvDipoleFitData * setup_dipole_fit_data(const QString &mriname, const QString &measname, const QString &bemname, Eigen::Vector3f *r0, FWDLIB::FwdEegSphereModel *eeg_model, int accurate_coils, const QString &badname, const QString &noisename, float grad_std, float mag_std, float eeg_std, float mag_reg, float grad_reg, float eeg_reg, int diagnoise, const QList< QString > &projnames, int include_meg, int include_eeg)
Master setup: read all inputs and build a ready-to-use fit workspace.
static int scale_noise_cov(InvDipoleFitData *f, int nave)
Scale the noise-covariance matrix for a given number of averages.
static bool fit_one(InvDipoleFitData *fit, InvGuessData *guess, float time, Eigen::Ref< Eigen::VectorXf > B, int verbose, InvEcd &res)
Fit a single dipole to the given data.
Settings for the mne_dipole_fit driver (forward model, guess grid, data, projections,...
Single equivalent current dipole with position, orientation, amplitude, and goodness-of-fit.
Definition inv_ecd.h:56
void print() const
Definition inv_ecd.cpp:68
Holds a set of Electric Current Dipoles.
Definition inv_ecd_set.h:64
qint32 size() const
void addEcd(const InvEcd &p_ecd)
Precomputed guess point grid with forward fields for initial dipole position candidates.
Eigen::VectorXi pick_deriv
Measurement data container for MNE inverse and dipole-fit computations.
MNEMeasDataSet * current
static MNEMeasData * mne_read_meas_data(const QString &name, int set, MNEInverseOperator *op, MNENamedMatrix *fwd, const QStringList &namesp, int nnamesp)
Read an evoked-response data set into a new container.
static int getValuesFromChannelData(float time, float integ, float **data, int nsamp, int nch, float tmin, float sfreq, bool use_abs, float *value)
int getValuesAtTime(float time, float integ, int nch, bool use_abs, float *value) const
A comprehensive raw data structure.
std::unique_ptr< MNELIB::MNERawInfo > info
std::unique_ptr< MNELIB::MNEDeriv > deriv_matched
static MNERawData * open_file(const QString &name, int omit_skip, int allow_maxshield, const MNEFilterDef &filter)
int pick_data_filt(mneChSelection sel, int firsts, int ns, float **picked)