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