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) = ((float)(from-event_samp+i)) / raw.info.sfreq;
144 }
145
146 epoch->event = event;
147 epoch->tmin = tmin;
148 epoch->tmax = tmax;
149
150 epoch->bReject = checkForArtifact(epoch->epoch,
151 raw.info,
152 mapReject,
153 lExcludeChs);
154
155 if (epoch->bReject) {
156 dropCount++;
157 }
158
159 //Check if data block has the same size as the previous one
160 if(!data.isEmpty()) {
161 if(epoch->epoch.size() == data.last()->epoch.size()) {
162 data.append(MNEEpochData::SPtr(epoch.release()));//List takes ownwership of the pointer - no delete need
163 }
164 } else {
165 data.append(MNEEpochData::SPtr(epoch.release()));//List takes ownwership of the pointer - no delete need
166 }
167 } else {
168 qWarning("[MNEEpochDataList::readEpochs] Can't read the event data segments.");
169 }
170 }
171
172 qInfo().noquote() << "[MNEEpochDataList::readEpochs] Read a total of"<< data.size() <<"epochs of type" << event << "and marked"<< dropCount <<"for rejection.";
173
174 return data;
175}
176
177//=============================================================================================================
178
180 fiff_int_t first,
181 fiff_int_t last,
182 VectorXi sel,
183 bool proj)
184{
185 FiffEvoked p_evoked;
186
187 qInfo("[MNEEpochDataList::average] Calculate evoked. ");
188
189 MatrixXd matAverage;
190
191 if(this->size() > 0) {
192 matAverage = MatrixXd::Zero(this->at(0)->epoch.rows(), this->at(0)->epoch.cols());
193 } else {
195 return p_evoked;
196 }
197
198 if(sel.size() > 0) {
199 p_evoked.nave = sel.size();
200
201 for(qint32 i = 0; i < sel.size(); ++i) {
202 matAverage.array() += this->at(sel(i))->epoch.array();
203 }
204 } else {
205 p_evoked.nave = this->size();
206
207 for(qint32 i = 0; i < this->size(); ++i) {
208 matAverage.array() += this->at(i)->epoch.array();
209 }
210 }
211 matAverage.array() /= p_evoked.nave;
212
213 qInfo("[MNEEpochDataList::average] %d averages used [done]", p_evoked.nave);
214
215 p_evoked.setInfo(info, proj);
216
218
219 p_evoked.first = first;
220 p_evoked.last = last;
221
222 p_evoked.times = RowVectorXf::LinSpaced(this->first()->epoch.cols(), this->first()->tmin, this->first()->tmax);
223
224 p_evoked.times[static_cast<int>(this->first()->tmin * -1 * info.sfreq)] = 0;
225
226 p_evoked.comment = QString::number(this->at(0)->event);
227
228 if(p_evoked.proj.rows() > 0) {
229 matAverage = p_evoked.proj * matAverage;
230 qInfo("[MNEEpochDataList::average] SSP projectors applied to the evoked data");
231 }
232
233 p_evoked.data = matAverage;
234
235 return p_evoked;
236}
237
238//=============================================================================================================
239
240void MNEEpochDataList::applyBaselineCorrection(const QPair<float, float> &baseline)
241{
242 // Run baseline correction
243 QMutableListIterator<MNEEpochData::SPtr> i(*this);
244 while (i.hasNext()) {
245 i.next()->applyBaselineCorrection(baseline);
246 }
247}
248
249//=============================================================================================================
250
252{
253 QMutableListIterator<MNEEpochData::SPtr> i(*this);
254 while (i.hasNext()) {
255 if (i.next()->bReject) {
256 i.remove();
257 }
258 }
259}
260
261//=============================================================================================================
262
263void MNEEpochDataList::pick_channels(const RowVectorXi& sel)
264{
265 QMutableListIterator<MNEEpochData::SPtr> i(*this);
266 while (i.hasNext()) {
267 i.next()->pick_channels(sel);
268 }
269}
270
271//=============================================================================================================
272
273bool MNEEpochDataList::checkForArtifact(const MatrixXd& data,
274 const FiffInfo& pFiffInfo,
275 const QMap<QString,double>& mapReject,
276 const QStringList& lExcludeChs)
277{
278 //qDebug() << "MNEEpochDataList::checkForArtifact - Doing artifact reduction for" << mapReject;
279
280 bool bReject = false;
281
282 //Prepare concurrent data handling
283 QList<ArtifactRejectionData> lchData;
284 QList<int> lChTypes;
285
286 if(mapReject.contains("grad") ||
287 mapReject.contains("mag") ) {
288 lChTypes << FIFFV_MEG_CH;
289 }
290
291 if(mapReject.contains("eeg")) {
292 lChTypes << FIFFV_EEG_CH;
293 }
294
295 if(mapReject.contains("eog")) {
296 lChTypes << FIFFV_EOG_CH;
297 }
298
299 if(lChTypes.isEmpty()) {
300 return bReject;
301 }
302
303 for(int i = 0; i < pFiffInfo.chs.size(); ++i) {
304 if(lChTypes.contains(pFiffInfo.chs.at(i).kind)
305 && !lExcludeChs.contains(pFiffInfo.chs.at(i).ch_name)
306 && !pFiffInfo.bads.contains(pFiffInfo.chs.at(i).ch_name)
307 && pFiffInfo.chs.at(i).chpos.coil_type != FIFFV_COIL_BABY_REF_MAG
308 && pFiffInfo.chs.at(i).chpos.coil_type != FIFFV_COIL_BABY_REF_MAG2) {
309 ArtifactRejectionData tempData;
310 tempData.data = data.row(i);
311
312 switch (pFiffInfo.chs.at(i).kind) {
313 case FIFFV_MEG_CH:
314 if(pFiffInfo.chs.at(i).unit == FIFF_UNIT_T) {
315 tempData.dThreshold = mapReject["mag"];
316 } else if(pFiffInfo.chs.at(i).unit == FIFF_UNIT_T_M) {
317 tempData.dThreshold = mapReject["grad"];
318 }
319 break;
320
321 case FIFFV_EEG_CH:
322 tempData.dThreshold = mapReject["eeg"];
323 break;
324
325 case FIFFV_EOG_CH:
326 tempData.dThreshold = mapReject["eog"];
327 break;
328 }
329
330 tempData.sChName = pFiffInfo.chs.at(i).ch_name;
331 lchData.append(tempData);
332 }
333 }
334
335 if(lchData.isEmpty()) {
336 qWarning() << "[MNEEpochDataList::checkForArtifact] No channels found to scan for artifacts. Do not reject. Returning.";
337
338 return bReject;
339 }
340
341 //qDebug() << "MNEEpochDataList::checkForArtifact - lchData.size()" << lchData.size();
342
343 //Start the concurrent processing
344 QFuture<void> future = QtConcurrent::map(lchData, checkChThreshold);
345 future.waitForFinished();
346
347 for(int i = 0; i < lchData.size(); ++i) {
348 if(lchData.at(i).bRejected) {
349 bReject = true;
350 qInfo().noquote() << "[MNEEpochDataList::checkForArtifact] Reject trial because of channel"<<lchData.at(i).sChName;
351 break;
352 }
353 }
354
355 return bReject;
356}
357
358//=============================================================================================================
359
361{
362 RowVectorXd temp = inputData.data;
363
364 // Remove offset
365 //temp = temp.array() - temp(0);
366
367 double min = temp.minCoeff();
368 double max = temp.maxCoeff();
369
370 // Peak to Peak
371 double pp = max - min;
372
373 if(std::fabs(pp) > inputData.dThreshold) {
374 inputData.bRejected = true;
375 } else {
376 inputData.bRejected = false;
377 }
378
379// qDebug() << "MNEEpochDataList::checkChThreshold - min" << min;
380// qDebug() << "MNEEpochDataList::checkChThreshold - max" << max;
381// qDebug() << "MNEEpochDataList::checkChThreshold - pp" << pp;
382// qDebug() << "MNEEpochDataList::checkChThreshold - inputData.dThreshold" << inputData.dThreshold;
383
384// //If absolute vaue of min or max if bigger than threshold -> reject
385// if((std::fabs(min) > inputData.dThreshold) || (std::fabs(max) > inputData.dThreshold)) {
386// inputData.bRejected = true;
387// } else {
388// inputData.bRejected = false;
389// }
390}
391
392//=============================================================================================================
393
395 const MatrixXi &events,
396 const QList<int> &eventCodes,
397 const QStringList &comments,
398 float tmin,
399 float tmax,
400 const QMap<QString,double> &mapReject,
401 const QPair<float,float> &baseline,
402 bool proj)
403{
404 FiffEvokedSet evokedSet;
405 evokedSet.info = raw.info;
406
407 float sfreq = raw.info.sfreq;
408 int nchan = raw.info.nchan;
409
410 bool doBaseline = (baseline.first != baseline.second);
411
412 // Process each category (event code)
413 for (int j = 0; j < eventCodes.size(); ++j) {
414 int eventCode = eventCodes[j];
415 QString comment = (j < comments.size()) ? comments[j]
416 : QString("cat_%1").arg(eventCode);
417
418 // Read epochs for this event code using the existing readEpochs
419 MNEEpochDataList epochList = MNEEpochDataList::readEpochs(raw, events,
420 tmin, tmax,
421 eventCode,
422 mapReject);
423
424 // Apply baseline correction
425 if (doBaseline) {
426 epochList.applyBaselineCorrection(baseline);
427 }
428
429 // Drop rejected epochs
430 epochList.dropRejected();
431
432 // Compute the average
433 int minSamp = static_cast<int>(std::round(tmin * sfreq));
434 int maxSamp = static_cast<int>(std::round(tmax * sfreq));
435
436 FiffEvoked evoked = epochList.average(raw.info,
437 minSamp,
438 maxSamp,
439 defaultVectorXi,
440 proj);
441
442 evoked.comment = comment;
443 evokedSet.evoked.append(evoked);
444 }
445
446 return evokedSet;
447}
448
449//=============================================================================================================
450
452 const MatrixXi& matEvents,
453 float fTMinS,
454 float fTMaxS,
455 qint32 eventType,
456 bool bApplyBaseline,
457 float fTBaselineFromS,
458 float fTBaselineToS,
459 const QMap<QString,double>& mapReject,
460 const QStringList& lExcludeChs,
461 const RowVectorXi& picks)
462{
463 MNEEpochDataList lstEpochDataList = MNEEpochDataList::readEpochs(raw,
464 matEvents,
465 fTMinS,
466 fTMaxS,
467 eventType,
468 mapReject,
469 lExcludeChs,
470 picks);
471
472 if(bApplyBaseline) {
473 QPair<float, float> baselinePair(fTBaselineFromS, fTBaselineToS);
474 lstEpochDataList.applyBaselineCorrection(baselinePair);
475 }
476
477 if(!mapReject.isEmpty()) {
478 lstEpochDataList.dropRejected();
479 }
480
481 return lstEpochDataList.average(raw.info,
482 0,
483 lstEpochDataList.first()->epoch.cols());
484}
#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
FiffEvokedSet class declaration.
#define FIFFV_ASPECT_AVERAGE
Definition fiff_file.h:438
#define FIFFV_ASPECT_STD_ERR
Definition fiff_file.h:439
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
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)
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())