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//, m_TriCoords()
54//, m_pGeometryData(NULL)
55{
56 // Override some base class defaults to match MNEHemisphere semantics
57 this->type = 1;
58 this->id = -1;
59 this->np = -1;
60 this->ntri = -1;
61 this->coord_frame = -1;
62 this->nuse = -1;
63 this->nuse_tri = -1;
64 this->dist_limit = -1;
65}
66
67//=============================================================================================================
68
71, pinfo(p_MNEHemisphere.pinfo)
72, patch_inds(p_MNEHemisphere.patch_inds)
73, tri_cent(p_MNEHemisphere.tri_cent)
74, tri_nn(p_MNEHemisphere.tri_nn)
75, tri_area(p_MNEHemisphere.tri_area)
76, use_tri_cent(p_MNEHemisphere.use_tri_cent)
77, use_tri_nn(p_MNEHemisphere.use_tri_nn)
78, use_tri_area(p_MNEHemisphere.use_tri_area)
79, cluster_info(p_MNEHemisphere.cluster_info)
80, m_TriCoords(p_MNEHemisphere.m_TriCoords)
81{
82 // Copy base class (MNESurfaceOrVolume) fields that MNEHemisphere uses
83 this->type = p_MNEHemisphere.type;
84 this->id = p_MNEHemisphere.id;
85 this->np = p_MNEHemisphere.np;
86 this->ntri = p_MNEHemisphere.ntri;
87 this->coord_frame = p_MNEHemisphere.coord_frame;
88 this->rr = p_MNEHemisphere.rr;
89 this->nn = p_MNEHemisphere.nn;
90 this->nuse = p_MNEHemisphere.nuse;
91 this->inuse = p_MNEHemisphere.inuse;
92 this->vertno = p_MNEHemisphere.vertno;
93 this->itris = p_MNEHemisphere.itris;
94 this->use_itris = p_MNEHemisphere.use_itris;
95 this->nuse_tri = p_MNEHemisphere.nuse_tri;
96 this->dist_limit = p_MNEHemisphere.dist_limit;
97 this->dist = p_MNEHemisphere.dist;
98 this->nearest = p_MNEHemisphere.nearest;
99 this->neighbor_tri = p_MNEHemisphere.neighbor_tri;
100 this->neighbor_vert = p_MNEHemisphere.neighbor_vert;
101}
102
103//=============================================================================================================
104
106{
107 if (this != &other) {
108 // Copy base class (MNESurfaceOrVolume) fields
109 this->type = other.type;
110 this->id = other.id;
111 this->np = other.np;
112 this->ntri = other.ntri;
113 this->coord_frame = other.coord_frame;
114 this->rr = other.rr;
115 this->nn = other.nn;
116 this->nuse = other.nuse;
117 this->inuse = other.inuse;
118 this->vertno = other.vertno;
119 this->nuse_tri = other.nuse_tri;
120 this->dist_limit = other.dist_limit;
121 this->neighbor_tri = other.neighbor_tri;
122 this->neighbor_vert = other.neighbor_vert;
123
124 // Copy triangle index fields (inherited)
125 this->itris = other.itris;
126 this->use_itris = other.use_itris;
127
128 // Copy inherited fields with value semantics
129 this->dist = other.dist;
130 this->nearest = other.nearest;
131
132 // Copy MNEHemisphere fields
133 this->pinfo = other.pinfo;
134 this->patch_inds = other.patch_inds;
135 this->tri_cent = other.tri_cent;
136 this->tri_nn = other.tri_nn;
137 this->tri_area = other.tri_area;
138 this->use_tri_cent = other.use_tri_cent;
139 this->use_tri_nn = other.use_tri_nn;
140 this->use_tri_area = other.use_tri_area;
141 this->cluster_info = other.cluster_info;
142 this->m_TriCoords = other.m_TriCoords;
143 }
144 return *this;
145}
146
147//=============================================================================================================
148
152
153//=============================================================================================================
154
156{
157 return std::make_shared<MNEHemisphere>(*this);
158}
159
160//=============================================================================================================
161
163{
164 //
165 // Main triangulation
166 //
167 qInfo("\tCompleting triangulation info...");
168 tri_cent = MatrixX3d::Zero(ntri,3);
169 tri_nn = MatrixX3d::Zero(ntri,3);
170 tri_area = VectorXd::Zero(ntri);
171
172 Matrix3d r;
173 Vector3d a, b;
174 int k = 0;
175 float size = 0;
176 for (int i = 0; i < ntri; ++i)
177 {
178 for ( int j = 0; j < 3; ++j)
179 {
180 k = itris(i, j);
181
182 r(j,0) = rr(k, 0);
183 r(j,1) = rr(k, 1);
184 r(j,2) = rr(k, 2);
185
186 tri_cent(i, 0) += rr(k, 0);
187 tri_cent(i, 1) += rr(k, 1);
188 tri_cent(i, 2) += rr(k, 2);
189 }
190 tri_cent.row(i) /= 3.0f;
191
192 //cross product {cross((r2-r1),(r3-r1))}
193 a = (r.row(1) - r.row(0 )).transpose();
194 b = (r.row(2) - r.row(0)).transpose();
195 tri_nn(i,0) = a(1)*b(2)-a(2)*b(1);
196 tri_nn(i,1) = a(2)*b(0)-a(0)*b(2);
197 tri_nn(i,2) = a(0)*b(1)-a(1)*b(0);
198
199 //area
200 size = tri_nn.row(i)*tri_nn.row(i).transpose();
201 size = std::pow(size, 0.5f );
202
203 tri_area(i) = size/2.0f;
204 tri_nn.row(i) /= size;
205
206 }
207 qInfo("[done]\n");
208
209 //
210 // Selected triangles
211 //
212 qInfo("\tCompleting selection triangulation info...");
213 if (nuse_tri > 0)
214 {
215 use_tri_cent = MatrixX3d::Zero(nuse_tri,3);
216 use_tri_nn = MatrixX3d::Zero(nuse_tri,3);
217 use_tri_area = VectorXd::Zero(nuse_tri);
218
219 for (int i = 0; i < nuse_tri; ++i)
220 {
221 for ( int j = 0; j < 3; ++j)
222 {
223 k = use_itris(i, j);
224
225 r(j,0) = rr(k, 0);
226 r(j,1) = rr(k, 1);
227 r(j,2) = rr(k, 2);
228
229 use_tri_cent(i, 0) += rr(k, 0);
230 use_tri_cent(i, 1) += rr(k, 1);
231 use_tri_cent(i, 2) += rr(k, 2);
232 }
233 use_tri_cent.row(i) /= 3.0f;
234
235 //cross product {cross((r2-r1),(r3-r1))}
236 a = r.row(1) - r.row(0 );
237 b = r.row(2) - r.row(0);
238 use_tri_nn(i,0) = a(1)*b(2)-a(2)*b(1);
239 use_tri_nn(i,1) = a(2)*b(0)-a(0)*b(2);
240 use_tri_nn(i,2) = a(0)*b(1)-a(1)*b(0);
241
242 //area
243 size = use_tri_nn.row(i)*use_tri_nn.row(i).transpose();
244 size = std::pow(size, 0.5f );
245
246 use_tri_area(i) = size/2.0f;
247 }
248
249 }
250 qInfo("[done]\n");
251
252 qInfo("\tCompleting triangle and vertex neighboring info...");
254 qInfo("[done]\n");
255
256 return true;
257}
258
259//=============================================================================================================
260
262{
263 if (nearest.empty())
264 {
265 pinfo.clear();
266 patch_inds = VectorXi();
267 return false;
268 }
269
270 qInfo("\tComputing patch statistics...");
271
272 std::vector< std::pair<int,int> > t_vIndn;
273
274 for(size_t i = 0; i < nearest.size(); ++i)
275 {
276 std::pair<int,int> t_pair(static_cast<int>(i), nearest[i].nearest);
277 t_vIndn.push_back(t_pair);
278 }
279 std::sort(t_vIndn.begin(),t_vIndn.end(), Linalg::compareIdxValuePairSmallerThan<int> );
280
281 VectorXi nearest_sorted(t_vIndn.size());
282
283 int current = 0;
284 std::vector<int> t_vfirsti;
285 t_vfirsti.push_back(current);
286 std::vector<int> t_vlasti;
287
288 for(int i = 0; i < static_cast<int>(t_vIndn.size()); ++i)
289 {
290 nearest_sorted[i] = t_vIndn[i].second;
291 if (t_vIndn[current].second != t_vIndn[i].second)
292 {
293 current = i;
294 t_vlasti.push_back(i-1);
295 t_vfirsti.push_back(current);
296 }
297 }
298 t_vlasti.push_back(static_cast<int>(t_vIndn.size()-1));
299
300 for(int k = 0; k < static_cast<int>(t_vfirsti.size()); ++k)
301 {
302 Eigen::VectorXi t_vIndex(t_vlasti[k] - t_vfirsti[k] + 1);
303
304 for(int l = t_vfirsti[k]; l <= t_vlasti[k]; ++l)
305 t_vIndex[l - t_vfirsti[k]] = t_vIndn[l].first;
306
307 std::sort(t_vIndex.data(), t_vIndex.data() + t_vIndex.size());
308
309 pinfo.append(t_vIndex);
310 }
311
312 // compute patch indices of the in-use source space vertices
313 Eigen::VectorXi patch_verts(t_vlasti.size());
314 for(int i = 0; i < static_cast<int>(t_vlasti.size()); ++i)
315 patch_verts[i] = nearest_sorted[t_vlasti[i]];
316
317 patch_inds.resize(vertno.size());
318 for(int i = 0; i < vertno.size(); ++i)
319 {
320 const int* ptr = std::find(patch_verts.data(), patch_verts.data() + patch_verts.size(), vertno[i]);
321 patch_inds[i] = static_cast<int>(ptr - patch_verts.data());
322 }
323
324 return true;
325}
326
327//=============================================================================================================
328
330{
331 int k,c,p,q;
332 bool found;
333
334 //Create neighboring triangle vector using temporary std::vector for efficient appending
335 {
336 std::vector<std::vector<int>> temp_ntri(this->itris.rows());
337 for (p = 0; p < this->itris.rows(); p++) {
338 for (k = 0; k < 3; k++) {
339 temp_ntri[this->itris(p,k)].push_back(p);
340 }
341 }
342 neighbor_tri.resize(this->itris.rows());
343 for (k = 0; k < static_cast<int>(temp_ntri.size()); k++) {
344 neighbor_tri[k] = Eigen::Map<Eigen::VectorXi>(temp_ntri[k].data(), temp_ntri[k].size());
345 }
346 }
347
348 //Create the neighboring vertices vector using temporary std::vector
349 {
350 std::vector<std::vector<int>> temp_nvert(this->np);
351 for (k = 0; k < this->np; k++) {
352 for (p = 0; p < neighbor_tri[k].size(); p++) {
353 //Fit in the other vertices of the neighboring triangle
354 for (c = 0; c < 3; c++) {
355 int vert = this->itris(neighbor_tri[k][p], c);
356
357 if (vert != k) {
358 found = false;
359
360 for (q = 0; q < static_cast<int>(temp_nvert[k].size()); q++) {
361 if (temp_nvert[k][q] == vert) {
362 found = true;
363 break;
364 }
365 }
366
367 if(!found) {
368 temp_nvert[k].push_back(vert);
369 }
370 }
371 }
372 }
373 }
374 neighbor_vert.resize(this->np);
375 for (k = 0; k < this->np; k++) {
376 neighbor_vert[k] = Eigen::Map<Eigen::VectorXi>(temp_nvert[k].data(), temp_nvert[k].size());
377 }
378 }
379
380 return true;
381}
382
383//=============================================================================================================
384
386{
387 // Reset base class fields
388 type = 1;
389 id = -1;
390 np = -1;
391 ntri = -1;
392 coord_frame = -1;
393 rr = PointsT::Zero(0,3);
394 nn = NormalsT::Zero(0,3);
395 nuse = -1;
396 inuse = VectorXi::Zero(0);
397 vertno = VectorXi::Zero(0);
398 nuse_tri = -1;
399 dist_limit = -1;
400 neighbor_tri.clear();
401 neighbor_vert.clear();
402
403 // Reset inherited value-semantic fields
404 itris = TrianglesT::Zero(0,3);
405 use_itris = TrianglesT::Zero(0,3);
407 nearest.clear();
408
409 // Reset MNEHemisphere fields
410 pinfo.clear();
411 patch_inds = VectorXi::Zero(0);
412 tri_cent = MatrixX3d::Zero(0,3);
413 tri_nn = MatrixX3d::Zero(0,3);
414 tri_area = VectorXd::Zero(0);
415 use_tri_cent = MatrixX3d::Zero(0,3);
416 use_tri_nn = MatrixX3d::Zero(0,3);
417 use_tri_area = VectorXd::Zero(0);
418
419 cluster_info.clear();
420
421 m_TriCoords = MatrixXf();
422}
423
424//=============================================================================================================
425
426MatrixXf& MNEHemisphere::getTriCoords(float p_fScaling)
427{
428 if(m_TriCoords.size() == 0)
429 {
430 m_TriCoords = MatrixXf(3,3*itris.rows());
431 for(int i = 0; i < itris.rows(); ++i)
432 {
433 m_TriCoords.col(i*3) = rr.row( itris(i,0) ).transpose().cast<float>();
434 m_TriCoords.col(i*3+1) = rr.row( itris(i,1) ).transpose().cast<float>();
435 m_TriCoords.col(i*3+2) = rr.row( itris(i,2) ).transpose().cast<float>();
436 }
437 }
438
439 m_TriCoords *= p_fScaling;
440
441 return m_TriCoords;
442}
443
444//=============================================================================================================
445
446bool MNEHemisphere::transform_hemisphere_to(fiff_int_t dest, const FiffCoordTrans &p_Trans)
447{
448 FiffCoordTrans trans(p_Trans);
449
450 if (this->coord_frame == dest)
451 {
452// res = src;
453 return true;
454 }
455
456 if (trans.to == this->coord_frame && trans.from == dest)
457 trans.invert_transform();
458 else if(trans.from != this->coord_frame || trans.to != dest)
459 {
460 qWarning("Cannot transform the source space using this coordinate transformation");//Consider throw
461 return false;
462 }
463
464 MatrixXf t = trans.trans.block(0,0,3,4);
465// res = src;
466 this->coord_frame = dest;
467 MatrixXf t_rr = MatrixXf::Ones(this->np, 4);
468 t_rr.block(0, 0, this->np, 3) = this->rr;
469 MatrixXf t_nn = MatrixXf::Zero(this->np, 4);
470 t_nn.block(0, 0, this->np, 3) = this->nn;
471
472 this->rr = (t*t_rr.transpose()).transpose();
473 this->nn = (t*t_nn.transpose()).transpose();
474
475 return true;
476}
477
478//=============================================================================================================
479//ToDo
481{
482 if(this->type == 1 || this->type == 2)
483 p_pStream->write_int(FIFF_MNE_SOURCE_SPACE_TYPE, &this->type);
484 else
485 qWarning("Unknown source space type (%d)", this->type);
486 p_pStream->write_int(FIFF_MNE_SOURCE_SPACE_ID, &this->id);
487
488// data = this.get('subject_his_id', None)
489// if data:
490// write_string(fid, FIFF.FIFF_SUBJ_HIS_ID, data)
491 p_pStream->write_int(FIFF_MNE_COORD_FRAME, &this->coord_frame);
492
493 if(this->type == 2) //2 = Vol
494 {
495 qDebug() << "ToDo: Write Volume not implemented yet!!!!!!!!";
496// p_pStream->write_int(FIFF_MNE_SOURCE_SPACE_VOXEL_DIMS, this->shape)
497// p_pStream->write_coord_trans(this->src_mri_t);
498
500// write_coord_trans(fid, this['vox_mri_t'])
501
502// write_coord_trans(fid, this['mri_ras_t'])
503
504// write_float_sparse_rcs(fid, FIFF.FIFF_MNE_SOURCE_SPACE_INTERPOLATOR,
505// this['interpolator'])
506
507// if 'mri_file' in this and this['mri_file'] is not None:
508// write_string(fid, FIFF.FIFF_MNE_SOURCE_SPACE_MRI_FILE,
509// this['mri_file'])
510
511// write_int(fid, FIFF.FIFF_MRI_WIDTH, this['mri_width'])
512// write_int(fid, FIFF.FIFF_MRI_HEIGHT, this['mri_height'])
513// write_int(fid, FIFF.FIFF_MRI_DEPTH, this['mri_depth'])
514
516 }
517
518 p_pStream->write_int(FIFF_MNE_SOURCE_SPACE_NPOINTS, &this->np);
521
522 // Which vertices are active
523 p_pStream->write_int(FIFF_MNE_SOURCE_SPACE_SELECTION, this->inuse.data(), this->inuse.size());
524 p_pStream->write_int(FIFF_MNE_SOURCE_SPACE_NUSE, &this->nuse);
525
526 p_pStream->write_int(FIFF_MNE_SOURCE_SPACE_NTRI, &this->ntri);
527 if (this->ntri > 0)
528 p_pStream->write_int_matrix(FIFF_MNE_SOURCE_SPACE_TRIANGLES, (this->itris.array() + 1).matrix());
529
530 if (this->type != 2 && this->use_itris.rows() > 0)
531 {
532 // Use triangulation
534 p_pStream->write_int_matrix(FIFF_MNE_SOURCE_SPACE_USE_TRIANGLES, (this->use_itris.array() + 1).matrix());
535 }
536
537 // Patch-related information
538 if (!this->nearest.empty())
539 {
540 Eigen::VectorXi nearestIdx = this->nearestVertIdx();
541 Eigen::VectorXf nearestDistF = this->nearestDistVec().cast<float>();
542 p_pStream->write_int(FIFF_MNE_SOURCE_SPACE_NEAREST, nearestIdx.data(), nearestIdx.size());
543 p_pStream->write_float(FIFF_MNE_SOURCE_SPACE_NEAREST_DIST, nearestDistF.data(), nearestDistF.size());
544 }
545
546 // Distances
547 if (!this->dist.is_empty())
548 {
549 // Extract upper triangular portion from the dist matrix
550 const Eigen::SparseMatrix<float>& eigenDist = this->dist.eigen();
551 typedef Eigen::Triplet<float> T;
552 std::vector<T> tripletList;
553 tripletList.reserve(eigenDist.nonZeros());
554 for (int k=0; k < eigenDist.outerSize(); ++k)
555 for (Eigen::SparseMatrix<float>::InnerIterator it(eigenDist,k); it; ++it)
556 if(it.col() >= it.row())//only upper triangle -> todo iteration can be optimized
557 tripletList.push_back(T(it.row(), it.col(), it.value()));
558 Eigen::SparseMatrix<float> dists(eigenDist.rows(), eigenDist.cols());
559 dists.setFromTriplets(tripletList.begin(), tripletList.end());
560
562 //ToDo check if write_float_matrix or write float is okay
563 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);
564 }
565}
566
568
569//QGeometryData* MNEHemisphere::getGeometryData(float p_fScaling)
570//{
571// if(m_pGeometryData == NULL)
572// {
573// m_pGeometryData = new QGeometryData();
574
575// MatrixXd* triCoords = getTriCoords(p_fScaling);
576
577// m_pGeometryData->appendVertexArray(QArray<QVector3D>::fromRawData( reinterpret_cast<const QVector3D*>(triCoords->data()), triCoords->cols() ));
578// }
579
580// return m_pGeometryData;
581//}
Per-hemisphere cortical surface bundle with decimation, patch info and rendering buffers.
Per-source-space-vertex nearest-cortex-vertex mapping.
#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.
Core MNE data structures (source spaces, source estimates, hemispheres).
FIFF file I/O, in-memory data structures and high-level readers/writers.
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:357
void writeToStream(FIFFLIB::FiffStream *p_pStream)
Eigen::MatrixX3d tri_cent
Eigen::VectorXd use_tri_area
Eigen::MatrixX3d use_tri_cent
Eigen::MatrixXf & getTriCoords(float p_fScaling=1.0f)
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