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