68 qRegisterMetaType<QSharedPointer<FIFFLIB::FiffCov>>(
"QSharedPointer<FIFFLIB::FiffCov>");
69 qRegisterMetaType<FIFFLIB::FiffCov>(
"FIFFLIB::FiffCov");
80 if (!
read(p_IODevice, *
this))
81 throw std::runtime_error(
"Fiff covariance not found");
83 qRegisterMetaType<QSharedPointer<FIFFLIB::FiffCov>>(
"QSharedPointer<FIFFLIB::FiffCov>");
84 qRegisterMetaType<FIFFLIB::FiffCov>(
"FIFFLIB::FiffCov");
92 if (!t_pStream->open()) {
108: QSharedData(p_FiffCov)
120 qRegisterMetaType<QSharedPointer<FIFFLIB::FiffCov>>(
"QSharedPointer<FIFFLIB::FiffCov>");
121 qRegisterMetaType<FIFFLIB::FiffCov>(
"FIFFLIB::FiffCov");
155 res.
dim = sel.size();
157 for (qint32 k = 0; k < sel.size(); ++k)
158 res.
names << this->names[sel(k)];
161 for (qint32 i = 0; i < res.
dim; ++i)
162 for (qint32 j = 0; j < res.
dim; ++j)
163 res.
data(i, j) = this->
data(sel(i), sel(j));
166 for (qint32 k = 0; k < this->
bads.size(); ++k)
167 if (res.
names.contains(this->bads[k]))
180 VectorXi C_ch_idx = VectorXi::Zero(p_NoiseCov.
names.size());
182 for (qint32 i = 0; i < p_ChNames.size(); ++i) {
183 qint32 idx = p_NoiseCov.
names.indexOf(p_ChNames[i]);
185 C_ch_idx[count] = idx;
189 C_ch_idx.conservativeResize(count);
191 MatrixXd C(count, count);
193 if (!p_NoiseCov.
diag)
194 for (qint32 i = 0; i < count; ++i)
195 for (qint32 j = 0; j < count; ++j)
196 C(i, j) = p_NoiseCov.
data(C_ch_idx(i), C_ch_idx(j));
198 qWarning(
"Warning in FiffCov::prepare_noise_cov: This has to be debugged - not done before!");
199 C = MatrixXd::Zero(count, count);
200 for (qint32 i = 0; i < count; ++i)
201 C.diagonal()[i] = p_NoiseCov.
data(C_ch_idx(i), 0);
208 if (ncomp > 0 && proj.rows() == count) {
209 qInfo(
"Created an SSP operator (subspace dimension = %d)\n", ncomp);
210 C = proj * (C * proj.transpose());
212 qWarning(
"Warning in FiffCov::prepare_noise_cov: No projections applied since no projectors specified or projector dimensions do not match!");
215 RowVectorXi pick_meg = p_Info.
pick_types(
true,
false,
false, defaultQStringList, p_Info.
bads);
216 RowVectorXi pick_eeg = p_Info.
pick_types(
false,
true,
false, defaultQStringList, p_Info.
bads);
218 QStringList meg_names, eeg_names;
220 for (qint32 i = 0; i < pick_meg.size(); ++i)
221 meg_names << p_Info.
chs[pick_meg[i]].ch_name;
222 VectorXi C_meg_idx = VectorXi::Zero(p_NoiseCov.
names.size());
224 for (qint32 k = 0; k < C.rows(); ++k) {
225 if (meg_names.indexOf(p_ChNames[k]) > -1) {
226 C_meg_idx[count] = k;
231 C_meg_idx.conservativeResize(count);
233 C_meg_idx = VectorXi();
236 for (qint32 i = 0; i < pick_eeg.size(); ++i)
237 eeg_names << p_Info.
chs[pick_eeg(0, i)].ch_name;
238 VectorXi C_eeg_idx = VectorXi::Zero(p_NoiseCov.
names.size());
240 for (qint32 k = 0; k < C.rows(); ++k) {
241 if (eeg_names.indexOf(p_ChNames[k]) > -1) {
242 C_eeg_idx[count] = k;
248 C_eeg_idx.conservativeResize(count);
250 C_eeg_idx = VectorXi();
252 bool has_meg = C_meg_idx.size() > 0;
253 bool has_eeg = C_eeg_idx.size() > 0;
255 MatrixXd C_meg, C_eeg;
256 VectorXd C_meg_eig, C_eeg_eig;
257 MatrixXd C_meg_eigvec, C_eeg_eigvec;
259 count = C_meg_idx.rows();
260 C_meg = MatrixXd(count, count);
261 for (qint32 i = 0; i < count; ++i)
262 for (qint32 j = 0; j < count; ++j)
263 C_meg(i, j) = C(C_meg_idx(i), C_meg_idx(j));
268 count = C_eeg_idx.rows();
269 C_eeg = MatrixXd(count, count);
270 for (qint32 i = 0; i < count; ++i)
271 for (qint32 j = 0; j < count; ++j)
272 C_eeg(i, j) = C(C_eeg_idx(i), C_eeg_idx(j));
276 qint32 n_chan = p_ChNames.size();
277 p_NoiseCov.
eigvec = MatrixXd::Zero(n_chan, n_chan);
278 p_NoiseCov.
eig = VectorXd::Zero(n_chan);
281 for (qint32 i = 0; i < C_meg_idx.rows(); ++i)
282 for (qint32 j = 0; j < C_meg_idx.rows(); ++j)
283 p_NoiseCov.
eigvec(C_meg_idx[i], C_meg_idx[j]) = C_meg_eigvec(i, j);
284 for (qint32 i = 0; i < C_meg_idx.rows(); ++i)
285 p_NoiseCov.
eig(C_meg_idx[i]) = C_meg_eig[i];
288 for (qint32 i = 0; i < C_eeg_idx.rows(); ++i)
289 for (qint32 j = 0; j < C_eeg_idx.rows(); ++j)
290 p_NoiseCov.
eigvec(C_eeg_idx[i], C_eeg_idx[j]) = C_eeg_eigvec(i, j);
291 for (qint32 i = 0; i < C_eeg_idx.rows(); ++i)
292 p_NoiseCov.
eig(C_eeg_idx[i]) = C_eeg_eig[i];
295 if (C_meg_idx.size() + C_eeg_idx.size() != n_chan) {
296 qWarning(
"FiffCov::prepare_noise_cov: %lld MEG + %lld EEG channels != %d total (unclassified channels present)",
297 (
long long)C_meg_idx.size(), (
long long)C_eeg_idx.size(), n_chan);
301 p_NoiseCov.
dim = p_ChNames.size();
302 p_NoiseCov.
diag =
false;
303 p_NoiseCov.
names = p_ChNames;
314 if (p_exclude.size() == 0) {
315 p_exclude = p_info.
bads;
316 for (qint32 i = 0; i < cov.
bads.size(); ++i)
317 if (!p_exclude.contains(cov.
bads[i]))
318 p_exclude << cov.
bads[i];
322 for (
int i = 0; i < p_info.
chs.size(); i++) {
324 p_exclude << p_info.
chs[i].ch_name;
328 RowVectorXi sel_eeg = p_info.
pick_types(
false,
true,
false, defaultQStringList, p_exclude);
329 RowVectorXi sel_mag = p_info.
pick_types(QString(
"mag"),
false,
false, defaultQStringList, p_exclude);
330 RowVectorXi sel_grad = p_info.
pick_types(QString(
"grad"),
false,
false, defaultQStringList, p_exclude);
332 QStringList info_ch_names = p_info.
ch_names;
333 QStringList ch_names_eeg, ch_names_mag, ch_names_grad;
334 for (qint32 i = 0; i < sel_eeg.size(); ++i)
335 ch_names_eeg << info_ch_names[sel_eeg(i)];
336 for (qint32 i = 0; i < sel_mag.size(); ++i)
337 ch_names_mag << info_ch_names[sel_mag(i)];
338 for (qint32 i = 0; i < sel_grad.size(); ++i)
339 ch_names_grad << info_ch_names[sel_grad(i)];
344 QStringList ch_names = cov_good.
names;
346 std::vector<qint32> idx_eeg, idx_mag, idx_grad;
347 for (qint32 i = 0; i < ch_names.size(); ++i) {
348 if (ch_names_eeg.contains(ch_names[i]))
349 idx_eeg.push_back(i);
350 else if (ch_names_mag.contains(ch_names[i]))
351 idx_mag.push_back(i);
352 else if (ch_names_grad.contains(ch_names[i]))
353 idx_grad.push_back(i);
356 MatrixXd C(cov_good.
data);
359 if (
static_cast<unsigned>(C.rows()) != idx_eeg.size() + idx_mag.size() + idx_grad.size()) {
360 qWarning(
"FiffCov::regularize: %lld channels in cov but only %zu classified as EEG/MAG/GRAD (others will not be regularized)",
361 static_cast<long long>(C.rows()), idx_eeg.size() + idx_mag.size() + idx_grad.size());
364 QList<FiffProj> t_listProjs;
371 QMap<QString, QPair<double, std::vector<qint32>>> regData;
372 regData.insert(
"EEG", QPair<
double, std::vector<qint32>>(p_fRegEeg, idx_eeg));
373 regData.insert(
"MAG", QPair<
double, std::vector<qint32>>(p_fRegMag, idx_mag));
374 regData.insert(
"GRAD", QPair<
double, std::vector<qint32>>(p_fRegGrad, idx_grad));
379 QMap<QString, QPair<double, std::vector<qint32>>>::Iterator it;
380 for (it = regData.begin(); it != regData.end(); ++it) {
381 QString desc(it.key());
382 double reg = it.value().first;
383 std::vector<qint32> idx = it.value().second;
385 if (idx.size() == 0 || reg == 0.0)
386 qInfo(
"\tNothing to regularize within %s data.\n", desc.toUtf8().constData());
388 qInfo(
"\tRegularize %s: %f\n", desc.toUtf8().constData(), reg);
389 MatrixXd this_C(idx.size(), idx.size());
390 for (quint32 i = 0; i < idx.size(); ++i)
391 for (quint32 j = 0; j < idx.size(); ++j)
392 this_C(i, j) = cov_good.
data(idx[i], idx[j]);
400 QStringList this_ch_names;
401 for (quint32 k = 0; k < idx.size(); ++k)
402 this_ch_names << ch_names[idx[k]];
407 JacobiSVD<MatrixXd>
svd(P, ComputeFullU);
409 VectorXd t_s =
svd.singularValues();
410 MatrixXd t_U =
svd.matrixU();
413 U = t_U.block(0, 0, t_U.rows(), t_U.cols() - ncomp);
416 qInfo(
"\tCreated an SSP operator for %s (dimension = %d).\n", desc.toUtf8().constData(), ncomp);
417 this_C = U.transpose() * (this_C * U);
421 double sigma = this_C.diagonal().mean();
422 this_C.diagonal() = this_C.diagonal().array() + reg * sigma;
423 if (p_bProj && ncomp > 0)
424 this_C = U * (this_C * U.transpose());
426 for (qint32 i = 0; i < this_C.rows(); ++i)
427 for (qint32 j = 0; j < this_C.cols(); ++j)
428 C(idx[i], idx[j]) = this_C(i, j);
434 for (qint32 i = 0; i < idx.size(); ++i)
435 for (qint32 j = 0; j < idx.size(); ++j)
436 cov.
data(idx[i], idx[j]) = C(i, j);
465 const MatrixXi& events,
466 const QList<int>& eventCodes,
473 unsigned int ignoreMask,
481 int minSamp =
static_cast<int>(std::round(tmin * sfreq));
482 int maxSamp =
static_cast<int>(std::round(tmax * sfreq));
483 int ns = maxSamp - minSamp + 1;
484 int delaySamp =
static_cast<int>(std::round(delay * sfreq));
487 qWarning() <<
"[FiffCov::compute_from_epochs] Invalid time window.";
491 int bminSamp = 0, bmaxSamp = 0;
493 bminSamp =
static_cast<int>(std::round(bmin * sfreq)) - minSamp;
494 bmaxSamp =
static_cast<int>(std::round(bmax * sfreq)) - minSamp;
498 QList<QList<MatrixXd>> epochs(eventCodes.size());
499 for (
int k = 0; k < events.rows(); ++k) {
500 int evFrom = events(k, 1) & ~static_cast<int>(ignoreMask);
501 int evTo = events(k, 2) & ~static_cast<int>(ignoreMask);
503 const int iCode = evFrom == 0 ?
static_cast<int>(eventCodes.indexOf(evTo)) : -1;
507 int evSample = events(k, 0);
508 int epochStart = evSample + delaySamp + minSamp;
509 int epochEnd = evSample + delaySamp + maxSamp;
511 if (epochStart < raw.first_samp || epochEnd > raw.
last_samp)
520 qInfo().noquote() <<
"[FiffCov::compute_from_epochs] Rejected epoch at" << evSample << reason;
526 int bminIdx = qMax(0, bminSamp);
527 int bmaxIdx = qMin(
static_cast<int>(epochData.cols()) - 1, bmaxSamp);
528 if (bmaxIdx >= bminIdx) {
529 int nBase = bmaxIdx - bminIdx + 1;
530 for (
int c = 0; c < nchan; ++c) {
531 double baseVal = epochData.row(c).segment(bminIdx, nBase).mean();
532 epochData.row(c).array() -= baseVal;
537 epochs[iCode].append(epochData);
550 const int nchan = info.
nchan;
551 MatrixXd covAccum = MatrixXd::Zero(nchan, nchan);
552 int totalSamples = 0;
555 for (
const QList<MatrixXd>& codeEpochs : epochs) {
556 if (codeEpochs.isEmpty())
558 MatrixXd sum = MatrixXd::Zero(nchan, codeEpochs.first().cols());
559 for (
const MatrixXd& epoch : codeEpochs) {
560 covAccum += epoch * epoch.transpose();
562 totalSamples +=
static_cast<int>(epoch.cols());
564 nEpochs +=
static_cast<int>(codeEpochs.size());
567 covAccum -= sum * sum.transpose() /
static_cast<double>(codeEpochs.size());
568 norm +=
static_cast<double>(sum.cols()) * (codeEpochs.size() - 1);
572 if (totalSamples < 2) {
573 qWarning() <<
"[FiffCov::compute_from_epochs] Not enough data.";
576 if (removeMean && norm <= 0.0) {
577 qWarning() <<
"[FiffCov::compute_from_epochs] Removing the sample mean needs two epochs of one event code.";
580 cov.
data = covAccum / (removeMean ? norm :
static_cast<double>(totalSamples - 1));
585 cov.
nfree = totalSamples - 1;
589 qInfo() <<
"[FiffCov::compute_from_epochs] Computed:" << nchan <<
"channels,"
590 << nEpochs <<
"epochs," << totalSamples <<
"total samples.";
599 if (fileName.isEmpty()) {
600 qWarning() <<
"[FiffCov::save] Output file not specified.";
604 QFile file(fileName);
607 qWarning() <<
"[FiffCov::save] Cannot open" << fileName;
613 pStream->write_cov(*
this);
617 qInfo() <<
"[FiffCov::save] Saved covariance matrix to" << fileName;
627 if (covs.isEmpty()) {
628 qWarning() <<
"[FiffCov::computeGrandAverage] No covariance matrices provided.";
633 MatrixXd sumCov = grandCov.
data *
static_cast<double>(grandCov.
nfree);
634 int totalNfree = grandCov.
nfree;
636 for (
int k = 1; k < covs.size(); ++k) {
637 if (covs[k].
dim != grandCov.
dim) {
638 qWarning() <<
"[FiffCov::computeGrandAverage] Dimension mismatch.";
641 sumCov += covs[k].data *
static_cast<double>(covs[k].nfree);
642 totalNfree += covs[k].nfree;
645 grandCov.
data = sumCov /
static_cast<double>(totalNfree);
646 grandCov.
nfree = totalNfree;
constexpr auto FIFFV_STIM_CH
constexpr auto FIFFV_MNE_NOISE_COV
Set of averaged evoked responses sharing a FiffInfo, plus the ave-style category / rejection descript...
FIFF binary tag-stream layer: wraps a QIODevice to read and write FIFF tags, directories,...
FIFF tag-kind, block-kind and type-code numerical definitions, authoritative for FIFFLIB.
constexpr auto FIFFB_MEAS
constexpr auto FIFF_BLOCK_ID
Eigen::JacobiSVD< Eigen::Matrix3f > svd(S, Eigen::ComputeFullU|Eigen::ComputeFullV)
Recursive node of the parsed FIFF block tree (FIFFB_* hierarchy with directory entries and children).
FIFF continuous raw recording: FiffInfo plus a directory of FIFF_DATA_BUFFER tags for random-access s...
Noise / data covariance matrix as stored under FIFFB_MNE_COV, with channel names, kind,...
Minimal measurement-info subset (channel list, sampling rate, basic transforms) shared by FIFF reader...
Static linear-algebra helpers: SVD-based conditioning, block-diagonal assembly, sorted index pairs.
FIFF file I/O, in-memory data structures and high-level readers/writers.
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
FIFF noise / data covariance: matrix, channel names, kind, applied projectors, bads,...
static FiffCov compute_from_epochs(const FiffRawData &raw, const Eigen::MatrixXi &events, const QList< int > &eventCodes, float tmin, float tmax, float bmin=0.0f, float bmax=0.0f, bool doBaseline=false, bool removeMean=true, unsigned int ignoreMask=0, float delay=0.0f, const RejectionParams *rej=nullptr)
FiffCov regularize(const FiffInfo &p_info, double p_fMag=0.1, double p_fGrad=0.1, double p_fEeg=0.1, bool p_bProj=true, QStringList p_exclude=defaultQStringList) const
FiffCov pick_channels(const QStringList &p_include=defaultQStringList, const QStringList &p_exclude=defaultQStringList)
static FiffCov computeGrandAverage(const QList< FiffCov > &covs)
FiffCov & operator=(const FiffCov &rhs)
static bool read(QIODevice &p_IODevice, FiffCov &p_cov)
FiffCov prepare_noise_cov(const FiffInfo &p_info, const QStringList &p_chNames) const
bool save(const QString &fileName) const
Artifact-rejection thresholds for the MNE-C batch averaging pipeline (gradiometer / magnetometer / EE...
static bool checkArtifacts(const Eigen::MatrixXd &epoch, const FiffInfo &info, const QStringList &bads, const RejectionParams &rej, QString &reason)
Full FIFF measurement info: per-channel descriptors, sampling and filter setup, projectors,...
qint32 make_projector(Eigen::MatrixXd &proj) const
static Eigen::RowVectorXi pick_channels(const QStringList &ch_names, const QStringList &include=defaultQStringList, const QStringList &exclude=defaultQStringList)
Eigen::RowVectorXi pick_types(const QString meg, bool eeg=false, bool stim=false, const QStringList &include=defaultQStringList, const QStringList &exclude=defaultQStringList) const
static fiff_int_t make_projector(const QList< FiffProj > &projs, const QStringList &ch_names, Eigen::MatrixXd &proj, const QStringList &bads=defaultQStringList, Eigen::MatrixXd &U=defaultMatrixXd)
static void activate_projs(QList< FiffProj > &p_qListFiffProj)
Continuous FIFF raw recording: FiffInfo plus a random-access directory of FIFF_DATA_BUFFER tags.
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
FIFF tag-stream reader/writer: wraps a QIODevice and exposes typed read_* / write_* methods for every...
QSharedPointer< FiffStream > SPtr
static FiffStream::SPtr start_file(QIODevice &p_IODevice)
static Eigen::VectorXi sort(Eigen::Matrix< T, Eigen::Dynamic, 1 > &v, bool desc=true)
static void get_whitener(Eigen::MatrixXd &A, bool pca, QString ch_type, Eigen::VectorXd &eig, Eigen::MatrixXd &eigvec)