v2.0.0
Loading...
Searching...
No Matches
mne_hemisphere.cpp
Go to the documentation of this file.
1//=============================================================================================================
19
20//=============================================================================================================
21// INCLUDES
22//=============================================================================================================
23
24#include "mne_hemisphere.h"
25#include "mne_nearest.h"
26
27#include <math/linalg.h>
28
29#include <algorithm>
30
31//=============================================================================================================
32// USED NAMESPACES
33//=============================================================================================================
34
35using namespace MNELIB;
36using namespace UTILSLIB;
37using namespace Eigen;
38using namespace FIFFLIB;
39
40//=============================================================================================================
41// DEFINE MEMBER METHODS
42//=============================================================================================================
43
46, patch_inds(VectorXi::Zero(0))
47, tri_cent(MatrixX3d::Zero(0, 3))
48, tri_nn(MatrixX3d::Zero(0, 3))
49, tri_area(VectorXd::Zero(0))
50, use_tri_cent(MatrixX3d::Zero(0, 3))
51, use_tri_nn(MatrixX3d::Zero(0, 3))
52, use_tri_area(VectorXd::Zero(0))
53{
54 // Override some base class defaults to match MNEHemisphere semantics
55 this->type = 1;
56 this->id = -1;
57 this->np = -1;
58 this->ntri = -1;
59 this->coord_frame = -1;
60 this->nuse = -1;
61 this->nuse_tri = -1;
62 this->dist_limit = -1;
63}
64
65//=============================================================================================================
66
69, pinfo(p_MNEHemisphere.pinfo)
70, patch_inds(p_MNEHemisphere.patch_inds)
71, tri_cent(p_MNEHemisphere.tri_cent)
72, tri_nn(p_MNEHemisphere.tri_nn)
73, tri_area(p_MNEHemisphere.tri_area)
74, use_tri_cent(p_MNEHemisphere.use_tri_cent)
75, use_tri_nn(p_MNEHemisphere.use_tri_nn)
76, use_tri_area(p_MNEHemisphere.use_tri_area)
77, cluster_info(p_MNEHemisphere.cluster_info)
78{
79 // Copy base class (MNESurfaceOrVolume) fields that MNEHemisphere uses
80 this->type = p_MNEHemisphere.type;
81 this->id = p_MNEHemisphere.id;
82 this->np = p_MNEHemisphere.np;
83 this->ntri = p_MNEHemisphere.ntri;
84 this->coord_frame = p_MNEHemisphere.coord_frame;
85 this->rr = p_MNEHemisphere.rr;
86 this->nn = p_MNEHemisphere.nn;
87 this->nuse = p_MNEHemisphere.nuse;
88 this->inuse = p_MNEHemisphere.inuse;
89 this->vertno = p_MNEHemisphere.vertno;
90 this->itris = p_MNEHemisphere.itris;
91 this->use_itris = p_MNEHemisphere.use_itris;
92 this->nuse_tri = p_MNEHemisphere.nuse_tri;
93 this->dist_limit = p_MNEHemisphere.dist_limit;
94 this->dist = p_MNEHemisphere.dist;
95 this->nearest = p_MNEHemisphere.nearest;
96 this->neighbor_tri = p_MNEHemisphere.neighbor_tri;
97 this->neighbor_vert = p_MNEHemisphere.neighbor_vert;
98}
99
100//=============================================================================================================
101
103{
104 if (this != &other) {
105 // Copy base class (MNESurfaceOrVolume) fields
106 this->type = other.type;
107 this->id = other.id;
108 this->np = other.np;
109 this->ntri = other.ntri;
110 this->coord_frame = other.coord_frame;
111 this->rr = other.rr;
112 this->nn = other.nn;
113 this->nuse = other.nuse;
114 this->inuse = other.inuse;
115 this->vertno = other.vertno;
116 this->nuse_tri = other.nuse_tri;
117 this->dist_limit = other.dist_limit;
118 this->neighbor_tri = other.neighbor_tri;
119 this->neighbor_vert = other.neighbor_vert;
120
121 // Copy triangle index fields (inherited)
122 this->itris = other.itris;
123 this->use_itris = other.use_itris;
124
125 // Copy inherited fields with value semantics
126 this->dist = other.dist;
127 this->nearest = other.nearest;
128
129 // Copy MNEHemisphere fields
130 this->pinfo = other.pinfo;
131 this->patch_inds = other.patch_inds;
132 this->tri_cent = other.tri_cent;
133 this->tri_nn = other.tri_nn;
134 this->tri_area = other.tri_area;
135 this->use_tri_cent = other.use_tri_cent;
136 this->use_tri_nn = other.use_tri_nn;
137 this->use_tri_area = other.use_tri_area;
138 this->cluster_info = other.cluster_info;
139 }
140 return *this;
141}
142
143//=============================================================================================================
144
148
149//=============================================================================================================
150
152{
153 return std::make_shared<MNEHemisphere>(*this);
154}
155
156//=============================================================================================================
157
159{
160 //
161 // Main triangulation
162 //
163 qInfo("\tCompleting triangulation info...");
164 tri_cent = MatrixX3d::Zero(ntri, 3);
165 tri_nn = MatrixX3d::Zero(ntri, 3);
166 tri_area = VectorXd::Zero(ntri);
167
168 Matrix3d r;
169 Vector3d a, b;
170 int k = 0;
171 float size = 0;
172 for (int i = 0; i < ntri; ++i) {
173 for (int j = 0; j < 3; ++j) {
174 k = itris(i, j);
175
176 r(j, 0) = rr(k, 0);
177 r(j, 1) = rr(k, 1);
178 r(j, 2) = rr(k, 2);
179
180 tri_cent(i, 0) += rr(k, 0);
181 tri_cent(i, 1) += rr(k, 1);
182 tri_cent(i, 2) += rr(k, 2);
183 }
184 tri_cent.row(i) /= 3.0f;
185
186 //cross product {cross((r2-r1),(r3-r1))}
187 a = (r.row(1) - r.row(0)).transpose();
188 b = (r.row(2) - r.row(0)).transpose();
189 tri_nn(i, 0) = a(1) * b(2) - a(2) * b(1);
190 tri_nn(i, 1) = a(2) * b(0) - a(0) * b(2);
191 tri_nn(i, 2) = a(0) * b(1) - a(1) * b(0);
192
193 //area
194 size = tri_nn.row(i) * tri_nn.row(i).transpose();
195 size = std::pow(size, 0.5f);
196
197 tri_area(i) = size / 2.0f;
198 tri_nn.row(i) /= size;
199 }
200 qInfo("[done]\n");
201
202 //
203 // Selected triangles
204 //
205 qInfo("\tCompleting selection triangulation info...");
206 if (nuse_tri > 0) {
207 use_tri_cent = MatrixX3d::Zero(nuse_tri, 3);
208 use_tri_nn = MatrixX3d::Zero(nuse_tri, 3);
209 use_tri_area = VectorXd::Zero(nuse_tri);
210
211 for (int i = 0; i < nuse_tri; ++i) {
212 for (int j = 0; j < 3; ++j) {
213 k = use_itris(i, j);
214
215 r(j, 0) = rr(k, 0);
216 r(j, 1) = rr(k, 1);
217 r(j, 2) = rr(k, 2);
218
219 use_tri_cent(i, 0) += rr(k, 0);
220 use_tri_cent(i, 1) += rr(k, 1);
221 use_tri_cent(i, 2) += rr(k, 2);
222 }
223 use_tri_cent.row(i) /= 3.0f;
224
225 //cross product {cross((r2-r1),(r3-r1))}
226 a = r.row(1) - r.row(0);
227 b = r.row(2) - r.row(0);
228 use_tri_nn(i, 0) = a(1) * b(2) - a(2) * b(1);
229 use_tri_nn(i, 1) = a(2) * b(0) - a(0) * b(2);
230 use_tri_nn(i, 2) = a(0) * b(1) - a(1) * b(0);
231
232 //area
233 size = use_tri_nn.row(i) * use_tri_nn.row(i).transpose();
234 size = std::pow(size, 0.5f);
235
236 use_tri_area(i) = size / 2.0f;
237 }
238 }
239 qInfo("[done]\n");
240
241 qInfo("\tCompleting triangle and vertex neighboring info...");
243 qInfo("[done]\n");
244
245 return true;
246}
247
248//=============================================================================================================
249
251{
252 if (nearest.empty()) {
253 pinfo.clear();
254 patch_inds = VectorXi();
255 return false;
256 }
257
258 qInfo("\tComputing patch statistics...");
259
260 std::vector<std::pair<int, int>> t_vIndn;
261
262 for (size_t i = 0; i < nearest.size(); ++i) {
263 std::pair<int, int> t_pair(static_cast<int>(i), nearest[i].nearest);
264 t_vIndn.push_back(t_pair);
265 }
266 std::sort(t_vIndn.begin(), t_vIndn.end(), Linalg::compareIdxValuePairSmallerThan<int>);
267
268 VectorXi nearest_sorted(t_vIndn.size());
269
270 int current = 0;
271 std::vector<int> t_vfirsti;
272 t_vfirsti.push_back(current);
273 std::vector<int> t_vlasti;
274
275 for (int i = 0; i < static_cast<int>(t_vIndn.size()); ++i) {
276 nearest_sorted[i] = t_vIndn[i].second;
277 if (t_vIndn[current].second != t_vIndn[i].second) {
278 current = i;
279 t_vlasti.push_back(i - 1);
280 t_vfirsti.push_back(current);
281 }
282 }
283 t_vlasti.push_back(static_cast<int>(t_vIndn.size() - 1));
284
285 for (int k = 0; k < static_cast<int>(t_vfirsti.size()); ++k) {
286 Eigen::VectorXi t_vIndex(t_vlasti[k] - t_vfirsti[k] + 1);
287
288 for (int l = t_vfirsti[k]; l <= t_vlasti[k]; ++l)
289 t_vIndex[l - t_vfirsti[k]] = t_vIndn[l].first;
290
291 std::sort(t_vIndex.data(), t_vIndex.data() + t_vIndex.size());
292
293 pinfo.append(t_vIndex);
294 }
295
296 // compute patch indices of the in-use source space vertices
297 Eigen::VectorXi patch_verts(t_vlasti.size());
298 for (int i = 0; i < static_cast<int>(t_vlasti.size()); ++i)
299 patch_verts[i] = nearest_sorted[t_vlasti[i]];
300
301 patch_inds.resize(vertno.size());
302 for (int i = 0; i < vertno.size(); ++i) {
303 const int* ptr = std::find(patch_verts.data(), patch_verts.data() + patch_verts.size(), vertno[i]);
304 patch_inds[i] = static_cast<int>(ptr - patch_verts.data());
305 }
306
307 return true;
308}
309
310//=============================================================================================================
311
313{
314 int k, c, p, q;
315 bool found;
316
317 //Create neighboring triangle vector using temporary std::vector for efficient appending
318 {
319 std::vector<std::vector<int>> temp_ntri(this->itris.rows());
320 for (p = 0; p < this->itris.rows(); p++) {
321 for (k = 0; k < 3; k++) {
322 temp_ntri[this->itris(p, k)].push_back(p);
323 }
324 }
325 neighbor_tri.resize(this->itris.rows());
326 for (k = 0; k < static_cast<int>(temp_ntri.size()); k++) {
327 neighbor_tri[k] = Eigen::Map<Eigen::VectorXi>(temp_ntri[k].data(), temp_ntri[k].size());
328 }
329 }
330
331 //Create the neighboring vertices vector using temporary std::vector
332 {
333 std::vector<std::vector<int>> temp_nvert(this->np);
334 for (k = 0; k < this->np; k++) {
335 for (p = 0; p < neighbor_tri[k].size(); p++) {
336 //Fit in the other vertices of the neighboring triangle
337 for (c = 0; c < 3; c++) {
338 int vert = this->itris(neighbor_tri[k][p], c);
339
340 if (vert != k) {
341 found = false;
342
343 for (q = 0; q < static_cast<int>(temp_nvert[k].size()); q++) {
344 if (temp_nvert[k][q] == vert) {
345 found = true;
346 break;
347 }
348 }
349
350 if (!found) {
351 temp_nvert[k].push_back(vert);
352 }
353 }
354 }
355 }
356 }
357 neighbor_vert.resize(this->np);
358 for (k = 0; k < this->np; k++) {
359 neighbor_vert[k] = Eigen::Map<Eigen::VectorXi>(temp_nvert[k].data(), temp_nvert[k].size());
360 }
361 }
362
363 return true;
364}
365
366//=============================================================================================================
367
369{
370 // Reset base class fields
371 type = 1;
372 id = -1;
373 np = -1;
374 ntri = -1;
375 coord_frame = -1;
376 rr = PointsT::Zero(0, 3);
377 nn = NormalsT::Zero(0, 3);
378 nuse = -1;
379 inuse = VectorXi::Zero(0);
380 vertno = VectorXi::Zero(0);
381 nuse_tri = -1;
382 dist_limit = -1;
383 neighbor_tri.clear();
384 neighbor_vert.clear();
385
386 // Reset inherited value-semantic fields
387 itris = TrianglesT::Zero(0, 3);
388 use_itris = TrianglesT::Zero(0, 3);
390 nearest.clear();
391
392 // Reset MNEHemisphere fields
393 pinfo.clear();
394 patch_inds = VectorXi::Zero(0);
395 tri_cent = MatrixX3d::Zero(0, 3);
396 tri_nn = MatrixX3d::Zero(0, 3);
397 tri_area = VectorXd::Zero(0);
398 use_tri_cent = MatrixX3d::Zero(0, 3);
399 use_tri_nn = MatrixX3d::Zero(0, 3);
400 use_tri_area = VectorXd::Zero(0);
401
402 cluster_info.clear();
403}
404
405//=============================================================================================================
406
408{
409 FiffCoordTrans trans(p_Trans);
410
411 if (this->coord_frame == dest) {
412 // res = src;
413 return true;
414 }
415
416 if (trans.to == this->coord_frame && trans.from == dest)
417 trans.invert_transform();
418 else if (trans.from != this->coord_frame || trans.to != dest) {
419 qWarning("Cannot transform the source space using this coordinate transformation"); //Consider throw
420 return false;
421 }
422
423 MatrixXf t = trans.trans.block(0, 0, 3, 4);
424 // res = src;
425 this->coord_frame = dest;
426 MatrixXf t_rr = MatrixXf::Ones(this->np, 4);
427 t_rr.block(0, 0, this->np, 3) = this->rr;
428 MatrixXf t_nn = MatrixXf::Zero(this->np, 4);
429 t_nn.block(0, 0, this->np, 3) = this->nn;
430
431 this->rr = (t * t_rr.transpose()).transpose();
432 this->nn = (t * t_nn.transpose()).transpose();
433
434 return true;
435}
436
437//=============================================================================================================
438//ToDo
440{
441 if (this->type == 1 || this->type == 2)
442 p_pStream->write_int(FIFF_MNE_SOURCE_SPACE_TYPE, &this->type);
443 else
444 qWarning("Unknown source space type (%d)", this->type);
445 p_pStream->write_int(FIFF_MNE_SOURCE_SPACE_ID, &this->id);
446
447 // data = this.get('subject_his_id', None)
448 // if data:
449 // write_string(fid, FIFF.FIFF_SUBJ_HIS_ID, data)
450 p_pStream->write_int(FIFF_MNE_COORD_FRAME, &this->coord_frame);
451
452 if (this->type == 2) //2 = Vol
453 {
454 qDebug() << "ToDo: Write Volume not implemented yet!!!!!!!!";
455 // p_pStream->write_int(FIFF_MNE_SOURCE_SPACE_VOXEL_DIMS, this->shape)
456 // p_pStream->write_coord_trans(this->src_mri_t);
457
459 // write_coord_trans(fid, this['vox_mri_t'])
460
461 // write_coord_trans(fid, this['mri_ras_t'])
462
463 // write_float_sparse_rcs(fid, FIFF.FIFF_MNE_SOURCE_SPACE_INTERPOLATOR,
464 // this['interpolator'])
465
466 // if 'mri_file' in this and this['mri_file'] is not None:
467 // write_string(fid, FIFF.FIFF_MNE_SOURCE_SPACE_MRI_FILE,
468 // this['mri_file'])
469
470 // write_int(fid, FIFF.FIFF_MRI_WIDTH, this['mri_width'])
471 // write_int(fid, FIFF.FIFF_MRI_HEIGHT, this['mri_height'])
472 // write_int(fid, FIFF.FIFF_MRI_DEPTH, this['mri_depth'])
473
475 }
476
477 p_pStream->write_int(FIFF_MNE_SOURCE_SPACE_NPOINTS, &this->np);
480
481 // Which vertices are active
482 p_pStream->write_int(FIFF_MNE_SOURCE_SPACE_SELECTION, this->inuse.data(), this->inuse.size());
483 p_pStream->write_int(FIFF_MNE_SOURCE_SPACE_NUSE, &this->nuse);
484
485 p_pStream->write_int(FIFF_MNE_SOURCE_SPACE_NTRI, &this->ntri);
486 if (this->ntri > 0)
487 p_pStream->write_int_matrix(FIFF_MNE_SOURCE_SPACE_TRIANGLES, (this->itris.array() + 1).matrix());
488
489 if (this->type != 2 && this->use_itris.rows() > 0) {
490 // Use triangulation
492 p_pStream->write_int_matrix(FIFF_MNE_SOURCE_SPACE_USE_TRIANGLES, (this->use_itris.array() + 1).matrix());
493 }
494
495 // Patch-related information
496 if (!this->nearest.empty()) {
497 Eigen::VectorXi nearestIdx = this->nearestVertIdx();
498 Eigen::VectorXf nearestDistF = this->nearestDistVec().cast<float>();
499 p_pStream->write_int(FIFF_MNE_SOURCE_SPACE_NEAREST, nearestIdx.data(), nearestIdx.size());
500 p_pStream->write_float(FIFF_MNE_SOURCE_SPACE_NEAREST_DIST, nearestDistF.data(), nearestDistF.size());
501 }
502
503 // Distances
504 if (!this->dist.is_empty()) {
505 // Extract upper triangular portion from the dist matrix
506 const Eigen::SparseMatrix<float>& eigenDist = this->dist.eigen();
507 typedef Eigen::Triplet<float> T;
508 std::vector<T> tripletList;
509 tripletList.reserve(eigenDist.nonZeros());
510 for (int k = 0; k < eigenDist.outerSize(); ++k)
511 for (Eigen::SparseMatrix<float>::InnerIterator it(eigenDist, k); it; ++it)
512 if (it.col() >= it.row()) //only upper triangle -> todo iteration can be optimized
513 tripletList.push_back(T(it.row(), it.col(), it.value()));
514 Eigen::SparseMatrix<float> dists(eigenDist.rows(), eigenDist.cols());
515 dists.setFromTriplets(tripletList.begin(), tripletList.end());
516
518 //ToDo check if write_float_matrix or write float is okay
519 p_pStream->write_float(FIFF_MNE_SOURCE_SPACE_DIST_LIMIT, &this->dist_limit); //p_pStream->write_float_matrix(FIFF_MNE_SOURCE_SPACE_DIST_LIMIT, this->dist_limit);
520 }
521}
#define FIFF_MNE_SOURCE_SPACE_ID
#define FIFF_MNE_SOURCE_SPACE_DIST_LIMIT
#define FIFF_MNE_COORD_FRAME
#define FIFF_MNE_SOURCE_SPACE_TYPE
#define FIFF_MNE_SOURCE_SPACE_SELECTION
#define FIFF_MNE_SOURCE_SPACE_USE_TRIANGLES
#define FIFF_MNE_SOURCE_SPACE_NUSE_TRI
#define FIFF_MNE_SOURCE_SPACE_NORMALS
#define FIFF_MNE_SOURCE_SPACE_NEAREST_DIST
#define FIFF_MNE_SOURCE_SPACE_DIST
#define FIFF_MNE_SOURCE_SPACE_POINTS
#define FIFF_MNE_SOURCE_SPACE_NTRI
#define FIFF_MNE_SOURCE_SPACE_NPOINTS
#define FIFF_MNE_SOURCE_SPACE_TRIANGLES
#define FIFF_MNE_SOURCE_SPACE_NUSE
#define FIFF_MNE_SOURCE_SPACE_NEAREST
#define FIFFB_MNE_PARENT_MRI_FILE
Static linear-algebra helpers: SVD-based conditioning, block-diagonal assembly, sorted index pairs.
Per-hemisphere cortical surface bundle with decimation, patch info and rendering buffers.
Per-source-space-vertex nearest-cortex-vertex mapping.
Core MNE data structures (source spaces, source estimates, hemispheres).
FIFF file I/O, in-memory data structures and high-level readers/writers.
qint32 fiff_int_t
Definition fiff_types.h:86
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
Labelled 4x4 FIFF affine: source frame, destination frame, rotation, translation and cached inverse.
Eigen::Matrix< float, 4, 4, Eigen::DontAlign > trans
Sparse FIFF matrix: CCS or RCS storage with the value / index / pointer triple as written by FiffStre...
FIFF tag-stream reader/writer: wraps a QIODevice and exposes typed read_* / write_* methods for every...
fiff_long_t start_block(fiff_int_t kind)
fiff_long_t write_float_matrix(fiff_int_t kind, const Eigen::MatrixXf &mat)
fiff_long_t write_int_matrix(fiff_int_t kind, const Eigen::MatrixXi &mat)
fiff_long_t write_int(fiff_int_t kind, const fiff_int_t *data, fiff_int_t nel=1, fiff_int_t next=FIFFV_NEXT_SEQ)
fiff_long_t write_float(fiff_int_t kind, const float *data, fiff_int_t nel=1)
fiff_long_t write_float_sparse_rcs(fiff_int_t kind, const Eigen::SparseMatrix< float > &mat)
fiff_long_t end_block(fiff_int_t kind, fiff_int_t next=FIFFV_NEXT_SEQ)
static bool compareIdxValuePairSmallerThan(const std::pair< int, T > &lhs, const std::pair< int, T > &rhs)
Definition linalg.h:372
void writeToStream(FIFFLIB::FiffStream *p_pStream)
Eigen::MatrixX3d tri_cent
Eigen::VectorXd use_tri_area
Eigen::MatrixX3d use_tri_cent
Eigen::MatrixX3d tri_nn
MNEHemisphere & operator=(const MNEHemisphere &other)
MNESourceSpace::SPtr clone() const override
bool transform_hemisphere_to(FIFFLIB::fiff_int_t dest, const FIFFLIB::FiffCoordTrans &p_Trans)
MNEClusterInfo cluster_info
Eigen::MatrixX3d use_tri_nn
Eigen::VectorXd tri_area
QList< Eigen::VectorXi > pinfo
Eigen::VectorXi patch_inds
std::shared_ptr< MNESourceSpace > SPtr
std::vector< Eigen::VectorXi > neighbor_tri
std::vector< Eigen::VectorXi > neighbor_vert
Eigen::VectorXd nearestDistVec() const
FIFFLIB::FiffSparseMatrix dist
std::vector< MNENearest > nearest
Eigen::VectorXi nearestVertIdx() const