v2.0.0
Loading...
Searching...
No Matches
mne_epoch_data_list.cpp
Go to the documentation of this file.
1//=============================================================================================================
37
38//=============================================================================================================
39// INCLUDES
40//=============================================================================================================
41
42#include "mne_epoch_data_list.h"
43
45
46//=============================================================================================================
47// QT INCLUDES
48//=============================================================================================================
49
50#include <QPointer>
51#include <QtConcurrent>
52#include <QDebug>
53
54//=============================================================================================================
55// USED NAMESPACES
56//=============================================================================================================
57
58using namespace FIFFLIB;
59using namespace MNELIB;
60using namespace UTILSLIB;
61using namespace Eigen;
62
63//=============================================================================================================
64// DEFINE MEMBER METHODS
65//=============================================================================================================
66
70
71//=============================================================================================================
72
74{
75// MNEEpochDataList::iterator i;
76// for( i = this->begin(); i!=this->end(); ++i) {
77// if (*i)
78// delete (*i);
79// }
80}
81
82//=============================================================================================================
83
85 const MatrixXi& events,
86 float tmin,
87 float tmax,
88 qint32 event,
89 const QMap<QString,double>& mapReject,
90 const QStringList& lExcludeChs,
91 const RowVectorXi& picks)
92{
94
95 // Select the desired events
96 qint32 count = 0;
97 qint32 p;
98 MatrixXi selected = MatrixXi::Zero(1, events.rows());
99 for (p = 0; p < events.rows(); ++p)
100 {
101 if (events(p,1) == 0 && events(p,2) == event)
102 {
103 selected(0,count) = p;
104 ++count;
105 }
106 }
107 selected.conservativeResize(1, count);
108 if (count > 0) {
109 qInfo("[MNEEpochDataList::readEpochs] %d matching events found",count);
110 } else {
111 qWarning("[MNEEpochDataList::readEpochs] No desired events found.");
112 return MNEEpochDataList();
113 }
114
115 // If picks are empty, pick all
116 RowVectorXi picksNew = picks;
117 if(picks.cols() <= 0) {
118 picksNew.resize(raw.info.chs.size());
119 for(int i = 0; i < raw.info.chs.size(); ++i) {
120 picksNew(i) = i;
121 }
122 }
123
124 fiff_int_t event_samp, from, to;
125 fiff_int_t dropCount = 0;
126 MatrixXd timesDummy;
127 MatrixXd times;
128
129 std::unique_ptr<MNEEpochData> epoch(Q_NULLPTR);
130
131 for (p = 0; p < count; ++p) {
132 // Read a data segment
133 event_samp = events(selected(p),0);
134 from = event_samp + tmin*raw.info.sfreq;
135 to = event_samp + floor(tmax*raw.info.sfreq + 0.5);
136
137 epoch.reset(new MNEEpochData());
138
139 if(raw.read_raw_segment(epoch->epoch, timesDummy, from, to, picksNew)) {
140 if (p == 0) {
141 times.resize(1, to-from+1);
142 for (qint32 i = 0; i < times.cols(); ++i)
143 times(0, i) = static_cast<float>(from-event_samp+i) / raw.info.sfreq;
144 }
145
146 epoch->event = event;
147 epoch->eventSample = event_samp;
148 epoch->tmin = tmin;
149 epoch->tmax = tmax;
150
151 epoch->bReject = checkForArtifact(epoch->epoch,
152 raw.info,
153 mapReject,
154 lExcludeChs);
155
156 if (epoch->bReject) {
157 dropCount++;
158 }
159
160 //Check if data block has the same size as the previous one
161 if(!data.isEmpty()) {
162 if(epoch->epoch.size() == data.last()->epoch.size()) {
163 data.append(MNEEpochData::SPtr(epoch.release()));//List takes ownwership of the pointer - no delete need
164 }
165 } else {
166 data.append(MNEEpochData::SPtr(epoch.release()));//List takes ownwership of the pointer - no delete need
167 }
168 } else {
169 qWarning("[MNEEpochDataList::readEpochs] Can't read the event data segments.");
170 }
171 }
172
173 qInfo().noquote() << "[MNEEpochDataList::readEpochs] Read a total of"<< data.size() <<"epochs of type" << event << "and marked"<< dropCount <<"for rejection.";
174
175 return data;
176}
177
178//=============================================================================================================
179
181 fiff_int_t first,
182 fiff_int_t last,
183 VectorXi sel,
184 bool proj) const
185{
186 FiffEvoked p_evoked;
187
188 qInfo("[MNEEpochDataList::average] Calculate evoked. ");
189
190 MatrixXd matAverage;
191
192 if(this->size() > 0) {
193 matAverage = MatrixXd::Zero(this->at(0)->epoch.rows(), this->at(0)->epoch.cols());
194 } else {
196 return p_evoked;
197 }
198
199 if(sel.size() > 0) {
200 p_evoked.nave = sel.size();
201
202 for(qint32 i = 0; i < sel.size(); ++i) {
203 matAverage.array() += this->at(sel(i))->epoch.array();
204 }
205 } else {
206 p_evoked.nave = this->size();
207
208 for(qint32 i = 0; i < this->size(); ++i) {
209 matAverage.array() += this->at(i)->epoch.array();
210 }
211 }
212 matAverage.array() /= p_evoked.nave;
213
214 qInfo("[MNEEpochDataList::average] %d averages used [done]", p_evoked.nave);
215
216 p_evoked.setInfo(info, proj);
217
219
220 p_evoked.first = first;
221 p_evoked.last = last;
222
223 p_evoked.times = RowVectorXf::LinSpaced(this->first()->epoch.cols(), this->first()->tmin, this->first()->tmax);
224
225 p_evoked.times[static_cast<int>(this->first()->tmin * -1 * info.sfreq)] = 0;
226
227 p_evoked.comment = QString::number(this->at(0)->event);
228
229 if(p_evoked.proj.rows() > 0) {
230 matAverage = p_evoked.proj * matAverage;
231 qInfo("[MNEEpochDataList::average] SSP projectors applied to the evoked data");
232 }
233
234 p_evoked.data = matAverage;
235
236 return p_evoked;
237}
238
239//=============================================================================================================
240
241void MNEEpochDataList::applyBaselineCorrection(const QPair<float, float> &baseline)
242{
243 // Run baseline correction
244 QMutableListIterator<MNEEpochData::SPtr> i(*this);
245 while (i.hasNext()) {
246 i.next()->applyBaselineCorrection(baseline);
247 }
248}
249
250//=============================================================================================================
251
253{
254 QMutableListIterator<MNEEpochData::SPtr> i(*this);
255 while (i.hasNext()) {
256 if (i.next()->isRejected()) {
257 i.remove();
258 }
259 }
260}
261
262//=============================================================================================================
263
264void MNEEpochDataList::pick_channels(const RowVectorXi& sel)
265{
266 QMutableListIterator<MNEEpochData::SPtr> i(*this);
267 while (i.hasNext()) {
268 i.next()->pick_channels(sel);
269 }
270}
271
272//=============================================================================================================
273
274bool MNEEpochDataList::checkForArtifact(const MatrixXd& data,
275 const FiffInfo& pFiffInfo,
276 const QMap<QString,double>& mapReject,
277 const QStringList& lExcludeChs)
278{
279 //qDebug() << "MNEEpochDataList::checkForArtifact - Doing artifact reduction for" << mapReject;
280
281 bool bReject = false;
282
283 //Prepare concurrent data handling
284 QList<ArtifactRejectionData> lchData;
285 QList<int> lChTypes;
286
287 if(mapReject.contains("grad") ||
288 mapReject.contains("mag") ) {
289 lChTypes << FIFFV_MEG_CH;
290 }
291
292 if(mapReject.contains("eeg")) {
293 lChTypes << FIFFV_EEG_CH;
294 }
295
296 if(mapReject.contains("eog")) {
297 lChTypes << FIFFV_EOG_CH;
298 }
299
300 if(lChTypes.isEmpty()) {
301 return bReject;
302 }
303
304 for(int i = 0; i < pFiffInfo.chs.size(); ++i) {
305 if(lChTypes.contains(pFiffInfo.chs.at(i).kind)
306 && !lExcludeChs.contains(pFiffInfo.chs.at(i).ch_name)
307 && !pFiffInfo.bads.contains(pFiffInfo.chs.at(i).ch_name)
308 && pFiffInfo.chs.at(i).chpos.coil_type != FIFFV_COIL_BABY_REF_MAG
309 && pFiffInfo.chs.at(i).chpos.coil_type != FIFFV_COIL_BABY_REF_MAG2) {
310 ArtifactRejectionData tempData;
311 tempData.data = data.row(i);
312
313 switch (pFiffInfo.chs.at(i).kind) {
314 case FIFFV_MEG_CH:
315 if(pFiffInfo.chs.at(i).unit == FIFF_UNIT_T) {
316 tempData.dThreshold = mapReject["mag"];
317 } else if(pFiffInfo.chs.at(i).unit == FIFF_UNIT_T_M) {
318 tempData.dThreshold = mapReject["grad"];
319 }
320 break;
321
322 case FIFFV_EEG_CH:
323 tempData.dThreshold = mapReject["eeg"];
324 break;
325
326 case FIFFV_EOG_CH:
327 tempData.dThreshold = mapReject["eog"];
328 break;
329 }
330
331 tempData.sChName = pFiffInfo.chs.at(i).ch_name;
332 lchData.append(tempData);
333 }
334 }
335
336 if(lchData.isEmpty()) {
337 qWarning() << "[MNEEpochDataList::checkForArtifact] No channels found to scan for artifacts. Do not reject. Returning.";
338
339 return bReject;
340 }
341
342 //qDebug() << "MNEEpochDataList::checkForArtifact - lchData.size()" << lchData.size();
343
344 //Start the concurrent processing
345 QFuture<void> future = QtConcurrent::map(lchData, checkChThreshold);
346 future.waitForFinished();
347
348 for(int i = 0; i < lchData.size(); ++i) {
349 if(lchData.at(i).bRejected) {
350 bReject = true;
351 qInfo().noquote() << "[MNEEpochDataList::checkForArtifact] Reject trial because of channel"<<lchData.at(i).sChName;
352 break;
353 }
354 }
355
356 return bReject;
357}
358
359//=============================================================================================================
360
362{
363 RowVectorXd temp = inputData.data;
364
365 // Remove offset
366 //temp = temp.array() - temp(0);
367
368 double min = temp.minCoeff();
369 double max = temp.maxCoeff();
370
371 // Peak to Peak
372 double pp = max - min;
373
374 if(std::fabs(pp) > inputData.dThreshold) {
375 inputData.bRejected = true;
376 } else {
377 inputData.bRejected = false;
378 }
379
380// qDebug() << "MNEEpochDataList::checkChThreshold - min" << min;
381// qDebug() << "MNEEpochDataList::checkChThreshold - max" << max;
382// qDebug() << "MNEEpochDataList::checkChThreshold - pp" << pp;
383// qDebug() << "MNEEpochDataList::checkChThreshold - inputData.dThreshold" << inputData.dThreshold;
384
385// //If absolute vaue of min or max if bigger than threshold -> reject
386// if((std::fabs(min) > inputData.dThreshold) || (std::fabs(max) > inputData.dThreshold)) {
387// inputData.bRejected = true;
388// } else {
389// inputData.bRejected = false;
390// }
391}
392
393//=============================================================================================================
394
396 const MatrixXi &events,
397 const QList<int> &eventCodes,
398 const QStringList &comments,
399 float tmin,
400 float tmax,
401 const QMap<QString,double> &mapReject,
402 const QPair<float,float> &baseline,
403 bool proj)
404{
405 FiffEvokedSet evokedSet;
406 evokedSet.info = raw.info;
407
408 float sfreq = raw.info.sfreq;
409 int nchan = raw.info.nchan;
410
411 bool doBaseline = (baseline.first != baseline.second);
412
413 // Process each category (event code)
414 for (int j = 0; j < eventCodes.size(); ++j) {
415 int eventCode = eventCodes[j];
416 QString comment = (j < comments.size()) ? comments[j]
417 : QString("cat_%1").arg(eventCode);
418
419 // Read epochs for this event code using the existing readEpochs
420 MNEEpochDataList epochList = MNEEpochDataList::readEpochs(raw, events,
421 tmin, tmax,
422 eventCode,
423 mapReject);
424
425 if (epochList.isEmpty()) {
426 qWarning() << "[MNEEpochDataList::averageCategories] No epochs found for event"
427 << eventCode << "- skipping category.";
428 continue;
429 }
430
431 // Apply baseline correction
432 if (doBaseline) {
433 epochList.applyBaselineCorrection(baseline);
434 }
435
436 // Drop rejected epochs
437 epochList.dropRejected();
438
439 if (epochList.isEmpty()) {
440 qWarning() << "[MNEEpochDataList::averageCategories] All epochs rejected for event"
441 << eventCode << "- skipping category.";
442 continue;
443 }
444
445 // Compute the average
446 int minSamp = static_cast<int>(std::round(tmin * sfreq));
447 int maxSamp = static_cast<int>(std::round(tmax * sfreq));
448
449 FiffEvoked evoked = epochList.average(raw.info,
450 minSamp,
451 maxSamp,
452 defaultVectorXi,
453 proj);
454
455 evoked.comment = comment;
456 evoked.baseline = doBaseline ? baseline : QPair<float,float>(0.0f, 0.0f);
457 evokedSet.evoked.append(evoked);
458 }
459
460 return evokedSet;
461}
462
463//=============================================================================================================
464
466 const MatrixXi& matEvents,
467 float fTMinS,
468 float fTMaxS,
469 qint32 eventType,
470 bool bApplyBaseline,
471 float fTBaselineFromS,
472 float fTBaselineToS,
473 const QMap<QString,double>& mapReject,
474 const QStringList& lExcludeChs,
475 const RowVectorXi& picks)
476{
477 MNEEpochDataList lstEpochDataList = MNEEpochDataList::readEpochs(raw,
478 matEvents,
479 fTMinS,
480 fTMaxS,
481 eventType,
482 mapReject,
483 lExcludeChs,
484 picks);
485
486 if(bApplyBaseline) {
487 QPair<float, float> baselinePair(fTBaselineFromS, fTBaselineToS);
488 lstEpochDataList.applyBaselineCorrection(baselinePair);
489 }
490
491 if(!mapReject.isEmpty()) {
492 lstEpochDataList.dropRejected();
493 }
494
495 FiffEvoked evoked = lstEpochDataList.average(raw.info,
496 0,
497 lstEpochDataList.first()->epoch.cols());
498 evoked.baseline = bApplyBaseline ? QPair<float,float>(fTBaselineFromS, fTBaselineToS)
499 : QPair<float,float>(0.0f, 0.0f);
500 return evoked;
501}
FiffEvokedSet class declaration.
#define FIFFV_ASPECT_AVERAGE
Definition fiff_file.h:438
#define FIFFV_ASPECT_STD_ERR
Definition fiff_file.h:439
#define FIFFV_EOG_CH
#define FIFFV_EEG_CH
#define FIFFV_COIL_BABY_REF_MAG
#define FIFFV_MEG_CH
#define FIFF_UNIT_T
#define FIFF_UNIT_T_M
#define FIFFV_COIL_BABY_REF_MAG2
MNEEpochDataList class declaration.
Core MNE data structures (source spaces, source estimates, hemispheres).
FIFF file I/O and data structures (raw, epochs, evoked, covariance, forward).
qint32 fiff_int_t
Definition fiff_types.h:89
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
Eigen::MatrixXd proj
Eigen::RowVectorXf times
Eigen::MatrixXd data
void setInfo(const FiffInfo &p_info, bool proj=true)
fiff_int_t aspect_kind
QPair< float, float > baseline
QList< FiffEvoked > evoked
FIFF measurement file information.
Definition fiff_info.h:86
QList< FiffChInfo > chs
FIFF raw measurement data.
bool read_raw_segment(Eigen::MatrixXd &data, Eigen::MatrixXd &times, fiff_int_t from=-1, fiff_int_t to=-1, const Eigen::RowVectorXi &sel=defaultRowVectorXi, bool do_debug=false) const
QSharedPointer< MNEEpochData > SPtr
Artifact rejection thresholds and flags for each channel type (grad, mag, eeg, eog) used during epoch...
FIFFLIB::FiffEvoked average(const FIFFLIB::FiffInfo &p_info, FIFFLIB::fiff_int_t first, FIFFLIB::fiff_int_t last, Eigen::VectorXi sel=FIFFLIB::defaultVectorXi, bool proj=false) const
void applyBaselineCorrection(const QPair< float, float > &baseline)
static MNEEpochDataList readEpochs(const FIFFLIB::FiffRawData &raw, const Eigen::MatrixXi &events, float tmin, float tmax, qint32 event, const QMap< QString, double > &mapReject, const QStringList &lExcludeChs=QStringList(), const Eigen::RowVectorXi &picks=Eigen::RowVectorXi())
static FIFFLIB::FiffEvoked computeAverage(const FIFFLIB::FiffRawData &raw, const Eigen::MatrixXi &matEvents, float fTMinS, float fTMaxS, qint32 eventType, bool bApplyBaseline, float fTBaselineFromS, float fTBaselineToS, const QMap< QString, double > &mapReject, const QStringList &lExcludeChs=QStringList(), const Eigen::RowVectorXi &vecPicks=Eigen::RowVectorXi())
static void checkChThreshold(ArtifactRejectionData &inputData)
void pick_channels(const Eigen::RowVectorXi &sel)
static FIFFLIB::FiffEvokedSet averageCategories(const FIFFLIB::FiffRawData &raw, const Eigen::MatrixXi &events, const QList< int > &eventCodes, const QStringList &comments, float tmin, float tmax, const QMap< QString, double > &mapReject=QMap< QString, double >(), const QPair< float, float > &baseline=QPair< float, float >(0.0f, 0.0f), bool proj=false)
static bool checkForArtifact(const Eigen::MatrixXd &data, const FIFFLIB::FiffInfo &pFiffInfo, const QMap< QString, double > &mapReject, const QStringList &lExcludeChs=QStringList())