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