58 QPair<float, float> t_baseline,
65 throw std::runtime_error(
"Fiff evoked data not found");
101 times = RowVectorXf();
109 const QStringList& exclude)
const
111 if (include.size() == 0 && exclude.size() == 0)
115 if (sel.cols() == 0) {
116 qWarning(
"Warning : No channels match the selection.\n");
128 MatrixXd selBlock = MatrixXd::Zero(sel.cols(), res.
data.cols());
129 for (qint32 l = 0; l < sel.cols(); ++l) {
130 if (sel(0, l) < res.
data.rows()) {
131 selBlock.block(l, 0, 1, selBlock.cols()) = res.
data.block(sel(0, l), 0, 1, selBlock.cols());
133 qWarning(
"FiffEvoked::pick_channels - Warning : Selected channel index out of bound.\n");
136 res.
data.resize(sel.cols(), res.
data.cols());
147 QPair<float, float> t_baseline,
151 p_FiffEvoked.
clear();
157 QString t_sFileName = t_pStream->streamName();
159 qInfo(
"Reading %s ...\n", t_sFileName.toUtf8().constData());
161 if (!t_pStream->open())
168 if (!t_pStream->read_meas_info(t_pStream->dirtree(),
info, meas))
170 info.filename = t_sFileName;
175 if (processed.size() == 0) {
176 qWarning(
"Could not find processed data");
180 QList<FiffDirNode::SPtr> evoked_node = meas->dir_tree_find(
FIFFB_EVOKED);
181 if (evoked_node.size() == 0) {
182 qWarning(
"Could not find evoked data");
187 if (!setno.isValid()) {
188 if (evoked_node.size() > 1) {
189 QStringList comments;
190 QList<fiff_int_t> aspect_kinds;
192 if (!t_pStream->get_evoked_entries(evoked_node, comments, aspect_kinds, t))
193 t = QString(
"None found, must use integer");
194 qWarning(
"%lld datasets present, setno parameter must be set. Candidate setno names:\n%s",
static_cast<long long>(evoked_node.size()), t.toUtf8().constData());
200 bool t_bIsInteger =
true;
201 setno.toInt(&t_bIsInteger);
204 qWarning(
"kindStat must be \"FIFFV_ASPECT_AVERAGE\" or \"FIFFV_ASPECT_STD_ERR\"");
208 QStringList comments;
209 QList<fiff_int_t> aspect_kinds;
211 t_pStream->get_evoked_entries(evoked_node, comments, aspect_kinds, t);
214 for (qint32 i = 0; i < comments.size(); ++i) {
215 if (comments[i].compare(setno.toString()) == 0 && p_aspect_kind == aspect_kinds[i]) {
222 qWarning() <<
"setno " << setno <<
" (" << p_aspect_kind <<
") not found, out of found datasets:\n " << t;
228 if (setno.toInt() >= evoked_node.size() || setno.toInt() < 0) {
229 qWarning(
"Data set selector out of range");
238 QList<FiffDirNode::SPtr> aspects = my_evoked->dir_tree_find(
FIFFB_ASPECT);
240 if (aspects.size() > 1)
241 qInfo(
"\tMultiple (%lld) aspects found. Taking first one.\n",
static_cast<long long>(aspects.size()));
250 QList<FiffChInfo> chs;
255 for (k = 0; k < my_evoked->nent(); ++k) {
256 kind = my_evoked->dir[k]->kind;
257 pos = my_evoked->dir[k]->pos;
260 t_pStream->read_tag(t_pTag, pos);
264 t_pStream->read_tag(t_pTag, pos);
265 first = *t_pTag->toInt();
268 t_pStream->read_tag(t_pTag, pos);
269 last = *t_pTag->toInt();
272 t_pStream->read_tag(t_pTag, pos);
273 nchan = *t_pTag->toInt();
276 t_pStream->read_tag(t_pTag, pos);
277 sfreq = *t_pTag->toFloat();
280 t_pStream->read_tag(t_pTag, pos);
281 chs.append(t_pTag->toChInfo());
286 comment = QString(
"No comment");
292 if (chs.size() == 0) {
293 qWarning(
"Local channel information was not found when it was expected.");
296 if (chs.size() != nchan) {
297 qWarning(
"Number of channels and number of channel definitions are different.");
302 qInfo(
"\tFound channel information in evoked data. nchan = %d\n", nchan);
307 qInfo(
"\tFound the data of interest:\n");
308 qInfo(
"\t\tt = %10.2f ... %10.2f ms (%s)\n", 1000 *
static_cast<float>(
first) /
info.sfreq, 1000 *
static_cast<float>(
last) /
info.sfreq,
comment.toUtf8().constData());
309 if (
info.comps.size() > 0)
310 qInfo(
"\t\t%lld CTF compensation matrices available\n",
static_cast<long long>(
info.comps.size()));
317 QList<FiffTag> epoch;
318 for (k = 0; k < my_aspect->nent(); ++k) {
319 kind = my_aspect->dir[k]->kind;
320 pos = my_aspect->dir[k]->pos;
324 t_pStream->read_tag(t_pTag, pos);
328 t_pStream->read_tag(t_pTag, pos);
332 t_pStream->read_tag(t_pTag, pos);
333 nave = *t_pTag->toInt();
336 t_pStream->read_tag(t_pTag, pos);
337 epoch.append(
FiffTag(*t_pTag));
345 qint32 nepoch = epoch.size();
346 if (nepoch != 1 && nepoch !=
info.nchan) {
347 qWarning(
"Number of epoch tags is unreasonable (nepoch = %d nchan = %d)", nepoch,
info.nchan);
355 all_data = epoch[0].toFloatMatrix().cast<
double>();
356 all_data.transposeInPlace();
360 if (all_data.cols() == 1 &&
info.nchan == 1)
361 all_data.transposeInPlace();
366 all_data.resize(nepoch, epoch[0].size() /
static_cast<int>(
sizeof(
float)));
367 for (k = 0; k < nepoch; ++k) {
368 const int n = epoch[k].size() /
static_cast<int>(
sizeof(float));
369 if (n != all_data.cols()) {
370 qWarning(
"Old style epochs have different lengths (%d and %d)", n,
static_cast<int>(all_data.cols()));
373 all_data.row(k) = Map<const RowVectorXf>(epoch[k].toFloat(), n).cast<
double>();
376 if (all_data.cols() != nsamp) {
377 qWarning(
"Incorrect number of samples (%d instead of %d)",
static_cast<int>(all_data.cols()), nsamp);
384 qInfo(
"\n\tPreprocessing...\n");
385 qInfo(
"\t%d channels remain after picking\n",
info.nchan);
387 using T = Eigen::Triplet<double>;
388 std::vector<T> tripletList;
389 tripletList.reserve(
info.nchan);
390 for (k = 0; k <
info.nchan; ++k)
391 tripletList.push_back(T(k, k,
info.chs[k].cal));
392 SparseMatrix<double> cals(
info.nchan,
info.nchan);
393 cals.setFromTriplets(tripletList.begin(), tripletList.end());
395 all_data = cals * all_data;
398 for (k = 0; k <
times.size(); ++k)
404 if (
info.projs.size() == 0 || !
proj) {
405 qInfo(
"\tNo projector specified for these data.\n");
406 p_FiffEvoked.
proj = MatrixXd();
410 qint32 nproj =
info.make_projector(projection);
412 qWarning(
"\tThe projection vectors do not apply to these channels\n");
413 p_FiffEvoked.
proj = MatrixXd();
415 qInfo(
"\tCreated an SSP operator (subspace dimension = %d)\n", nproj);
416 p_FiffEvoked.
proj = projection;
423 if (p_FiffEvoked.
proj.rows() > 0) {
424 all_data = p_FiffEvoked.
proj * all_data;
425 qInfo(
"\tSSP projectors applied to the evoked data\n");
436 p_FiffEvoked.
data = all_data;
441 if (t_baseline.first != t_baseline.second) {
445 qInfo(
"\tNo baseline correction applied\n");
460 if (
info.projs.size() == 0 || !applyProj) {
461 qInfo(
"\tNo projector specified for these data.\n");
462 this->
proj = MatrixXd();
466 qint32 nproj =
info.make_projector(projection);
468 qWarning(
"\tThe projection vectors do not apply to these channels\n");
469 this->
proj = MatrixXd();
471 qInfo(
"\tCreated an SSP operator (subspace dimension = %d)\n", nproj);
472 this->
proj = projection;
486 data = MatrixXd::Zero(newData.rows(), newData.cols());
489 if (
data.cols() == newData.cols() &&
data.rows() == newData.rows()) {
511 VectorXd w(evokeds.size());
512 double naveSum = 0.0;
516 for (
int i = 0; i < evokeds.size(); ++i) {
518 : 1.0 /
static_cast<double>(evokeds.size());
527 if (evokeds.isEmpty() || weights.size() != evokeds.size()) {
528 qWarning() <<
"[FiffEvoked::combine] weights must be the same size as the evoked list.";
533 if (e.info.ch_names != ref.
info.
ch_names || e.times.size() != ref.
times.size() || (e.times - ref.
times).cwiseAbs().maxCoeff() > 1e-6f) {
534 qWarning() <<
"[FiffEvoked::combine] All evoked data must have the same channels and times.";
540 res.data = MatrixXd::Zero(ref.
data.rows(), ref.
data.cols());
541 double invNave = 0.0;
543 for (
int i = 0; i < evokeds.size(); ++i) {
545 const double w = weights(i);
546 res.data += w * e.
data;
547 invNave += w * w / e.
nave;
548 for (
const QString& bad : e.
info.
bads) {
549 if (!res.info.bads.contains(bad)) {
550 res.info.bads.append(bad);
554 const QString sign = w >= 0.0 ? QString() : QString(
"-");
555 const QString weight = qAbs(qAbs(w) - 1.0) <= 1e-8 + 1e-5 ? QString()
556 : QString::number(qAbs(w),
'f', 3);
557 const QString multiplier = weight.isEmpty() ? QString() : QString::fromUtf8(
" × ");
558 const QString thisComment = e.
comment.contains(
" + ") ? QString(
"(%1)").arg(e.
comment)
559 : e.
comment.isEmpty() ? QString(
"unknown")
562 comment += sign + weight + multiplier + thisComment;
564 comment += QString(
" %1 ").arg(sign.isEmpty() ? QString(
"+") : sign) + weight + multiplier + thisComment;
567 res.comment =
comment.replace(
" - - ",
" + ");
568 res.nave =
static_cast<fiff_int_t>(std::lround(1.0 / invNave));
577 if (p_baseline.first == p_baseline.second) {
578 qInfo(
"\tNo baseline correction applied\n");
583 qInfo(
"Applying baseline correction ... (mode: mean)\n");
FIFF tag: the 16-byte tag header (kind, type, size, next) plus its decoded payload.
Single averaged evoked response: time axis, samples, baseline, channel info and processing history.
FIFF binary tag-stream layer: wraps a QIODevice to read and write FIFF tags, directories,...
constexpr auto FIFFB_PROCESSED_DATA
constexpr auto FIFF_EPOCH
constexpr auto FIFF_COMMENT
constexpr auto FIFFB_ASPECT
constexpr auto FIFF_LAST_SAMPLE
constexpr auto FIFF_SFREQ
constexpr auto FIFF_NCHAN
constexpr auto FIFF_FIRST_SAMPLE
constexpr auto FIFFV_ASPECT_AVERAGE
constexpr auto FIFFB_EVOKED
constexpr auto FIFF_ASPECT_KIND
constexpr auto FIFFV_ASPECT_STD_ERR
constexpr auto FIFF_CH_INFO
Recursive node of the parsed FIFF block tree (FIFFB_* hierarchy with directory entries and children).
General numerical helpers: GCD, log2, histogram binning, baseline rescaling, sparsity tests.
FIFF file I/O, in-memory data structures and high-level readers/writers.
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
QSharedPointer< FiffDirNode > SPtr
Single averaged evoked response: time axis, data, baseline, channel info and averaging metadata.
static FiffEvoked combine(const QList< FiffEvoked > &evokeds, const Eigen::VectorXd &weights)
FiffEvoked pick_channels(const QStringList &include=defaultQStringList, const QStringList &exclude=defaultQStringList) const
QPair< float, float > baseline
void setInfo(const FiffInfo &p_info, bool applyProj=true)
FiffEvoked & operator+=(const Eigen::MatrixXd &newData)
static bool read(QIODevice &p_IODevice, FiffEvoked &p_FiffEvoked, QVariant setno=0, QPair< float, float > t_baseline=defaultFloatPair, bool proj=true, fiff_int_t p_aspect_kind=FIFFV_ASPECT_AVERAGE)
void applyBaselineCorrection(QPair< float, float > &p_baseline)
Full FIFF measurement info: per-channel descriptors, sampling and filter setup, projectors,...
FiffInfo pick_info(const Eigen::RowVectorXi &sel=defaultVectorXi) const
static Eigen::RowVectorXi pick_channels(const QStringList &ch_names, const QStringList &include=defaultQStringList, const QStringList &exclude=defaultQStringList)
static void activate_projs(QList< FiffProj > &p_qListFiffProj)
FIFF tag-stream reader/writer: wraps a QIODevice and exposes typed read_* / write_* methods for every...
QSharedPointer< FiffStream > SPtr
FIFF tag: 16-byte header (kind, type, size, next) plus payload, with typed decoders for every FIFFT_*...
std::unique_ptr< FiffTag > UPtr
static Eigen::MatrixXd rescale(const Eigen::MatrixXd &data, const Eigen::RowVectorXf ×, const QPair< float, float > &baseline, QString mode)