39#include <Eigen/SparseCore>
64 qRegisterMetaType<FIFFLIB::FiffEvokedSet>(
"FIFFLIB::FiffEvokedSet");
65 qRegisterMetaType<FIFFLIB::FiffEvokedSet::SPtr>(
"FIFFLIB::FiffEvokedSet::SPtr");
72 qRegisterMetaType<FIFFLIB::FiffEvokedSet>(
"FIFFLIB::FiffEvokedSet");
73 qRegisterMetaType<FIFFLIB::FiffEvokedSet::SPtr>(
"FIFFLIB::FiffEvokedSet::SPtr");
77 throw std::runtime_error(
"Fiff evoked data set not found");
84: info(p_FiffEvokedSet.info)
85, evoked(p_FiffEvokedSet.evoked)
106 const QStringList& exclude)
const
113 RowVectorXi sel = FiffInfo::pick_channels(this->
info.ch_names, include, exclude);
114 if (sel.cols() > 0) {
115 res.
info = this->
info.pick_info(sel);
120 QList<FiffEvoked>::ConstIterator ev;
122 res.
evoked.push_back(ev->pick_channels(include, exclude));
132 qint32 now = p_FiffEvokedSet.
info.get_current_comp();
137 qInfo(
"Data is already compensated as desired.\n");
144 for(qsizetype i=0; i < p_FiffEvokedSet.
evoked.size(); ++i)
150 p_FiffEvokedSet.
info.set_current_comp(to);
159 if(!p_FiffEvokedSet.
evoked.size()) {
160 qWarning(
"No evoked response data sets in %s\n",p_FiffEvokedSet.
info.filename.toUtf8().constData());
164 qInfo(
"\nFound %lld evoked response data sets in %s :\n",p_FiffEvokedSet.
evoked.size(),p_FiffEvokedSet.
info.filename.toUtf8().constData());
166 for(qint32 i = 0; i < p_FiffEvokedSet.
evoked.size(); ++i) {
167 qInfo(
"%s (%s)\n",p_FiffEvokedSet.
evoked.at(i).comment.toUtf8().constData(),p_FiffEvokedSet.
evoked.at(i).aspectKindToString().toUtf8().constBegin());
179 p_FiffEvokedSet.
clear();
184 FiffStream::SPtr t_pStream(
new FiffStream(&p_IODevice));
185 QString t_sFileName = t_pStream->streamName();
187 qInfo(
"Exploring %s ...\n",t_sFileName.toUtf8().constData());
189 if(!t_pStream->open())
194 FiffDirNode::SPtr meas;
195 if(!t_pStream->read_meas_info(t_pStream->dirtree(), p_FiffEvokedSet.
info, meas))
197 p_FiffEvokedSet.
info.filename = t_sFileName;
202 if (processed.size() == 0)
204 qWarning(
"Could not find processed data");
208 QList<FiffDirNode::SPtr> evoked_node = meas->dir_tree_find(
FIFFB_EVOKED);
209 if (evoked_node.size() == 0)
211 qWarning(
"Could not find evoked data");
215 QStringList comments;
216 QList<fiff_int_t> aspect_kinds;
218 if(!t_pStream->get_evoked_entries(evoked_node, comments, aspect_kinds, t))
219 t = QString(
"None found, must use integer");
220 qInfo(
"\tFound %lld datasets\n", evoked_node.size());
222 for(qint32 i = 0; i < comments.size(); ++i)
224 QFile t_file(p_FiffEvokedSet.
info.filename);
225 qInfo(
">> Processing %s <<\n", comments[i].toUtf8().constData());
226 FiffEvoked t_FiffEvoked;
227 if(FiffEvoked::read(t_file, t_FiffEvoked, i, baseline, proj))
228 p_FiffEvokedSet.
evoked.push_back(t_FiffEvoked);
238 if (fileName.isEmpty()) {
239 qWarning() <<
"[FiffEvokedSet::save] Output file not specified.";
243 QFile file(fileName);
244 FiffStream::SPtr pStream = FiffStream::start_file(file);
246 qWarning() <<
"[FiffEvokedSet::save] Cannot open" << fileName;
250 pStream->write_evoked_set(*
this);
253 qInfo() <<
"[FiffEvokedSet::save] Saved" <<
evoked.size()
254 <<
"average(s) to" << fileName;
264 if (evokedSets.isEmpty()) {
265 qWarning() <<
"[FiffEvokedSet::computeGrandAverage] No evoked sets provided.";
269 grandAvg = evokedSets[0];
271 for (
int f = 1; f < evokedSets.size(); ++f) {
273 int nCat = qMin(grandAvg.
evoked.size(), eset.
evoked.size());
274 for (
int j = 0; j < nCat; ++j) {
275 if (grandAvg.
evoked[j].data.cols() == eset.
evoked[j].data.cols() &&
276 grandAvg.
evoked[j].data.rows() == eset.
evoked[j].data.rows()) {
283 for (
int j = 0; j < grandAvg.
evoked.size(); ++j) {
284 grandAvg.
evoked[j].data /=
static_cast<double>(evokedSets.size());
294 if (bminSamp < 0) bminSamp = 0;
295 if (bmaxSamp >= epoch.cols()) bmaxSamp =
static_cast<int>(epoch.cols()) - 1;
296 if (bminSamp >= bmaxSamp)
return;
298 int nBase = bmaxSamp - bminSamp + 1;
299 for (
int c = 0; c < epoch.rows(); ++c) {
300 double baseVal = epoch.row(c).segment(bminSamp, nBase).mean();
301 epoch.row(c).array() -= baseVal;
309 const MatrixXi &events,
315 float sfreq = raw.
info.sfreq;
316 int nchan = raw.
info.nchan;
319 log += QString(
"Averaging: %1\n").arg(desc.
comment);
322 for (
int j = 0; j < desc.
categories.size(); ++j) {
323 const AverageCategory &cat = desc.
categories[j];
326 int minSamp =
static_cast<int>(std::round(cat.
tmin * sfreq));
327 int maxSamp =
static_cast<int>(std::round(cat.
tmax * sfreq));
328 int ns = maxSamp - minSamp + 1;
329 int delaySamp =
static_cast<int>(std::round(cat.
delay * sfreq));
332 int bminSamp = 0, bmaxSamp = 0;
334 bminSamp =
static_cast<int>(std::round(cat.
bmin * sfreq)) - minSamp;
335 bmaxSamp =
static_cast<int>(std::round(cat.
bmax * sfreq)) - minSamp;
339 MatrixXd sumData = MatrixXd::Zero(nchan, ns);
340 MatrixXd sumSqData = MatrixXd::Zero(nchan, ns);
343 log += QString(
"\n Category: %1\n").arg(cat.
comment);
344 log += QString(
" t = %.1f ... %.1f ms\n").arg(1000.0 * cat.
tmin).arg(1000.0 * cat.
tmax);
347 for (
int k = 0; k < events.rows(); ++k) {
348 if (!FiffEvents::matchEvent(cat, events, k))
351 int evSample = events(k, 0);
352 int epochStart = evSample + delaySamp + minSamp;
353 int epochEnd = evSample + delaySamp + maxSamp;
356 if (epochStart < raw.first_samp || epochEnd > raw.
last_samp)
363 log += QString(
" Error reading epoch at sample %1\n").arg(evSample);
370 log += QString(
" %1 %2 %3 %4 [%5] %6 [omit]\n")
372 .arg(
static_cast<float>(evSample) / sfreq, -10,
'f', 3)
373 .arg(events(k, 1), 3)
374 .arg(events(k, 2), 3)
387 epochData = epochData.cwiseAbs();
391 sumData += epochData;
393 sumSqData += epochData.cwiseProduct(epochData);
397 log += QString(
" %1 %2 %3 %4 [%5]\n")
399 .arg(
static_cast<float>(evSample) / sfreq, -10,
'f', 3)
400 .arg(events(k, 1), 3)
401 .arg(events(k, 2), 3)
413 RowVectorXf times(ns);
414 for (
int s = 0; s < ns; ++s)
415 times(s) =
static_cast<float>(minSamp + s) / sfreq;
419 evoked.data = sumData /
static_cast<double>(nave);
421 evoked.data = MatrixXd::Zero(nchan, ns);
427 log += QString(
" nave = %1\n").arg(nave);
437 const QStringList &bads,
438 const RejectionParams &rej,
441 for (
int c = 0; c < epoch.rows(); ++c) {
443 if (bads.contains(
info.ch_names[c]))
446 double minVal = epoch.row(c).minCoeff();
447 double maxVal = epoch.row(c).maxCoeff();
448 double pp = maxVal - minVal;
450 int chKind =
info.chs[c].kind;
451 int chUnit =
info.chs[c].unit;
456 if (rej.megMagReject > 0 && pp > rej.megMagReject) {
457 reason = QString(
"%1 : %.1f fT > %.1f fT")
458 .arg(
info.ch_names[c])
459 .arg(pp * 1e15).arg(rej.megMagReject * 1e15);
462 if (rej.megMagFlat > 0 && pp < rej.megMagFlat) {
463 reason = QString(
"%1 : %.1f fT < %.1f fT (flat)")
464 .arg(
info.ch_names[c])
465 .arg(pp * 1e15).arg(rej.megMagFlat * 1e15);
470 if (rej.megGradReject > 0 && pp > rej.megGradReject) {
471 reason = QString(
"%1 : %.1f fT/cm > %.1f fT/cm")
472 .arg(
info.ch_names[c])
473 .arg(pp * 1e13).arg(rej.megGradReject * 1e13);
476 if (rej.megGradFlat > 0 && pp < rej.megGradFlat) {
477 reason = QString(
"%1 : %.1f fT/cm < %.1f fT/cm (flat)")
478 .arg(
info.ch_names[c])
479 .arg(pp * 1e13).arg(rej.megGradFlat * 1e13);
484 if (rej.eegReject > 0 && pp > rej.eegReject) {
485 reason = QString(
"%1 : %.1f uV > %.1f uV")
486 .arg(
info.ch_names[c])
487 .arg(pp * 1e6).arg(rej.eegReject * 1e6);
490 if (rej.eegFlat > 0 && pp < rej.eegFlat) {
491 reason = QString(
"%1 : %.1f uV < %.1f uV (flat)")
492 .arg(
info.ch_names[c])
493 .arg(pp * 1e6).arg(rej.eegFlat * 1e6);
497 if (rej.eogReject > 0 && pp > rej.eogReject) {
498 reason = QString(
"%1 : %.1f uV > %.1f uV (EOG)")
499 .arg(
info.ch_names[c])
500 .arg(pp * 1e6).arg(rej.eogReject * 1e6);
503 if (rej.eogFlat > 0 && pp < rej.eogFlat) {
504 reason = QString(
"%1 : EOG flat").arg(
info.ch_names[c]);
508 if (rej.ecgReject > 0 && pp > rej.ecgReject) {
509 reason = QString(
"%1 : %.2f mV > %.2f mV (ECG)")
510 .arg(
info.ch_names[c])
511 .arg(pp * 1e3).arg(rej.ecgReject * 1e3);
514 if (rej.ecgFlat > 0 && pp < rej.ecgFlat) {
515 reason = QString(
"%1 : ECG flat").arg(
info.ch_names[c]);
FIFF continuous raw recording: FiffInfo plus a directory of FIFF_DATA_BUFFER tags for random-access s...
Symbolic FIFF tag, block, value, unit and channel-type constants shared across FIFFLIB.
Stim-channel event list (sample, previous value, new value triples) with FIFF read/write helpers.
Recursive node of the parsed FIFF block tree (FIFFB_* hierarchy with directory entries and children).
Set of averaged evoked responses sharing a FiffInfo, plus the ave-style category / rejection descript...
FIFF tag-kind, block-kind and type-code numerical definitions, authoritative for FIFFLIB.
#define FIFFB_PROCESSED_DATA
FIFF tag: the 16-byte tag header (kind, type, size, next) plus its decoded payload.
FIFF binary tag-stream layer: wraps a QIODevice to read and write FIFF tags, directories,...
FIFF file I/O, in-memory data structures and high-level readers/writers.
Top-level MNE-C ave-description record: comment, category list, shared rejection limits and output fi...
QList< AverageCategory > categories
Set of FiffEvoked instances sharing one FiffInfo, plus channel-picking and compensation helpers.
bool compensate_to(FiffEvokedSet &p_FiffEvokedSet, fiff_int_t to) const
bool find_evoked(const FiffEvokedSet &p_FiffEvokedSet) const
static void subtractBaseline(Eigen::MatrixXd &epoch, int bminSamp, int bmaxSamp)
Subtract baseline from each channel of an epoch.
static bool checkArtifacts(const Eigen::MatrixXd &epoch, const FiffInfo &info, const QStringList &bads, const RejectionParams &rej, QString &reason)
static FiffEvokedSet computeAverages(const FiffRawData &raw, const AverageDescription &desc, const Eigen::MatrixXi &events, QString &log)
FiffEvokedSet pick_channels(const QStringList &include=defaultQStringList, const QStringList &exclude=defaultQStringList) const
static FiffEvokedSet computeGrandAverage(const QList< FiffEvokedSet > &evokedSets)
static bool read(QIODevice &p_IODevice, FiffEvokedSet &p_FiffEvokedSet, QPair< float, float > baseline=defaultFloatPair, bool proj=true)
QList< FiffEvoked > evoked
bool save(const QString &fileName) const
Full FIFF measurement info: per-channel descriptors, sampling and filter setup, projectors,...
bool read_raw_segment(Eigen::MatrixXd &data, Eigen::MatrixXd ×, fiff_int_t from=-1, fiff_int_t to=-1, const Eigen::RowVectorXi &sel=defaultRowVectorXi, bool do_debug=false) const