v2.0.0
Loading...
Searching...
No Matches
mne_proj_op.cpp
Go to the documentation of this file.
1//=============================================================================================================
36
37//=============================================================================================================
38// INCLUDES
39//=============================================================================================================
40
41#include <fiff/fiff_constants.h>
42#include <fiff/fiff_tag.h>
43
44#include "mne_proj_op.h"
45#include "mne_proj_item.h"
46
47#include <QFile>
48#include <QTextStream>
49
50#include <Eigen/Core>
51
52#ifndef TRUE
53#define TRUE 1
54#endif
55
56#ifndef FALSE
57#define FALSE 0
58#endif
59
60#ifndef FAIL
61#define FAIL -1
62#endif
63
64#ifndef OK
65#define OK 0
66#endif
67
68#define MALLOC_23(x,t) (t *)malloc((x)*sizeof(t))
69
70#define REALLOC_23(x,y,t) (t *)((x == NULL) ? malloc((y)*sizeof(t)) : realloc((x),(y)*sizeof(t)))
71
72#define FREE_23(x) if ((char *)(x) != NULL) free((char *)(x))
73
74#define FREE_CMATRIX_23(m) mne_free_cmatrix_23((m))
75
76void mne_free_cmatrix_23 (float **m)
77{
78 if (m) {
79 FREE_23(*m);
80 FREE_23(m);
81 }
82}
83
84#define ALLOC_CMATRIX_23(x,y) mne_cmatrix_23((x),(y))
85
86static void matrix_error_23(int kind, int nr, int nc)
87
88{
89 if (kind == 1)
90 printf("Failed to allocate memory pointers for a %d x %d matrix\n",nr,nc);
91 else if (kind == 2)
92 printf("Failed to allocate memory for a %d x %d matrix\n",nr,nc);
93 else
94 printf("Allocation error for a %d x %d matrix\n",nr,nc);
95 if (sizeof(void *) == 4) {
96 printf("This is probably because you seem to be using a computer with 32-bit architecture.\n");
97 printf("Please consider moving to a 64-bit platform.");
98 }
99 printf("Cannot continue. Sorry.\n");
100 exit(1);
101}
102
103float **mne_cmatrix_23(int nr,int nc)
104
105{
106 int i;
107 float **m;
108 float *whole;
109
110 m = MALLOC_23(nr,float *);
111 if (!m) matrix_error_23(1,nr,nc);
112 whole = MALLOC_23(nr*nc,float);
113 if (!whole) matrix_error_23(2,nr,nc);
114
115 for(i=0;i<nr;i++)
116 m[i] = whole + i*nc;
117 return m;
118}
119
120float mne_dot_vectors_23 (float *v1,
121 float *v2,
122 int nn)
123
124{
125#ifdef BLAS
126 int one = 1;
127 float res = sdot(&nn,v1,&one,v2,&one);
128 return res;
129#else
130 float res = 0.0;
131 int k;
132
133 for (k = 0; k < nn; k++)
134 res = res + v1[k]*v2[k];
135 return res;
136#endif
137}
138
139//============================= mne_named_matrix.c =============================
140
141void mne_string_to_name_list_23(const QString& s, QStringList& listp,int &nlistp)
142/*
143 * Convert a colon-separated list into a string array
144 */
145{
146 QStringList list;
147
148 if (!s.isEmpty() && s.size() > 0) {
150 //list = s.split(":");
151 }
152 listp = list;
153 nlistp = list.size();
154 return;
155}
156
157void fromFloatEigenMatrix_23(const Eigen::MatrixXf& from_mat, float **& to_mat, const int m, const int n)
158{
159 for ( int i = 0; i < m; ++i)
160 for ( int j = 0; j < n; ++j)
161 to_mat[i][j] = from_mat(i,j);
162}
163
164void fromFloatEigenMatrix_23(const Eigen::MatrixXf& from_mat, float **& to_mat)
165{
166 fromFloatEigenMatrix_23(from_mat, to_mat, from_mat.rows(), from_mat.cols());
167}
168
169QString mne_name_list_to_string_23(const QStringList& list)
170/*
171 * Convert a string array to a colon-separated string
172 */
173{
174 int nlist = list.size();
175 QString res;
176 if (nlist == 0 || list.isEmpty())
177 return res;
178// res[0] = '\0';
179 for (int k = 0; k < nlist-1; k++) {
180 res += list[k];
181 res += ":";
182 }
183 res += list[nlist-1];
184 return res;
185}
186
187QString mne_channel_names_to_string_23(const QList<FIFFLIB::FiffChInfo>& chs, int nch)
188/*
189 * Make a colon-separated string out of channel names
190 */
191{
192 QStringList names;
193 QString res;
194 if (nch <= 0)
195 return res;
196 for (int k = 0; k < nch; k++)
197 names.append(chs.at(k).ch_name);
198 res = mne_name_list_to_string_23(names);
199 return res;
200}
201
202//=============================================================================================================
203// USED NAMESPACES
204//=============================================================================================================
205
206using namespace Eigen;
207using namespace FIFFLIB;
208using namespace MNELIB;
209
210//=============================================================================================================
211// DEFINE MEMBER METHODS
212//=============================================================================================================
213
215: nitems (0)
216, nch (0)
217, nvec (0)
218{
219}
220
221//=============================================================================================================
222
226
227//=============================================================================================================
228
230{
231 proj_data.resize(0, 0);
232
233 names.clear();
234 nch = 0;
235 nvec = 0;
236
237 return;
238}
239
240//=============================================================================================================
241
243/*
244 * Copy items from 'from' operator to this operator
245 */
246{
247 if (from) {
248 for (int k = 0; k < from->nitems; k++) {
249 const auto& it = from->items[k];
250 add_item(it.vecs.get(),it.kind,it.desc);
251 items[nitems-1].active_file = it.active_file;
252 }
253 }
254 return this;
255}
256
257//=============================================================================================================
258
259void MNEProjOp::add_item_active(const MNENamedMatrix *vecs, int kind, const QString& desc, int is_active)
260/*
261 * Add a new item to an existing projection operator
262 */
263{
264 items.append(MNEProjItem());
265 auto& new_item = items.back();
266
267 new_item.active = is_active;
268 new_item.vecs = std::make_unique<MNENamedMatrix>(*vecs);
269
270 if (kind == FIFFV_MNE_PROJ_ITEM_EEG_AVREF) {
271 new_item.has_meg = FALSE;
272 new_item.has_eeg = TRUE;
273 }
274 else {
275 for (int k = 0; k < vecs->ncol; k++) {
276 if (vecs->collist[k].contains("EEG"))//strstr(vecs->collist[k],"EEG") == vecs->collist[k])
277 new_item.has_eeg = TRUE;
278 if (vecs->collist[k].contains("MEG"))//strstr(vecs->collist[k],"MEG") == vecs->collist[k])
279 new_item.has_meg = TRUE;
280 }
281 if (!new_item.has_meg && !new_item.has_eeg) {
282 new_item.has_meg = TRUE;
283 new_item.has_eeg = FALSE;
284 }
285 else if (new_item.has_meg && new_item.has_eeg) {
286 new_item.has_meg = TRUE;
287 new_item.has_eeg = FALSE;
288 }
289 }
290 if (!desc.isEmpty())
291 new_item.desc = desc;
292 new_item.kind = kind;
293 new_item.nvec = new_item.vecs->nrow;
294
295 nitems++;
296
297 free_proj(); /* These data are not valid any more */
298 return;
299}
300
301//=============================================================================================================
302
303void MNEProjOp::add_item(const MNENamedMatrix *vecs, int kind, const QString& desc)
304{
305 add_item_active(vecs, kind, desc, TRUE);
306}
307
308//=============================================================================================================
309
311/*
312 * Provide a duplicate (item data only)
313 */
314{
315 MNEProjOp* res = new MNEProjOp();
316
317 for (int k = 0; k < nitems; k++) {
318 const auto& it = items[k];
319 res->add_item_active(it.vecs.get(),it.kind,it.desc,it.active);
320 res->items[k].active_file = it.active_file;
321 }
322 return res;
323}
324
325//=============================================================================================================
326
327MNEProjOp *MNEProjOp::create_average_eeg_ref(const QList<FiffChInfo>& chs, int nch)
328/*
329 * Make the projection operator for average electrode reference
330 */
331{
332 int eegcount = 0;
333 int k;
334 QStringList names;
335 MNEProjOp* op;
336
337 for (k = 0; k < nch; k++)
338 if (chs.at(k).kind == FIFFV_EEG_CH)
339 eegcount++;
340 if (eegcount == 0) {
341 qCritical("No EEG channels specified for average reference.");
342 return NULL;
343 }
344
345 for (k = 0; k < nch; k++)
346 if (chs.at(k).kind == FIFFV_EEG_CH)
347 names.append(chs.at(k).ch_name);
348
349 Eigen::MatrixXf vec_data = Eigen::MatrixXf::Constant(1, eegcount, 1.0f/sqrt((double)eegcount));
350
351 QStringList emptyList;
352 auto vecs = MNENamedMatrix::build(1,eegcount,emptyList,names,vec_data);
353
354 op = new MNEProjOp();
355 op->add_item(vecs.get(),FIFFV_MNE_PROJ_ITEM_EEG_AVREF,"Average EEG reference");
356
357 return op;
358}
359
360//=============================================================================================================
361
362int MNEProjOp::affect(const QStringList& list, int nlist)
363{
364 int k;
365 int naff;
366
367 for (k = 0, naff = 0; k < nitems; k++)
368 if (items[k].active && items[k].affect(list,nlist))
369 naff += items[k].nvec;
370
371 return naff;
372}
373
374//=============================================================================================================
375
376int MNEProjOp::affect_chs(const QList<FiffChInfo>& chs, int nch)
377{
378 QString ch_string;
379 int res;
380 QStringList list;
381 int nlist;
382
383 if (nch == 0)
384 return FALSE;
385 ch_string = mne_channel_names_to_string_23(chs,nch);
386 mne_string_to_name_list_23(ch_string,list,nlist);
387 res = affect(list,nlist);
388 list.clear();
389 return res;
390}
391
392//=============================================================================================================
393
394int MNEProjOp::project_vector(float *vec, int nvec, int do_complement)
395/*
396 * Apply projection operator to a vector (floats)
397 * Assume that all dimension checking etc. has been done before
398 */
399{
400 static float *res = NULL;
401 int res_size = 0;
402 float *pvec;
403 float w;
404 int k,p;
405
407 return OK;
408
409 if (nch != nvec) {
410 printf("Data vector size does not match projection operator");
411 return FAIL;
412 }
413
414 if (nch > res_size) {
415 res = REALLOC_23(res,nch,float);
416 res_size = nch;
417 }
418
419 for (k = 0; k < nch; k++)
420 res[k] = 0.0;
421
422 for (p = 0; p < this->nvec; p++) {
423 pvec = proj_data.row(p).data();
424 w = mne_dot_vectors_23(pvec,vec,nch);
425 for (k = 0; k < nch; k++)
426 res[k] = res[k] + w*pvec[k];
427 }
428 if (do_complement) {
429 for (k = 0; k < nch; k++)
430 vec[k] = vec[k] - res[k];
431 }
432 else {
433 for (k = 0; k < nch; k++)
434 vec[k] = res[k];
435 }
436 return OK;
437}
438
439//=============================================================================================================
440
442/*
443 * Load all the linear projection data
444 */
445{
446 MNEProjOp* op = NULL;
447 QList<FiffDirNode::SPtr> proj;
448 FiffDirNode::SPtr start_node;
449 QList<FiffDirNode::SPtr> items;
451 int k;
452 QString item_desc,desc_tag;
453 int global_nchan,item_nchan;
454 QStringList item_names;
455 int item_kind;
456 int item_nvec;
457 int item_active;
458 FiffTag::SPtr t_pTag;
459
460 if (!stream) {
461 qCritical("File not open read_from_node");
462 goto bad;
463 }
464
465 if (!start || start->isEmpty())
466 start_node = stream->dirtree();
467 else
468 start_node = start;
469
470 op = new MNEProjOp();
471 proj = start_node->dir_tree_find(FIFFB_PROJ);
472 if (proj.size() == 0 || proj[0]->isEmpty()) /* The caller must recognize an empty projection */
473 goto out;
474 /*
475 * Only the first projection block is recognized
476 */
477 items = proj[0]->dir_tree_find(FIFFB_PROJ_ITEM);
478 if (items.size() == 0 || items[0]->isEmpty()) /* The caller must recognize an empty projection */
479 goto out;
480 /*
481 * Get a common number of channels
482 */
483 node = proj[0];
484 if(!node->find_tag(stream, FIFF_NCHAN, t_pTag))
485 global_nchan = 0;
486 else {
487 global_nchan = *t_pTag->toInt();
488 // TAG_FREE(tag);
489 }
490 /*
491 * Proceess each item
492 */
493 for (k = 0; k < items.size(); k++) {
494 node = items[k];
495 /*
496 * Complicated procedure for getting the description
497 */
498 item_desc.clear();
499
500 if (node->find_tag(stream, FIFF_NAME, t_pTag)) {
501 item_desc += t_pTag->toString();
502 }
503
504 /*
505 * Take the first line of description if it exists
506 */
507 if (node->find_tag(stream, FIFF_DESCRIPTION, t_pTag)) {
508 desc_tag = t_pTag->toString();
509 int pos;
510 if((pos = desc_tag.indexOf("\n")) >= 0)
511 desc_tag.truncate(pos);
512 if (!item_desc.isEmpty())
513 item_desc += " ";
514 item_desc += desc_tag;
515 }
516 /*
517 * Possibility to override number of channels here
518 */
519 if (!node->find_tag(stream, FIFF_NCHAN, t_pTag)) {
520 item_nchan = global_nchan;
521 }
522 else {
523 item_nchan = *t_pTag->toInt();
524 }
525 if (item_nchan <= 0) {
526 qCritical("Number of channels incorrectly specified for one of the projection items.");
527 goto bad;
528 }
529 /*
530 * Take care of the channel names
531 */
532 if (!node->find_tag(stream, FIFF_PROJ_ITEM_CH_NAME_LIST, t_pTag))
533 goto bad;
534
535 item_names = FiffStream::split_name_list(t_pTag->toString());
536
537 if (item_names.size() != item_nchan) {
538 printf("Channel name list incorrectly specified for proj item # %d",k+1);
539 item_names.clear();
540 goto bad;
541 }
542 /*
543 * Kind of item
544 */
545 if (!node->find_tag(stream, FIFF_PROJ_ITEM_KIND, t_pTag))
546 goto bad;
547 item_kind = *t_pTag->toInt();
548 /*
549 * How many vectors
550 */
551 if (!node->find_tag(stream,FIFF_PROJ_ITEM_NVEC, t_pTag))
552 goto bad;
553 item_nvec = *t_pTag->toInt();
554 /*
555 * The projection data
556 */
557 if (!node->find_tag(stream,FIFF_PROJ_ITEM_VECTORS, t_pTag))
558 goto bad;
559
560 MatrixXf item_vectors = t_pTag->toFloatMatrix().transpose();
561
562 /*
563 * Is this item active?
564 */
565 if (node->find_tag(stream, FIFF_MNE_PROJ_ITEM_ACTIVE, t_pTag)) {
566 item_active = *t_pTag->toInt();
567 }
568 else
569 item_active = FALSE;
570 /*
571 * Ready to add
572 */
573 QStringList emptyList;
574 auto item = MNENamedMatrix::build(item_nvec,item_nchan,emptyList,item_names,item_vectors);
575 op->add_item_active(item.get(),item_kind,item_desc,item_active);
576 op->items[op->nitems-1].active_file = item_active;
577 }
578
579out :
580 return op;
581
582bad : {
583 if(op)
584 delete op;
585 return NULL;
586 }
587}
588
589//=============================================================================================================
590
591MNEProjOp *MNEProjOp::read(const QString &name)
592{
593 QFile file(name);
594 FiffStream::SPtr stream(new FiffStream(&file));
595
596 if(!stream->open())
597 return NULL;
598
599 MNEProjOp* res = NULL;
600
601 FiffDirNode::SPtr t_default;
602 res = read_from_node(stream,t_default);
603
604 stream->close();
605
606 return res;
607}
608
609//=============================================================================================================
610
611void MNEProjOp::report_data(QTextStream &out, const char *tag, int list_data, char **exclude, int nexclude)
612/*
613 * Output info about the projection operator
614 */
615{
616 int j,p,q;
617 MNENamedMatrix* vecs;
618 int found;
619
620 if (nitems <= 0) {
621 out << "Empty operator\n";
622 return;
623 }
624
625 for (int k = 0; k < nitems; k++) {
626 const auto& it = items[k];
627 if (list_data && tag)
628 out << tag << "\n";
629 if (tag)
630 out << tag;
631 out << "# " << (k+1) << " : " << it.desc << " : " << it.nvec << " vecs : " << it.vecs->ncol << " chs "
632 << (it.has_meg ? "MEG" : "EEG") << " "
633 << (it.active ? "active" : "idle") << "\n";
634 if (list_data && tag)
635 out << tag << "\n";
636 if (list_data) {
637 vecs = items[k].vecs.get();
638
639 for (q = 0; q < vecs->ncol; q++) {
640 out << qSetFieldWidth(10) << Qt::left << vecs->collist[q] << qSetFieldWidth(0);
641 out << (q < vecs->ncol-1 ? " " : "\n");
642 }
643 for (p = 0; p < vecs->nrow; p++)
644 for (q = 0; q < vecs->ncol; q++) {
645 for (j = 0, found = 0; j < nexclude; j++) {
646 if (QString::compare(exclude[j],vecs->collist[q]) == 0) {
647 found = 1;
648 break;
649 }
650 }
651 out << qSetFieldWidth(10) << qSetRealNumberPrecision(5) << Qt::forcepoint
652 << (found ? 0.0 : vecs->data(p, q)) << qSetFieldWidth(0) << " ";
653 out << (q < vecs->ncol-1 ? " " : "\n");
654 }
655 if (list_data && tag)
656 out << tag << "\n";
657 }
658 }
659 return;
660}
661
662//=============================================================================================================
663
664void MNEProjOp::report(QTextStream &out, const char *tag)
665{
666 report_data(out,tag, FALSE, NULL, 0);
667}
FiffTag class declaration, which provides fiff tag I/O and processing methods.
Fiff constants.
#define FIFFV_EEG_CH
#define FIFF_MNE_PROJ_ITEM_ACTIVE
#define FIFFV_MNE_PROJ_ITEM_EEG_AVREF
#define FIFFB_PROJ
Definition fiff_file.h:409
#define FIFF_NCHAN
Definition fiff_file.h:453
#define FIFF_NAME
Definition fiff_file.h:485
#define FIFF_DESCRIPTION
Definition fiff_file.h:486
#define FIFFB_PROJ_ITEM
Definition fiff_file.h:410
#define FIFF_PROJ_ITEM_VECTORS
Definition fiff_file.h:805
#define FIFF_PROJ_ITEM_KIND
Definition fiff_file.h:800
#define FIFF_PROJ_ITEM_CH_NAME_LIST
Definition fiff_file.h:809
#define FIFF_PROJ_ITEM_NVEC
Definition fiff_file.h:804
MNEProjOp class declaration.
MNEProjItem class declaration.
void mne_string_to_name_list_23(const QString &s, QStringList &listp, int &nlistp)
#define MALLOC_23(x, t)
void mne_free_cmatrix_23(float **m)
QString mne_name_list_to_string_23(const QStringList &list)
QString mne_channel_names_to_string_23(const QList< FIFFLIB::FiffChInfo > &chs, int nch)
float mne_dot_vectors_23(float *v1, float *v2, int nn)
void fromFloatEigenMatrix_23(const Eigen::MatrixXf &from_mat, float **&to_mat, const int m, const int n)
#define FREE_23(x)
#define REALLOC_23(x, y, t)
float ** mne_cmatrix_23(int nr, int nc)
#define TRUE
#define FALSE
#define OK
#define FAIL
Core MNE data structures (source spaces, source estimates, hemispheres).
FIFF file I/O and data structures (raw, epochs, evoked, covariance, forward).
QSharedPointer< FiffDirNode > SPtr
FIFF File I/O routines.
static QStringList split_name_list(QString p_sNameList)
QSharedPointer< FiffStream > SPtr
QSharedPointer< FiffTag > SPtr
Definition fiff_tag.h:155
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.
A single SSP (Signal-Space Projection) item.
QStringList names
void free_proj()
Release the compiled projector data.
static MNEProjOp * read_from_node(FIFFLIB::FiffStream::SPtr &stream, const FIFFLIB::FiffDirNode::SPtr &start)
Read all linear projection items from a FIFF tree node.
RowMajorMatrixXf proj_data
int project_vector(float *vec, int nvec, int do_complement)
static MNEProjOp * read(const QString &name)
static MNEProjOp * create_average_eeg_ref(const QList< FIFFLIB::FiffChInfo > &chs, int nch)
Create an average EEG reference projector.
void report_data(QTextStream &out, const char *tag, int list_data, char **exclude, int nexclude)
~MNEProjOp()
Destructor.
int affect(const QStringList &list, int nlist)
void report(QTextStream &out, const char *tag)
MNEProjOp()
Default constructor.
MNEProjOp * combine(MNEProjOp *from)
Append all projection items from another operator.
MNEProjOp * dup() const
Create a deep copy of this projection operator.
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)
QList< MNELIB::MNEProjItem > items
void add_item_active(const MNENamedMatrix *vecs, int kind, const QString &desc, int is_active)
Add a projection item with an explicit active/inactive state.