v2.0.0
Loading...
Searching...
No Matches
mne_surface_or_volume.cpp
Go to the documentation of this file.
1//=============================================================================================================
17
18//=============================================================================================================
19// INCLUDES
20//=============================================================================================================
21
23#include "mne_surface.h"
24#include "mne_source_space.h"
25#include "mne_patch_info.h"
26//#include "fwd_bem_model.h"
27#include "mne_nearest.h"
28#include "filter_thread_arg.h"
29#include "mne_triangle.h"
31#include "mne_proj_data.h"
32#include "mne_vol_geom.h"
33#include "mne_mgh_tag_group.h"
34#include "mne_mgh_tag.h"
35
36#include <fiff/fiff_stream.h>
39#include <fiff/fiff_dig_point.h>
40
41#include <math/sphere.h>
42#include <utils/ioutils.h>
43
44#include <QFile>
45#include <QTextStream>
46#include <QCoreApplication>
47#include <QtConcurrent>
48#include <QDebug>
49
50// MSVC builds already define _USE_MATH_DEFINES globally (see src/CMakeLists.txt),
51// so define it here only for the toolchains that do not.
52#ifndef _USE_MATH_DEFINES
53#define _USE_MATH_DEFINES
54#endif
55#include <math.h>
56
57#include <Eigen/Dense>
58#include <Eigen/Sparse>
59
60#include <algorithm>
61
62
63//=============================================================================================================
64// USED NAMESPACES
65//=============================================================================================================
66
67using namespace Eigen;
68using namespace FIFFLIB;
69using namespace MNELIB;
70
71//============================= dot.h =============================
72
73constexpr int FAIL = -1;
74constexpr int OK = 0;
75
76// Axis indices and mesh neighbour count (kept for documentation).
77[[maybe_unused]] constexpr int X = 0;
78[[maybe_unused]] constexpr int Y = 1;
79[[maybe_unused]] constexpr int Z = 2;
80[[maybe_unused]] constexpr int NNEIGHBORS = 26;
81
82
83//============================= make_volume_source_space.c =============================
84
85[[maybe_unused]] static std::optional<FiffCoordTrans> make_voxel_ras_trans(const Eigen::Vector3f& r0,
86 const Eigen::Vector3f& x_ras,
87 const Eigen::Vector3f& y_ras,
88 const Eigen::Vector3f& z_ras,
89 const Eigen::Vector3f& voxel_size)
90{
91 Eigen::Matrix3f rot;
92 rot.row(0) = x_ras.transpose() * voxel_size[0];
93 rot.row(1) = y_ras.transpose() * voxel_size[1];
94 rot.row(2) = z_ras.transpose() * voxel_size[2];
95
97}
98
99//=============================================================================================================
100// DEFINE MEMBER METHODS
101//=============================================================================================================
102
106
107//=============================================================================================================
108
110{
111 if (curv.size() > 0)
112 return;
113 curv = Eigen::VectorXf::Ones(np);
114}
115
116//=============================================================================================================
117
119{
120 // rr, nn, itris, use_itris are Eigen matrices — auto-cleanup
121 // inuse, vertno are Eigen VectorXi — auto-cleanup
122 // tris, use_tris are std::vector<MNETriangle> — auto-cleanup
123 // neighbor_tri is std::vector<VectorXi> — auto-cleanup
124 // nneighbor_tri is Eigen VectorXi — auto-cleanup
125 // curv is Eigen VectorXf — auto-cleanup
126
127 // neighbor_vert is std::vector<VectorXi> — auto-cleanup
128 // nneighbor_vert is Eigen VectorXi — auto-cleanup
129 // vert_dist is std::vector<VectorXf> — auto-cleanup
130 // nearest is std::vector<MNENearest> — auto-cleanup
131 // patches is std::vector<optional<MNEPatchInfo>> — auto-cleanup
132 // dist is FiffSparseMatrix value, interpolator/vol_geom/mgh_tags are optional — auto-cleanup
133 // voxel_surf_RAS_t, MRI_voxel_surf_RAS_t, MRI_surf_RAS_RAS_t are optional — auto-cleanup
134 this->MRI_volume.clear();
135}
136
137//=============================================================================================================
138
140{
141 Eigen::VectorXi result(static_cast<int>(nearest.size()));
142 for (int i = 0; i < result.size(); ++i)
143 result[i] = nearest[i].nearest;
144 return result;
145}
146
147//=============================================================================================================
148
150{
151 Eigen::VectorXd result(static_cast<int>(nearest.size()));
152 for (int i = 0; i < result.size(); ++i)
153 result[i] = static_cast<double>(nearest[i].dist);
154 return result;
155}
156
157//=============================================================================================================
158
159void MNESurfaceOrVolume::setNearestData(const Eigen::VectorXi& nearestIdx, const Eigen::VectorXd& nearestDist)
160{
161 const int n = nearestIdx.size();
162 nearest.resize(n);
163 for (int i = 0; i < n; ++i) {
164 nearest[i].vert = i;
165 nearest[i].nearest = nearestIdx[i];
166 nearest[i].dist = static_cast<float>(nearestDist[i]);
167 nearest[i].patch = nullptr;
168 }
169}
170
171//=============================================================================================================
172
173double MNESurfaceOrVolume::solid_angle(const Eigen::Vector3f& from, const MNETriangle& tri) /* ...to this triangle */
174/*
175 * Compute the solid angle according to van Oosterom's
176 * formula
177 */
178{
179 Eigen::Vector3d v1 = (tri.r1 - from).cast<double>();
180 Eigen::Vector3d v2 = (tri.r2 - from).cast<double>();
181 Eigen::Vector3d v3 = (tri.r3 - from).cast<double>();
182
183 double triple = v1.cross(v2).dot(v3);
184
185 double l1 = v1.norm();
186 double l2 = v2.norm();
187 double l3 = v3.norm();
188 double s = (l1 * l2 * l3 + v1.dot(v2) * l3 + v1.dot(v3) * l2 + v2.dot(v3) * l1);
189
190 return (2.0 * atan2(triple, s));
191}
192
193//=============================================================================================================
194
196/*
197 * Add the triangle data structures
198 */
199{
200 int k;
201 MNETriangle* tri;
202
204 return;
205
206 tris.clear();
207 use_tris.clear();
208 /*
209 * Add information for the complete triangulation
210 */
211 if (itris.rows() > 0 && ntri > 0) {
212 tris.resize(ntri);
213 tot_area = 0.0;
214 for (k = 0, tri = tris.data(); k < ntri; k++, tri++) {
215 tri->vert = &itris(k, 0);
216 tri->r1 = rr.row(tri->vert[0]).transpose();
217 tri->r2 = rr.row(tri->vert[1]).transpose();
218 tri->r3 = rr.row(tri->vert[2]).transpose();
219 tri->compute_data();
220 tot_area += tri->area;
221 }
222#ifdef TRIANGLE_SIZE_WARNING
223 for (k = 0, tri = tris.data(); k < ntri; k++, tri++)
224 if (tri->area < 1e-5 * tot_area / ntri)
225 qWarning("Warning: Triangle area is only %g um^2 (%.5f %% of expected average)\n",
226 1e12 * tri->area, 100 * ntri * tri->area / tot_area);
227#endif
228 }
229#ifdef DEBUG
230 qInfo("\ttotal area = %-.1f cm^2\n", 1e4 * tot_area);
231#endif
232 /*
233 * Add information for the selected subset if applicable
234 */
235 if (use_itris.rows() > 0 && nuse_tri > 0) {
236 use_tris.resize(nuse_tri);
237 for (k = 0, tri = use_tris.data(); k < nuse_tri; k++, tri++) {
238 tri->vert = &use_itris(k, 0);
239 tri->r1 = rr.row(tri->vert[0]).transpose();
240 tri->r2 = rr.row(tri->vert[1]).transpose();
241 tri->r3 = rr.row(tri->vert[2]).transpose();
242 tri->compute_data();
243 }
244 }
245 return;
246}
247
248//=============================================================================================================
249
251/*
252 * Compute the center of mass of a set of points
253 */
254{
255 int q;
256 cm[0] = cm[1] = cm[2] = 0.0;
257 for (q = 0; q < np; q++) {
258 cm[0] += rr(q, 0);
259 cm[1] += rr(q, 1);
260 cm[2] += rr(q, 2);
261 }
262 if (np > 0) {
263 cm[0] = cm[0] / np;
264 cm[1] = cm[1] / np;
265 cm[2] = cm[2] / np;
266 }
267 return;
268}
269
270//=============================================================================================================
271
273/*
274 * Compute the center of mass of a surface
275 */
276{
277 compute_cm(rr, np, cm);
278 return;
279}
280
281//=============================================================================================================
282
284{
285 int k, p, ndist;
286 int nneigh;
287
288 if (neighbor_vert.empty() || nneighbor_vert.size() == 0)
289 return;
290
291 vert_dist.clear();
292 vert_dist.resize(np);
293 qInfo("\tDistances between neighboring vertices...");
294 for (k = 0, ndist = 0; k < np; k++) {
295 nneigh = nneighbor_vert[k];
296 vert_dist[k] = Eigen::VectorXf(nneigh);
297 const Eigen::VectorXi& neigh = neighbor_vert[k];
298 for (p = 0; p < nneigh; p++) {
299 if (neigh[p] >= 0) {
300 vert_dist[k][p] = (rr.row(neigh[p]) - rr.row(k)).norm();
301 } else
302 vert_dist[k][p] = -1.0;
303 ndist++;
304 }
305 }
306 qInfo("[%d distances done]\n", ndist);
307 return;
308}
309
310//=============================================================================================================
311
313{
314 int k, c, p;
315 int* ii;
316 float w, size;
317 MNETriangle* tri;
318
320 return OK;
321 /*
322 * Reallocate the stuff and initialize
323 */
324 nn = MNESurfaceOrVolume::NormalsT::Zero(np, 3);
325 /*
326 * One pass through the triangles will do it
327 */
329 for (p = 0, tri = tris.data(); p < ntri; p++, tri++) {
330 ii = tri->vert;
331 w = 1.0; /* This should be related to the triangle size */
332 /*
333 * Then the vertex normals
334 */
335 for (k = 0; k < 3; k++)
336 for (c = 0; c < 3; c++)
337 nn(ii[k], c) += w * tri->nn[c];
338 }
339 for (k = 0; k < np; k++) {
340 size = nn.row(k).norm();
341 if (size > 0.0)
342 nn.row(k) /= size;
343 }
345 return OK;
346}
347
348//=============================================================================================================
349
350int MNESurfaceOrVolume::add_geometry_info(bool do_normals, bool check_too_many_neighbors)
351/*
352 * Add vertex normals and neighbourhood information
353 */
354{
355 int k, c, p, q;
356 int vert;
357 int* ii;
358 int nneighbors;
359 float w, size;
360 int found;
361 int nfix_distinct, nfix_no_neighbors, nfix_defect;
362 MNETriangle* tri;
363
366 return OK;
367 }
369 return OK;
370 /*
371 * Reallocate the stuff and initialize
372 */
373 if (do_normals) {
374 nn = MNESurfaceOrVolume::NormalsT::Zero(np, 3);
375 }
376 neighbor_tri.clear();
377 neighbor_tri.resize(np);
378 nneighbor_tri = Eigen::VectorXi::Zero(np);
379
380 /* nn is already zero-initialized above */
381 /*
382 * One pass through the triangles will do it
383 */
385 for (p = 0, tri = tris.data(); p < ntri; p++, tri++)
386 if (tri->area == 0)
387 qWarning("\tWarning : zero size triangle # %d\n", p);
388 qInfo("\tTriangle ");
389 if (do_normals)
390 qInfo("and vertex ");
391 qInfo("normals and neighboring triangles...");
392 for (p = 0, tri = tris.data(); p < ntri; p++, tri++) {
393 ii = tri->vert;
394 w = 1.0; /* This should be related to the triangle size */
395 for (k = 0; k < 3; k++) {
396 /*
397 * Then the vertex normals
398 */
399 if (do_normals)
400 for (c = 0; c < 3; c++)
401 nn(ii[k], c) += w * tri->nn[c];
402 /*
403 * Add to the list of neighbors
404 */
405 neighbor_tri[ii[k]].conservativeResize(nneighbor_tri[ii[k]] + 1);
406 neighbor_tri[ii[k]][nneighbor_tri[ii[k]]] = p;
407 nneighbor_tri[ii[k]]++;
408 }
409 }
410 nfix_no_neighbors = 0;
411 nfix_defect = 0;
412 for (k = 0; k < np; k++) {
413 if (nneighbor_tri[k] <= 0) {
414#ifdef STRICT_ERROR
415 err_printf_set_error("Vertex %d does not have any neighboring triangles!", k);
416 return FAIL;
417#else
418#ifdef REPORT_WARNINGS
419 qWarning("Warning: Vertex %d does not have any neighboring triangles!\n", k);
420#endif
421#endif
422 nfix_no_neighbors++;
423 } else if (nneighbor_tri[k] < 3) {
424#ifdef REPORT_WARNINGS
425 qWarning("\n\tTopological defect: Vertex %d has only %d neighboring triangle%s Vertex omitted.\n\t",
426 k, nneighbor_tri[k], nneighbor_tri[k] > 1 ? "s." : ".");
427#endif
428 nfix_defect++;
429 nneighbor_tri[k] = 0;
430 neighbor_tri[k].resize(0);
431 }
432 }
433 /*
434 * Scale the vertex normals to unit length
435 */
436 for (k = 0; k < np; k++)
437 if (nneighbor_tri[k] > 0) {
438 size = nn.row(k).norm();
439 if (size > 0.0)
440 nn.row(k) /= size;
441 }
442 qInfo("[done]\n");
443 /*
444 * Determine the neighboring vertices
445 */
446 qInfo("\tVertex neighbors...");
447 neighbor_vert.clear();
448 neighbor_vert.resize(np);
449 nneighbor_vert = VectorXi::Zero(np);
450 /*
451 * We know the number of neighbors beforehand
452 */
453 for (k = 0; k < np; k++) {
454 if (nneighbor_tri[k] > 0) {
455 neighbor_vert[k] = VectorXi(nneighbor_tri[k]);
457 } else {
458 nneighbor_vert[k] = 0;
459 }
460 }
461 nfix_distinct = 0;
462 for (k = 0; k < np; k++) {
463 Eigen::VectorXi& neighbors = neighbor_vert[k];
464 nneighbors = 0;
465 for (p = 0; p < nneighbor_tri[k]; p++) {
466 /*
467 * Fit in the other vertices of the neighboring triangle
468 */
469 for (c = 0; c < 3; c++) {
470 vert = tris[neighbor_tri[k][p]].vert[c];
471 if (vert != k) {
472 for (q = 0, found = false; q < nneighbors; q++) {
473 if (neighbors[q] == vert) {
474 found = true;
475 break;
476 }
477 }
478 if (!found) {
479 if (nneighbors < nneighbor_vert[k])
480 neighbors[nneighbors++] = vert;
481 else {
482 if (check_too_many_neighbors) {
483 qCritical("Too many neighbors for vertex %d.", k);
484 return FAIL;
485 } else
486 qWarning("\tWarning: Too many neighbors for vertex %d\n", k);
487 }
488 }
489 }
490 }
491 }
492 if (nneighbors != nneighbor_vert[k]) {
493#ifdef REPORT_WARNINGS
494 qWarning("\n\tIncorrect number of distinct neighbors for vertex %d (%d instead of %d) [fixed].",
495 k, nneighbors, nneighbor_vert[k]);
496#endif
497 nfix_distinct++;
498 nneighbor_vert[k] = nneighbors;
499 }
500 }
501 qInfo("[done]\n");
502 /*
503 * Distance calculation follows
504 */
507 /*
508 * Summarize the defects
509 */
510 if (nfix_defect > 0)
511 qWarning("\tWarning: %d topological defects were fixed.\n", nfix_defect);
512 if (nfix_distinct > 0)
513 qWarning("\tWarning: %d vertices had incorrect number of distinct neighbors (fixed).\n", nfix_distinct);
514 if (nfix_no_neighbors > 0)
515 qWarning("\tWarning: %d vertices did not have any neighboring triangles (fixed)\n", nfix_no_neighbors);
516#ifdef DEBUG
517 for (k = 0; k < np; k++) {
518 if (nneighbor_vert[k] <= 0)
519 qCritical("No neighbors for vertex %d\n", k);
520 if (nneighbor_tri[k] <= 0)
521 qCritical("No neighbor tris for vertex %d\n", k);
522 }
523#endif
524 return OK;
525}
526
527//=============================================================================================================
528
530{
531 return add_geometry_info(do_normals, true);
532}
533
534//=============================================================================================================
535
537
538{
539 return add_geometry_info(do_normals, false);
540}
541
542//=============================================================================================================
#define FIFFV_MNE_COORD_MRI_VOXEL
#define FIFFV_COORD_MRI
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-...
return FiffCoordTrans(from_frame, to_frame, R, moveVec)
Single digitization point (FIFF_DIG_POINT) with kind (cardinal/HPI/EEG/extra), identifier and 3D coor...
High-level digitization data: dig points plus the device→head transform and fitting metadata that tog...
Header-only Eigen matrix text I/O — round-trips dense matrices to whitespace-separated ASCII for cros...
constexpr int FAIL
constexpr int Y
constexpr int Z
constexpr int OK
constexpr int X
Best-fit sphere from a 3-D point cloud with closed-form and Nelder–Mead solvers.
One renderable surface (cortex / pial / inflated / BEM) inside an MSH display set.
#define MNE_SOURCE_SPACE_SURFACE
Definition mne_types.h:81
#define MNE_SOURCE_SPACE_VOLUME
Definition mne_types.h:82
Legacy MNE-C aggregator for SSP projections plus the channel list they apply to.
Patch information (cluster of cortex vertices around each decimated source) used by orientation prior...
Triangle descriptor with cached centroid, area and normal vectors.
Lightweight triangulated surface (vertices, triangles, normals) used by surface-based routines.
Single-hemisphere source space (cortical surface or volume grid) loaded from FIFF.
Discriminated geometry container that can represent either a triangulated surface or a discrete/volum...
Single FreeSurfer MGH/MGZ tag (key, length, opaque payload).
constexpr int NNEIGHBORS
FreeSurfer volume geometry header carried by surface and tag streams.
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).
FIFF file I/O, in-memory data structures and high-level readers/writers.
std::vector< Eigen::VectorXi > neighbor_tri
void setNearestData(const Eigen::VectorXi &nearestIdx, const Eigen::VectorXd &nearestDist)
std::vector< Eigen::VectorXi > neighbor_vert
MNESurfaceOrVolume()
Constructs the MNE FsSurface or Volume.
Eigen::VectorXd nearestDistVec() const
int add_geometry_info(bool do_normals, bool check_too_many_neighbors)
FIFFLIB::FiffSparseMatrix dist
std::vector< MNENearest > nearest
static void compute_cm(const PointsT &rr, int np, float(&cm)[3])
static double solid_angle(const Eigen::Vector3f &from, const MNELIB::MNETriangle &tri)
virtual ~MNESurfaceOrVolume()
Destroys the MNE FsSurface or Volume description.
std::vector< MNETriangle > tris
std::vector< Eigen::VectorXf > vert_dist
Eigen::Matrix< float, Eigen::Dynamic, 3, Eigen::RowMajor > PointsT
int add_geometry_info2(bool do_normals)
Eigen::VectorXi nearestVertIdx() const
std::vector< MNETriangle > use_tris
Per-triangle geometric data for a cortical or BEM surface.
Eigen::Vector3f nn
Eigen::Vector3f r2
Eigen::Vector3f r1
Eigen::Vector3f r3