42#include <QtConcurrent>
50#ifndef _USE_MATH_DEFINES
51#define _USE_MATH_DEFINES
95int read_int3(QFile& in,
int& ival)
102 if (in.read(
reinterpret_cast<char*
>(&s), 3) != 3) {
103 qCritical(
"read_int3 could not read data");
107 ival = ((s >> 8) & 0xffffff);
111int read_int(QFile& in, qint32& ival)
117 if (in.read(
reinterpret_cast<char*
>(&s),
sizeof(qint32)) !=
static_cast<qint64
>(
sizeof(qint32))) {
118 qCritical(
"read_int could not read data");
125int read_int2(QFile& in,
int& ival)
131 if (in.read(
reinterpret_cast<char*
>(&s),
sizeof(
short)) !=
static_cast<qint64
>(
sizeof(
short))) {
132 qCritical(
"read_int2 could not read data");
139int read_float(QFile& in,
float& fval)
145 if (in.read(
reinterpret_cast<char*
>(&f),
sizeof(
float)) !=
static_cast<qint64
>(
sizeof(
float))) {
146 qCritical(
"read_float could not read data");
153int read_long(QFile& in,
long long& lval)
159 if (in.read(
reinterpret_cast<char*
>(&s),
sizeof(
long long)) !=
static_cast<qint64
>(
sizeof(
long long))) {
160 qCritical(
"read_long could not read data");
171int check_vertex(
int no,
int maxno)
173 if (no < 0 || no > maxno - 1) {
174 qCritical(
"Illegal vertex number %d (max %d).", no, maxno);
193std::unique_ptr<MNEVolGeom> read_vol_geom(QFile& fp)
202 auto vg = std::make_unique<MNEVolGeom>();
210 const auto readFloats = [](
const QList<QByteArray>& tok,
float* dest,
int n) {
211 for (
int i = 0; i < n; ++i) {
212 if (tok.size() <= 2 + i)
215 const float v = tok.at(2 + i).toFloat(&ok);
222 while (!fp.atEnd() && counter < 8) {
223 const QByteArray lineData = fp.readLine(256);
224 if (lineData.isEmpty())
227 const QList<QByteArray> tok = lineData.simplified().split(
' ');
228 if (tok.isEmpty() || tok.first().isEmpty())
231 const QByteArray& param = tok.first();
232 if (param ==
"valid") {
234 vg->valid = tok.at(2).toInt();
237 }
else if (param ==
"filename") {
239 vg->filename = QString::fromUtf8(tok.at(2));
241 }
else if (param ==
"volume") {
242 if (tok.size() > 4) {
243 vg->width = tok.at(2).toInt();
244 vg->height = tok.at(3).toInt();
245 vg->depth = tok.at(4).toInt();
248 }
else if (param ==
"voxelsize") {
249 float size[3] = {vg->xsize, vg->ysize, vg->zsize};
250 readFloats(tok, size, 3);
254 vg->xsize = size[0] / 1000.0f;
255 vg->ysize = size[1] / 1000.0f;
256 vg->zsize = size[2] / 1000.0f;
258 }
else if (param ==
"xras") {
259 readFloats(tok, vg->x_ras, 3);
261 }
else if (param ==
"yras") {
262 readFloats(tok, vg->y_ras, 3);
264 }
else if (param ==
"zras") {
265 readFloats(tok, vg->z_ras, 3);
267 }
else if (param ==
"cras") {
268 readFloats(tok, vg->c_ras, 3);
269 vg->c_ras[0] = vg->c_ras[0] / 1000.0f;
270 vg->c_ras[1] = vg->c_ras[1] / 1000.0f;
271 vg->c_ras[2] = vg->c_ras[2] / 1000.0f;
283 vg = std::make_unique<MNEVolGeom>();
292int read_tag_data(QFile& fp,
int tag,
long long nbytes, QByteArray& val,
long long& nbytesp)
299 val = fp.read(nbytes);
300 if (val.size() != nbytes) {
301 qCritical(
"Failed to read %d bytes of tag data",
static_cast<int>(nbytes));
307 auto g = read_vol_geom(fp);
318 int width, height, depth;
319 float xsize, ysize, zsize;
320 float x_ras[3], y_ras[3], z_ras[3];
324 pod.valid = g->
valid;
325 pod.width = g->
width;
327 pod.depth = g->
depth;
328 pod.xsize = g->
xsize;
329 pod.ysize = g->
ysize;
330 pod.zsize = g->
zsize;
331 std::memcpy(pod.x_ras, g->
x_ras, 3 *
sizeof(
float));
332 std::memcpy(pod.y_ras, g->
y_ras, 3 *
sizeof(
float));
333 std::memcpy(pod.z_ras, g->
z_ras, 3 *
sizeof(
float));
334 std::memcpy(pod.c_ras, g->
c_ras, 3 *
sizeof(
float));
335 val = QByteArray(
reinterpret_cast<const char*
>(&pod),
sizeof(VolGeomPOD));
338 nbytesp = val.size();
341 if (read_int(fp, useRealRas) ==
FAIL)
343 val = QByteArray(
reinterpret_cast<const char*
>(&useRealRas),
sizeof(
int));
344 nbytesp =
sizeof(int);
346 qWarning(
"Encountered an unknown tag with no length specification : %d", tag);
357void add_mgh_tag_to_group(std::optional<MNEMghTagGroup>& g,
int tag,
long long len,
const QByteArray& data)
361 auto new_tag = std::make_unique<MNEMghTag>();
364 new_tag->data = data;
365 g->tags.push_back(std::move(new_tag));
372int read_next_tag(QFile& fp,
int& tagp,
long long& lenp, QByteArray& datap)
377 int ilen = 0, tag = 0;
381 if (fp.atEnd() || read_int(fp, tag) ==
FAIL) {
391 if (read_int(fp, ilen) ==
FAIL)
401 if (read_long(fp, len) ==
FAIL)
407 if (read_tag_data(fp, tag, len, datap, lenp) ==
FAIL)
416int read_mgh_tags(QFile& fp, std::optional<MNEMghTagGroup>& tagsp)
426 if (read_next_tag(fp, tag, len, tag_data) ==
FAIL)
430 add_mgh_tag_to_group(tagsp, tag, len, tag_data);
439int read_curvature_file(
const QString& fname,
440 Eigen::VectorXf& curv)
446 float curvmin, curvmax;
448 int nface = 0, val_pervert = 0;
452 if (!fp.open(QIODevice::ReadOnly)) {
453 qCritical() << fname;
457 if (read_int3(fp, magic) != 0) {
458 qCritical() <<
"Bad magic in" << fname;
466 if (read_int(fp, ncurv) != 0) {
470 if (read_int(fp, nface) != 0) {
475 qInfo(
"nvert = %d nface = %d\n", ncurv, nface);
477 if (read_int(fp, val_pervert) != 0) {
481 if (val_pervert != 1) {
482 qCritical(
"Values per vertex not equal to one.");
490 curvmin = curvmax = 0.0;
491 for (k = 0; k < ncurv; k++) {
492 if (read_float(fp, fval) != 0) {
497 if (curv[k] > curvmax)
499 if (curv[k] < curvmin)
507 if (read_int3(fp, nface) != 0) {
512 qInfo(
"nvert = %d nface = %d\n", ncurv, nface);
518 curvmin = curvmax = 0.0;
519 for (k = 0; k < ncurv; k++) {
520 if (read_int2(fp, val) != 0) {
524 curv[k] =
static_cast<float>(val) / 100.0;
525 if (curv[k] > curvmax)
527 if (curv[k] < curvmin)
532 qInfo(
"Curvature range: %f...%f\n", curvmin, curvmax);
541int read_triangle_file(
const QString& fname,
543 TrianglesT& triangles,
544 std::optional<MNEMghTagGroup>* tagsp)
553 qint32 nvert = 0, ntri = 0, nquad = 0;
561 if (!fp.open(QIODevice::ReadOnly)) {
562 qCritical() << fname;
565 if (read_int3(fp, magic) != 0) {
566 qCritical() <<
"Bad magic in" << fname;
572 qCritical() <<
"Bad magic in" << fname;
579 qInfo(
"Triangle file : ");
580 for (fp.getChar(&c); c !=
'\n'; fp.getChar(&c)) {
582 qCritical() <<
"Bad triangle file.";
591 if (read_int(fp, nvert) != 0)
593 if (read_int(fp, ntri) != 0)
595 qInfo(
" nvert = %d ntri = %d\n", nvert, ntri);
596 vert.resize(nvert, 3);
601 for (k = 0; k < nvert; k++) {
602 if (read_float(fp, vert(k, 0)) != 0)
604 if (read_float(fp, vert(k, 1)) != 0)
606 if (read_float(fp, vert(k, 2)) != 0)
612 for (k = 0; k < ntri; k++) {
613 if (read_int(fp, tri(k, 0)) != 0)
615 if (check_vertex(tri(k, 0), nvert) !=
OK)
617 if (read_int(fp, tri(k, 1)) != 0)
619 if (check_vertex(tri(k, 1), nvert) !=
OK)
621 if (read_int(fp, tri(k, 2)) != 0)
623 if (check_vertex(tri(k, 2), nvert) !=
OK)
628 if (read_int3(fp, nvert) != 0)
630 if (read_int3(fp, nquad) != 0)
632 qInfo(
"%s file : nvert = %d nquad = %d\n",
635 vert.resize(nvert, 3);
637 for (k = 0; k < nvert; k++) {
638 if (read_int2(fp, val) != 0)
640 vert(k, 0) = val / 100.0;
641 if (read_int2(fp, val) != 0)
643 vert(k, 1) = val / 100.0;
644 if (read_int2(fp, val) != 0)
646 vert(k, 2) = val / 100.0;
649 for (k = 0; k < nvert; k++) {
650 if (read_float(fp, vert(k, 0)) != 0)
652 if (read_float(fp, vert(k, 1)) != 0)
654 if (read_float(fp, vert(k, 2)) != 0)
660 for (k = 0, ntri = 0; k < nquad; k++) {
661 for (p = 0; p < 4; p++) {
662 if (read_int3(fp, quad[p]) != 0)
670#define EVEN(n) ((((n) / 2) * 2) == n)
672#define WHICH_FACE_SPLIT(vno0, vno1) \
673 (1 * nearbyint(sqrt(1.9 * vno0) + sqrt(3.5 * vno1)))
675 which = WHICH_FACE_SPLIT(quad[0], quad[1]);
683 tri(ntri, 0) = quad[0];
684 tri(ntri, 1) = quad[1];
685 tri(ntri, 2) = quad[3];
688 tri(ntri, 0) = quad[2];
689 tri(ntri, 1) = quad[3];
690 tri(ntri, 2) = quad[1];
693 tri(ntri, 0) = quad[0];
694 tri(ntri, 1) = quad[1];
695 tri(ntri, 2) = quad[2];
698 tri(ntri, 0) = quad[0];
699 tri(ntri, 1) = quad[2];
700 tri(ntri, 2) = quad[3];
709 std::optional<MNEMghTagGroup> tags;
710 if (read_mgh_tags(fp, tags) ==
FAIL) {
713 *tagsp = std::move(tags);
719 vertices = std::move(vert);
720 triangles = std::move(tri);
728std::optional<MNEVolGeom> get_volume_geom_from_tag(
const MNEMghTagGroup* tagsp)
736 int width, height, depth;
737 float xsize, ysize, zsize;
738 float x_ras[3], y_ras[3], z_ras[3];
742 for (
const auto& t : tagsp->
tags) {
744 if (t->len <
static_cast<long long>(
sizeof(VolGeomPOD)))
747 const unsigned char* d =
reinterpret_cast<const unsigned char*
>(t->data.constData());
749 std::memcpy(&pod, d,
sizeof(VolGeomPOD));
752 result.
valid = pod.valid;
753 result.
width = pod.width;
754 result.
height = pod.height;
755 result.
depth = pod.depth;
756 result.
xsize = pod.xsize;
757 result.
ysize = pod.ysize;
758 result.
zsize = pod.zsize;
759 std::memcpy(result.
x_ras, pod.x_ras, 3 *
sizeof(
float));
760 std::memcpy(result.
y_ras, pod.y_ras, 3 *
sizeof(
float));
761 std::memcpy(result.
z_ras, pod.z_ras, 3 *
sizeof(
float));
762 std::memcpy(result.
c_ras, pod.c_ras, 3 *
sizeof(
float));
764 if (t->len >
static_cast<long long>(
sizeof(VolGeomPOD)))
765 result.
filename = QString::fromUtf8(
766 reinterpret_cast<const char*
>(d +
sizeof(VolGeomPOD)));
784 rr = PointsT::Zero(
np, 3);
785 nn = NormalsT::Zero(
np, 3);
824 cm[0] =
cm[1] =
cm[2] = 0.0;
839 auto copy = std::make_shared<MNESourceSpace>(this->
np);
840 copy->type = this->
type;
843 copy->ntri = this->
ntri;
847 copy->nuse = this->
nuse;
848 copy->inuse = this->
inuse;
849 copy->vertno = this->
vertno;
850 copy->itris = this->
itris;
854 copy->dist = this->
dist;
866 for (k = 0; k <
np; k++)
882 for (k = 0, xave = 0.0; k <
np; k++)
894 double xave =
rr.col(0).sum();
908 int k, p, nuse_count;
910 inuse = std::move(new_inuse);
912 for (k = 0, nuse_count = 0; k <
np; k++)
919 for (k = 0, p = 0; k <
np; k++)
939 qCritical(
"Coordinate transformation does not match with the source space coordinate system.");
942 for (k = 0; k <
np; k++) {
947 for (k = 0; k <
ntri; k++)
960 std::vector<std::optional<MNEPatchInfo>> pinfo(
nuse);
963 qInfo(
"Computing patch statistics...\n");
969 qCritical(
"The patch information is not available.");
979 qInfo(
"\tareas, average normals, and mean deviations...");
984 for (p = 1, q = 0; p <
np; p++) {
987 qCritical(
"No vertices belong to the patch of vertex %d", nearest_data[p - 1].
nearest);
993 pinfo[q]->vert = nearest_data[p - 1].
nearest;
994 this_patch = nearest_data + p - nave;
995 pinfo[q]->memb_vert.resize(nave);
996 for (k = 0; k < nave; k++) {
997 pinfo[q]->memb_vert[k] = this_patch[k].
vert;
998 this_patch[k].
patch = &(*pinfo[q]);
1000 pinfo[q]->calculate_area(
this);
1001 pinfo[q]->calculate_normal_stats(
this);
1009 qCritical(
"No vertices belong to the patch of vertex %d", nearest_data[p - 1].
nearest);
1014 pinfo[q]->vert = nearest_data[p - 1].
nearest;
1015 this_patch = nearest_data + p - nave;
1016 pinfo[q]->memb_vert.resize(nave);
1017 for (k = 0; k < nave; k++) {
1018 pinfo[q]->memb_vert[k] = this_patch[k].
vert;
1019 this_patch[k].
patch = &(*pinfo[q]);
1021 pinfo[q]->calculate_area(
this);
1022 pinfo[q]->calculate_normal_stats(
this);
1025 qInfo(
" %d/%d [done]\n", q,
nuse);
1038 for (k = 0,
nuse = 0; k <
np; k++)
1046 for (k = 0, p = 0; k <
np; k++)
1062 auto res = std::make_unique<MNESourceSpace>();
1065 res->rr = PointsT::Zero(
np, 3);
1066 res->nn = NormalsT::Zero(
np, 3);
1067 res->inuse = VectorXi::Zero(
np);
1068 res->vertno = VectorXi::Zero(
np);
1072 res->tot_area = 0.0;
1079 res->subject.clear();
1083 res->dist_limit = -1.0;
1085 res->voxel_surf_RAS_t.reset();
1086 res->vol_dims[0] = res->vol_dims[1] = res->vol_dims[2] = 0;
1088 res->MRI_volume.clear();
1089 res->MRI_surf_RAS_RAS_t.reset();
1090 res->MRI_voxel_surf_RAS_t.reset();
1091 res->MRI_vol_dims[0] = res->MRI_vol_dims[1] = res->MRI_vol_dims[2] = 0;
1092 res->interpolator.reset();
1094 res->vol_geom.reset();
1095 res->mgh_tags.reset();
1097 res->cm[0] = res->cm[1] = res->cm[2] = 0.0;
1105 const QString& curv_file)
1113 const QString& curv_file,
1115 bool check_too_many_neighbors)
1120 std::unique_ptr<MNESourceSpace> s;
1121 std::optional<MNEMghTagGroup> tags;
1122 Eigen::VectorXf curvs;
1126 if (read_triangle_file(surf_file,
1132 if (!curv_file.isEmpty()) {
1133 if (read_curvature_file(curv_file, curvs) == -1)
1135 if (curvs.size() != verts.rows()) {
1136 qCritical() <<
"Incorrect number of vertices in the curvature file.";
1141 s = std::make_unique<MNESourceSpace>(0);
1142 s->rr = std::move(verts);
1143 s->itris = std::move(
tris);
1144 s->ntri = s->itris.rows();
1145 s->np = s->rr.rows();
1146 if (curvs.size() > 0) {
1147 s->curv = std::move(curvs);
1149 s->val = Eigen::VectorXf::Zero(s->np);
1151 if (check_too_many_neighbors) {
1152 if (s->add_geometry_info(
true) !=
OK)
1155 if (s->add_geometry_info2(
true) !=
OK)
1158 }
else if (s->nn.rows() == 0) {
1159 if (s->add_vertex_normals() !=
OK)
1162 s->add_triangle_data();
1164 s->inuse = Eigen::VectorXi::Ones(s->np);
1165 s->vertno = Eigen::VectorXi::LinSpaced(s->np, 0, s->np - 1);
1166 s->mgh_tags = std::move(tags);
1167 s->vol_geom = get_volume_geom_from_tag(s->mgh_tags ? &(*s->mgh_tags) :
nullptr);
1174static std::optional<FiffCoordTrans> make_voxel_ras_trans(
const Eigen::Vector3f& r0,
1175 const Eigen::Vector3f& x_ras,
1176 const Eigen::Vector3f& y_ras,
1177 const Eigen::Vector3f& z_ras,
1178 const Eigen::Vector3f& voxel_size)
1180 Eigen::Matrix3f rot;
1181 rot.row(0) = x_ras.transpose() * voxel_size[0];
1182 rot.row(1) = y_ras.transpose() * voxel_size[1];
1183 rot.row(2) = z_ras.transpose() * voxel_size[2];
1193 Eigen::Vector3f minV, maxV,
cm;
1194 int minn[3], maxn[3];
1195 float maxdist,
dist;
1197 std::unique_ptr<MNESourceSpace> sp;
1198 int np, nplane, nrow;
1205 minV = maxV = surf.
rr.row(0).transpose();
1207 for (k = 0; k < surf.
np; k++) {
1208 Eigen::Vector3f node = surf.
rr.row(k).transpose();
1210 minV = minV.cwiseMin(node);
1211 maxV = maxV.cwiseMax(node);
1213 cm /=
static_cast<float>(surf.
np);
1218 for (k = 0; k < surf.
np; k++) {
1219 dist = (surf.
rr.row(k).transpose() -
cm).norm();
1223 qInfo(
"FsSurface CM = (%6.1f %6.1f %6.1f) mm\n",
1224 1000 *
cm[
X], 1000 *
cm[
Y], 1000 *
cm[
Z]);
1225 qInfo(
"FsSurface fits inside a sphere with radius %6.1f mm\n", 1000 * maxdist);
1226 qInfo(
"FsSurface extent:\n"
1227 "\tx = %6.1f ... %6.1f mm\n"
1228 "\ty = %6.1f ... %6.1f mm\n"
1229 "\tz = %6.1f ... %6.1f mm\n",
1230 1000 * minV[
X], 1000 * maxV[
X],
1231 1000 * minV[
Y], 1000 * maxV[
Y],
1232 1000 * minV[
Z], 1000 * maxV[
Z]);
1233 for (c = 0; c < 3; c++) {
1235 maxn[c] = floor(std::fabs(maxV[c]) / grid) + 1;
1237 maxn[c] = -floor(std::fabs(maxV[c]) / grid) - 1;
1239 minn[c] = floor(std::fabs(minV[c]) / grid) + 1;
1241 minn[c] = -floor(std::fabs(minV[c]) / grid) - 1;
1243 qInfo(
"Grid extent:\n"
1244 "\tx = %6.1f ... %6.1f mm\n"
1245 "\ty = %6.1f ... %6.1f mm\n"
1246 "\tz = %6.1f ... %6.1f mm\n",
1247 1000 * (minn[0] * grid), 1000 * (maxn[0] * grid),
1248 1000 * (minn[1] * grid), 1000 * (maxn[1] * grid),
1249 1000 * (minn[2] * grid), 1000 * (maxn[2] * grid));
1254 for (c = 0; c < 3; c++)
1255 np =
np * (maxn[c] - minn[c] + 1);
1256 nplane = (maxn[0] - minn[0] + 1) * (maxn[1] - minn[1] + 1);
1257 nrow = (maxn[0] - minn[0] + 1);
1260 sp->nneighbor_vert = Eigen::VectorXi::Constant(sp->np,
NNEIGHBORS);
1261 sp->neighbor_vert.resize(sp->np);
1262 for (k = 0; k < sp->np; k++) {
1265 sp->nn(k, 0) = sp->nn(k, 1) = 0.0;
1267 sp->neighbor_vert[k] = Eigen::VectorXi::Constant(
NNEIGHBORS, -1);
1270 for (k = 0, z = minn[2]; z <= maxn[2]; z++) {
1271 for (y = minn[1]; y <= maxn[1]; y++) {
1272 for (x = minn[0]; x <= maxn[0]; x++, k++) {
1273 sp->rr(k, 0) = x * grid;
1274 sp->rr(k, 1) = y * grid;
1275 sp->rr(k, 2) = z * grid;
1280 Eigen::VectorXi& neigh = sp->neighbor_vert[k];
1282 neigh[0] = k - nplane;
1286 neigh[2] = k + nrow;
1290 neigh[4] = k - nrow;
1292 neigh[5] = k + nplane;
1299 neigh[6] = k + 1 - nplane;
1301 neigh[7] = k + 1 + nrow - nplane;
1304 neigh[8] = k + nrow - nplane;
1307 neigh[9] = k - 1 + nrow - nplane;
1308 neigh[10] = k - 1 - nplane;
1310 neigh[11] = k - 1 - nrow - nplane;
1313 neigh[12] = k - nrow - nplane;
1315 neigh[13] = k + 1 - nrow - nplane;
1321 if (x < maxn[0] && y < maxn[1])
1322 neigh[14] = k + 1 + nrow;
1325 neigh[15] = k - 1 + nrow;
1327 neigh[16] = k - 1 - nrow;
1330 if (y > minn[1] && x < maxn[0])
1331 neigh[17] = k + 1 - nrow;
1337 neigh[18] = k + 1 + nplane;
1339 neigh[19] = k + 1 + nrow + nplane;
1342 neigh[20] = k + nrow + nplane;
1345 neigh[21] = k - 1 + nrow + nplane;
1346 neigh[22] = k - 1 + nplane;
1348 neigh[23] = k - 1 - nrow + nplane;
1351 neigh[24] = k - nrow + nplane;
1353 neigh[25] = k + 1 - nrow + nplane;
1359 qInfo(
"%d sources before omitting any.\n", sp->nuse);
1363 for (k = 0; k < sp->np; k++) {
1364 dist = (sp->rr.row(k).transpose() -
cm).norm();
1370 qInfo(
"%d sources after omitting infeasible sources.\n", sp->nuse);
1372 std::vector<std::unique_ptr<MNESourceSpace>> sp_vec;
1373 sp_vec.push_back(std::move(sp));
1377 sp = std::move(sp_vec[0]);
1379 qInfo(
"%d sources remaining after excluding the sources outside the surface and less than %6.1f mm inside.\n", sp->nuse, 1000 * mindist);
1382 Eigen::VectorXi used(sp->nuse);
1383 for (k = 0, c = 0; k < sp->np; k++)
1391 qInfo(
"Adjusting the neighborhood info...");
1392 for (k = 0; k < sp->np; k++) {
1393 Eigen::VectorXi& neigh = sp->neighbor_vert[k];
1394 nneigh = sp->nneighbor_vert[k];
1396 for (c = 0; c < nneigh; c++)
1397 if (!sp->inuse[neigh[c]])
1400 for (c = 0; c < nneigh; c++)
1409 Eigen::Vector3f r0(minn[0] * grid, minn[1] * grid, minn[2] * grid);
1410 Eigen::Vector3f
voxel_size(grid, grid, grid);
1411 Eigen::Vector3f x_ras = Eigen::Vector3f::UnitX();
1412 Eigen::Vector3f y_ras = Eigen::Vector3f::UnitY();
1413 Eigen::Vector3f z_ras = Eigen::Vector3f::UnitZ();
1414 int width = (maxn[0] - minn[0] + 1);
1415 int height = (maxn[1] - minn[1] + 1);
1416 int depth = (maxn[2] - minn[2] + 1);
1418 sp->voxel_surf_RAS_t = make_voxel_ras_trans(r0, x_ras, y_ras, z_ras,
voxel_size);
1419 if (!sp->voxel_surf_RAS_t || sp->voxel_surf_RAS_t->isEmpty())
1422 sp->vol_dims[0] = width;
1423 sp->vol_dims[1] = height;
1424 sp->vol_dims[2] = depth;
1425 Eigen::Map<Eigen::Vector3f>(sp->voxel_size) =
voxel_size;
1441 float mindist,
dist;
1442 int omit, omit_outside;
1444 int nspace =
static_cast<int>(spaces.size());
1447 qCritical(
"Source spaces are in head coordinates and no coordinate transform was provided!");
1453 qInfo(
"Source spaces are in ");
1455 qInfo(
"head coordinates.\n");
1457 qInfo(
"MRI coordinates.\n");
1459 qWarning(
"unknown (%d) coordinates.\n", spaces[0]->
coord_frame);
1460 qInfo(
"Checking that the sources are inside the bounding surface ");
1462 qInfo(
"and at least %6.1f mm away", 1000 * limit);
1463 qInfo(
" (will take a few...)\n");
1466 for (k = 0; k < nspace; k++) {
1467 s = spaces[k].get();
1468 for (p1 = 0; p1 < s->
np; p1++)
1470 r1 = s->
rr.row(p1).transpose();
1476 tot_angle = surf.
sum_solids(r1) / (4 * M_PI);
1477 if (std::fabs(tot_angle - 1.0) > 1e-5) {
1482 *filtered << qSetFieldWidth(10) << qSetRealNumberPrecision(3) << Qt::fixed
1483 << 1000 * r1[
X] <<
" " << 1000 * r1[
Y] <<
" " << 1000 * r1[
Z] <<
"\n"
1484 << qSetFieldWidth(0);
1485 }
else if (limit > 0.0) {
1490 for (p2 = 0; p2 < surf.
np; p2++) {
1491 dist = (surf.
rr.row(p2).transpose() - r1).norm();
1495 if (mindist < limit) {
1500 *filtered << qSetFieldWidth(10) << qSetRealNumberPrecision(3) << Qt::fixed
1501 << 1000 * r1[
X] <<
" " << 1000 * r1[
Y] <<
" " << 1000 * r1[
Z] <<
"\n"
1502 << qSetFieldWidth(0);
1507 if (omit_outside > 0)
1508 qInfo(
"%d source space points omitted because they are outside the inner skull surface.\n",
1511 qInfo(
"%d source space points omitted because of the %6.1f-mm distance limit.\n",
1512 omit, 1000 * limit);
1513 qInfo(
"Thank you for waiting.\n");
1524 int omit, omit_outside;
1526 float mindist,
dist;
1528 QSharedPointer<MNESurface> surf = a->
surf.toStrongRef();
1537 for (p1 = 0; p1 < a->
s->
np; p1++) {
1539 r1 = a->
s->
rr.row(p1).transpose();
1547 tot_angle = surf->sum_solids(r1) / (4 * M_PI);
1548 if (std::fabs(tot_angle - 1.0) > 1e-5) {
1553 *a->
filtered << qSetFieldWidth(10) << qSetRealNumberPrecision(3) << Qt::fixed
1554 << 1000 * r1[
X] <<
" " << 1000 * r1[
Y] <<
" " << 1000 * r1[
Z] <<
"\n"
1555 << qSetFieldWidth(0);
1556 }
else if (a->
limit > 0.0) {
1561 for (p2 = 0; p2 < surf->np; p2++) {
1562 dist = (surf->rr.row(p2).transpose() - r1).norm();
1566 if (mindist < a->limit) {
1571 *a->
filtered << qSetFieldWidth(10) << qSetRealNumberPrecision(3) << Qt::fixed
1572 << 1000 * r1[
X] <<
" " << 1000 * r1[
Y] <<
" " << 1000 * r1[
Z] <<
"\n"
1573 << qSetFieldWidth(0);
1578 if (omit_outside > 0)
1579 qInfo(
"%d source space points omitted because they are outside the inner skull surface.\n",
1582 qInfo(
"%d source space points omitted because of the %6.1f-mm distance limit.\n",
1583 omit, 1000 * a->
limit);
1595 QSharedPointer<MNESurface> surf;
1597 int nproc = QThread::idealThreadCount();
1598 int nspace =
static_cast<int>(spaces.size());
1600 if (bemfile.isEmpty())
1606 qCritical(
"BEM model does not have the inner skull triangulation!");
1609 surf.reset(rawSurf.release());
1614 qInfo(
"Source spaces are in ");
1616 qInfo(
"head coordinates.\n");
1618 qInfo(
"MRI coordinates.\n");
1620 qWarning(
"unknown (%d) coordinates.\n", spaces[0]->
coord_frame);
1621 qInfo(
"Checking that the sources are inside the inner skull ");
1623 qInfo(
"and at least %6.1f mm away", 1000 * limit);
1624 qInfo(
" (will take a few...)\n");
1625 if (nproc < 2 || nspace == 1 || !use_threads) {
1629 for (k = 0; k < nspace; k++) {
1630 auto a_ptr = std::make_unique<FilterThreadArg>();
1631 a_ptr->s = spaces[k].get();
1632 a_ptr->mri_head_t = std::make_unique<FiffCoordTrans>(mri_head_t);
1634 a_ptr->limit = limit;
1635 a_ptr->filtered = filtered;
1637 spaces[k]->rearrange_source_space();
1643 QList<FilterThreadArg*> args;
1645 std::vector<std::unique_ptr<FilterThreadArg>> arg_owners;
1646 for (k = 0; k < nspace; k++) {
1647 auto a_ptr = std::make_unique<FilterThreadArg>();
1648 a_ptr->s = spaces[k].get();
1649 a_ptr->mri_head_t = std::make_unique<FiffCoordTrans>(mri_head_t);
1651 a_ptr->limit = limit;
1652 a_ptr->filtered = filtered;
1653 args.append(a_ptr.get());
1654 arg_owners.push_back(std::move(a_ptr));
1661 for (k = 0; k < nspace; k++) {
1662 spaces[k]->rearrange_source_space();
1665 qInfo(
"Thank you for waiting.\n\n");
1680 std::vector<std::unique_ptr<MNESourceSpace>> local_spaces;
1681 std::unique_ptr<MNESourceSpace> new_space;
1682 QList<FiffDirNode::SPtr> sources;
1688 if (!stream->open()) {
1694 if (sources.size() == 0) {
1695 qCritical(
"No source spaces available here");
1699 for (j = 0; j < sources.size(); j++) {
1709 new_space->np = *t_pTag->toInt();
1710 if (new_space->np == 0) {
1711 qCritical(
"No points in this source space");
1719 MatrixXf tmp_rr = t_pTag->toFloatMatrix().transpose();
1720 new_space->rr = tmp_rr;
1725 MatrixXf tmp_nn = t_pTag->toFloatMatrix().transpose();
1726 new_space->nn = tmp_nn;
1730 new_space->coord_frame = *t_pTag->toInt();
1733 new_space->id = *t_pTag->toInt();
1736 new_space->subject = t_pTag->toString();
1739 new_space->type = *t_pTag->toInt();
1743 ntri = *t_pTag->toInt();
1745 ntri = *t_pTag->toInt();
1755 MatrixXi tmp_itris = t_pTag->toIntMatrix().transpose();
1756 tmp_itris.array() -= 1;
1757 new_space->itris = tmp_itris;
1758 new_space->ntri =
ntri;
1765 new_space->nuse = new_space->np;
1766 new_space->inuse = Eigen::VectorXi::Ones(new_space->nuse);
1767 new_space->vertno = Eigen::VectorXi::LinSpaced(new_space->nuse, 0, new_space->nuse - 1);
1773 new_space->nuse = 0;
1774 new_space->inuse = Eigen::VectorXi::Zero(new_space->np);
1775 new_space->vertno.resize(0);
1778 new_space->nuse = *t_pTag->toInt();
1785 Eigen::Map<const Eigen::VectorXi> inuseMap(t_pTag->toInt(), new_space->np);
1786 new_space->inuse = inuseMap;
1788 if (new_space->nuse > 0) {
1789 new_space->vertno = Eigen::VectorXi::Zero(new_space->nuse);
1790 for (k = 0, p = 0; k < new_space->np; k++) {
1791 if (new_space->inuse[k])
1792 new_space->vertno[p++] = k;
1795 new_space->vertno.resize(0);
1802 ntri = *t_pTag->toInt();
1810 MatrixXi tmp_itris = t_pTag->toIntMatrix().transpose();
1811 tmp_itris.array() -= 1;
1812 new_space->use_itris = tmp_itris;
1813 new_space->nuse_tri =
ntri;
1819 Eigen::Map<const Eigen::VectorXi> nearestMap(t_pTag->toInt(), new_space->np);
1820 new_space->nearest.resize(new_space->np);
1821 for (k = 0; k < new_space->np; k++) {
1822 new_space->nearest[k].vert = k;
1823 new_space->nearest[k].nearest = nearestMap[k];
1824 new_space->nearest[k].patch =
nullptr;
1831 Eigen::Map<const Eigen::VectorXf> nearestDistMap(t_pTag->toFloat(), new_space->np);
1832 for (k = 0; k < new_space->np; k++) {
1833 new_space->nearest[k].dist = nearestDistMap[k];
1840 new_space->dist_limit = *t_pTag->toFloat();
1848 auto dist_full = dist_lower->mne_add_upper_triangle_rcs();
1853 new_space->dist = std::move(*dist_full);
1855 new_space->dist_limit = 0.0;
1862 int ntot, nvert, ntot_count, nneigh;
1864 Eigen::VectorXi neighborsVec;
1865 Eigen::VectorXi nneighborsVec;
1868 ntot =
static_cast<int>(t_pTag->size() /
sizeof(
fiff_int_t));
1869 neighborsVec = Eigen::Map<const Eigen::VectorXi>(t_pTag->toInt(), ntot);
1872 nvert =
static_cast<int>(t_pTag->size() /
sizeof(
fiff_int_t));
1873 nneighborsVec = Eigen::Map<const Eigen::VectorXi>(t_pTag->toInt(), nvert);
1875 if (neighborsVec.size() > 0 && nneighborsVec.size() > 0) {
1876 if (nvert != new_space->np) {
1877 qCritical(
"Inconsistent neighborhood data in file.");
1881 for (k = 0, ntot_count = 0; k < nvert; k++)
1882 ntot_count += nneighborsVec[k];
1883 if (ntot_count != ntot) {
1884 qCritical(
"Inconsistent neighborhood data in file.");
1888 new_space->nneighbor_vert = Eigen::VectorXi::Zero(nvert);
1889 new_space->neighbor_vert.resize(nvert);
1890 for (k = 0, q = 0; k < nvert; k++) {
1891 new_space->nneighbor_vert[k] = nneigh = nneighborsVec[k];
1892 new_space->neighbor_vert[k] = Eigen::VectorXi(nneigh);
1893 for (p = 0; p < nneigh; p++, q++)
1894 new_space->neighbor_vert[k][p] = neighborsVec[q];
1902 Eigen::Map<const Eigen::Vector3i> volDimsMap(t_pTag->toInt());
1903 Eigen::Map<Eigen::Vector3i>(new_space->vol_dims) = volDimsMap;
1908 if (mris.size() == 0) {
1911 new_space->MRI_volume = t_pTag->toString();
1918 new_space->MRI_volume = t_pTag->toString();
1927 new_space->MRI_vol_dims[0] = *t_pTag->toInt();
1930 new_space->MRI_vol_dims[1] = *t_pTag->toInt();
1933 new_space->MRI_vol_dims[2] = *t_pTag->toInt();
1938 new_space->add_triangle_data();
1939 local_spaces.push_back(std::move(new_space));
1943 spaces = std::move(local_spaces);
1957 int nspace =
static_cast<int>(spaces.size());
1959 for (k = 0; k < nspace; k++) {
1960 s = spaces[k].get();
1972 qCritical(
"Could not transform a source space because of transformation incompatibility.");
1976 qCritical(
"Could not transform a source space because of missing coordinate transformation.");
1986#define LH_LABEL_TAG "-lh.label"
1987#define RH_LABEL_TAG "-rh.label"
1997 Eigen::VectorXi lh_inuse;
1998 Eigen::VectorXi rh_inuse;
1999 Eigen::VectorXi sel;
2000 Eigen::VectorXi*
inuse =
nullptr;
2002 int nspace =
static_cast<int>(spaces.size());
2007 for (k = 0; k < nspace; k++) {
2009 lh = spaces[k].get();
2010 lh_inuse = Eigen::VectorXi::Zero(lh->
np);
2012 rh = spaces[k].get();
2013 rh_inuse = Eigen::VectorXi::Zero(rh->
np);
2019 for (k = 0; k < nlabel; k++) {
2030 qWarning(
"\tWarning: cannot assign label file %s to a hemisphere.\n", labels[k].toUtf8().constData());
2036 for (p = 0; p < sel.size(); p++) {
2037 if (sel[p] >= 0 && sel[p] < sp->
np)
2038 (*inuse)[sel[p]] = sp->
inuse[sel[p]];
2040 qWarning(
"vertex number out of range in %s (%d vs %d)\n",
2041 labels[k].toUtf8().constData(), sel[p], sp->
np);
2043 qInfo(
"Processed label file %s\n", labels[k].toUtf8().constData());
2066 QFile inFile(label);
2067 if (!inFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
2068 qCritical() << label;
2074 qCritical(
"FsLabel file does not start correctly.");
2081 while (inFile.getChar(&c) && c !=
'\n')
2084 QTextStream in(&inFile);
2086 if (in.status() != QTextStream::Ok) {
2087 qCritical(
"Could not read the number of labelled points.");
2092 for (k = 0; k < nlabel; k++) {
2093 in >> p >> fdum >> fdum >> fdum >> fdum;
2094 if (in.status() != QTextStream::Ok) {
2095 qCritical(
"Could not read label point # %d", k + 1);
2108int MNESourceSpace::writeVolumeInfo(
FiffStream::SPtr& stream,
bool selected_only)
const
2119 Eigen::VectorXi nneighbors;
2120 Eigen::VectorXi neighbors;
2122 if (selected_only) {
2123 Eigen::VectorXi inuse_map = Eigen::VectorXi::Constant(
np, -1);
2124 for (k = 0, p = 0, ntot = 0; k <
np; k++) {
2130 nneighbors.resize(
nuse);
2131 neighbors.resize(ntot);
2132 for (k = 0, nvert = 0, ntot = 0; k <
np; k++) {
2136 nneighbors[nvert++] = nneigh;
2137 for (p = 0; p < nneigh; p++)
2138 neighbors[ntot++] = neigh[p] < 0 ? -1 : inuse_map[neigh[p]];
2142 for (k = 0, ntot = 0; k <
np; k++)
2144 nneighbors.resize(
np);
2145 neighbors.resize(ntot);
2147 for (k = 0, ntot = 0; k <
np; k++) {
2150 nneighbors[k] = nneigh;
2151 for (p = 0; p < nneigh; p++)
2152 neighbors[ntot++] = neigh[p];
2159 if (!selected_only) {
2183 qCritical(
"Cannot write the interpolator for selection yet");
2197 qCritical(
"No points in the source space being saved");
2214 if (selected_only) {
2216 qCritical(
"No vertices in use. Cannot write active-only vertices from this source space");
2220 Eigen::MatrixXf sel(
nuse, 3);
2223 for (p = 0, pp = 0; p <
np; p++) {
2225 sel.row(pp) =
rr.row(p);
2231 for (p = 0, pp = 0; p <
np; p++) {
2233 sel.row(pp) =
nn.row(p);
2250 Eigen::MatrixXi file_tris =
itris.array() + 1;
2256 Eigen::MatrixXi file_use_tris =
use_itris.array() + 1;
2261 Eigen::VectorXi nearest_v(
np);
2262 Eigen::VectorXf nearest_dist_v(
np);
2264 std::sort(
const_cast<std::vector<MNENearest>&
>(
nearest).begin(),
2265 const_cast<std::vector<MNENearest>&
>(
nearest).end(),
2267 for (p = 0; p <
np; p++) {
2268 nearest_v[p] =
nearest[p].nearest;
2269 nearest_dist_v[p] =
nearest[p].dist;
2276 if (!
dist.is_empty()) {
2277 auto m =
dist.pickLowerTriangleRcs();
2285 if (writeVolumeInfo(stream, selected_only) !=
OK)
2298static Eigen::MatrixX3f generateIcoVertices(
int grade)
2302 const float z = 1.0f / std::sqrt(5.0f);
2303 const float r = 2.0f * z;
2304 std::vector<Eigen::Vector3f> verts = {{0, 0, 1}};
2305 for (
int k = 0; k < 5; ++k)
2306 verts.emplace_back(r * std::cos(0.4f * EIGEN_PI * k), r * std::sin(0.4f * EIGEN_PI * k), z);
2307 for (
int k = 0; k < 5; ++k)
2308 verts.emplace_back(r * std::cos(0.4f * EIGEN_PI * k - 0.2f * EIGEN_PI), r * std::sin(0.4f * EIGEN_PI * k - 0.2f * EIGEN_PI), -z);
2309 verts.emplace_back(0, 0, -1);
2311 std::vector<std::array<int, 3>> faces = {
2312 {0, 3, 4}, {0, 4, 5}, {0, 5, 1}, {0, 1, 2}, {0, 2, 3}, {3, 2, 8}, {3, 8, 9}, {3, 9, 4}, {4, 9, 10}, {4, 10, 5}, {5, 10, 6}, {5, 6, 1}, {1, 6, 7}, {1, 7, 2}, {2, 7, 8}, {8, 11, 9}, {9, 11, 10}, {10, 11, 6}, {6, 11, 7}, {7, 11, 8}};
2315 for (
int g = 0; g < grade; ++g) {
2316 std::map<std::pair<int, int>,
int> midpointCache;
2317 std::vector<std::array<int, 3>> newFaces;
2319 auto getMidpoint = [&](
int i1,
int i2) ->
int {
2320 auto key = std::make_pair(std::min(i1, i2), std::max(i1, i2));
2321 auto it = midpointCache.find(key);
2322 if (it != midpointCache.end())
2324 Eigen::Vector3f mid = (verts[i1] + verts[i2]).normalized();
2325 int idx =
static_cast<int>(verts.size());
2326 verts.push_back(mid);
2327 midpointCache[key] = idx;
2331 for (
const auto& f : faces) {
2332 int a = getMidpoint(f[0], f[1]);
2333 int b = getMidpoint(f[1], f[2]);
2334 int c = getMidpoint(f[2], f[0]);
2335 newFaces.push_back({f[0], a, c});
2336 newFaces.push_back({f[1], b, a});
2337 newFaces.push_back({f[2], c, b});
2338 newFaces.push_back({a, b, c});
2343 Eigen::MatrixX3f result(
static_cast<int>(verts.size()), 3);
2344 for (
int i = 0; i < static_cast<int>(verts.size()); ++i)
2345 result.row(i) = verts[i];
2353 QList<Eigen::VectorXi> vertices;
2354 const int nIco = 10 * (1 << (2 * grade)) + 2;
2355 for (
const char* hemi : {
"lh",
"rh"}) {
2356 const QString path = QString(
"%1/%2/surf/%3.sphere.reg").arg(subjectsDir,
subject, hemi);
2357 const std::unique_ptr<MNESourceSpace> sphere =
load_surface_geom(path, QString(),
false,
false);
2359 qWarning() <<
"[MNESourceSpace::gradeToVertices] Cannot read" << path;
2366 if (down.
nuse != nIco) {
2367 qWarning() <<
"[MNESourceSpace::gradeToVertices] Cannot use icosahedral grade" << grade <<
"with subject"
2368 <<
subject <<
"(repeated vertices); use a lower grade.";
2371 vertices.append(down.
vertno);
2382 if (hemi.
np <= 0 || hemi.
rr.rows() == 0) {
2383 qWarning(
"MNESourceSpace::icoDownsample - Hemisphere has no vertices.");
2388 Eigen::MatrixX3f icoVerts = generateIcoVertices(icoGrade);
2391 Eigen::MatrixX3f hemiNorm(hemi.
np, 3);
2392 for (
int i = 0; i < hemi.
np; ++i) {
2393 Eigen::Vector3f v = hemi.
rr.row(i);
2394 float len = v.norm();
2396 hemiNorm.row(i) = (v / len).transpose();
2398 hemiNorm.row(i) = v.transpose();
2402 result.
inuse = Eigen::VectorXi::Zero(result.
np);
2405 for (
int i = 0; i < icoVerts.rows(); ++i) {
2406 Eigen::Vector3f icoV = icoVerts.row(i);
2407 float bestDist = std::numeric_limits<float>::max();
2409 for (
int j = 0; j < hemi.
np; ++j) {
2410 float d = (hemiNorm.row(j).transpose() - icoV).squaredNorm();
2417 result.
inuse[bestIdx] = 1;
2424 for (
int i = 0; i < result.
np; ++i) {
2425 if (result.
inuse[i])
Symbolic FIFF tag, block, value, unit and channel-type constants shared across FIFFLIB.
constexpr auto FIFF_MNE_SOURCE_SPACE_NPOINTS
constexpr auto FIFFV_MNE_COORD_RAS
constexpr auto FIFF_MNE_SOURCE_SPACE_MRI_FILE
constexpr auto FIFFV_COORD_HEAD
constexpr auto FIFF_MNE_FILE_NAME
constexpr auto FIFF_MNE_SOURCE_SPACE_DIST_LIMIT
constexpr auto FIFFV_MOVE
constexpr auto FIFFV_MNE_SPACE_VOLUME
constexpr auto FIFF_MNE_SOURCE_SPACE_POINTS
constexpr auto FIFF_MNE_SOURCE_SPACE_SELECTION
constexpr auto FIFF_MNE_SOURCE_SPACE_NORMALS
constexpr auto FIFF_MNE_SOURCE_SPACE_NTRI
constexpr auto FIFF_MNE_COORD_FRAME
constexpr auto FIFFV_MNE_SPACE_UNKNOWN
constexpr auto FIFF_MNE_SOURCE_SPACE_NUSE
constexpr auto FIFF_MNE_SOURCE_SPACE_VOXEL_DIMS
constexpr auto FIFF_MNE_SOURCE_SPACE_USE_TRIANGLES
constexpr auto FIFFV_MNE_SURF_UNKNOWN
constexpr auto FIFFV_MNE_COORD_MRI_VOXEL
constexpr auto FIFF_MNE_SOURCE_SPACE_DIST
constexpr auto FIFF_MNE_SOURCE_SPACE_NEAREST
constexpr auto FIFFB_MNE_PARENT_MRI_FILE
constexpr auto FIFF_MNE_SOURCE_SPACE_TRIANGLES
constexpr auto FIFFV_COORD_MRI
constexpr auto FIFFV_NO_MOVE
constexpr auto FIFFV_MNE_SPACE_SURFACE
constexpr auto FIFF_MNE_SOURCE_SPACE_NEAREST_DIST
constexpr auto FIFFB_MNE_SOURCE_SPACE
constexpr auto FIFF_MNE_SOURCE_SPACE_INTERPOLATOR
constexpr auto FIFF_MNE_SOURCE_SPACE_TYPE
constexpr auto FIFFV_MNE_SURF_RIGHT_HEMI
constexpr auto FIFFV_MNE_SURF_LEFT_HEMI
constexpr auto FIFF_MNE_SOURCE_SPACE_ID
constexpr auto FIFF_MNE_SOURCE_SPACE_NUSE_TRI
Endianness swap helpers for the FIFF binary tag I/O layer (FIFF is always written big-endian on disk)...
FIFF tag: the 16-byte tag header (kind, type, size, next) plus its decoded payload.
FIFF binary tag-stream layer: wraps a QIODevice to read and write FIFF tags, directories,...
4x4 affine FIFF coordinate transform (FIFF_COORD_TRANS) annotated with source/destination coordinate-...
constexpr auto FIFF_MRI_DEPTH
constexpr auto FIFF_MRI_HEIGHT
constexpr auto FIFF_BEM_SURF_TRIANGLES
constexpr auto FIFF_SUBJ_HIS_ID
constexpr auto FIFF_BEM_SURF_NTRI
constexpr auto FIFF_MRI_WIDTH
constexpr auto FIFFV_BEM_SURF_ID_BRAIN
return FiffCoordTrans(from_frame, to_frame, R, moveVec)
FIFF sparse matrix: column / row-compressed sparse storage backed by Eigen::SparseMatrix.
Per-hemisphere cortical surface bundle with decimation, patch info and rendering buffers.
Patch information (cluster of cortex vertices around each decimated source) used by orientation prior...
Lightweight triangulated surface (vertices, triangles, normals) used by surface-based routines.
Single-hemisphere source space (cortical surface or volume grid) loaded from FIFF.
constexpr auto NEW_QUAD_FILE_MAGIC_NUMBER
constexpr auto TRIANGLE_FILE_MAGIC_NUMBER
constexpr auto FIFF_MNE_SOURCE_SPACE_NEIGHBORS
#define FIFFV_MNE_COORD_SURFACE_RAS
constexpr auto TAG_OLD_SURF_GEOM
constexpr auto QUAD_FILE_MAGIC_NUMBER
constexpr auto FIFF_MNE_SOURCE_SPACE_NNEIGHBORS
constexpr int CURVATURE_FILE_MAGIC_NUMBER
constexpr int TAG_USEREALRAS
constexpr int TAG_OLD_MGH_XFORM
constexpr int TAG_OLD_USEREALRAS
constexpr int TAG_OLD_COLORTABLE
Per-source-space-vertex nearest-cortex-vertex mapping.
Ordered group of MNELIB::MNEMghTag entries appended to an MGH/MGZ file.
Argument record passed to a background raw-data filter worker.
Core MNE data structures (source spaces, source estimates, hemispheres).
constexpr auto MNE_SOURCE_SPACE_VOLUME
FIFF file I/O, in-memory data structures and high-level readers/writers.
float swap_float(float source)
qint32 swap_int(qint32 source)
qint64 swap_long(qint64 source)
qint16 swap_short(qint16 source)
Labelled 4x4 FIFF affine: source frame, destination frame, rotation, translation and cached inverse.
static FiffCoordTrans readTransformFromNode(FiffStream::SPtr &stream, const FiffDirNode::SPtr &node, int from, int to)
Eigen::MatrixX3f apply_inverse_trans(const Eigen::MatrixX3f &rr, bool do_move=true) const
FiffCoordTrans inverted() const
Eigen::MatrixX3f apply_trans(const Eigen::MatrixX3f &rr, bool do_move=true) const
QSharedPointer< FiffDirNode > SPtr
Sparse FIFF matrix: CCS or RCS storage with the value / index / pointer triple as written by FiffStre...
static FiffSparseMatrix::UPtr fiff_get_float_sparse_matrix(const FIFFLIB::FiffTag::UPtr &tag)
FIFF tag-stream reader/writer: wraps a QIODevice and exposes typed read_* / write_* methods for every...
QSharedPointer< FiffStream > SPtr
std::unique_ptr< FiffTag > UPtr
Thread-local arguments for parallel raw data filtering (channel range, filter kernel,...
QWeakPointer< MNESurface > surf
std::unique_ptr< FIFFLIB::FiffCoordTrans > mri_head_t
Hemisphere provides geometry information.
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.
Patch information for a single source space point including vertex members and area.
~MNESourceSpace() override
virtual MNESourceSpace::SPtr clone() const
static std::unique_ptr< MNESourceSpace > load_surface(const QString &surf_file, const QString &curv_file)
void rearrange_source_space()
qint32 find_source_space_hemi() const
static QList< Eigen::VectorXi > gradeToVertices(const QString &subjectsDir, const QString &subject, int grade)
std::shared_ptr< MNESourceSpace > SPtr
static std::unique_ptr< MNESourceSpace > make_volume_source_space(const MNESurface &surf, float grid, float exclude, float mindist)
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)
bool is_left_hemi() const
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 std::unique_ptr< MNESourceSpace > load_surface_geom(const QString &surf_file, const QString &curv_file, bool add_geometry, bool check_too_many_neighbors)
static MNEHemisphere icoDownsample(const MNEHemisphere &hemi, int icoGrade)
static void filter_source_space(FilterThreadArg *arg)
int writeToStream(FIFFLIB::FiffStream::SPtr &stream, bool selected_only) const
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)
void enable_all_sources()
Lightweight triangulated surface (vertices, triangles, normals).
double sum_solids(const Eigen::Vector3f &from) const
static std::unique_ptr< MNESurface > read_bem_surface(const QString &name, int which, bool add_geometry)
std::vector< Eigen::VectorXi > neighbor_tri
std::vector< Eigen::VectorXi > neighbor_vert
std::optional< FIFFLIB::FiffCoordTrans > MRI_surf_RAS_RAS_t
int add_geometry_info(bool do_normals, bool check_too_many_neighbors)
FIFFLIB::FiffSparseMatrix dist
std::vector< MNENearest > nearest
Eigen::VectorXi nneighbor_vert
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.