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#define _USE_MATH_DEFINES
51#include <math.h>
52
53#include <Eigen/Dense>
54#include <Eigen/Sparse>
55
56#include <algorithm>
57
58
59
60//=============================================================================================================
61// USED NAMESPACES
62//=============================================================================================================
63
64using namespace Eigen;
65using namespace FIFFLIB;
66using namespace MNELIB;
67
68//============================= dot.h =============================
69
70constexpr int FAIL = -1;
71constexpr int OK = 0;
72
73constexpr int X = 0;
74constexpr int Y = 1;
75constexpr int Z = 2;
76
77constexpr int NNEIGHBORS = 26;
78
79
80
81
82//============================= make_volume_source_space.c =============================
83
84static std::optional<FiffCoordTrans> make_voxel_ras_trans(const Eigen::Vector3f& r0,
85 const Eigen::Vector3f& x_ras,
86 const Eigen::Vector3f& y_ras,
87 const Eigen::Vector3f& z_ras,
88 const Eigen::Vector3f& voxel_size)
89{
90 Eigen::Matrix3f rot;
91 rot.row(0) = x_ras.transpose() * voxel_size[0];
92 rot.row(1) = y_ras.transpose() * voxel_size[1];
93 rot.row(2) = z_ras.transpose() * voxel_size[2];
94
96}
97
98//=============================================================================================================
99// DEFINE MEMBER METHODS
100//=============================================================================================================
101
105
106//=============================================================================================================
107
109{
110 if (curv.size() > 0)
111 return;
112 curv = Eigen::VectorXf::Ones(np);
113}
114
115//=============================================================================================================
116
118{
119 // rr, nn, itris, use_itris are Eigen matrices — auto-cleanup
120 // inuse, vertno are Eigen VectorXi — auto-cleanup
121 // tris, use_tris are std::vector<MNETriangle> — auto-cleanup
122 // neighbor_tri is std::vector<VectorXi> — auto-cleanup
123 // nneighbor_tri is Eigen VectorXi — auto-cleanup
124 // curv is Eigen VectorXf — auto-cleanup
125
126 // neighbor_vert is std::vector<VectorXi> — auto-cleanup
127 // nneighbor_vert is Eigen VectorXi — auto-cleanup
128 // vert_dist is std::vector<VectorXf> — auto-cleanup
129 // nearest is std::vector<MNENearest> — auto-cleanup
130 // patches is std::vector<optional<MNEPatchInfo>> — auto-cleanup
131 // dist is FiffSparseMatrix value, interpolator/vol_geom/mgh_tags are optional — auto-cleanup
132 // voxel_surf_RAS_t, MRI_voxel_surf_RAS_t, MRI_surf_RAS_RAS_t are optional — auto-cleanup
133 this->MRI_volume.clear();
134}
135
136//=============================================================================================================
137
139{
140 Eigen::VectorXi result(static_cast<int>(nearest.size()));
141 for (int i = 0; i < result.size(); ++i)
142 result[i] = nearest[i].nearest;
143 return result;
144}
145
146//=============================================================================================================
147
149{
150 Eigen::VectorXd result(static_cast<int>(nearest.size()));
151 for (int i = 0; i < result.size(); ++i)
152 result[i] = static_cast<double>(nearest[i].dist);
153 return result;
154}
155
156//=============================================================================================================
157
158void MNESurfaceOrVolume::setNearestData(const Eigen::VectorXi& nearestIdx, const Eigen::VectorXd& nearestDist)
159{
160 const int n = nearestIdx.size();
161 nearest.resize(n);
162 for (int i = 0; i < n; ++i) {
163 nearest[i].vert = i;
164 nearest[i].nearest = nearestIdx[i];
165 nearest[i].dist = static_cast<float>(nearestDist[i]);
166 nearest[i].patch = nullptr;
167 }
168}
169
170//=============================================================================================================
171
172double MNESurfaceOrVolume::solid_angle(const Eigen::Vector3f& from, const MNETriangle& tri) /* ...to this triangle */
173/*
174 * Compute the solid angle according to van Oosterom's
175 * formula
176 */
177{
178 Eigen::Vector3d v1 = (tri.r1 - from).cast<double>();
179 Eigen::Vector3d v2 = (tri.r2 - from).cast<double>();
180 Eigen::Vector3d v3 = (tri.r3 - from).cast<double>();
181
182 double triple = v1.cross(v2).dot(v3);
183
184 double l1 = v1.norm();
185 double l2 = v2.norm();
186 double l3 = v3.norm();
187 double s = (l1*l2*l3+v1.dot(v2)*l3+v1.dot(v3)*l2+v2.dot(v3)*l1);
188
189 return (2.0*atan2(triple,s));
190}
191
192//=============================================================================================================
193
195/*
196 * Add the triangle data structures
197 */
198{
199 int k;
200 MNETriangle* tri;
201
203 return;
204
205 tris.clear();
206 use_tris.clear();
207 /*
208 * Add information for the complete triangulation
209 */
210 if (itris.rows() > 0 && ntri > 0) {
211 tris.resize(ntri);
212 tot_area = 0.0;
213 for (k = 0, tri = tris.data(); k < ntri; k++, tri++) {
214 tri->vert = &itris(k,0);
215 tri->r1 = rr.row(tri->vert[0]).transpose();
216 tri->r2 = rr.row(tri->vert[1]).transpose();
217 tri->r3 = rr.row(tri->vert[2]).transpose();
218 tri->compute_data();
219 tot_area += tri->area;
220 }
221#ifdef TRIANGLE_SIZE_WARNING
222 for (k = 0, tri = tris.data(); k < ntri; k++, tri++)
223 if (tri->area < 1e-5*tot_area/ntri)
224 qWarning("Warning: Triangle area is only %g um^2 (%.5f %% of expected average)\n",
225 1e12*tri->area,100*ntri*tri->area/tot_area);
226#endif
227 }
228#ifdef DEBUG
229 qInfo("\ttotal area = %-.1f cm^2\n",1e4*tot_area);
230#endif
231 /*
232 * Add information for the selected subset if applicable
233 */
234 if (use_itris.rows() > 0 && nuse_tri > 0) {
235 use_tris.resize(nuse_tri);
236 for (k = 0, tri = use_tris.data(); k < nuse_tri; k++, tri++) {
237 tri->vert = &use_itris(k,0);
238 tri->r1 = rr.row(tri->vert[0]).transpose();
239 tri->r2 = rr.row(tri->vert[1]).transpose();
240 tri->r3 = rr.row(tri->vert[2]).transpose();
241 tri->compute_data();
242 }
243 }
244 return;
245}
246
247//=============================================================================================================
248
250/*
251 * Compute the center of mass of a set of points
252 */
253{
254 int q;
255 cm[0] = cm[1] = cm[2] = 0.0;
256 for (q = 0; q < np; q++) {
257 cm[0] += rr(q,0);
258 cm[1] += rr(q,1);
259 cm[2] += rr(q,2);
260 }
261 if (np > 0) {
262 cm[0] = cm[0]/np;
263 cm[1] = cm[1]/np;
264 cm[2] = cm[2]/np;
265 }
266 return;
267}
268
269//=============================================================================================================
270
272/*
273 * Compute the center of mass of a surface
274 */
275{
277 return;
278}
279
280//=============================================================================================================
281
283{
284 int k,p,ndist;
285 int nneigh;
286
287 if (neighbor_vert.empty() || nneighbor_vert.size() == 0)
288 return;
289
290 vert_dist.clear();
291 vert_dist.resize(np);
292 qInfo("\tDistances between neighboring vertices...");
293 for (k = 0, ndist = 0; k < np; k++) {
294 nneigh = nneighbor_vert[k];
295 vert_dist[k] = Eigen::VectorXf(nneigh);
296 const Eigen::VectorXi& neigh = neighbor_vert[k];
297 for (p = 0; p < nneigh; p++) {
298 if (neigh[p] >= 0) {
299 vert_dist[k][p] = (rr.row(neigh[p]) - rr.row(k)).norm();
300 }
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 }
424 else if (nneighbor_tri[k] < 3) {
425#ifdef REPORT_WARNINGS
426 qWarning("\n\tTopological defect: Vertex %d has only %d neighboring triangle%s Vertex omitted.\n\t",
427 k,nneighbor_tri[k],nneighbor_tri[k] > 1 ? "s." : ".");
428#endif
429 nfix_defect++;
430 nneighbor_tri[k] = 0;
431 neighbor_tri[k].resize(0);
432 }
433 }
434 /*
435 * Scale the vertex normals to unit length
436 */
437 for (k = 0; k < np; k++)
438 if (nneighbor_tri[k] > 0) {
439 size = nn.row(k).norm();
440 if (size > 0.0)
441 nn.row(k) /= size;
442 }
443 qInfo("[done]\n");
444 /*
445 * Determine the neighboring vertices
446 */
447 qInfo("\tVertex neighbors...");
448 neighbor_vert.clear();
449 neighbor_vert.resize(np);
450 nneighbor_vert = VectorXi::Zero(np);
451 /*
452 * We know the number of neighbors beforehand
453 */
454 for (k = 0; k < np; k++) {
455 if (nneighbor_tri[k] > 0) {
456 neighbor_vert[k] = VectorXi(nneighbor_tri[k]);
458 }
459 else {
460 nneighbor_vert[k] = 0;
461 }
462 }
463 nfix_distinct = 0;
464 for (k = 0; k < np; k++) {
465 Eigen::VectorXi& neighbors = neighbor_vert[k];
466 nneighbors = 0;
467 for (p = 0; p < nneighbor_tri[k]; p++) {
468 /*
469 * Fit in the other vertices of the neighboring triangle
470 */
471 for (c = 0; c < 3; c++) {
472 vert = tris[neighbor_tri[k][p]].vert[c];
473 if (vert != k) {
474 for (q = 0, found = false; q < nneighbors; q++) {
475 if (neighbors[q] == vert) {
476 found = true;
477 break;
478 }
479 }
480 if (!found) {
481 if (nneighbors < nneighbor_vert[k])
482 neighbors[nneighbors++] = vert;
483 else {
484 if (check_too_many_neighbors) {
485 qCritical("Too many neighbors for vertex %d.",k);
486 return FAIL;
487 }
488 else
489 qWarning("\tWarning: Too many neighbors for vertex %d\n",k);
490 }
491 }
492 }
493 }
494 }
495 if (nneighbors != nneighbor_vert[k]) {
496#ifdef REPORT_WARNINGS
497 qWarning("\n\tIncorrect number of distinct neighbors for vertex %d (%d instead of %d) [fixed].",
498 k,nneighbors,nneighbor_vert[k]);
499#endif
500 nfix_distinct++;
501 nneighbor_vert[k] = nneighbors;
502 }
503 }
504 qInfo("[done]\n");
505 /*
506 * Distance calculation follows
507 */
510 /*
511 * Summarize the defects
512 */
513 if (nfix_defect > 0)
514 qWarning("\tWarning: %d topological defects were fixed.\n",nfix_defect);
515 if (nfix_distinct > 0)
516 qWarning("\tWarning: %d vertices had incorrect number of distinct neighbors (fixed).\n",nfix_distinct);
517 if (nfix_no_neighbors > 0)
518 qWarning("\tWarning: %d vertices did not have any neighboring triangles (fixed)\n",nfix_no_neighbors);
519#ifdef DEBUG
520 for (k = 0; k < np; k++) {
521 if (nneighbor_vert[k] <= 0)
522 qCritical("No neighbors for vertex %d\n",k);
523 if (nneighbor_tri[k] <= 0)
524 qCritical("No neighbor tris for vertex %d\n",k);
525 }
526#endif
527 return OK;
528}
529
530//=============================================================================================================
531
533{
534 return add_geometry_info(do_normals,true);
535}
536
537//=============================================================================================================
538
540
541{
542 return add_geometry_info(do_normals,false);
543}
544
545//=============================================================================================================
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
Discriminated geometry container that can represent either a triangulated surface or a discrete/volum...
FreeSurfer volume geometry header carried by surface and tag streams.
Ordered group of MNELIB::MNEMghTag entries appended to an MGH/MGZ file.
Lightweight triangulated surface (vertices, triangles, normals) used by surface-based routines.
Single-hemisphere source space (cortical surface or volume grid) loaded from FIFF.
#define MNE_SOURCE_SPACE_SURFACE
Definition mne_types.h:80
#define MNE_SOURCE_SPACE_VOLUME
Definition mne_types.h:81
Patch information (cluster of cortex vertices around each decimated source) used by orientation prior...
Legacy MNE-C aggregator for SSP projections plus the channel list they apply to.
Triangle descriptor with cached centroid, area and normal vectors.
Argument record passed to a background raw-data filter worker.
constexpr int NNEIGHBORS
One renderable surface (cortex / pial / inflated / BEM) inside an MSH display set.
Per-source-space-vertex nearest-cortex-vertex mapping.
Single FreeSurfer MGH/MGZ tag (key, length, opaque payload).
return FiffCoordTrans(from_frame, to_frame, R, moveVec)
#define FIFFV_MNE_COORD_MRI_VOXEL
#define FIFFV_COORD_MRI
High-level digitization data: dig points plus the device→head transform and fitting metadata that tog...
4x4 affine FIFF coordinate transform (FIFF_COORD_TRANS) annotated with source/destination coordinate-...
FIFF binary tag-stream layer: wraps a QIODevice to read and write FIFF tags, directories,...
Single digitization point (FIFF_DIG_POINT) with kind (cardinal/HPI/EEG/extra), identifier and 3D coor...
Best-fit sphere from a 3-D point cloud with closed-form and Nelder–Mead solvers.
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