v2.0.0
Loading...
Searching...
No Matches
inv_dipole_fit.cpp
Go to the documentation of this file.
1
2//=============================================================================================================
37
38//=============================================================================================================
39// INCLUDES
40//=============================================================================================================
41
42#include "inv_dipole_fit.h"
44#include "inv_guess_data.h"
45
46#include <memory>
47#include <vector>
48
49//=============================================================================================================
50// USED NAMESPACES
51//=============================================================================================================
52
53using namespace INVLIB;
54using namespace MNELIB;
55using namespace FWDLIB;
56
57//=============================================================================================================
58// CONSTANTS
59//=============================================================================================================
60
61static constexpr float SEG_LEN = 10.0f;
62
63//=============================================================================================================
64// STATIC DEFINITIONS
65//=============================================================================================================
66
78static mneChSelection mne_ch_selection_these(const QString& selname, const QStringList& names, int nch)
79{
80 auto sel = new MNEChSelection();
81 sel->name = selname;
82 sel->ndef = nch;
83 sel->kind = MNE_CH_SELECTION_USER;
84
85 for (int c = 0; c < nch; c++)
86 sel->chdef.append(names[c]);
87
88 return sel;
89}
90
105static int mne_ch_selection_assign_chs(mneChSelection sel,
106 MNERawData* data)
107{
108 if (!sel || !data)
109 return 0;
110
111 auto info = data->info.get();
112 sel->chspick = sel->chdef;
113 sel->chspick_nospace = sel->chdef;
114 for (auto& name : sel->chspick_nospace)
115 name = name.trimmed();
116 sel->nchan = sel->ndef;
117
118 sel->pick.setConstant(sel->nchan, -1);
119 sel->pick_deriv.setConstant(sel->nchan, -1);
120 sel->ch_kind.setConstant(sel->nchan, -1);
121
122 for (int c = 0; c < sel->nchan; c++) {
123 for (int rc = 0; rc < info->nchan; rc++) {
124 if (QString::compare(sel->chspick[c],info->chInfo[rc].ch_name,Qt::CaseInsensitive) == 0 ||
125 QString::compare(sel->chspick_nospace[c],info->chInfo[rc].ch_name,Qt::CaseInsensitive) == 0) {
126 sel->pick[c] = rc;
127 sel->ch_kind[c] = info->chInfo[rc].kind;
128 break;
129 }
130 }
131 }
132 /*
133 * Maybe the derivations will help
134 */
135 sel->nderiv = 0;
136 if (data->deriv_matched) {
137 QStringList deriv_names = data->deriv_matched->deriv_data->rowlist;
138 int nderiv = data->deriv_matched->deriv_data->nrow;
139
140 for (int c = 0; c < sel->nchan; c++) {
141 if (sel->pick[c] == -1) {
142 for (int d = 0; d < nderiv; d++) {
143 if (QString::compare(sel->chspick[c],deriv_names[d],Qt::CaseInsensitive) == 0 &&
144 data->deriv_matched->valid.size() > 0 && data->deriv_matched->valid[d]) {
145 sel->pick_deriv[c] = d;
146 sel->ch_kind[c] = data->deriv_matched->chs[d].kind;
147 sel->nderiv++;
148 break;
149 }
150 }
151 }
152 }
153 }
154 /*
155 * Try simple channels again without the part after dashes
156 */
157 for (int c = 0; c < sel->nchan; c++) {
158 if (sel->pick[c] == -1 && sel->pick_deriv[c] == -1) {
159 for (int rc = 0; rc < info->nchan; rc++) {
160 QString dash = QString(info->chInfo[rc].ch_name).mid(QString(info->chInfo[rc].ch_name).indexOf("-")+1);
161 if (!dash.isNull()) {
162 if (QString::compare(sel->chspick[c],info->chInfo[rc].ch_name,Qt::CaseInsensitive) == 0 ||
163 QString::compare(sel->chspick_nospace[c],info->chInfo[rc].ch_name,Qt::CaseInsensitive) == 0) {
164 sel->pick[c] = rc;
165 sel->ch_kind[c] = info->chInfo[rc].kind;
166 break;
167 }
168 }
169 }
170 }
171 }
172 int nch = 0;
173 for (int c = 0; c < sel->nchan; c++) {
174 if (sel->pick[c] >= 0)
175 nch++;
176 }
177 if (sel->nderiv > 0)
178 qInfo("Selection \"%s\" has %d matched derived channels.",sel->name.toUtf8().constData(),sel->nderiv);
179 return nch;
180}
181
182//=============================================================================================================
183// DEFINE MEMBER METHODS
184//=============================================================================================================
185
187: settings(p_settings)
188{
189}
190
191//=============================================================================================================
193{
194 InvEcdSet set;
195
196 qInfo("---- Setting up...\n");
197 std::unique_ptr<FwdEegSphereModel> eeg_model;
198 if (settings->include_eeg) {
199 eeg_model = FwdEegSphereModel::setup_eeg_sphere_model(settings->eeg_model_file,settings->eeg_model_name,settings->eeg_sphere_rad);
200 if (!eeg_model)
201 return set;
202 }
203
204 std::unique_ptr<InvDipoleFitData> fit_data(InvDipoleFitData::setup_dipole_fit_data(
205 settings->mriname,
206 settings->measname,
207 settings->bemname,
208 &settings->r0,
209 eeg_model.get(),
210 settings->accurate,
211 settings->badname,
212 settings->noisename,
213 settings->grad_std,
214 settings->mag_std,
215 settings->eeg_std,
216 settings->mag_reg,
217 settings->grad_reg,
218 settings->eeg_reg,
219 settings->diagnoise,
220 settings->projnames,
221 settings->include_meg,
222 settings->include_eeg));
223 if (!fit_data)
224 return set;
225
226 eeg_model.release(); // ownership transferred to fit_data->eeg_model
227
228 fit_data->fit_mag_dipoles = settings->fit_mag_dipoles;
229
230 std::unique_ptr<MNERawData> raw;
231 std::unique_ptr<MNEMeasData> data;
232 std::unique_ptr<MNEChSelection> sel;
233
234 if (settings->is_raw) {
235 qInfo("\n---- Opening a raw data file...\n");
236 raw.reset(MNERawData::open_file(settings->measname, true, false, settings->filter));
237 if (!raw)
238 return set;
239 /*
240 * Set up a channel selection to pick data from the raw file
241 */
242 sel.reset(mne_ch_selection_these("fit",fit_data->ch_names,fit_data->nmeg+fit_data->neeg));
243 mne_ch_selection_assign_chs(sel.get(),raw.get());
244 for (int c = 0; c < sel->nchan; c++)
245 if (sel->pick[c] < 0) {
246 qCritical("All desired channels were not available");
247 return set;
248 }
249 qInfo("\tChannel selection created.");
250 /*
251 * Let's be a little generous here
252 */
253 float t1 = raw->first_samp/raw->info->sfreq;
254 float t2 = (raw->first_samp+raw->nsamp-1)/raw->info->sfreq;
255 if (settings->tmin < t1 + settings->integ)
256 settings->tmin = t1 + settings->integ;
257 if (settings->tmax > t2 - settings->integ)
258 settings->tmax = t2 - settings->integ;
259 if (settings->tstep < 0)
260 settings->tstep = 1.0f/raw->info->sfreq;
261
262 qInfo("\tOpened raw data file %s : %d MEG and %d EEG",
263 settings->measname.toUtf8().constData(),fit_data->nmeg,fit_data->neeg);
264 }
265 else {
266 qInfo("\n---- Reading data...\n");
267 data.reset(MNEMeasData::mne_read_meas_data(settings->measname,
268 settings->setno,
269 nullptr,
270 nullptr,
271 fit_data->ch_names,
272 fit_data->nmeg+fit_data->neeg));
273 if (!data)
274 return set;
275 if (settings->do_baseline)
276 data->adjust_baselines(settings->bmin,settings->bmax);
277 else
278 qInfo("\tNo baseline setting in effect.");
279 if (settings->tmin < data->current->tmin + settings->integ/2.0f)
280 settings->tmin = data->current->tmin + settings->integ/2.0f;
281 if (settings->tmax > data->current->tmin + (data->current->np-1)*data->current->tstep - settings->integ/2.0f)
282 settings->tmax = data->current->tmin + (data->current->np-1)*data->current->tstep - settings->integ/2.0f;
283 if (settings->tstep < 0)
284 settings->tstep = data->current->tstep;
285
286 qInfo("\tRead data set %d from %s : %d MEG and %d EEG",
287 settings->setno,settings->measname.toUtf8().constData(),fit_data->nmeg,fit_data->neeg);
288 if (!settings->noisename.isEmpty()) {
289 qInfo("Scaling the noise covariance...");
290 if (InvDipoleFitData::scale_noise_cov(fit_data.get(),data->current->nave) < 0)
291 return set;
292 }
293 }
294
295 /*
296 * Proceed to computing the fits
297 */
298 qInfo("\n---- Computing the forward solution for the guesses...\n");
299 auto guess = std::make_unique<InvGuessData>(settings->guessname,
300 settings->guess_surfname,
301 settings->guess_mindist, settings->guess_exclude, settings->guess_grid, fit_data.get());
302 if (!guess)
303 return set;
304
305 qInfo("\n---- Fitting : %7.1f ... %7.1f ms (step: %6.1f ms integ: %6.1f ms)\n",
306 1000*settings->tmin,1000*settings->tmax,1000*settings->tstep,1000*settings->integ);
307
308 if (raw) {
309 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))
310 return set;
311 }
312 else {
313 if (!fit_dipoles(settings->measname,data.get(),fit_data.get(),guess.get(),settings->tmin,settings->tmax,settings->tstep,settings->integ,settings->verbose,set))
314 return set;
315 }
316 qInfo("%d dipoles fitted",set.size());
317
318 return set;
319}
320
321//=============================================================================================================
322
323bool 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)
324{
325 Eigen::VectorXf one(data->nchan);
326 InvEcdSet set;
327 InvEcd dip;
328 constexpr int report_interval = 10;
329
330 set.dataname = dataname;
331
332 if (verbose)
333 qInfo("Fitting...");
334 for (int s = 0; tmin + s*tstep < tmax; s++) {
335 float time = tmin + s*tstep;
336 if (data->current->getValuesAtTime(time, integ, data->nchan, false, one.data()) < 0) {
337 qWarning("Cannot pick time: %7.1f ms",1000.0f*time);
338 continue;
339 }
340
341 if (!InvDipoleFitData::fit_one(fit,guess,time,one,verbose,dip))
342 qWarning("t = %7.1f ms : fit error",1000.0f*time);
343 else {
344 set.addEcd(dip);
345 if (verbose)
346 dip.print();
347 else {
348 if (set.size() % report_interval == 0)
349 qInfo("%d..",set.size());
350 }
351 }
352 }
353 if (!verbose)
354 qInfo("[done]");
355 p_set = set;
356 return true;
357}
358
359//=============================================================================================================
360
361bool 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)
362{
363 const int nchan = sel->nchan;
364 const float sfreq = raw->info->sfreq;
365 const float myinteg = integ > 0.0f ? 2*integ : 0.1f;
366 const int overlap = static_cast<int>(std::ceil(myinteg*sfreq));
367 const int length = static_cast<int>(SEG_LEN*sfreq);
368 const int step = length - overlap;
369 const int stepo = step + overlap/2;
370 int start = raw->first_samp;
371 constexpr int report_interval = 10;
372
373 Eigen::VectorXf one(nchan);
374
375 // Row-major storage compatible with float** interface
376 std::vector<float> storage(nchan * length);
377 std::vector<float*> rows(nchan);
378 for (int i = 0; i < nchan; ++i)
379 rows[i] = storage.data() + i * length;
380 float** data = rows.data();
381
382 InvEcd dip;
383 InvEcdSet set;
384 set.dataname = dataname;
385
386 /*
387 * Load the initial data segment
388 */
389 float stime = start/sfreq;
390 if (raw->pick_data_filt(sel,start,length,data) < 0)
391 return false;
392 if (verbose)
393 qInfo("Fitting...");
394 for (int s = 0; tmin + s*tstep < tmax; s++) {
395 float time = tmin + s*tstep;
396 int picks = time*sfreq - start;
397 if (picks > stepo) {
398 start = start + step;
399 if (raw->pick_data_filt(sel,start,length,data) < 0)
400 return false;
401 picks = time*sfreq - start;
402 stime = start/sfreq;
403 }
404 if (MNEMeasDataSet::getValuesFromChannelData(time, integ, data, length, nchan, stime, sfreq, false, one.data()) < 0) {
405 qWarning("Cannot pick time: %8.3f s",time);
406 continue;
407 }
408 if (!InvDipoleFitData::fit_one(fit,guess,time,one,verbose,dip))
409 qWarning("t = %8.3f s : fit error",time);
410 else {
411 set.addEcd(dip);
412 if (verbose)
413 dip.print();
414 else {
415 if (set.size() % report_interval == 0)
416 qInfo("%d..",set.size());
417 }
418 }
419 }
420 if (!verbose)
421 qInfo("[done]");
422 p_set = set;
423 return true;
424}
425
426//=============================================================================================================
427
428bool 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)
429{
430 InvEcdSet set;
431 return fit_dipoles_raw(dataname, raw, sel, fit, guess, tmin, tmax, tstep, integ, verbose, set);
432}
InvGuessData class declaration.
Dipole Fit class declaration.
#define MNE_CH_SELECTION_USER
Definition mne_types.h:119
MNEMeasDataSet class declaration.
Core MNE data structures (source spaces, source estimates, hemispheres).
MNEChSelection * mneChSelection
Inverse source estimation (MNE, dSPM, sLORETA, dipole fitting).
Forward modelling (BEM, MEG/EEG lead fields).
Definition compute_fwd.h:91
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.
Dipole Fit setting implementation.
Single equivalent current dipole with position, orientation, amplitude, and goodness-of-fit.
Definition inv_ecd.h:73
void print() const
Definition inv_ecd.cpp:88
Holds a set of Electric Current Dipoles.
Definition inv_ecd_set.h:81
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)