v2.0.0
Loading...
Searching...
No Matches
mne_source_space.cpp
Go to the documentation of this file.
1//=============================================================================================================
36
37//=============================================================================================================
38// INCLUDES
39//=============================================================================================================
40
41#include "mne_source_space.h"
42#include "mne_nearest.h"
43#include "mne_patch_info.h"
44#include "mne_mgh_tag_group.h"
45#include "mne_surface.h"
46#include "filter_thread_arg.h"
47
49#include <fiff/fiff_constants.h>
51#include <fiff/fiff_stream.h>
52#include <fiff/fiff_tag.h>
53
54#include <utils/ioutils.h>
55
56#include <QFile>
57#include <QTextStream>
58#include <QtConcurrent>
59
60#define _USE_MATH_DEFINES
61#include <math.h>
62
64
65#define X_51 0
66#define Y_51 1
67#define Z_51 2
68
69#define ALLOC_INT_51(x) MALLOC_51(x,int)
70
71#define MALLOC_51(x,t) (t *)malloc((x)*sizeof(t))
72
73#define ALLOC_CMATRIX_51(x,y) mne_cmatrix_51((x),(y))
74
75#ifndef TRUE
76#define TRUE 1
77#endif
78
79#ifndef FALSE
80#define FALSE 0
81#endif
82
83#ifndef FAIL
84#define FAIL -1
85#endif
86
87#ifndef OK
88#define OK 0
89#endif
90
91#define X_17 0
92#define Y_17 1
93#define Z_17 2
94
95#define VEC_DOT_17(x,y) ((x)[X_17]*(y)[X_17] + (x)[Y_17]*(y)[Y_17] + (x)[Z_17]*(y)[Z_17])
96
97#define VEC_LEN_17(x) sqrt(VEC_DOT_17(x,x))
98
99#define VEC_DIFF_17(from,to,diff) {\
100 (diff)[X_17] = (to)[X_17] - (from)[X_17];\
101 (diff)[Y_17] = (to)[Y_17] - (from)[Y_17];\
102 (diff)[Z_17] = (to)[Z_17] - (from)[Z_17];\
103}
104
105#define VEC_COPY_17(to,from) {\
106 (to)[X_17] = (from)[X_17];\
107 (to)[Y_17] = (from)[Y_17];\
108 (to)[Z_17] = (from)[Z_17];\
109}
110
111#define FREE_17(x) if ((char *)(x) != NULL) free((char *)(x))
112
113#define MALLOC_17(x,t) (t *)malloc((x)*sizeof(t))
114
115#define ALLOC_CMATRIX_17(x,y) mne_cmatrix_17((x),(y))
116
117#define ALLOC_ICMATRIX_17(x,y) mne_imatrix_17((x),(y))
118
119#define FREE_CMATRIX_17(m) mne_free_cmatrix_17((m))
120
121#define FREE_ICMATRIX_17(m) mne_free_icmatrix_17((m))
122
123#define NNEIGHBORS 26
124
125#define CURVATURE_FILE_MAGIC_NUMBER (16777215)
126
127#define TAG_OLD_MGH_XFORM 30
128#define TAG_OLD_COLORTABLE 1
129#define TAG_OLD_USEREALRAS 2
130#define TAG_USEREALRAS 4
131
132static void matrix_error_17(int kind, int nr, int nc)
133
134{
135 if (kind == 1)
136 printf("Failed to allocate memory pointers for a %d x %d matrix\n",nr,nc);
137 else if (kind == 2)
138 printf("Failed to allocate memory for a %d x %d matrix\n",nr,nc);
139 else
140 printf("Allocation error for a %d x %d matrix\n",nr,nc);
141 if (sizeof(void *) == 4) {
142 printf("This is probably because you seem to be using a computer with 32-bit architecture.\n");
143 printf("Please consider moving to a 64-bit platform.");
144 }
145 printf("Cannot continue. Sorry.\n");
146 exit(1);
147}
148
149static float** mne_cmatrix_17(int nr, int nc)
150{
151 int i;
152 float **m;
153 float *whole;
154
155 m = MALLOC_17(nr,float *);
156 if (!m) matrix_error_17(1,nr,nc);
157 whole = MALLOC_17(nr*nc,float);
158 if (!whole) matrix_error_17(2,nr,nc);
159
160 for(i=0;i<nr;i++)
161 m[i] = whole + i*nc;
162 return m;
163}
164
165static int **mne_imatrix_17(int nr, int nc)
166{
167 int i,**m;
168 int *whole;
169
170 m = MALLOC_17(nr,int *);
171 if (!m) matrix_error_17(1,nr,nc);
172 whole = MALLOC_17(nr*nc,int);
173 if (!whole) matrix_error_17(2,nr,nc);
174 for(i=0;i<nr;i++)
175 m[i] = whole + i*nc;
176 return m;
177}
178
179static void mne_free_cmatrix_17(float **m)
180{
181 if (m) {
182 FREE_17(*m);
183 FREE_17(m);
184 }
185}
186
187static void mne_free_icmatrix_17(int **m)
188{
189 if (m) {
190 FREE_17(*m);
191 FREE_17(m);
192 }
193}
194
195static void matrix_error_51(int kind, int nr, int nc)
196
197{
198 if (kind == 1)
199 printf("Failed to allocate memory pointers for a %d x %d matrix\n",nr,nc);
200 else if (kind == 2)
201 printf("Failed to allocate memory for a %d x %d matrix\n",nr,nc);
202 else
203 printf("Allocation error for a %d x %d matrix\n",nr,nc);
204 if (sizeof(void *) == 4) {
205 printf("This is probably because you seem to be using a computer with 32-bit architecture.\n");
206 printf("Please consider moving to a 64-bit platform.");
207 }
208 printf("Cannot continue. Sorry.\n");
209 exit(1);
210}
211
212float **mne_cmatrix_51(int nr,int nc)
213
214{
215 int i;
216 float **m;
217 float *whole;
218
219 m = MALLOC_51(nr,float *);
220 if (!m) matrix_error_51(1,nr,nc);
221 whole = MALLOC_51(nr*nc,float);
222 if (!whole) matrix_error_51(2,nr,nc);
223
224 for(i=0;i<nr;i++)
225 m[i] = whole + i*nc;
226 return m;
227}
228
229//=============================================================================================================
230// USED NAMESPACES
231//=============================================================================================================
232
233using namespace Eigen;
234using namespace FIFFLIB;
235using namespace MNELIB;
236
237//=============================================================================================================
238// FreeSurfer I/O helpers (file-scope, used only from mne_source_space.cpp)
239//=============================================================================================================
240
241namespace {
242
243using PointsT = MNESurfaceOrVolume::PointsT;
244using TrianglesT = MNESurfaceOrVolume::TrianglesT;
245
246//=========================================================================
247// Leaf I/O functions
248//=========================================================================
249
250int read_int3(QFile &in, int &ival)
251/*
252 * Read the strange 3-byte integer
253 */
254{
255 unsigned int s = 0;
256
257 if (in.read(reinterpret_cast<char*>(&s), 3) != 3) {
258 qCritical("read_int3 could not read data");
259 return FAIL;
260 }
261 s = (unsigned int)UTILSLIB::IOUtils::swap_int(s);
262 ival = ((s >> 8) & 0xffffff);
263 return OK;
264}
265
266int read_int(QFile &in, qint32 &ival)
267/*
268 * Read a 32-bit integer
269 */
270{
271 qint32 s ;
272 if (in.read(reinterpret_cast<char*>(&s), sizeof(qint32)) != static_cast<qint64>(sizeof(qint32))) {
273 qCritical("read_int could not read data");
274 return FAIL;
275 }
277 return OK;
278}
279
280int read_int2(QFile &in, int &ival)
281/*
282 * Read int from short
283 */
284{
285 short s ;
286 if (in.read(reinterpret_cast<char*>(&s), sizeof(short)) != static_cast<qint64>(sizeof(short))) {
287 qCritical("read_int2 could not read data");
288 return FAIL;
289 }
291 return OK;
292}
293
294int read_float(QFile &in, float &fval)
295/*
296 * Read float
297 */
298{
299 float f ;
300 if (in.read(reinterpret_cast<char*>(&f), sizeof(float)) != static_cast<qint64>(sizeof(float))) {
301 qCritical("read_float could not read data");
302 return FAIL;
303 }
305 return OK;
306}
307
308int read_long(QFile &in, long long &lval)
309/*
310 * Read a 64-bit integer
311 */
312{
313 long long s ;
314 if (in.read(reinterpret_cast<char*>(&s), sizeof(long long)) != static_cast<qint64>(sizeof(long long))) {
315 qCritical("read_long could not read data");
316 return FAIL;
317 }
319 return OK;
320}
321
322//=========================================================================
323// Leaf validation / copy
324//=========================================================================
325
326int check_vertex(int no, int maxno)
327{
328 if (no < 0 || no > maxno-1) {
329 printf("Illegal vertex number %d (max %d).",no,maxno);
330 return FAIL;
331 }
332 return OK;
333}
334
335MNEVolGeom dup_vol_geom(const MNEVolGeom& g)
336{
337 MNEVolGeom dup;
338 dup = g;
339 dup.filename = g.filename;
340 return dup;
341}
342
343//=========================================================================
344// read_vol_geom
345//=========================================================================
346
347MNEVolGeom* read_vol_geom(QFile &fp)
348/*
349 * This the volume geometry reading code from FreeSurfer
350 */
351{
352 char param[64];
353 char eq[2];
354 char buf[256];
355 int vgRead = 0;
356 int counter = 0;
357 qint64 pos = 0;
358
359 MNEVolGeom* vg = new MNEVolGeom();
360
361 while (!fp.atEnd() && counter < 8)
362 {
363 QByteArray lineData = fp.readLine(256);
364 if (lineData.isEmpty())
365 break;
366 const char *line = lineData.constData();
367 if (strlen(line) == 0)
368 break ;
369 sscanf(line, "%s %s %*s", param, eq);
370 if (!strcmp(param, "valid")) {
371 sscanf(line, "%s %s %d", param, eq, &vg->valid);
372 vgRead = 1;
373 counter++;
374 }
375 else if (!strcmp(param, "filename")) {
376 if (sscanf(line, "%s %s %s", param, eq, buf) >= 3)
377 vg->filename = strdup(buf);
378 counter++;
379 }
380 else if (!strcmp(param, "volume")) {
381 sscanf(line, "%s %s %d %d %d",
382 param, eq, &vg->width, &vg->height, &vg->depth);
383 counter++;
384 }
385 else if (!strcmp(param, "voxelsize")) {
386 sscanf(line, "%s %s %f %f %f",
387 param, eq, &vg->xsize, &vg->ysize, &vg->zsize);
388 /*
389 * We like these to be in meters
390 */
391 vg->xsize = vg->xsize/1000.0;
392 vg->ysize = vg->ysize/1000.0;
393 vg->zsize = vg->zsize/1000.0;
394 counter++;
395 }
396 else if (!strcmp(param, "xras")) {
397 sscanf(line, "%s %s %f %f %f",
398 param, eq, vg->x_ras, vg->x_ras+1, vg->x_ras+2);
399 counter++;
400 }
401 else if (!strcmp(param, "yras")) {
402 sscanf(line, "%s %s %f %f %f",
403 param, eq, vg->y_ras, vg->y_ras+1, vg->y_ras+2);
404 counter++;
405 }
406 else if (!strcmp(param, "zras")) {
407 sscanf(line, "%s %s %f %f %f",
408 param, eq, vg->z_ras, vg->z_ras+1, vg->z_ras+2);
409 counter++;
410 }
411 else if (!strcmp(param, "cras")) {
412 sscanf(line, "%s %s %f %f %f",
413 param, eq, vg->c_ras, vg->c_ras+1, vg->c_ras+2);
414 vg->c_ras[0] = vg->c_ras[0]/1000.0;
415 vg->c_ras[1] = vg->c_ras[1]/1000.0;
416 vg->c_ras[2] = vg->c_ras[2]/1000.0;
417 counter++;
418 }
419 /* remember the current position */
420 pos = fp.pos();
421 };
422 if (!fp.atEnd()) { /* we read one more line */
423 if (pos > 0 ) /* if success in getting pos, then */
424 fp.seek(pos); /* restore the position */
425 /* note that this won't allow compression using pipe */
426 }
427 if (!vgRead) {
428 delete vg;
429 vg = new MNEVolGeom();
430 }
431 return vg;
432}
433
434//=========================================================================
435// read_tag_data
436//=========================================================================
437
438int read_tag_data(QFile &fp, int tag, long long nbytes, unsigned char *&val, long long &nbytesp)
439/*
440 * Read the data of one tag
441 */
442{
443 unsigned char *dum = NULL;
444 size_t snbytes = nbytes;
445
446 val = NULL;
447 if (nbytes > 0) {
448 dum = MALLOC_17(nbytes+1,unsigned char);
449 if (fp.read(reinterpret_cast<char*>(dum), nbytes) != static_cast<qint64>(snbytes)) {
450 printf("Failed to read %d bytes of tag data",static_cast<int>(nbytes));
451 FREE_17(dum);
452 return FAIL;
453 }
454 dum[nbytes] = '\0'; /* Ensure null termination */
455 val = dum;
456 nbytesp = nbytes;
457 }
458 else { /* Need to handle special cases */
459 if (tag == TAG_OLD_SURF_GEOM) {
460 MNEVolGeom* g = read_vol_geom(fp);
461 if (!g)
462 return FAIL;
463 val = (unsigned char *)g;
464 nbytesp = sizeof(MNEVolGeom);
465 }
466 else if (tag == TAG_OLD_USEREALRAS || tag == TAG_USEREALRAS) {
467 int *vi = MALLOC_17(1,int);
468 if (read_int(fp,*vi) == FAIL)
469 vi = 0;
470 val = (unsigned char *)vi;
471 nbytesp = sizeof(int);
472 }
473 else {
474 printf("Encountered an unknown tag with no length specification : %d\n",tag);
475 val = NULL;
476 nbytesp = 0;
477 }
478 }
479 return OK;
480}
481
482//=========================================================================
483// add_mgh_tag_to_group
484//=========================================================================
485
486void add_mgh_tag_to_group(std::optional<MNEMghTagGroup>& g, int tag, long long len, unsigned char *data)
487{
488 if (!g)
489 g = MNEMghTagGroup();
490 auto new_tag = std::make_unique<MNEMghTag>();
491 new_tag->tag = tag;
492 new_tag->len = len;
493 new_tag->data = QByteArray(reinterpret_cast<const char*>(data), static_cast<int>(len));
494 free(data);
495 g->tags.push_back(std::move(new_tag));
496}
497
498//=========================================================================
499// read_next_tag
500//=========================================================================
501
502int read_next_tag(QFile &fp, int &tagp, long long &lenp, unsigned char *&datap)
503/*
504 * Read the next tag in the file
505 */
506{
507 int ilen,tag;
508 long long len;
509
510 if (read_int(fp,tag) == FAIL) {
511 tagp = 0;
512 return OK;
513 }
514 if (fp.atEnd()) {
515 tagp = 0;
516 return OK;
517 }
518 switch (tag) {
519 case TAG_OLD_MGH_XFORM: /* This is obviously a burden of the past */
520 if (read_int(fp,ilen) == FAIL)
521 return FAIL;
522 len = ilen - 1;
523 break ;
527 len = 0 ;
528 break ;
529 default:
530 if (read_long(fp,len) == FAIL)
531 return FAIL;
532 break;
533 }
534 lenp = len;
535 tagp = tag;
536 if (read_tag_data(fp,tag,len,datap,lenp) == FAIL)
537 return FAIL;
538 return OK;
539}
540
541//=========================================================================
542// read_mgh_tags
543//=========================================================================
544
545int read_mgh_tags(QFile &fp, std::optional<MNEMghTagGroup>& tagsp)
546/*
547 * Read all the tags from the file
548 */
549{
550 long long len;
551 int tag;
552 unsigned char *tag_data;
553
554 while (1) {
555 if (read_next_tag(fp,tag,len,tag_data) == FAIL)
556 return FAIL;
557 if (tag == 0)
558 break;
559 add_mgh_tag_to_group(tagsp,tag,len,tag_data);
560 }
561 return OK;
562}
563
564//=========================================================================
565// read_curvature_file
566//=========================================================================
567
568int read_curvature_file(const QString& fname,
569 Eigen::VectorXf& curv)
570
571{
572 QFile fp(fname);
573 int magic;
574
575 float curvmin,curvmax;
576 int ncurv = 0;
577 int nface,val_pervert;
578 int val,k;
579 float fval;
580
581 if (!fp.open(QIODevice::ReadOnly)) {
582 qCritical() << fname;
583 goto bad;
584 }
585 if (read_int3(fp,magic) != 0) {
586 qCritical() << "Bad magic in" << fname;
587 goto bad;
588 }
589 if (magic == CURVATURE_FILE_MAGIC_NUMBER) { /* A new-style curvature file */
590 /*
591 * How many and faces
592 */
593 if (read_int(fp,ncurv) != 0)
594 goto bad;
595 if (read_int(fp,nface) != 0)
596 goto bad;
597#ifdef DEBUG
598 printf("nvert = %d nface = %d\n",ncurv,nface);
599#endif
600 if (read_int(fp,val_pervert) != 0)
601 goto bad;
602 if (val_pervert != 1) {
603 qCritical("Values per vertex not equal to one.");
604 goto bad;
605 }
606 /*
607 * Read the curvature values
608 */
609 curv.resize(ncurv);
610 curvmin = curvmax = 0.0;
611 for (k = 0; k < ncurv; k++) {
612 if (read_float(fp,fval) != 0)
613 goto bad;
614 curv[k] = fval;
615 if (curv[k] > curvmax)
616 curvmax = curv[k];
617 if (curv[k] < curvmin)
618 curvmin = curv[k];
619 }
620 }
621 else { /* An old-style curvature file */
622 ncurv = magic;
623 /*
624 * How many vertices
625 */
626 if (read_int3(fp,nface) != 0)
627 goto bad;
628#ifdef DEBUG
629 printf("nvert = %d nface = %d\n",ncurv,nface);
630#endif
631 /*
632 * Read the curvature values
633 */
634 curv.resize(ncurv);
635 curvmin = curvmax = 0.0;
636 for (k = 0; k < ncurv; k++) {
637 if (read_int2(fp,val) != 0)
638 goto bad;
639 curv[k] = (float)val/100.0;
640 if (curv[k] > curvmax)
641 curvmax = curv[k];
642 if (curv[k] < curvmin)
643 curvmin = curv[k];
644
645 }
646 }
647#ifdef DEBUG
648 printf("Curvature range: %f...%f\n",curvmin,curvmax);
649#endif
650 return OK;
651
652bad : {
653 curv.resize(0);
654 return FAIL;
655 }
656}
657
658//=========================================================================
659// read_triangle_file
660//=========================================================================
661
662int read_triangle_file(const QString& fname,
663 PointsT& vertices,
664 TrianglesT& triangles,
665 std::optional<MNEMghTagGroup>* tagsp)
666/*
667 * Read the FS triangulated surface
668 */
669{
670 QFile fp(fname);
671 int magic;
672 char c;
673
674 qint32 nvert,ntri,nquad;
675 float **vert = NULL;
676 int **tri = NULL;
677 int k,p;
678 int quad[4];
679 int val;
680 float *rr[5];
681 int which;
682
683 if (!fp.open(QIODevice::ReadOnly)) {
684 qCritical() << fname;
685 goto bad;
686 }
687 if (read_int3(fp,magic) != 0) {
688 qCritical() << "Bad magic in" << fname;
689 goto bad;
690 }
691 if (magic != TRIANGLE_FILE_MAGIC_NUMBER &&
692 magic != QUAD_FILE_MAGIC_NUMBER &&
694 qCritical() << "Bad magic in" << fname;
695 goto bad;
696 }
697 if (magic == TRIANGLE_FILE_MAGIC_NUMBER) {
698 /*
699 * Get the comment
700 */
701 printf("Triangle file : ");
702 for (fp.getChar(&c); c != '\n'; fp.getChar(&c)) {
703 if (fp.atEnd()) {
704 qCritical()<<"Bad triangle file.";
705 goto bad;
706 }
707 putc(c,stderr);
708 }
709 fp.getChar(&c);
710 /*
711 * How many vertices and triangles?
712 */
713 if (read_int(fp,nvert) != 0)
714 goto bad;
715 if (read_int(fp,ntri) != 0)
716 goto bad;
717 printf(" nvert = %d ntri = %d\n",nvert,ntri);
718 vert = ALLOC_CMATRIX_17(nvert,3);
719 tri = ALLOC_ICMATRIX_17(ntri,3);
720 /*
721 * Read the vertices
722 */
723 for (k = 0; k < nvert; k++) {
724 if (read_float(fp,vert[k][X_17]) != 0)
725 goto bad;
726 if (read_float(fp,vert[k][Y_17]) != 0)
727 goto bad;
728 if (read_float(fp,vert[k][Z_17]) != 0)
729 goto bad;
730 }
731 /*
732 * Read the triangles
733 */
734 for (k = 0; k < ntri; k++) {
735 if (read_int(fp,tri[k][X_17]) != 0)
736 goto bad;
737 if (check_vertex(tri[k][X_17],nvert) != OK)
738 goto bad;
739 if (read_int(fp,tri[k][Y_17]) != 0)
740 goto bad;
741 if (check_vertex(tri[k][Y_17],nvert) != OK)
742 goto bad;
743 if (read_int(fp,tri[k][Z_17]) != 0)
744 goto bad;
745 if (check_vertex(tri[k][Z_17],nvert) != OK)
746 goto bad;
747 }
748 }
749 else if (magic == QUAD_FILE_MAGIC_NUMBER ||
751 if (read_int3(fp,nvert) != 0)
752 goto bad;
753 if (read_int3(fp,nquad) != 0)
754 goto bad;
755 printf("%s file : nvert = %d nquad = %d\n",
756 magic == QUAD_FILE_MAGIC_NUMBER ? "Quad" : "New quad",
757 nvert,nquad);
758 vert = ALLOC_CMATRIX_17(nvert,3);
759 if (magic == QUAD_FILE_MAGIC_NUMBER) {
760 for (k = 0; k < nvert; k++) {
761 if (read_int2(fp,val) != 0)
762 goto bad;
763 vert[k][X_17] = val/100.0;
764 if (read_int2(fp,val) != 0)
765 goto bad;
766 vert[k][Y_17] = val/100.0;
767 if (read_int2(fp,val) != 0)
768 goto bad;
769 vert[k][Z_17] = val/100.0;
770 }
771 }
772 else { /* NEW_QUAD_FILE_MAGIC_NUMBER */
773 for (k = 0; k < nvert; k++) {
774 if (read_float(fp,vert[k][X_17]) != 0)
775 goto bad;
776 if (read_float(fp,vert[k][Y_17]) != 0)
777 goto bad;
778 if (read_float(fp,vert[k][Z_17]) != 0)
779 goto bad;
780 }
781 }
782 ntri = 2*nquad;
783 tri = ALLOC_ICMATRIX_17(ntri,3);
784 for (k = 0, ntri = 0; k < nquad; k++) {
785 for (p = 0; p < 4; p++) {
786 if (read_int3(fp,quad[p]) != 0)
787 goto bad;
788 rr[p] = vert[quad[p]];
789 }
790 rr[4] = vert[quad[0]];
791
792 /*
793 * The randomization is borrowed from FreeSurfer code
794 * Strange...
795 */
796#define EVEN(n) ((((n) / 2) * 2) == n)
797#ifdef FOO
798#define WHICH_FACE_SPLIT(vno0, vno1) \
799 (1*nearbyint(sqrt(1.9*vno0) + sqrt(3.5*vno1)))
800
801 which = WHICH_FACE_SPLIT(quad[0], quad[1]) ;
802#endif
803 which = quad[0];
804 /*
805 printf("%f ",sqrt(1.9*quad[0]) + sqrt(3.5*quad[1]));
806 */
807
808 if (EVEN(which)) {
809 tri[ntri][X_17] = quad[0];
810 tri[ntri][Y_17] = quad[1];
811 tri[ntri][Z_17] = quad[3];
812 ntri++;
813
814 tri[ntri][X_17] = quad[2];
815 tri[ntri][Y_17] = quad[3];
816 tri[ntri][Z_17] = quad[1];
817 ntri++;
818 }
819 else {
820 tri[ntri][X_17] = quad[0];
821 tri[ntri][Y_17] = quad[1];
822 tri[ntri][Z_17] = quad[2];
823 ntri++;
824
825 tri[ntri][X_17] = quad[0];
826 tri[ntri][Y_17] = quad[2];
827 tri[ntri][Z_17] = quad[3];
828 ntri++;
829 }
830 }
831 }
832 /*
833 * Optionally read the tags
834 */
835 if (tagsp) {
836 std::optional<MNEMghTagGroup> tags;
837 if (read_mgh_tags(fp, tags) == FAIL) {
838 goto bad;
839 }
840 *tagsp = std::move(tags);
841 }
842 /*
843 * Convert mm to m and store as Eigen matrices
844 */
845 for (k = 0; k < nvert; k++) {
846 vert[k][X_17] = vert[k][X_17]/1000.0;
847 vert[k][Y_17] = vert[k][Y_17]/1000.0;
848 vert[k][Z_17] = vert[k][Z_17]/1000.0;
849 }
850 vertices = Eigen::Map<PointsT>(vert[0], nvert, 3);
851 FREE_CMATRIX_17(vert);
852 triangles = Eigen::Map<TrianglesT>(tri[0], ntri, 3);
853 FREE_ICMATRIX_17(tri);
854 return OK;
855
856bad : {
857 FREE_CMATRIX_17(vert);
858 FREE_ICMATRIX_17(tri);
859 return FAIL;
860 }
861}
862
863//=========================================================================
864// get_volume_geom_from_tag
865//=========================================================================
866
867std::optional<MNEVolGeom> get_volume_geom_from_tag(const MNEMghTagGroup *tagsp)
868{
869 MNEMghTag* tag = NULL;
870
871 if (tagsp) {
872 for (const auto &t : tagsp->tags)
873 if (t->tag == TAG_OLD_SURF_GEOM) {
874 tag = t.get();
875 break;
876 }
877 if (tag)
878 return dup_vol_geom(*reinterpret_cast<MNEVolGeom*>(tag->data.data()));
879 }
880 return std::nullopt;
881}
882
883} // anonymous namespace
884
885//=============================================================================================================
886// DEFINE MEMBER METHODS
887//=============================================================================================================
888
890{
891 this->np = np;
892 if (np > 0) {
893 rr = PointsT::Zero(np, 3);
894 nn = NormalsT::Zero(np, 3);
895 inuse = VectorXi::Zero(np);
896 vertno = VectorXi::Zero(np);
897 }
898 nuse = 0;
899 ntri = 0;
900 tot_area = 0.0;
901
902 nuse_tri = 0;
903
904 // tris, use_tris are std::vector<MNETriangle> — default-constructed empty
905
906 // neighbor_tri, nneighbor_tri, curv, val,
907 // neighbor_vert, nneighbor_vert, vert_dist
908 // are Eigen/std::vector types — default-constructed empty
909
912 subject = "";
914
915 // nearest is std::vector<MNENearest> — default-constructed empty
916 // patches is std::vector<optional<MNEPatchInfo>> — default-constructed empty
917
919 dist_limit = -1.0;
920
921 voxel_surf_RAS_t.reset();
922 vol_dims[0] = vol_dims[1] = vol_dims[2] = 0;
923
924 MRI_volume = "";
925 MRI_surf_RAS_RAS_t.reset();
926 MRI_voxel_surf_RAS_t.reset();
927 MRI_vol_dims[0] = MRI_vol_dims[1] = MRI_vol_dims[2] = 0;
928 interpolator.reset();
929
930 vol_geom.reset();
931 mgh_tags.reset();
932
933 cm[X_51] = cm[Y_51] = cm[Z_51] = 0.0;
934}
935
936//=============================================================================================================
937
941
942//=============================================================================================================
943
945{
946 // Base class clone — creates a MNESourceSpace with the same fields.
947 // Derived classes (e.g., MNEHemisphere) override this to preserve their type.
948 auto copy = std::make_shared<MNESourceSpace>(this->np);
949 copy->type = this->type;
950 copy->id = this->id;
951 copy->np = this->np;
952 copy->ntri = this->ntri;
953 copy->coord_frame = this->coord_frame;
954 copy->rr = this->rr;
955 copy->nn = this->nn;
956 copy->nuse = this->nuse;
957 copy->inuse = this->inuse;
958 copy->vertno = this->vertno;
959 copy->itris = this->itris;
960 copy->use_itris = this->use_itris;
961 copy->nuse_tri = this->nuse_tri;
962 copy->dist_limit = this->dist_limit;
963 copy->dist = this->dist;
964 copy->nearest = this->nearest;
965 copy->neighbor_tri = this->neighbor_tri;
966 copy->neighbor_vert = this->neighbor_vert;
967 return copy;
968}
969
970//=============================================================================================================
971
973{
974 int k;
975 for (k = 0; k < np; k++)
976 inuse[k] = TRUE;
977 nuse = np;
978 return;
979}
980
981//=============================================================================================================
982
984/*
985 * Left or right hemisphere?
986 */
987{
988 int k;
989 float xave;
990
991 for (k = 0, xave = 0.0; k < np; k++)
992 xave += rr(k,0);
993 if (xave < 0.0)
994 return TRUE;
995 else
996 return FALSE;
997}
998
999//=============================================================================================================
1000
1002{
1003 double xave = rr.col(0).sum();
1004 if (xave < 0)
1006 else
1008}
1009
1010//=============================================================================================================
1011
1012void MNESourceSpace::update_inuse(Eigen::VectorXi new_inuse)
1013/*
1014 * Update the active vertices
1015 */
1016{
1017 int k,p,nuse_count;
1018
1019 inuse = std::move(new_inuse);
1020
1021 for (k = 0, nuse_count = 0; k < np; k++)
1022 if (inuse[k])
1023 nuse_count++;
1024
1025 nuse = nuse_count;
1026 if (nuse > 0) {
1027 vertno.conservativeResize(nuse);
1028 for (k = 0, p = 0; k < np; k++)
1029 if (inuse[k])
1030 vertno[p++] = k;
1031 }
1032 else {
1033 vertno.resize(0);
1034 }
1035 return;
1036}
1037
1038//=============================================================================================================
1039
1041/*
1042 * Transform source space data into another coordinate frame
1043 */
1044{
1045 int k;
1046 if (coord_frame == t.to)
1047 return OK;
1048 if (coord_frame != t.from) {
1049 printf("Coordinate transformation does not match with the source space coordinate system.");
1050 return FAIL;
1051 }
1052 for (k = 0; k < np; k++) {
1055 }
1056 if (!tris.empty()) {
1057 for (k = 0; k < ntri; k++)
1059 }
1060 coord_frame = t.to;
1061 return OK;
1062}
1063
1064//=============================================================================================================
1065
1067{
1068 MNENearest* nearest_data = nearest.data();
1069 MNENearest* this_patch;
1070 std::vector<std::optional<MNEPatchInfo>> pinfo(nuse);
1071 int nave,p,q,k;
1072
1073 printf("Computing patch statistics...\n");
1074 if (neighbor_tri.empty())
1075 if (add_geometry_info(FALSE) != OK)
1076 return FAIL;
1077
1078 if (nearest.empty()) {
1079 qCritical("The patch information is not available.");
1080 return FAIL;
1081 }
1082 if (nuse == 0) {
1083 patches.clear();
1084 return OK;
1085 }
1086 /*
1087 * Calculate the average normals and the patch areas
1088 */
1089 printf("\tareas, average normals, and mean deviations...");
1090 std::sort(nearest.begin(), nearest.end(),
1091 [](const MNENearest& a, const MNENearest& b) { return a.nearest < b.nearest; });
1092 nearest_data = nearest.data(); // refresh after sort
1093 nave = 1;
1094 for (p = 1, q = 0; p < np; p++) {
1095 if (nearest_data[p].nearest != nearest_data[p-1].nearest) {
1096 if (nave == 0) {
1097 qCritical("No vertices belong to the patch of vertex %d",nearest_data[p-1].nearest);
1098 return FAIL;
1099 }
1100 if (vertno[q] == nearest_data[p-1].nearest) { /* Some source space points may have been omitted since
1101 * the patch information was computed */
1102 pinfo[q] = MNEPatchInfo();
1103 pinfo[q]->vert = nearest_data[p-1].nearest;
1104 this_patch = nearest_data+p-nave;
1105 pinfo[q]->memb_vert.resize(nave);
1106 for (k = 0; k < nave; k++) {
1107 pinfo[q]->memb_vert[k] = this_patch[k].vert;
1108 this_patch[k].patch = &(*pinfo[q]);
1109 }
1110 pinfo[q]->calculate_area(this);
1111 pinfo[q]->calculate_normal_stats(this);
1112 q++;
1113 }
1114 nave = 0;
1115 }
1116 nave++;
1117 }
1118 if (nave == 0) {
1119 qCritical("No vertices belong to the patch of vertex %d",nearest_data[p-1].nearest);
1120 return FAIL;
1121 }
1122 if (vertno[q] == nearest_data[p-1].nearest) {
1123 pinfo[q] = MNEPatchInfo();
1124 pinfo[q]->vert = nearest_data[p-1].nearest;
1125 this_patch = nearest_data+p-nave;
1126 pinfo[q]->memb_vert.resize(nave);
1127 for (k = 0; k < nave; k++) {
1128 pinfo[q]->memb_vert[k] = this_patch[k].vert;
1129 this_patch[k].patch = &(*pinfo[q]);
1130 }
1131 pinfo[q]->calculate_area(this);
1132 pinfo[q]->calculate_normal_stats(this);
1133 q++;
1134 }
1135 printf(" %d/%d [done]\n",q,nuse);
1136
1137 patches = std::move(pinfo);
1138
1139 return OK;
1140}
1141
1142//=============================================================================================================
1143
1145{
1146 int k,p;
1147
1148 for (k = 0, nuse = 0; k < np; k++)
1149 if (inuse[k])
1150 nuse++;
1151
1152 if (nuse == 0) {
1153 vertno.resize(0);
1154 }
1155 else {
1156 vertno.conservativeResize(nuse);
1157 for (k = 0, p = 0; k < np; k++)
1158 if (inuse[k])
1159 vertno[p++] = k;
1160 }
1161 if (!nearest.empty())
1163 return;
1164}
1165
1166//=============================================================================================================
1167
1168std::unique_ptr<MNESourceSpace> MNESourceSpace::create_source_space(int np)
1169/*
1170 * Create a new source space and all associated data
1171 */
1172{
1173 auto res = std::make_unique<MNESourceSpace>();
1174 res->np = np;
1175 if (np > 0) {
1176 res->rr = PointsT::Zero(np, 3);
1177 res->nn = NormalsT::Zero(np, 3);
1178 res->inuse = VectorXi::Zero(np);
1179 res->vertno = VectorXi::Zero(np);
1180 }
1181 res->nuse = 0;
1182 res->ntri = 0;
1183 res->tot_area = 0.0;
1184
1185 res->nuse_tri = 0;
1186
1187 res->sigma = -1.0;
1188 res->coord_frame = FIFFV_COORD_MRI;
1189 res->id = FIFFV_MNE_SURF_UNKNOWN;
1190 res->subject.clear();
1191 res->type = FIFFV_MNE_SPACE_SURFACE;
1192
1193 res->dist = FIFFLIB::FiffSparseMatrix();
1194 res->dist_limit = -1.0;
1195
1196 res->voxel_surf_RAS_t.reset();
1197 res->vol_dims[0] = res->vol_dims[1] = res->vol_dims[2] = 0;
1198
1199 res->MRI_volume.clear();
1200 res->MRI_surf_RAS_RAS_t.reset();
1201 res->MRI_voxel_surf_RAS_t.reset();
1202 res->MRI_vol_dims[0] = res->MRI_vol_dims[1] = res->MRI_vol_dims[2] = 0;
1203 res->interpolator.reset();
1204
1205 res->vol_geom.reset();
1206 res->mgh_tags.reset();
1207
1208 res->cm[0] = res->cm[1] = res->cm[2] = 0.0;
1209
1210 return res;
1211}
1212
1213//=============================================================================================================
1214
1215std::unique_ptr<MNESourceSpace> MNESourceSpace::load_surface(const QString& surf_file,
1216 const QString& curv_file)
1217{
1218 return load_surface_geom(surf_file,curv_file,TRUE,TRUE);
1219}
1220
1221//=============================================================================================================
1222
1223std::unique_ptr<MNESourceSpace> MNESourceSpace::load_surface_geom(const QString& surf_file,
1224 const QString& curv_file,
1225 int add_geometry,
1226 int check_too_many_neighbors)
1227 /*
1228 * Load the surface and add the geometry information
1229 */
1230{
1231 int k;
1232 std::unique_ptr<MNESourceSpace> s;
1233 std::optional<MNEMghTagGroup> tags;
1234 Eigen::VectorXf curvs;
1235 PointsT verts;
1237
1238 if (read_triangle_file(surf_file,
1239 verts,
1240 tris,
1241 &tags) == -1)
1242 goto bad;
1243
1244 if (!curv_file.isEmpty()) {
1245 if (read_curvature_file(curv_file, curvs) == -1)
1246 goto bad;
1247 if (curvs.size() != verts.rows()) {
1248 qCritical()<<"Incorrect number of vertices in the curvature file.";
1249 goto bad;
1250 }
1251 }
1252
1253 s = std::make_unique<MNESourceSpace>(0);
1254 s->rr = std::move(verts);
1255 s->itris = std::move(tris);
1256 s->ntri = s->itris.rows();
1257 s->np = s->rr.rows();
1258 if (curvs.size() > 0) {
1259 s->curv = std::move(curvs);
1260 }
1261 s->val = Eigen::VectorXf::Zero(s->np);
1262 if (add_geometry) {
1263 if (check_too_many_neighbors) {
1264 if (s->add_geometry_info(TRUE) != OK)
1265 goto bad;
1266 }
1267 else {
1268 if (s->add_geometry_info2(TRUE) != OK)
1269 goto bad;
1270 }
1271 }
1272 else if (s->nn.rows() == 0) { /* Normals only */
1273 if (s->add_vertex_normals() != OK)
1274 goto bad;
1275 }
1276 else
1277 s->add_triangle_data();
1278 s->nuse = s->np;
1279 s->inuse = Eigen::VectorXi::Ones(s->np);
1280 s->vertno = Eigen::VectorXi::LinSpaced(s->np, 0, s->np - 1);
1281 s->mgh_tags = std::move(tags);
1282 s->vol_geom = get_volume_geom_from_tag(s->mgh_tags ? &(*s->mgh_tags) : nullptr);
1283
1284 return s;
1285
1286bad : {
1287 return nullptr;
1288 }
1289}
1290
1291//=============================================================================================================
1292
1293static std::optional<FiffCoordTrans> make_voxel_ras_trans(float *r0,
1294 float *x_ras,
1295 float *y_ras,
1296 float *z_ras,
1297 float *voxel_size)
1298
1299{
1300 float rot[3][3],move[3];
1301 int j,k;
1302
1303 VEC_COPY_17(move,r0);
1304
1305 for (j = 0; j < 3; j++) {
1306 rot[j][0] = x_ras[j];
1307 rot[j][1] = y_ras[j];
1308 rot[j][2] = z_ras[j];
1309 }
1310
1311 for (j = 0; j < 3; j++)
1312 for (k = 0; k < 3; k++)
1313 rot[j][k] = voxel_size[k]*rot[j][k];
1314
1316 Eigen::Map<Eigen::Matrix3f>(&rot[0][0]),
1317 Eigen::Map<Eigen::Vector3f>(move));
1318}
1319
1320MNESourceSpace* MNESourceSpace::make_volume_source_space(const MNESurface& surf, float grid, float exclude, float mindist)
1321/*
1322 * Make a source space which covers the volume bounded by surf
1323 */
1324{
1325 float min[3],max[3],cm[3];
1326 int minn[3],maxn[3];
1327 const float *node;
1328 float maxdist,dist,diff[3];
1329 int k,c;
1330 std::unique_ptr<MNESourceSpace> sp;
1331 int np,nplane,nrow;
1332 int nneigh;
1333 int x,y,z;
1334 /*
1335 * Figure out the grid size
1336 */
1337 cm[X_17] = cm[Y_17] = cm[Z_17] = 0.0;
1338 node = &surf.rr(0,0);
1339 for (c = 0; c < 3; c++)
1340 min[c] = max[c] = node[c];
1341
1342 for (k = 0; k < surf.np; k++) {
1343 node = &surf.rr(k,0);
1344 for (c = 0; c < 3; c++) {
1345 cm[c] += node[c];
1346 if (node[c] < min[c])
1347 min[c] = node[c];
1348 if (node[c] > max[c])
1349 max[c] = node[c];
1350 }
1351 }
1352 for (c = 0; c < 3; c++)
1353 cm[c] = cm[c]/surf.np;
1354 /*
1355 * Define the sphere which fits the surface
1356 */
1357 maxdist = 0.0;
1358 for (k = 0; k < surf.np; k++) {
1359 VEC_DIFF_17(cm,&surf.rr(k,0),diff);
1360 dist = VEC_LEN_17(diff);
1361 if (dist > maxdist)
1362 maxdist = dist;
1363 }
1364 printf("Surface CM = (%6.1f %6.1f %6.1f) mm\n",
1365 1000*cm[X_17], 1000*cm[Y_17], 1000*cm[Z_17]);
1366 printf("Surface fits inside a sphere with radius %6.1f mm\n",1000*maxdist);
1367 printf("Surface extent:\n"
1368 "\tx = %6.1f ... %6.1f mm\n"
1369 "\ty = %6.1f ... %6.1f mm\n"
1370 "\tz = %6.1f ... %6.1f mm\n",
1371 1000*min[X_17],1000*max[X_17],
1372 1000*min[Y_17],1000*max[Y_17],
1373 1000*min[Z_17],1000*max[Z_17]);
1374 for (c = 0; c < 3; c++) {
1375 if (max[c] > 0)
1376 maxn[c] = floor(std::fabs(max[c])/grid)+1;
1377 else
1378 maxn[c] = -floor(std::fabs(max[c])/grid)-1;
1379 if (min[c] > 0)
1380 minn[c] = floor(std::fabs(min[c])/grid)+1;
1381 else
1382 minn[c] = -floor(std::fabs(min[c])/grid)-1;
1383 }
1384 printf("Grid extent:\n"
1385 "\tx = %6.1f ... %6.1f mm\n"
1386 "\ty = %6.1f ... %6.1f mm\n"
1387 "\tz = %6.1f ... %6.1f mm\n",
1388 1000*(minn[X_17]*grid),1000*(maxn[X_17]*grid),
1389 1000*(minn[Y_17]*grid),1000*(maxn[Y_17]*grid),
1390 1000*(minn[Z_17]*grid),1000*(maxn[Z_17]*grid));
1391 /*
1392 * Now make the initial grid
1393 */
1394 np = 1;
1395 for (c = 0; c < 3; c++)
1396 np = np*(maxn[c]-minn[c]+1);
1397 nplane = (maxn[X_17]-minn[X_17]+1)*(maxn[Y_17]-minn[Y_17]+1);
1398 nrow = (maxn[X_17]-minn[X_17]+1);
1400 sp->type = MNE_SOURCE_SPACE_VOLUME;
1401 sp->nneighbor_vert = Eigen::VectorXi::Constant(sp->np, NNEIGHBORS);
1402 sp->neighbor_vert.resize(sp->np);
1403 for (k = 0; k < sp->np; k++) {
1404 sp->inuse[k] = TRUE;
1405 sp->vertno[k] = k;
1406 sp->nn(k,X_17) = sp->nn(k,Y_17) = 0.0; /* Source orientation is immaterial */
1407 sp->nn(k,Z_17) = 1.0;
1408 sp->neighbor_vert[k] = Eigen::VectorXi::Constant(NNEIGHBORS, -1);
1409 sp->nuse++;
1410 }
1411 for (k = 0, z = minn[Z_17]; z <= maxn[Z_17]; z++) {
1412 for (y = minn[Y_17]; y <= maxn[Y_17]; y++) {
1413 for (x = minn[X_17]; x <= maxn[X_17]; x++, k++) {
1414 sp->rr(k,X_17) = x*grid;
1415 sp->rr(k,Y_17) = y*grid;
1416 sp->rr(k,Z_17) = z*grid;
1417 /*
1418 * Figure out the neighborhood:
1419 * 6-neighborhood first
1420 */
1421 Eigen::VectorXi& neigh = sp->neighbor_vert[k];
1422 if (z > minn[Z_17])
1423 neigh[0] = k - nplane;
1424 if (x < maxn[X_17])
1425 neigh[1] = k + 1;
1426 if (y < maxn[Y_17])
1427 neigh[2] = k + nrow;
1428 if (x > minn[X_17])
1429 neigh[3] = k - 1;
1430 if (y > minn[Y_17])
1431 neigh[4] = k - nrow;
1432 if (z < maxn[Z_17])
1433 neigh[5] = k + nplane;
1434 /*
1435 * Then the rest to complete the 26-neighborhood
1436 * First the plane below
1437 */
1438 if (z > minn[Z_17]) {
1439 if (x < maxn[X_17]) {
1440 neigh[6] = k + 1 - nplane;
1441 if (y < maxn[Y_17])
1442 neigh[7] = k + 1 + nrow - nplane;
1443 }
1444 if (y < maxn[Y_17])
1445 neigh[8] = k + nrow - nplane;
1446 if (x > minn[X_17]) {
1447 if (y < maxn[Y_17])
1448 neigh[9] = k - 1 + nrow - nplane;
1449 neigh[10] = k - 1 - nplane;
1450 if (y > minn[Y_17])
1451 neigh[11] = k - 1 - nrow - nplane;
1452 }
1453 if (y > minn[Y_17]) {
1454 neigh[12] = k - nrow - nplane;
1455 if (x < maxn[X_17])
1456 neigh[13] = k + 1 - nrow - nplane;
1457 }
1458 }
1459 /*
1460 * Then the same plane
1461 */
1462 if (x < maxn[X_17] && y < maxn[Y_17])
1463 neigh[14] = k + 1 + nrow;
1464 if (x > minn[X_17]) {
1465 if (y < maxn[Y_17])
1466 neigh[15] = k - 1 + nrow;
1467 if (y > minn[Y_17])
1468 neigh[16] = k - 1 - nrow;
1469 }
1470 if (y > minn[Y_17] && x < maxn[X_17])
1471 neigh[17] = k + 1 - nrow - nplane;
1472 /*
1473 * Finally one plane above
1474 */
1475 if (z < maxn[Z_17]) {
1476 if (x < maxn[X_17]) {
1477 neigh[18] = k + 1 + nplane;
1478 if (y < maxn[Y_17])
1479 neigh[19] = k + 1 + nrow + nplane;
1480 }
1481 if (y < maxn[Y_17])
1482 neigh[20] = k + nrow + nplane;
1483 if (x > minn[X_17]) {
1484 if (y < maxn[Y_17])
1485 neigh[21] = k - 1 + nrow + nplane;
1486 neigh[22] = k - 1 + nplane;
1487 if (y > minn[Y_17])
1488 neigh[23] = k - 1 - nrow + nplane;
1489 }
1490 if (y > minn[Y_17]) {
1491 neigh[24] = k - nrow + nplane;
1492 if (x < maxn[X_17])
1493 neigh[25] = k + 1 - nrow + nplane;
1494 }
1495 }
1496 }
1497 }
1498 }
1499 printf("%d sources before omitting any.\n",sp->nuse);
1500 /*
1501 * Exclude infeasible points
1502 */
1503 for (k = 0; k < sp->np; k++) {
1504 VEC_DIFF_17(cm,&sp->rr(k,0),diff);
1505 dist = VEC_LEN_17(diff);
1506 if (dist < exclude || dist > maxdist) {
1507 sp->inuse[k] = FALSE;
1508 sp->nuse--;
1509 }
1510 }
1511 printf("%d sources after omitting infeasible sources.\n",sp->nuse);
1512 {
1513 std::vector<std::unique_ptr<MNESourceSpace>> sp_vec;
1514 sp_vec.push_back(std::move(sp));
1515 if (filter_source_spaces(surf,mindist,FiffCoordTrans(),sp_vec,NULL) != OK) {
1516 sp = std::move(sp_vec[0]);
1517 goto bad;
1518 }
1519 sp = std::move(sp_vec[0]);
1520 }
1521 printf("%d sources remaining after excluding the sources outside the surface and less than %6.1f mm inside.\n",sp->nuse,1000*mindist);
1522 /*
1523 * Omit unused vertices from the neighborhoods
1524 */
1525 printf("Adjusting the neighborhood info...");
1526 for (k = 0; k < sp->np; k++) {
1527 Eigen::VectorXi& neigh = sp->neighbor_vert[k];
1528 nneigh = sp->nneighbor_vert[k];
1529 if (sp->inuse[k]) {
1530 for (c = 0; c < nneigh; c++)
1531 if (!sp->inuse[neigh[c]])
1532 neigh[c] = -1;
1533 }
1534 else {
1535 for (c = 0; c < nneigh; c++)
1536 neigh[c] = -1;
1537 }
1538 }
1539 printf("[done]\n");
1540 /*
1541 * Set up the volume data (needed for creating the interpolation matrix)
1542 */
1543 {
1544 float r0[3],voxel_size[3],x_ras[3],y_ras[3],z_ras[3];
1545 int width,height,depth;
1546
1547 r0[X_17] = minn[X_17]*grid;
1548 r0[Y_17] = minn[Y_17]*grid;
1549 r0[Z_17] = minn[Z_17]*grid;
1550
1551 voxel_size[0] = grid;
1552 voxel_size[1] = grid;
1553 voxel_size[2] = grid;
1554
1555 width = (maxn[X_17]-minn[X_17]+1);
1556 height = (maxn[Y_17]-minn[Y_17]+1);
1557 depth = (maxn[Z_17]-minn[Z_17]+1);
1558
1559 for (k = 0; k < 3; k++)
1560 x_ras[k] = y_ras[k] = z_ras[k] = 0.0;
1561
1562 x_ras[0] = 1.0;
1563 y_ras[1] = 1.0;
1564 z_ras[2] = 1.0;
1565
1566 sp->voxel_surf_RAS_t = make_voxel_ras_trans(r0,x_ras,y_ras,z_ras,voxel_size);
1567 if (!sp->voxel_surf_RAS_t || sp->voxel_surf_RAS_t->isEmpty())
1568 goto bad;
1569
1570 sp->vol_dims[0] = width;
1571 sp->vol_dims[1] = height;
1572 sp->vol_dims[2] = depth;
1573 VEC_COPY_17(sp->voxel_size,voxel_size);
1574 }
1575
1576 return sp.release();
1577
1578bad : {
1579 return NULL;
1580 }
1581}
1582
1583//=============================================================================================================
1584
1585int MNESourceSpace::filter_source_spaces(const MNESurface& surf, float limit, const FiffCoordTrans& mri_head_t, std::vector<std::unique_ptr<MNESourceSpace>>& spaces, QTextStream *filtered) /* Provide a list of filtered points here */
1586/*
1587 * Remove all source space points closer to the surface than a given limit
1588 */
1589{
1590 MNESourceSpace* s;
1591 int k,p1,p2;
1592 float r1[3];
1593 float mindist,dist,diff[3];
1594 int minnode;
1595 int omit,omit_outside;
1596 double tot_angle;
1597 int nspace = static_cast<int>(spaces.size());
1598
1599 if (spaces[0]->coord_frame == FIFFV_COORD_HEAD && mri_head_t.isEmpty()) {
1600 printf("Source spaces are in head coordinates and no coordinate transform was provided!");
1601 return FAIL;
1602 }
1603 /*
1604 * How close are the source points to the surface?
1605 */
1606 printf("Source spaces are in ");
1607 if (spaces[0]->coord_frame == FIFFV_COORD_HEAD)
1608 printf("head coordinates.\n");
1609 else if (spaces[0]->coord_frame == FIFFV_COORD_MRI)
1610 printf("MRI coordinates.\n");
1611 else
1612 printf("unknown (%d) coordinates.\n",spaces[0]->coord_frame);
1613 printf("Checking that the sources are inside the bounding surface ");
1614 if (limit > 0.0)
1615 printf("and at least %6.1f mm away",1000*limit);
1616 printf(" (will take a few...)\n");
1617 omit = 0;
1618 omit_outside = 0;
1619 for (k = 0; k < nspace; k++) {
1620 s = spaces[k].get();
1621 for (p1 = 0; p1 < s->np; p1++)
1622 if (s->inuse[p1]) {
1623 VEC_COPY_17(r1,&s->rr(p1,0)); /* Transform the point to MRI coordinates */
1624 if (s->coord_frame == FIFFV_COORD_HEAD)
1626 /*
1627 * Check that the source is inside the inner skull surface
1628 */
1629 tot_angle = surf.sum_solids(Eigen::Map<const Eigen::Vector3f>(r1))/(4*M_PI);
1630 if (std::fabs(tot_angle-1.0) > 1e-5) {
1631 omit_outside++;
1632 s->inuse[p1] = FALSE;
1633 s->nuse--;
1634 if (filtered)
1635 *filtered << qSetFieldWidth(10) << qSetRealNumberPrecision(3) << Qt::fixed
1636 << 1000*r1[X_17] << " " << 1000*r1[Y_17] << " " << 1000*r1[Z_17] << "\n" << qSetFieldWidth(0);
1637 }
1638 else if (limit > 0.0) {
1639 /*
1640 * Check the distance limit
1641 */
1642 mindist = 1.0;
1643 minnode = 0;
1644 for (p2 = 0; p2 < surf.np; p2++) {
1645 VEC_DIFF_17(r1,&surf.rr(p2,0),diff);
1646 dist = VEC_LEN_17(diff);
1647 if (dist < mindist) {
1648 mindist = dist;
1649 minnode = p2;
1650 }
1651 }
1652 if (mindist < limit) {
1653 omit++;
1654 s->inuse[p1] = FALSE;
1655 s->nuse--;
1656 if (filtered)
1657 *filtered << qSetFieldWidth(10) << qSetRealNumberPrecision(3) << Qt::fixed
1658 << 1000*r1[X_17] << " " << 1000*r1[Y_17] << " " << 1000*r1[Z_17] << "\n" << qSetFieldWidth(0);
1659 }
1660 }
1661 }
1662 }
1663 (void)minnode; // squash compiler warning, this is unused
1664 if (omit_outside > 0)
1665 printf("%d source space points omitted because they are outside the inner skull surface.\n",
1666 omit_outside);
1667 if (omit > 0)
1668 printf("%d source space points omitted because of the %6.1f-mm distance limit.\n",
1669 omit,1000*limit);
1670 printf("Thank you for waiting.\n");
1671 return OK;
1672}
1673
1674//=============================================================================================================
1675
1677{
1678 FilterThreadArg* a = arg;
1679 int p1,p2;
1680 double tot_angle;
1681 int omit,omit_outside;
1682 float r1[3];
1683 float mindist,dist,diff[3];
1684 int minnode;
1685
1686 QSharedPointer<MNESurface> surf = a->surf.toStrongRef();
1687 if (!surf) {
1688 a->stat = FAIL;
1689 return;
1690 }
1691
1692 omit = 0;
1693 omit_outside = 0;
1694
1695 for (p1 = 0; p1 < a->s->np; p1++) {
1696 if (a->s->inuse[p1]) {
1697 VEC_COPY_17(r1,&a->s->rr(p1,0)); /* Transform the point to MRI coordinates */
1698 if (a->s->coord_frame == FIFFV_COORD_HEAD) {
1699 Q_ASSERT(a->mri_head_t);
1701 }
1702 /*
1703 * Check that the source is inside the inner skull surface
1704 */
1705 tot_angle = surf->sum_solids(Eigen::Map<const Eigen::Vector3f>(r1))/(4*M_PI);
1706 if (std::fabs(tot_angle-1.0) > 1e-5) {
1707 omit_outside++;
1708 a->s->inuse[p1] = FALSE;
1709 a->s->nuse--;
1710 if (a->filtered)
1711 *a->filtered << qSetFieldWidth(10) << qSetRealNumberPrecision(3) << Qt::fixed
1712 << 1000*r1[X_17] << " " << 1000*r1[Y_17] << " " << 1000*r1[Z_17] << "\n" << qSetFieldWidth(0);
1713 }
1714 else if (a->limit > 0.0) {
1715 /*
1716 * Check the distance limit
1717 */
1718 mindist = 1.0;
1719 minnode = 0;
1720 for (p2 = 0; p2 < surf->np; p2++) {
1721 VEC_DIFF_17(r1,&surf->rr(p2,0),diff);
1722 dist = VEC_LEN_17(diff);
1723 if (dist < mindist) {
1724 mindist = dist;
1725 minnode = p2;
1726 }
1727 }
1728 if (mindist < a->limit) {
1729 omit++;
1730 a->s->inuse[p1] = FALSE;
1731 a->s->nuse--;
1732 if (a->filtered)
1733 *a->filtered << qSetFieldWidth(10) << qSetRealNumberPrecision(3) << Qt::fixed
1734 << 1000*r1[X_17] << " " << 1000*r1[Y_17] << " " << 1000*r1[Z_17] << "\n" << qSetFieldWidth(0);
1735 }
1736 }
1737 }
1738 }
1739 (void)minnode; // squash compiler warning, set but unused
1740 if (omit_outside > 0)
1741 printf("%d source space points omitted because they are outside the inner skull surface.\n",
1742 omit_outside);
1743 if (omit > 0)
1744 printf("%d source space points omitted because of the %6.1f-mm distance limit.\n",
1745 omit,1000*a->limit);
1746 a->stat = OK;
1747 return;
1748}
1749
1750//=============================================================================================================
1751
1752int MNESourceSpace::filter_source_spaces(float limit, const QString& bemfile, const FiffCoordTrans& mri_head_t, std::vector<std::unique_ptr<MNESourceSpace>>& spaces, QTextStream *filtered, bool use_threads)
1753/*
1754 * Remove all source space points closer to the surface than a given limit
1755 */
1756{
1757 QSharedPointer<MNESurface> surf;
1758 int k;
1759 int nproc = QThread::idealThreadCount();
1760 FilterThreadArg* a;
1761 int nspace = static_cast<int>(spaces.size());
1762
1763 if (bemfile.isEmpty())
1764 return OK;
1765
1766 {
1768 if (!rawSurf) {
1769 qCritical("BEM model does not have the inner skull triangulation!");
1770 return FAIL;
1771 }
1772 surf.reset(rawSurf);
1773 }
1774 /*
1775 * How close are the source points to the surface?
1776 */
1777 printf("Source spaces are in ");
1778 if (spaces[0]->coord_frame == FIFFV_COORD_HEAD)
1779 printf("head coordinates.\n");
1780 else if (spaces[0]->coord_frame == FIFFV_COORD_MRI)
1781 printf("MRI coordinates.\n");
1782 else
1783 printf("unknown (%d) coordinates.\n",spaces[0]->coord_frame);
1784 printf("Checking that the sources are inside the inner skull ");
1785 if (limit > 0.0)
1786 printf("and at least %6.1f mm away",1000*limit);
1787 printf(" (will take a few...)\n");
1788 if (nproc < 2 || nspace == 1 || !use_threads) {
1789 /*
1790 * This is the conventional calculation
1791 */
1792 for (k = 0; k < nspace; k++) {
1793 a = new FilterThreadArg();
1794 a->s = spaces[k].get();
1795 a->mri_head_t = std::make_unique<FiffCoordTrans>(mri_head_t);
1796 a->surf = surf;
1797 a->limit = limit;
1798 a->filtered = filtered;
1800 if(a)
1801 delete a;
1802 spaces[k]->rearrange_source_space();
1803 }
1804 }
1805 else {
1806 /*
1807 * Calculate all (both) source spaces simultaneously
1808 */
1809 QList<FilterThreadArg*> args;//filterThreadArg *args = MALLOC_17(nspace,filterThreadArg);
1810
1811 for (k = 0; k < nspace; k++) {
1812 a = new FilterThreadArg();
1813 a->s = spaces[k].get();
1814 a->mri_head_t = std::make_unique<FiffCoordTrans>(mri_head_t);
1815 a->surf = surf;
1816 a->limit = limit;
1817 a->filtered = filtered;
1818 args.append(a);
1819 }
1820 /*
1821 * Ready to start the threads & Wait for them to complete
1822 */
1823 QtConcurrent::blockingMap(args, filter_source_space);
1824
1825 for (k = 0; k < nspace; k++) {
1826 spaces[k]->rearrange_source_space();
1827 if(args[k])
1828 delete args[k];
1829 }
1830 }
1831 printf("Thank you for waiting.\n\n");
1832
1833 return OK;
1834}
1835
1836//=============================================================================================================
1837
1838int MNESourceSpace::read_source_spaces(const QString &name, std::vector<std::unique_ptr<MNESourceSpace>>& spaces)
1839/*
1840 * Read source spaces from a FIFF file
1841 */
1842{
1843 QFile file(name);
1844 FiffStream::SPtr stream(new FiffStream(&file));
1845
1846 std::vector<std::unique_ptr<MNESourceSpace>> local_spaces;
1847 std::unique_ptr<MNESourceSpace> new_space;
1848 QList<FiffDirNode::SPtr> sources;
1849 FiffDirNode::SPtr node;
1850 FiffTag::SPtr t_pTag;
1851 int j,k,p,q;
1852 int ntri;
1853 int *nearest = NULL;
1854 float *nearest_dist = NULL;
1855 int *nneighbors = NULL;
1856 int *neighbors = NULL;
1857 int *vol_dims = NULL;
1858
1859 if(!stream->open())
1860 goto bad;
1861
1862 sources = stream->dirtree()->dir_tree_find(FIFFB_MNE_SOURCE_SPACE);
1863 if (sources.size() == 0) {
1864 printf("No source spaces available here");
1865 goto bad;
1866 }
1867 for (j = 0; j < sources.size(); j++) {
1869 node = sources[j];
1870 /*
1871 * Get the mandatory data first
1872 */
1873 if (!node->find_tag(stream, FIFF_MNE_SOURCE_SPACE_NPOINTS, t_pTag)) {
1874 goto bad;
1875 }
1876 new_space->np = *t_pTag->toInt();
1877 if (new_space->np == 0) {
1878 printf("No points in this source space");
1879 goto bad;
1880 }
1881 if (!node->find_tag(stream, FIFF_MNE_SOURCE_SPACE_POINTS, t_pTag)) {
1882 goto bad;
1883 }
1884 MatrixXf tmp_rr = t_pTag->toFloatMatrix().transpose();
1885 new_space->rr = tmp_rr;
1886 if (!node->find_tag(stream, FIFF_MNE_SOURCE_SPACE_NORMALS, t_pTag)) {
1887 goto bad;
1888 }
1889 MatrixXf tmp_nn = t_pTag->toFloatMatrix().transpose();
1890 new_space->nn = tmp_nn;
1891 if (!node->find_tag(stream, FIFF_MNE_COORD_FRAME, t_pTag)) {
1892 new_space->coord_frame = FIFFV_COORD_MRI;
1893 }
1894 else {
1895 new_space->coord_frame = *t_pTag->toInt();
1896 }
1897 if (node->find_tag(stream, FIFF_MNE_SOURCE_SPACE_ID, t_pTag)) {
1898 new_space->id = *t_pTag->toInt();
1899 }
1900 if (node->find_tag(stream, FIFF_SUBJ_HIS_ID, t_pTag)) {
1901 new_space->subject = (char *)t_pTag->data();
1902 }
1903 if (node->find_tag(stream, FIFF_MNE_SOURCE_SPACE_TYPE, t_pTag)) {
1904 new_space->type = *t_pTag->toInt();
1905 }
1906 ntri = 0;
1907 if (node->find_tag(stream, FIFF_BEM_SURF_NTRI, t_pTag)) {
1908 ntri = *t_pTag->toInt();
1909 }
1910 else if (node->find_tag(stream, FIFF_MNE_SOURCE_SPACE_NTRI, t_pTag)) {
1911 ntri = *t_pTag->toInt();
1912 }
1913 if (ntri > 0) {
1914
1915 if (!node->find_tag(stream, FIFF_BEM_SURF_TRIANGLES, t_pTag)) {
1916 if (!node->find_tag(stream, FIFF_MNE_SOURCE_SPACE_TRIANGLES, t_pTag)) {
1917 goto bad;
1918 }
1919 }
1920
1921 MatrixXi tmp_itris = t_pTag->toIntMatrix().transpose();
1922 tmp_itris.array() -= 1;
1923 new_space->itris = tmp_itris;
1924 new_space->ntri = ntri;
1925 }
1926 if (!node->find_tag(stream, FIFF_MNE_SOURCE_SPACE_NUSE, t_pTag)) {
1927 if (new_space->type == FIFFV_MNE_SPACE_VOLUME) {
1928 /*
1929 * Use all
1930 */
1931 new_space->nuse = new_space->np;
1932 new_space->inuse = Eigen::VectorXi::Ones(new_space->nuse);
1933 new_space->vertno = Eigen::VectorXi::LinSpaced(new_space->nuse, 0, new_space->nuse - 1);
1934 }
1935 else {
1936 /*
1937 * None in use
1938 * NOTE: The consequences of this change have to be evaluated carefully
1939 */
1940 new_space->nuse = 0;
1941 new_space->inuse = Eigen::VectorXi::Zero(new_space->np);
1942 new_space->vertno.resize(0);
1943 }
1944 }
1945 else {
1946 new_space->nuse = *t_pTag->toInt();
1947 if (!node->find_tag(stream, FIFF_MNE_SOURCE_SPACE_SELECTION, t_pTag)) {
1948 goto bad;
1949 }
1950
1951 qDebug() << "ToDo: Check whether new_space->inuse contains the right stuff!!! - use VectorXi instead";
1952 new_space->inuse = Eigen::VectorXi::Zero(new_space->np);
1953 if (new_space->nuse > 0) {
1954 new_space->vertno = Eigen::VectorXi::Zero(new_space->nuse);
1955 for (k = 0, p = 0; k < new_space->np; k++) {
1956 new_space->inuse[k] = t_pTag->toInt()[k]; //DEBUG
1957 if (new_space->inuse[k])
1958 new_space->vertno[p++] = k;
1959 }
1960 }
1961 else {
1962 new_space->vertno.resize(0);
1963 }
1964 /*
1965 * Selection triangulation
1966 */
1967 ntri = 0;
1968 if (node->find_tag(stream, FIFF_MNE_SOURCE_SPACE_NUSE_TRI, t_pTag)) {
1969 ntri = *t_pTag->toInt();
1970 }
1971 if (ntri > 0) {
1972
1973 if (!node->find_tag(stream, FIFF_MNE_SOURCE_SPACE_USE_TRIANGLES, t_pTag)) {
1974 goto bad;
1975 }
1976
1977 MatrixXi tmp_itris = t_pTag->toIntMatrix().transpose();
1978 tmp_itris.array() -= 1;
1979 new_space->use_itris = tmp_itris;
1980 new_space->nuse_tri = ntri;
1981 }
1982 /*
1983 * The patch information becomes relevant here
1984 */
1985 if (node->find_tag(stream, FIFF_MNE_SOURCE_SPACE_NEAREST, t_pTag)) {
1986 nearest = t_pTag->toInt();
1987 new_space->nearest.resize(new_space->np);
1988 for (k = 0; k < new_space->np; k++) {
1989 new_space->nearest[k].vert = k;
1990 new_space->nearest[k].nearest = nearest[k];
1991 new_space->nearest[k].patch = nullptr;
1992 }
1993
1994 if (!node->find_tag(stream, FIFF_MNE_SOURCE_SPACE_NEAREST_DIST, t_pTag)) {
1995 goto bad;
1996 }
1997 qDebug() << "ToDo: Check whether nearest_dist contains the right stuff!!! - use VectorXf instead";
1998 nearest_dist = t_pTag->toFloat();
1999 for (k = 0; k < new_space->np; k++) {
2000 new_space->nearest[k].dist = nearest_dist[k];
2001 }
2002 // FREE_17(nearest); nearest = NULL;
2003 // FREE_17(nearest_dist); nearest_dist = NULL;
2004 }
2005 /*
2006 * We may have the distance matrix
2007 */
2008 if (node->find_tag(stream, FIFF_MNE_SOURCE_SPACE_DIST_LIMIT, t_pTag)) {
2009 new_space->dist_limit = *t_pTag->toFloat();
2010 if (node->find_tag(stream, FIFF_MNE_SOURCE_SPACE_DIST, t_pTag)) {
2011 // SparseMatrix<double> tmpSparse = t_pTag->toSparseFloatMatrix();
2012 auto dist_lower = FiffSparseMatrix::fiff_get_float_sparse_matrix(t_pTag);
2013 if (!dist_lower) {
2014 goto bad;
2015 }
2016 auto dist_full = dist_lower->mne_add_upper_triangle_rcs();
2017 if (!dist_full) {
2018 goto bad;
2019 }
2020 new_space->dist = std::move(*dist_full);
2021 }
2022 else
2023 new_space->dist_limit = 0.0;
2024 }
2025 }
2026 /*
2027 * For volume source spaces we might have the neighborhood information
2028 */
2029 if (new_space->type == FIFFV_MNE_SPACE_VOLUME) {
2030 int ntot,nvert,ntot_count,nneigh;
2031 int *neigh;
2032
2033 nneighbors = neighbors = NULL;
2034 ntot = nvert = 0;
2035 if (node->find_tag(stream, FIFF_MNE_SOURCE_SPACE_NEIGHBORS, t_pTag)) {
2036 qDebug() << "ToDo: Check whether neighbors contains the right stuff!!! - use VectorXi instead";
2037 neighbors = t_pTag->toInt();
2038 ntot = t_pTag->size()/sizeof(fiff_int_t);
2039 }
2040 if (node->find_tag(stream, FIFF_MNE_SOURCE_SPACE_NNEIGHBORS, t_pTag)) {
2041 qDebug() << "ToDo: Check whether nneighbors contains the right stuff!!! - use VectorXi instead";
2042 nneighbors = t_pTag->toInt();
2043 nvert = t_pTag->size()/sizeof(fiff_int_t);
2044 }
2045 if (neighbors && nneighbors) {
2046 if (nvert != new_space->np) {
2047 printf("Inconsistent neighborhood data in file.");
2048 goto bad;
2049 }
2050 for (k = 0, ntot_count = 0; k < nvert; k++)
2051 ntot_count += nneighbors[k];
2052 if (ntot_count != ntot) {
2053 printf("Inconsistent neighborhood data in file.");
2054 goto bad;
2055 }
2056 new_space->nneighbor_vert = Eigen::VectorXi::Zero(nvert);
2057 new_space->neighbor_vert.resize(nvert);
2058 for (k = 0, q = 0; k < nvert; k++) {
2059 new_space->nneighbor_vert[k] = nneigh = nneighbors[k];
2060 new_space->neighbor_vert[k] = Eigen::VectorXi(nneigh);
2061 for (p = 0; p < nneigh; p++,q++)
2062 new_space->neighbor_vert[k][p] = neighbors[q];
2063 }
2064 }
2065 FREE_17(neighbors);
2066 FREE_17(nneighbors);
2067 nneighbors = neighbors = NULL;
2068 /*
2069 * There might be a coordinate transformation and dimensions
2070 */
2072 if (!node->find_tag(stream, FIFF_MNE_SOURCE_SPACE_VOXEL_DIMS, t_pTag)) {
2073 qDebug() << "ToDo: Check whether vol_dims contains the right stuff!!! - use VectorXi instead";
2074 vol_dims = t_pTag->toInt();
2075 }
2076 if (vol_dims)
2077 VEC_COPY_17(new_space->vol_dims,vol_dims);
2078 {
2079 QList<FiffDirNode::SPtr> mris = node->dir_tree_find(FIFFB_MNE_PARENT_MRI_FILE);
2080
2081 if (mris.size() == 0) { /* The old way */
2083 if (node->find_tag(stream, FIFF_MNE_SOURCE_SPACE_MRI_FILE, t_pTag)) {
2084 qDebug() << "ToDo: Check whether new_space->MRI_volume contains the right stuff!!! - use QString instead";
2085 new_space->MRI_volume = (char *)t_pTag->data();
2086 }
2087 if (node->find_tag(stream, FIFF_MNE_SOURCE_SPACE_INTERPOLATOR, t_pTag)) {
2088 new_space->interpolator = std::move(*FiffSparseMatrix::fiff_get_float_sparse_matrix(t_pTag));
2089 }
2090 }
2091 else {
2092 if (node->find_tag(stream, FIFF_MNE_FILE_NAME, t_pTag)) {
2093 new_space->MRI_volume = (char *)t_pTag->data();
2094 }
2097
2098 if (mris[0]->find_tag(stream, FIFF_MNE_SOURCE_SPACE_INTERPOLATOR, t_pTag)) {
2099 new_space->interpolator = std::move(*FiffSparseMatrix::fiff_get_float_sparse_matrix(t_pTag));
2100 }
2101 if (mris[0]->find_tag(stream, FIFF_MRI_WIDTH, t_pTag)) {
2102 new_space->MRI_vol_dims[0] = *t_pTag->toInt();
2103 }
2104 if (mris[0]->find_tag(stream, FIFF_MRI_HEIGHT, t_pTag)) {
2105 new_space->MRI_vol_dims[1] = *t_pTag->toInt();
2106 }
2107 if (mris[0]->find_tag(stream, FIFF_MRI_DEPTH, t_pTag)) {
2108 new_space->MRI_vol_dims[2] = *t_pTag->toInt();
2109 }
2110 }
2111 }
2112 }
2113 new_space->add_triangle_data();
2114 local_spaces.push_back(std::move(new_space));
2115 }
2116 stream->close();
2117
2118 spaces = std::move(local_spaces);
2119
2120 return FIFF_OK;
2121
2122bad : {
2123 stream->close();
2124 // new_space and local_spaces auto-cleanup via unique_ptr
2126 FREE_17(nearest_dist);
2127 FREE_17(neighbors);
2128 FREE_17(nneighbors);
2130
2131 return FIFF_FAIL;
2132 }
2133}
2134
2135//=============================================================================================================
2136
2137int MNESourceSpace::transform_source_spaces_to(int coord_frame, const FiffCoordTrans& t, std::vector<std::unique_ptr<MNESourceSpace>>& spaces)
2138/*
2139 * Facilitate the transformation of the source spaces
2140 */
2141{
2142 MNESourceSpace* s;
2143 int k;
2144 int nspace = static_cast<int>(spaces.size());
2145
2146 for (k = 0; k < nspace; k++) {
2147 s = spaces[k].get();
2148 if (s->coord_frame != coord_frame) {
2149 if (!t.isEmpty()) {
2150 if (s->coord_frame == t.from && t.to == coord_frame) {
2151 if (s->transform_source_space(t) != OK)
2152 return FAIL;
2153 }
2154 else if (s->coord_frame == t.to && t.from == coord_frame) {
2155 FiffCoordTrans my_t = t.inverted();
2156 if (s->transform_source_space(my_t) != OK) {
2157 return FAIL;
2158 }
2159 }
2160 else {
2161 printf("Could not transform a source space because of transformation incompatibility.");
2162 return FAIL;
2163 }
2164 }
2165 else {
2166 printf("Could not transform a source space because of missing coordinate transformation.");
2167 return FAIL;
2168 }
2169 }
2170 }
2171 return OK;
2172}
2173
2174//=============================================================================================================
2175
2176#define LH_LABEL_TAG "-lh.label"
2177#define RH_LABEL_TAG "-rh.label"
2178
2179int MNESourceSpace::restrict_sources_to_labels(std::vector<std::unique_ptr<MNESourceSpace>>& spaces, const QStringList& labels, int nlabel)
2180/*
2181 * Pick only sources within a label
2182 */
2183{
2184 MNESourceSpace* lh = NULL;
2185 MNESourceSpace* rh = NULL;
2186 MNESourceSpace* sp;
2187 Eigen::VectorXi lh_inuse;
2188 Eigen::VectorXi rh_inuse;
2189 Eigen::VectorXi sel;
2190 Eigen::VectorXi *inuse = nullptr;
2191 int k,p;
2192 int nspace = static_cast<int>(spaces.size());
2193
2194 if (nlabel == 0)
2195 return OK;
2196
2197 for (k = 0; k < nspace; k++) {
2198 if (spaces[k]->is_left_hemi()) {
2199 lh = spaces[k].get();
2200 lh_inuse = Eigen::VectorXi::Zero(lh->np);
2201 }
2202 else {
2203 rh = spaces[k].get();
2204 rh_inuse = Eigen::VectorXi::Zero(rh->np);
2205 }
2206 }
2207 /*
2208 * Go through each label file
2209 */
2210 for (k = 0; k < nlabel; k++) {
2211 /*
2212 * Which hemi?
2213 */
2214 if (labels[k].contains(LH_LABEL_TAG)){ //strstr(labels[k],LH_LABEL_TAG) != NULL) {
2215 sp = lh;
2216 inuse = &lh_inuse;
2217 }
2218 else if (labels[k].contains(RH_LABEL_TAG)){ //strstr(labels[k],RH_LABEL_TAG) != NULL) {
2219 sp = rh;
2220 inuse = &rh_inuse;
2221 }
2222 else {
2223 printf("\tWarning: cannot assign label file %s to a hemisphere.\n",labels[k].toUtf8().constData());
2224 continue;
2225 }
2226 if (sp) {
2227 if (read_label(labels[k],sel) == FAIL)
2228 goto bad;
2229 for (p = 0; p < sel.size(); p++) {
2230 if (sel[p] >= 0 && sel[p] < sp->np)
2231 (*inuse)[sel[p]] = sp->inuse[sel[p]];
2232 else
2233 printf("vertex number out of range in %s (%d vs %d)\n",
2234 labels[k].toUtf8().constData(),sel[p],sp->np);
2235 }
2236 printf("Processed label file %s\n",labels[k].toUtf8().constData());
2237 }
2238 }
2239 if (lh) lh->update_inuse(std::move(lh_inuse));
2240 if (rh) rh->update_inuse(std::move(rh_inuse));
2241 return OK;
2242
2243bad : {
2244 return FAIL;
2245 }
2246}
2247
2248//=============================================================================================================
2249
2250int MNESourceSpace::read_label(const QString& label, Eigen::VectorXi& sel)
2251/*
2252 * Find the source points within a label
2253 */
2254{
2255 int res = FAIL;
2256
2257 int k,p,nlabel;
2258 char c;
2259 float fdum;
2260 /*
2261 * Read the label file
2262 */
2263 QFile inFile(label);
2264 if (!inFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
2265 qCritical() << label;//err_set_sys_error(label);
2266 goto out;
2267 }
2268 inFile.getChar(&c);
2269 if (c !='#') {
2270 qCritical("Label file does not start correctly.");
2271 goto out;
2272 }
2273 /*
2274 * Skip the comment line
2275 */
2276 while (inFile.getChar(&c) && c != '\n')
2277 ;
2278 {
2279 QTextStream in(&inFile);
2280 in >> nlabel;
2281 if (in.status() != QTextStream::Ok) {
2282 qCritical("Could not read the number of labelled points.");
2283 goto out;
2284 }
2285 sel.resize(nlabel);
2286 for (k = 0; k < nlabel; k++) {
2287 in >> p >> fdum >> fdum >> fdum >> fdum;
2288 if (in.status() != QTextStream::Ok) {
2289 qCritical("Could not read label point # %d",k+1);
2290 goto out;
2291 }
2292 sel[k] = p;
2293 }
2294 res = OK;
2295 }
2296
2297out : {
2298 if (res != OK) {
2299 sel.resize(0);
2300 }
2301 return res;
2302 }
2303}
2304
#define M_PI
FiffTag class declaration, which provides fiff tag I/O and processing methods.
Fiff constants.
#define FIFFV_MNE_SURF_RIGHT_HEMI
#define FIFF_MNE_SOURCE_SPACE_ID
#define FIFF_MNE_SOURCE_SPACE_DIST_LIMIT
#define FIFF_MNE_SOURCE_SPACE_VOXEL_DIMS
#define FIFF_MNE_COORD_FRAME
#define FIFF_OK
#define FIFF_MNE_SOURCE_SPACE_TYPE
#define FIFFV_MNE_SURF_UNKNOWN
#define FIFF_MNE_SOURCE_SPACE_SELECTION
#define FIFFV_MNE_SURF_LEFT_HEMI
#define FIFF_MNE_SOURCE_SPACE_USE_TRIANGLES
#define FIFF_MNE_SOURCE_SPACE_NUSE_TRI
#define FIFF_MNE_SOURCE_SPACE_INTERPOLATOR
#define FIFF_MNE_SOURCE_SPACE_NORMALS
#define FIFFV_MNE_COORD_MRI_VOXEL
#define FIFF_MNE_SOURCE_SPACE_NEAREST_DIST
#define FIFF_MNE_SOURCE_SPACE_DIST
#define FIFF_MNE_SOURCE_SPACE_POINTS
#define FIFF_FAIL
#define FIFFV_MNE_SPACE_SURFACE
#define FIFF_MNE_SOURCE_SPACE_NTRI
#define FIFF_MNE_SOURCE_SPACE_MRI_FILE
#define FIFFB_MNE_SOURCE_SPACE
#define FIFF_MNE_SOURCE_SPACE_NPOINTS
#define FIFFV_MNE_SPACE_VOLUME
#define FIFFV_NO_MOVE
#define FIFFV_COORD_HEAD
#define FIFFV_COORD_MRI
#define FIFF_MNE_SOURCE_SPACE_TRIANGLES
#define FIFF_MNE_SOURCE_SPACE_NUSE
#define FIFFV_MOVE
#define FIFFV_MNE_COORD_RAS
#define FIFF_MNE_FILE_NAME
#define FIFF_MNE_SOURCE_SPACE_NEAREST
#define FIFFB_MNE_PARENT_MRI_FILE
FiffStream class declaration.
#define FIFF_MRI_DEPTH
Definition fiff_file.h:657
#define FIFF_BEM_SURF_TRIANGLES
Definition fiff_file.h:736
#define FIFF_MRI_HEIGHT
Definition fiff_file.h:655
#define FIFF_BEM_SURF_NTRI
Definition fiff_file.h:734
#define FIFFV_BEM_SURF_ID_BRAIN
Definition fiff_file.h:749
#define FIFF_SUBJ_HIS_ID
Definition fiff_file.h:573
#define FIFF_MRI_WIDTH
Definition fiff_file.h:653
FiffSparseMatrix class declaration.
FiffCoordTrans class declaration.
IOUtils class declaration.
MNEMghTagGroup class declaration.
Filter Thread Argument (FilterThreadArg) class declaration.
MNE Patch Information (MNEPatchInfo) class declaration.
#define MNE_SOURCE_SPACE_VOLUME
Definition mne_types.h:104
MNESurface class declaration.
#define VEC_COPY_17(to, from)
#define Z_17
#define FREE_17(x)
#define Y_17
#define X_17
#define VEC_DIFF_17(from, to, diff)
#define VEC_LEN_17(x)
MNENearest class declaration.
MNESourceSpace class declaration.
#define MALLOC_17(x, t)
#define NNEIGHBORS
#define TAG_OLD_USEREALRAS
#define TAG_OLD_MGH_XFORM
#define TAG_USEREALRAS
#define Z_51
#define X_51
#define ALLOC_ICMATRIX_17(x, y)
#define FREE_17(x)
#define TAG_OLD_COLORTABLE
#define CURVATURE_FILE_MAGIC_NUMBER
#define FREE_CMATRIX_17(m)
#define LH_LABEL_TAG
float ** mne_cmatrix_51(int nr, int nc)
#define FREE_ICMATRIX_17(m)
#define EVEN(n)
#define Y_51
#define ALLOC_CMATRIX_17(x, y)
#define MALLOC_51(x, t)
#define RH_LABEL_TAG
#define QUAD_FILE_MAGIC_NUMBER
#define NEW_QUAD_FILE_MAGIC_NUMBER
#define FIFFV_MNE_COORD_SURFACE_RAS
#define FIFF_MNE_SOURCE_SPACE_NEIGHBORS
#define FIFF_MNE_SOURCE_SPACE_NNEIGHBORS
#define TAG_OLD_SURF_GEOM
#define TRIANGLE_FILE_MAGIC_NUMBER
#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).
qint32 fiff_int_t
Definition fiff_types.h:89
Coordinate transformation description.
static FiffCoordTrans readTransformFromNode(FiffStream::SPtr &stream, const FiffDirNode::SPtr &node, int from, int to)
FiffCoordTrans inverted() const
Eigen::MatrixX3f apply_inverse_trans(const Eigen::MatrixX3f &rr, bool do_move=true) const
Eigen::MatrixX3f apply_trans(const Eigen::MatrixX3f &rr, bool do_move=true) const
QSharedPointer< FiffDirNode > SPtr
FIFF sparse matrix storage.
static FiffSparseMatrix::UPtr fiff_get_float_sparse_matrix(FIFFLIB::FiffTag::SPtr &tag)
FIFF File I/O routines.
QSharedPointer< FiffStream > SPtr
QSharedPointer< FiffTag > SPtr
Definition fiff_tag.h:155
Thread-local arguments for parallel raw data filtering (channel range, filter kernel,...
QWeakPointer< MNESurface > surf
std::unique_ptr< FIFFLIB::FiffCoordTrans > mri_head_t
Single tag entry in a FreeSurfer MGH/MGZ file header.
Definition mne_mgh_tag.h:67
QByteArray data
Definition mne_mgh_tag.h:87
Collection of MNEMghTag entries from a FreeSurfer MGH/MGZ file footer.
std::vector< std::unique_ptr< MNEMghTag > > tags
This is used in the patch definitions.
Definition mne_nearest.h:78
MNEPatchInfo * patch
Patch information for a single source space point including vertex members and area.
virtual MNESourceSpace::SPtr clone() const
static std::unique_ptr< MNESourceSpace > load_surface_geom(const QString &surf_file, const QString &curv_file, int add_geometry, int check_too_many_neighbors)
static std::unique_ptr< MNESourceSpace > load_surface(const QString &surf_file, const QString &curv_file)
qint32 find_source_space_hemi() const
std::shared_ptr< MNESourceSpace > SPtr
static int restrict_sources_to_labels(std::vector< std::unique_ptr< MNESourceSpace > > &spaces, const QStringList &labels, int nlabel)
static int filter_source_spaces(const MNESurface &surf, float limit, const FIFFLIB::FiffCoordTrans &mri_head_t, std::vector< std::unique_ptr< MNESourceSpace > > &spaces, QTextStream *filtered)
static int read_source_spaces(const QString &name, std::vector< std::unique_ptr< MNESourceSpace > > &spaces)
static std::unique_ptr< MNESourceSpace > create_source_space(int np)
static void filter_source_space(FilterThreadArg *arg)
static MNESourceSpace * make_volume_source_space(const MNESurface &surf, float grid, float exclude, float mindist)
int transform_source_space(const FIFFLIB::FiffCoordTrans &t)
static int read_label(const QString &label, Eigen::VectorXi &sel)
static int transform_source_spaces_to(int coord_frame, const FIFFLIB::FiffCoordTrans &t, std::vector< std::unique_ptr< MNESourceSpace > > &spaces)
void update_inuse(Eigen::VectorXi new_inuse)
This defines a surface.
Definition mne_surface.h:79
double sum_solids(const Eigen::Vector3f &from) const
static MNESurface * read_bem_surface(const QString &name, int which, int add_geometry, float *sigmap)
std::vector< Eigen::VectorXi > neighbor_tri
int add_geometry_info(int do_normals, int check_too_many_neighbors)
std::vector< Eigen::VectorXi > neighbor_vert
std::optional< FIFFLIB::FiffCoordTrans > MRI_surf_RAS_RAS_t
FIFFLIB::FiffSparseMatrix dist
std::vector< MNENearest > nearest
std::optional< FIFFLIB::FiffSparseMatrix > interpolator
Eigen::Matrix< int, Eigen::Dynamic, 3, Eigen::RowMajor > TrianglesT
std::optional< MNEVolGeom > vol_geom
std::vector< std::optional< MNEPatchInfo > > patches
std::optional< FIFFLIB::FiffCoordTrans > MRI_voxel_surf_RAS_t
std::vector< MNETriangle > tris
std::optional< MNEMghTagGroup > mgh_tags
std::optional< FIFFLIB::FiffCoordTrans > voxel_surf_RAS_t
Eigen::Matrix< float, Eigen::Dynamic, 3, Eigen::RowMajor > PointsT
MRI data volume geometry information like FreeSurfer keeps it.
static qint32 swap_int(qint32 source)
Definition ioutils.cpp:128
static qint64 swap_long(qint64 source)
Definition ioutils.cpp:162
static float swap_float(float source)
Definition ioutils.cpp:207
static qint16 swap_short(qint16 source)
Definition ioutils.cpp:115