90 for (
int k = 0; k < from->
nitems; k++) {
91 const auto& it = from->
items[k];
92 add_item(it.vecs.get(),it.kind,it.desc);
107 auto& new_item =
items.back();
109 new_item.active = is_active;
110 new_item.vecs = std::make_unique<MNENamedMatrix>(*vecs);
113 new_item.has_meg =
false;
114 new_item.has_eeg =
true;
117 for (
int k = 0; k < vecs->
ncol; k++) {
118 if (vecs->
collist[k].contains(
"EEG"))
119 new_item.has_eeg =
true;
120 if (vecs->
collist[k].contains(
"MEG"))
121 new_item.has_meg =
true;
123 if (!new_item.has_meg && !new_item.has_eeg) {
124 new_item.has_meg =
true;
125 new_item.has_eeg =
false;
127 else if (new_item.has_meg && new_item.has_eeg) {
128 new_item.has_meg =
true;
129 new_item.has_eeg =
false;
133 new_item.desc = desc;
134 new_item.kind = kind;
135 new_item.nvec = new_item.vecs->nrow;
157 auto res = std::make_unique<MNEProjOp>();
159 for (
int k = 0; k <
nitems; k++) {
160 const auto& it =
items[k];
161 res->add_item_active(it.vecs.get(),it.kind,it.desc,it.active);
162 res->items[k].active_file = it.active_file;
178 for (k = 0; k <
nch; k++)
182 qCritical(
"No EEG channels specified for average reference.");
186 for (k = 0; k <
nch; k++)
188 names.append(chs.at(k).ch_name);
190 Eigen::MatrixXf vec_data = Eigen::MatrixXf::Constant(1, eegcount, 1.0f/sqrt(
static_cast<double>(eegcount)));
192 QStringList emptyList;
195 auto op = std::make_unique<MNEProjOp>();
208 for (k = 0, naff = 0; k <
nitems; k++)
210 naff +=
items[k].nvec;
223 for (
int k = 0; k <
nch; k++)
224 list.append(chs.at(k).ch_name);
239 if (
nch !=
static_cast<int>(vec.size())) {
240 qCritical(
"Data vector size does not match projection operator");
244 Eigen::VectorXf proj = Eigen::VectorXf::Zero(
nch);
246 for (
int p = 0; p < this->
nvec; p++) {
248 float w = row.dot(vec);
249 proj += w * row.transpose();
267 QList<FiffDirNode::SPtr> proj;
269 QList<FiffDirNode::SPtr>
items;
272 QString item_desc,desc_tag;
273 int global_nchan,item_nchan;
274 QStringList item_names;
281 qCritical(
"File not open read_from_node");
285 if (!start || start->isEmpty())
286 start_node = stream->dirtree();
290 auto op = std::make_unique<MNEProjOp>();
292 if (proj.size() == 0 || proj[0]->isEmpty())
304 if(!node->find_tag(stream,
FIFF_NCHAN, t_pTag))
307 global_nchan = *t_pTag->toInt();
313 for (k = 0; k <
items.size(); k++) {
320 if (node->find_tag(stream,
FIFF_NAME, t_pTag)) {
321 item_desc += t_pTag->toString();
328 desc_tag = t_pTag->toString();
330 if((pos = desc_tag.indexOf(
"\n")) >= 0)
331 desc_tag.truncate(pos);
332 if (!item_desc.isEmpty())
334 item_desc += desc_tag;
339 if (!node->find_tag(stream,
FIFF_NCHAN, t_pTag)) {
340 item_nchan = global_nchan;
343 item_nchan = *t_pTag->toInt();
345 if (item_nchan <= 0) {
346 qCritical(
"Number of channels incorrectly specified for one of the projection items.");
358 if (item_names.size() != item_nchan) {
359 qCritical(
"Channel name list incorrectly specified for proj item # %d",k+1);
369 item_kind = *t_pTag->toInt();
376 item_nvec = *t_pTag->toInt();
384 MatrixXf item_vectors = t_pTag->toFloatMatrix().transpose();
390 item_active = *t_pTag->toInt();
397 QStringList emptyList;
399 op->add_item_active(item.get(),item_kind,item_desc,item_active);
400 op->items[op->nitems-1].active_file = item_active;
436 out <<
"Empty operator\n";
440 for (
int k = 0; k <
nitems; k++) {
441 const auto& it =
items[k];
442 if (list_data && !tag.isEmpty())
446 out <<
"# " << (k+1) <<
" : " << it.desc <<
" : " << it.nvec <<
" vecs : " << it.vecs->ncol <<
" chs "
447 << (it.has_meg ?
"MEG" :
"EEG") <<
" "
448 << (it.active ?
"active" :
"idle") <<
"\n";
449 if (list_data && !tag.isEmpty())
452 vecs =
items[k].vecs.get();
454 for (q = 0; q < vecs->
ncol; q++) {
455 out << qSetFieldWidth(10) << Qt::left << vecs->
collist[q] << qSetFieldWidth(0);
456 out << (q < vecs->
ncol-1 ?
" " :
"\n");
458 for (p = 0; p < vecs->
nrow; p++)
459 for (q = 0; q < vecs->
ncol; q++) {
460 found = exclude.contains(vecs->
collist[q]);
461 out << qSetFieldWidth(10) << qSetRealNumberPrecision(5) << Qt::forcepoint
462 << (found ? 0.0 : vecs->
data(p, q)) << qSetFieldWidth(0) <<
" ";
463 out << (q < vecs->
ncol-1 ?
" " :
"\n");
465 if (list_data && !tag.isEmpty())
498void clear_channel_group(Eigen::Ref<Eigen::VectorXf> data,
const QStringList& ch_names,
int nnames,
const QString& prefix)
500 for (
int k = 0; k < nnames; k++)
501 if (ch_names[k].contains(prefix))
505constexpr float USE_LIMIT = 1e-5f;
506constexpr float SMALL_VALUE = 1e-4f;
512 using RowMatrixXf = Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
514 int k,p,q,r,nvec_total;
515 RowMatrixXf vv_meg_mat;
516 Eigen::VectorXf sing_meg_vec;
517 RowMatrixXf vv_eeg_mat;
518 Eigen::VectorXf sing_eeg_vec;
537 RowMatrixXf mat_meg_mat = RowMatrixXf::Zero(nvec_total,
nch);
538 RowMatrixXf mat_eeg_mat = RowMatrixXf::Zero(nvec_total,
nch);
540 for (k = 0, nvec_meg = nvec_eeg = 0; k <
nitems; k++) {
544 if (
items[k].has_meg) {
545 for (p = 0; p <
items[k].nvec; p++, nvec_meg++) {
547 Eigen::Map<Eigen::VectorXf> res_meg(mat_meg_mat.row(nvec_meg).data(),
nch);
552 else if (
items[k].has_eeg) {
553 for (p = 0; p <
items[k].nvec; p++, nvec_eeg++) {
555 Eigen::Map<Eigen::VectorXf> res_eeg(mat_eeg_mat.row(nvec_eeg).data(),
nch);
565 for (q = 0; q < bad.size(); q++)
566 for (r = 0; r <
nch; r++)
567 if (
names[r] == bad[q]) {
568 for (p = 0; p < nvec_meg; p++)
569 mat_meg_mat(p,r) = 0.0;
570 for (p = 0; p < nvec_eeg; p++)
571 mat_eeg_mat(p,r) = 0.0;
576 for (p = 0, nzero = 0; p < nvec_meg; p++) {
577 size = mat_meg_mat.row(p).norm();
579 mat_meg_mat.row(p) /= size;
584 if (nzero == nvec_meg) {
585 mat_meg_mat.resize(0, 0); nvec_meg = 0;
587 for (p = 0, nzero = 0; p < nvec_eeg; p++) {
588 size = mat_eeg_mat.row(p).norm();
590 mat_eeg_mat.row(p) /= size;
595 if (nzero == nvec_eeg) {
596 mat_eeg_mat.resize(0, 0); nvec_eeg = 0;
598 if (nvec_meg + nvec_eeg == 0) {
599 qWarning(
"No projection remains after excluding bad channels. Omitting projection.");
606 Eigen::JacobiSVD<Eigen::MatrixXf>
svd(mat_meg_mat.topRows(nvec_meg), Eigen::ComputeFullV);
607 sing_meg_vec =
svd.singularValues();
608 vv_meg_mat =
svd.matrixV().transpose().topRows(nvec_meg);
611 Eigen::JacobiSVD<Eigen::MatrixXf>
svd(mat_eeg_mat.topRows(nvec_eeg), Eigen::ComputeFullV);
612 sing_eeg_vec =
svd.singularValues();
613 vv_eeg_mat =
svd.matrixV().transpose().topRows(nvec_eeg);
618 for (p = 0,
nvec = 0; p < nvec_meg; p++,
nvec++)
619 if (sing_meg_vec[p]/sing_meg_vec[0] < USE_LIMIT)
621 for (p = 0; p < nvec_eeg; p++,
nvec++)
622 if (sing_eeg_vec[p]/sing_eeg_vec[0] < USE_LIMIT)
624 proj_data = Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>::Zero(
nvec,
nch);
625 for (p = 0,
nvec = 0; p < nvec_meg; p++,
nvec++) {
626 if (sing_meg_vec[p]/sing_meg_vec[0] < USE_LIMIT)
628 for (k = 0; k <
nch; k++) {
629 if (std::fabs(vv_meg_mat(p,k)) < SMALL_VALUE)
636 for (p = 0; p < nvec_eeg; p++,
nvec++) {
637 if (sing_eeg_vec[p]/sing_eeg_vec[0] < USE_LIMIT)
639 for (k = 0; k <
nch; k++) {
640 if (std::fabs(vv_eeg_mat(p,k)) < SMALL_VALUE)
650 for (k = 0; k <
nch; k++)
651 if (
names[k].contains(
"STI")) {
652 for (p = 0; p <
nvec; p++)
673 if (
nch !=
static_cast<int>(vec.size())) {
674 qCritical(
"Data vector size does not match projection operator");
678 Eigen::VectorXd proj = Eigen::VectorXd::Zero(vec.size());
680 for (
int p = 0; p <
nvec; p++) {
681 double w = vec.dot(
proj_data.row(p).cast<
double>());
682 proj += w *
proj_data.row(p).cast<
double>().transpose();
696 const QList<FiffChInfo>& chs,
698 std::unique_ptr<MNEProjOp>& result)
703 for (
int k = 0; k <
nch; k++)
707 if (projnames.size() == 0 && neeg == 0)
710 std::unique_ptr<MNEProjOp> all;
712 for (
int k = 0; k < projnames.size(); k++) {
715 qCritical(
"Failed to read projection from %s.", projnames[k].toUtf8().data());
718 if (one->nitems == 0) {
719 qInfo(
"No linear projection information in %s.", projnames[k].toUtf8().data());
722 qInfo(
"Loaded projection from %s:", projnames[k].toUtf8().data());
723 { QTextStream errStream(stderr); one->report(errStream, QStringLiteral(
"\t")); }
725 all = std::make_unique<MNEProjOp>();
726 all->combine(one.get());
733 for (
int k = 0; k < all->nitems; k++)
742 qInfo(
"Average EEG reference projection added:");
743 { QTextStream errStream(stderr); one->report(errStream, QStringLiteral(
"\t")); }
745 all = std::make_unique<MNEProjOp>();
746 all->combine(one.get());
750 if (all && all->affect_chs(chs,
nch) == 0) {
751 qInfo(
"Projection will not have any effect on selected channels. Projection omitted.");
754 result = std::move(all);
762 using RowMatrixXd = Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
765 bool do_complement =
true;
771 qCritical(
"Incompatible data in apply_cov");
775 RowMatrixXd dcovMat = RowMatrixXd::Zero(c->
ncov,c->
ncov);
778 for (j = 0, p = 0; j < c->
ncov; j++)
779 for (k = 0; k < c->
ncov; k++)
780 dcovMat(j,k) = (j == k) ? c->
cov_diag[j] : 0;
783 for (j = 0, p = 0; j < c->
ncov; j++)
784 for (k = 0; k <= j; k++)
785 dcovMat(j,k) = c->
cov[p++];
786 for (j = 0; j < c->
ncov; j++)
787 for (k = j+1; k < c->
ncov; k++)
788 dcovMat(j,k) = dcovMat(k,j);
791 for (k = 0; k < c->
ncov; k++) {
792 Eigen::Map<Eigen::VectorXd> row_k(dcovMat.row(k).data(), c->
ncov);
797 dcovMat.transposeInPlace();
799 for (k = 0; k < c->
ncov; k++) {
800 Eigen::Map<Eigen::VectorXd> row_k(dcovMat.row(k).data(), c->
ncov);
806 for (j = 0; j < c->
ncov; j++) {
812 for (j = 0, p = 0; j < c->
ncov; j++)
813 for (k = 0; k <= j; k++)
814 c->
cov[p++] = dcovMat(j,k);
Legacy MNE-C noise covariance container preserved for cross-toolchain compatibility.
Named one-dimensional counterpart of MNELIB::MNENamedMatrix.
Composite SSP projection operator P = I - U U^T assembled from a list of MNELIB::MNEProjItem.
Single SSP projection vector with kind/active flag and channel labels.
Row/column-labelled dense matrix used wherever FIFF stores per-channel data.
Eigen::JacobiSVD< Eigen::Matrix3f > svd(S, Eigen::ComputeFullU|Eigen::ComputeFullV)
Symbolic FIFF tag, block, value, unit and channel-type constants shared across FIFFLIB.
#define FIFF_MNE_PROJ_ITEM_ACTIVE
#define FIFFV_MNE_PROJ_ITEM_EEG_AVREF
#define FIFF_PROJ_ITEM_VECTORS
#define FIFF_PROJ_ITEM_KIND
#define FIFF_PROJ_ITEM_CH_NAME_LIST
#define FIFF_PROJ_ITEM_NVEC
FIFF tag: the 16-byte tag header (kind, type, size, next) plus its decoded payload.
Core MNE data structures (source spaces, source estimates, hemispheres).
FIFF file I/O, in-memory data structures and high-level readers/writers.
QSharedPointer< FiffDirNode > SPtr
FIFF tag-stream reader/writer: wraps a QIODevice and exposes typed read_* / write_* methods for every...
QSharedPointer< FiffStream > SPtr
static QStringList split_name_list(QString p_sNameList)
std::unique_ptr< FiffTag > UPtr
Covariance matrix storage.
A dense matrix with named rows and columns.
static std::unique_ptr< MNENamedMatrix > build(int nrow, int ncol, const QStringList &rowlist, const QStringList &collist, const Eigen::MatrixXf &data)
Factory: build a named matrix from its constituent parts.
int pick(const QStringList &names, int nnames, bool require_all, Eigen::Ref< Eigen::VectorXf > res) const
A single SSP (Signal-Space Projection) item.
Eigen::Matrix< float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > proj_data
void report_data(QTextStream &out, const QString &tag, bool list_data, const QStringList &exclude)
void free_proj()
Release the compiled projector data.
static std::unique_ptr< MNEProjOp > read(const QString &name)
void add_item_active(const MNENamedMatrix *vecs, int kind, const QString &desc, bool is_active)
Add a projection item with an explicit active/inactive state.
static bool makeProjection(const QList< QString > &projnames, const QList< FIFFLIB::FiffChInfo > &chs, int nch, std::unique_ptr< MNEProjOp > &result)
Load and combine SSP projection operators from files for the selected channels.
int apply_cov(MNECovMatrix *c)
int affect(const QStringList &list, int nlist)
MNEProjOp()
Default constructor.
int make_proj_bad(const QStringList &bad)
MNEProjOp * combine(MNEProjOp *from)
Append all projection items from another operator.
int project_vector(Eigen::Ref< Eigen::VectorXf > vec, bool do_complement)
int project_dvector(Eigen::Ref< Eigen::VectorXd > vec, bool do_complement)
void add_item(const MNENamedMatrix *vecs, int kind, const QString &desc)
Add a projection item that is active by default.
int affect_chs(const QList< FIFFLIB::FiffChInfo > &chs, int nch)
std::unique_ptr< MNEProjOp > dup() const
Create a deep copy of this projection operator.
int assign_channels(const QStringList &list, int nlist)
static std::unique_ptr< MNEProjOp > create_average_eeg_ref(const QList< FIFFLIB::FiffChInfo > &chs, int nch)
Create an average EEG reference projector.
static std::unique_ptr< MNEProjOp > read_from_node(FIFFLIB::FiffStream::SPtr &stream, const FIFFLIB::FiffDirNode::SPtr &start)
Read all linear projection items from a FIFF tree node.
QList< MNELIB::MNEProjItem > items
void report(QTextStream &out, const QString &tag)