v2.0.0
Loading...
Searching...
No Matches
fiff_coord_trans.cpp
Go to the documentation of this file.
1//=============================================================================================================
36
37//=============================================================================================================
38// INCLUDES
39//=============================================================================================================
40
41#include "fiff_coord_trans.h"
42
43#include "fiff_stream.h"
44#include "fiff_tag.h"
45#include "fiff_dir_node.h"
46
47#include <iostream>
48
49#include <QFile>
50#include <QTextStream>
51
52#define _USE_MATH_DEFINES
53#include <math.h>
54
55//=============================================================================================================
56// EIGEN INCLUDES
57//=============================================================================================================
58
59#include <Eigen/Dense>
60
61//=============================================================================================================
62// USED NAMESPACES
63//=============================================================================================================
64
65using namespace FIFFLIB;
66using namespace Eigen;
67
68//=============================================================================================================
69// DEFINE MEMBER METHODS
70//=============================================================================================================
71
73: from(-1)
74, to(-1)
75, trans(MatrixXf::Identity(4,4))
76, invtrans(MatrixXf::Identity(4,4))
77{
78}
79
80//=============================================================================================================
81
82FiffCoordTrans::FiffCoordTrans(QIODevice &p_IODevice)
83: from(-1)
84, to(-1)
85, trans(MatrixXf::Identity(4,4))
86, invtrans(MatrixXf::Identity(4,4))
87{
88 if(!read(p_IODevice, *this))
89 {
90 printf("\tCoordindate transform not found.\n");//ToDo Throw here
91 return;
92 }
93}
94
95//=============================================================================================================
96
98: from(p_FiffCoordTrans.from)
99, to(p_FiffCoordTrans.to)
100, trans(p_FiffCoordTrans.trans)
101, invtrans(p_FiffCoordTrans.invtrans)
102{
103}
104
105//=============================================================================================================
106
110
111//=============================================================================================================
112
114{
115 from = -1;
116 to = -1;
117 trans.setIdentity();
118 invtrans.setIdentity();
119}
120
121//=============================================================================================================
122
124{
125 fiff_int_t from_new = this->to;
126 this->to = this->from;
127 this->from = from_new;
128 this->trans = this->trans.inverse().eval();
129 this->invtrans = this->invtrans.inverse().eval();
130
131 return true;
132}
133
134//=============================================================================================================
135
136bool FiffCoordTrans::read(QIODevice& p_IODevice, FiffCoordTrans& p_Trans)
137{
138 FiffStream::SPtr pStream(new FiffStream(&p_IODevice));
139
140 printf("Reading coordinate transform from %s...\n", pStream->streamName().toUtf8().constData());
141 if(!pStream->open())
142 return false;
143
144 //
145 // Locate and read the coordinate transformation
146 //
147 FiffTag::SPtr t_pTag;
148 bool success = false;
149
150 //
151 // Get the MRI <-> head coordinate transformation
152 //
153 for ( qint32 k = 0; k < pStream->dir().size(); ++k )
154 {
155 if ( pStream->dir()[k]->kind == FIFF_COORD_TRANS )
156 {
157 pStream->read_tag(t_pTag,pStream->dir()[k]->pos);
158 p_Trans = t_pTag->toCoordTrans();
159 success = true;
160 }
161 }
162
163 return success;
164}
165
166//=============================================================================================================
167
168void FiffCoordTrans::write(QIODevice &qIODevice)
169{
170 // Create the file and save the essentials
171 FiffStream::SPtr pStream = FiffStream::start_file(qIODevice);
172 printf("Write coordinate transform in %s...\n", pStream->streamName().toUtf8().constData());
173 this->writeToStream(pStream.data());
174 pStream->end_file();
175 qIODevice.close();
176}
177
178//=============================================================================================================
179
181{
182 pStream->write_coord_trans(*this);
183}
184
185//=============================================================================================================
186
187MatrixX3f FiffCoordTrans::apply_trans(const MatrixX3f& rr, bool do_move) const
188{
189 MatrixX4f rr_ones(rr.rows(), 4);
190 if(do_move) {
191 rr_ones.setOnes();
192 } else {
193 rr_ones.setZero();
194 }
195 rr_ones.block(0,0,rr.rows(),3) = rr;
196 return rr_ones*trans.block<3,4>(0,0).transpose();
197}
198
199//=============================================================================================================
200
201MatrixX3f FiffCoordTrans::apply_inverse_trans(const MatrixX3f& rr, bool do_move) const
202{
203 MatrixX4f rr_ones(rr.rows(), 4);
204 if(do_move) {
205 rr_ones.setOnes();
206 } else {
207 rr_ones.setZero();
208 }
209 rr_ones.block(0,0,rr.rows(),3) = rr;
210 return rr_ones*invtrans.block<3,4>(0,0).transpose();
211}
212
213//=============================================================================================================
214
215QString FiffCoordTrans::frame_name (int frame)
216{
217 switch(frame) {
218 case FIFFV_COORD_UNKNOWN: return "unknown";
219 case FIFFV_COORD_DEVICE: return "MEG device";
220 case FIFFV_COORD_ISOTRAK: return "isotrak";
221 case FIFFV_COORD_HPI: return "hpi";
222 case FIFFV_COORD_HEAD: return "head";
223 case FIFFV_COORD_MRI: return "MRI (surface RAS)";
224 case FIFFV_MNE_COORD_MRI_VOXEL: return "MRI voxel";
225 case FIFFV_COORD_MRI_SLICE: return "MRI slice";
226 case FIFFV_COORD_MRI_DISPLAY: return "MRI display";
227 case FIFFV_MNE_COORD_CTF_DEVICE: return "CTF MEG device";
228 case FIFFV_MNE_COORD_CTF_HEAD: return "CTF/4D/KIT head";
229 case FIFFV_MNE_COORD_RAS: return "RAS (non-zero origin)";
230 case FIFFV_MNE_COORD_MNI_TAL: return "MNI Talairach";
231 case FIFFV_MNE_COORD_FS_TAL_GTZ: return "Talairach (MNI z > 0)";
232 case FIFFV_MNE_COORD_FS_TAL_LTZ: return "Talairach (MNI z < 0)";
233 default: return "unknown";
234 }
235}
236
237//=============================================================================================================
238
239FiffCoordTrans::FiffCoordTrans(int from, int to, const Matrix3f& rot, const Vector3f& move)
240{
241 this->trans = MatrixXf::Zero(4,4);
242
243 this->from = from;
244 this->to = to;
245
246 this->trans.block<3,3>(0,0) = rot;
247 this->trans.block<3,1>(0,3) = move;
248 this->trans(3,3) = 1.0f;
249
251}
252
253//=============================================================================================================
254
255FiffCoordTrans::FiffCoordTrans(int from, int to, const Matrix4f& matTrans, bool bStandard)
256{
257 this->trans = matTrans;
258 this->from = from;
259 this->to = to;
260
261 if(bStandard) {
262 // make sure that it is a standard transform if requested
263 this->trans.row(3) = Vector4f(0,0,0,1);
264 }
265
267}
268
269//=============================================================================================================
270
272{
273 t.invtrans = t.trans.inverse().eval();
274 return true;
275}
276
277//=============================================================================================================
278
280{
281 std::cout << "Coordinate transformation: ";
282 std::cout << (QString("%1 -> %2\n").arg(frame_name(this->from)).arg(frame_name(this->to))).toUtf8().data();
283
284 for (int p = 0; p < 3; p++)
285 printf("\t% 8.6f % 8.6f % 8.6f\t% 7.2f mm\n", trans(p,0),trans(p,1),trans(p,2),1000*trans(p,3));
286 printf("\t% 8.6f % 8.6f % 8.6f % 7.2f\n",trans(3,0),trans(3,1),trans(3,2),trans(3,3));
287}
288
289//=============================================================================================================
290
291float FiffCoordTrans::angleTo(Eigen::MatrixX4f mTransDest)
292{
293 MatrixX4f mDevHeadT = this->trans;
294 Matrix3f mRot = mDevHeadT.block(0,0,3,3);
295 Matrix3f mRotNew = mTransDest.block(0,0,3,3);
296
297 Quaternionf quat(mRot);
298 Quaternionf quatNew(mRotNew);
299
300 float fAngle;
301
302 // calculate rotation
303 Quaternionf quatCompare;
304
305 quatCompare = quat*quatNew.inverse();
306 fAngle = quat.angularDistance(quatNew);
307 fAngle = fAngle * 180 / M_PI;
308
309 return fAngle;
310}
311
312//=============================================================================================================
313
314float FiffCoordTrans::translationTo(Eigen::MatrixX4f mTransDest)
315{
316 VectorXf vTrans = this->trans.col(3);
317 VectorXf vTransDest = mTransDest.col(3);
318
319 float fMove = (vTrans-vTransDest).norm();
320 return fMove;
321}
322
323//=============================================================================================================
324
325void FiffCoordTrans::apply_trans(float r[3], const FiffCoordTrans& t, bool do_move)
326{
327 float res[3];
328 for (int j = 0; j < 3; j++) {
329 res[j] = do_move ? t.trans(j,3) : 0.0f;
330 for (int k = 0; k < 3; k++)
331 res[j] += t.trans(j,k) * r[k];
332 }
333 for (int j = 0; j < 3; j++)
334 r[j] = res[j];
335}
336
337//=============================================================================================================
338
339void FiffCoordTrans::apply_inverse_trans(float r[3], const FiffCoordTrans& t, bool do_move)
340{
341 float res[3];
342 for (int j = 0; j < 3; j++) {
343 res[j] = do_move ? t.invtrans(j,3) : 0.0f;
344 for (int k = 0; k < 3; k++)
345 res[j] += t.invtrans(j,k) * r[k];
346 }
347 for (int j = 0; j < 3; j++)
348 r[j] = res[j];
349}
350
351//=============================================================================================================
352
354{
356 t.from = from;
357 t.to = to;
358 // trans and invtrans are already identity from default constructor
359 return t;
360}
361
362//=============================================================================================================
363
365{
367 t.from = this->to;
368 t.to = this->from;
369 t.trans = this->invtrans;
370 t.invtrans = this->trans;
371 return t;
372}
373
374//=============================================================================================================
375
377{
378 FiffCoordTrans a, b;
379 bool found = false;
380
381 for (int swapped = 0; swapped < 2 && !found; swapped++) {
382 const FiffCoordTrans& s1 = (swapped == 0) ? t1 : t2;
383 const FiffCoordTrans& s2 = (swapped == 0) ? t2 : t1;
384
385 if (s1.to == to && s2.from == from) {
386 a = s1; b = s2; found = true;
387 } else if (s1.from == to && s2.from == from) {
388 a = s1.inverted(); b = s2; found = true;
389 } else if (s1.to == to && s2.to == from) {
390 a = s1; b = s2.inverted(); found = true;
391 } else if (s1.from == to && s2.to == from) {
392 a = s1.inverted(); b = s2.inverted(); found = true;
393 }
394 }
395
396 if (!found || a.from != b.to) {
397 qCritical("Cannot combine coordinate transforms");
398 return FiffCoordTrans();
399 }
400
401 // Catenate: result = a * b (apply b first, then a)
402 FiffCoordTrans result;
403 result.from = b.from;
404 result.to = a.to;
405 result.trans = a.trans * b.trans;
406 result.trans.row(3) << 0.0f, 0.0f, 0.0f, 1.0f;
407 addInverse(result);
408 return result;
409}
410
411//=============================================================================================================
412
414 const float* rL,
415 const float* rN,
416 const float* rR)
417{
418 Map<const Vector3f> L(rL);
419 Map<const Vector3f> N(rN);
420 Map<const Vector3f> R(rR);
421
422 Vector3f diff1 = N - L;
423 Vector3f diff2 = R - L;
424
425 float alpha = diff1.dot(diff2) / diff2.dot(diff2);
426 Vector3f r0 = (1.0f - alpha) * L + alpha * R;
427 Vector3f ex = diff2.normalized();
428 Vector3f ey = (N - r0).normalized();
429 Vector3f ez = ex.cross(ey);
430
431 FiffCoordTrans result;
432 result.from = from;
433 result.to = to;
434 result.rot().col(0) = ex;
435 result.rot().col(1) = ey;
436 result.rot().col(2) = ez;
437 result.move() = r0;
438 addInverse(result);
439 return result;
440}
441
442//=============================================================================================================
443
445{
446 QFile file(name);
447 FiffStream::SPtr stream(new FiffStream(&file));
448
449 if (!stream->open()) {
450 return FiffCoordTrans();
451 }
452
453 FiffTag::SPtr t_pTag;
454 for (int k = 0; k < stream->dir().size(); k++) {
455 if (stream->dir()[k]->kind == FIFF_COORD_TRANS) {
456 if (!stream->read_tag(t_pTag, stream->dir()[k]->pos))
457 continue;
458
459 FiffCoordTrans t = t_pTag->toCoordTrans();
460 if (t.from == from && t.to == to) {
461 stream->close();
462 return t;
463 } else if (t.from == to && t.to == from) {
465 stream->close();
466 return t;
467 }
468 }
469 }
470
471 qCritical("No suitable coordinate transformation found in %s.", name.toUtf8().constData());
472 stream->close();
473 return FiffCoordTrans();
474}
475
476//=============================================================================================================
477
482
483//=============================================================================================================
484
489
490//=============================================================================================================
491
493{
494 QFile file(name);
495 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
496 qCritical("Cannot open %s", name.toUtf8().constData());
497 return FiffCoordTrans();
498 }
499
500 QTextStream in(&file);
501 Matrix3f rot;
502 Vector3f moveVec;
503
504 int row = 0;
505 while (!in.atEnd() && row < 4) {
506 QString line = in.readLine();
507 // Strip comments and whitespace
508 int commentIdx = line.indexOf('#');
509 if (commentIdx >= 0)
510 line = line.left(commentIdx);
511 line = line.trimmed();
512 if (line.isEmpty())
513 continue;
514
515 if (row < 3) {
516 QStringList parts = line.simplified().split(' ', Qt::SkipEmptyParts);
517 if (parts.size() < 4) {
518 qCritical("Cannot read the coordinate transformation from %s",
519 name.toUtf8().constData());
520 return FiffCoordTrans();
521 }
522 bool ok1, ok2, ok3, ok4;
523 rot(row, 0) = parts[0].toFloat(&ok1);
524 rot(row, 1) = parts[1].toFloat(&ok2);
525 rot(row, 2) = parts[2].toFloat(&ok3);
526 moveVec[row] = parts[3].toFloat(&ok4) / 1000.0f;
527 if (!ok1 || !ok2 || !ok3 || !ok4) {
528 qCritical("Bad floating point number in coordinate transformation");
529 return FiffCoordTrans();
530 }
531 }
532 // Row 3 (the last row of the 4x4 matrix) is consumed but ignored
533 row++;
534 }
535
536 if (row < 4) {
537 qCritical("Cannot read the coordinate transformation from %s",
538 name.toUtf8().constData());
539 return FiffCoordTrans();
540 }
541
542 return FiffCoordTrans(from, to, rot, moveVec);
543}
544
545//=============================================================================================================
546
551
552//=============================================================================================================
553
554FiffCoordTrans FiffCoordTrans::readFromTag(const QSharedPointer<FiffTag>& tag)
555{
557 if (tag->isMatrix() || tag->getType() != FIFFT_COORD_TRANS_STRUCT || tag->data() == nullptr)
558 return t;
559
560 qint32* t_pInt32 = (qint32*)tag->data();
561 t.from = t_pInt32[0];
562 t.to = t_pInt32[1];
563
564 float* t_pFloat = (float*)tag->data();
565 int count = 0;
566 int r, c;
567 for (r = 0; r < 3; ++r) {
568 t.trans(r, 3) = t_pFloat[11 + r];
569 for (c = 0; c < 3; ++c) {
570 t.trans(r, c) = t_pFloat[2 + count];
571 ++count;
572 }
573 }
574 t.trans(3, 0) = 0.0f; t.trans(3, 1) = 0.0f; t.trans(3, 2) = 0.0f; t.trans(3, 3) = 1.0f;
575
576 count = 0;
577 for (r = 0; r < 3; ++r) {
578 t.invtrans(r, 3) = t_pFloat[23 + r];
579 for (c = 0; c < 3; ++c) {
580 t.invtrans(r, c) = t_pFloat[14 + count];
581 ++count;
582 }
583 }
584 t.invtrans(3, 0) = 0.0f; t.invtrans(3, 1) = 0.0f; t.invtrans(3, 2) = 0.0f; t.invtrans(3, 3) = 1.0f;
585
586 return t;
587}
588
589//=============================================================================================================
590
592 const FiffDirNode::SPtr& node,
593 int from, int to)
594{
595 FiffTag::SPtr t_pTag;
596 fiff_int_t kind, pos;
597 int k;
598
599 for (k = 0; k < node->nent(); k++)
600 kind = node->dir[k]->kind;
601 pos = node->dir[k]->pos;
602 if (kind == FIFF_COORD_TRANS) {
603 if (!stream->read_tag(t_pTag, pos))
604 return FiffCoordTrans();
605 FiffCoordTrans res = readFromTag(t_pTag);
606 if (res.isEmpty())
607 return FiffCoordTrans();
608 if (res.from == from && res.to == to) {
609 return res;
610 }
611 else if (res.from == to && res.to == from) {
612 return res.inverted();
613 }
614 }
615 printf("No suitable coordinate transformation found");
616 return FiffCoordTrans();
617}
618
619//=============================================================================================================
620
622 int to_frame,
623 float **fromp,
624 float **top,
625 float *w,
626 int np,
627 float max_diff)
628{
629 /*
630 * Map the raw C arrays into Eigen matrices for the computation.
631 * fromp and top are np×3 C matrices (float**)
632 */
633 Eigen::MatrixXf fromPts(np, 3), toPts(np, 3);
634 for (int j = 0; j < np; ++j)
635 for (int c = 0; c < 3; ++c) {
636 fromPts(j, c) = fromp[j][c];
637 toPts(j, c) = top[j][c];
638 }
639
640 /* Calculate centroids and subtract */
641 Eigen::Vector3f from0 = fromPts.colwise().mean();
642 Eigen::Vector3f to0 = toPts.colwise().mean();
643
644 Eigen::MatrixXf fromC = fromPts.rowwise() - from0.transpose();
645 Eigen::MatrixXf toC = toPts.rowwise() - to0.transpose();
646
647 /* Compute the cross-covariance matrix S */
648 Eigen::Matrix3f S;
649 if (w) {
650 Eigen::VectorXf wVec = Eigen::Map<Eigen::VectorXf>(w, np);
651 S = fromC.transpose() * wVec.asDiagonal() * toC;
652 } else {
653 S = fromC.transpose() * toC;
654 }
655
656 /* SVD of S to solve the orthogonal Procrustes problem */
657 Eigen::JacobiSVD<Eigen::Matrix3f> svd(S, Eigen::ComputeFullU | Eigen::ComputeFullV);
658 Eigen::Matrix3f R = svd.matrixV() * svd.matrixU().transpose();
659
660 /* Translation */
661 Eigen::Vector3f moveVec = to0 - R * from0;
662
663 /* Test the transformation */
664 for (int p = 0; p < np; ++p) {
665 Eigen::Vector3f rr = R * fromPts.row(p).transpose() + moveVec;
666 float diff = (toPts.row(p).transpose() - rr).norm();
667 if (diff > max_diff) {
668 printf("Too large difference in matching : %7.1f > %7.1f mm",
669 1000.0f * diff, 1000.0f * max_diff);
670 return FiffCoordTrans();
671 }
672 }
673
674 return FiffCoordTrans(from_frame, to_frame, R, moveVec);
675}
#define M_PI
FiffTag class declaration, which provides fiff tag I/O and processing methods.
#define FIFFV_MNE_COORD_MNI_TAL
#define FIFFV_MNE_COORD_FS_TAL_LTZ
#define FIFFV_COORD_MRI_SLICE
#define FIFFV_MNE_COORD_CTF_DEVICE
#define FIFFV_COORD_DEVICE
#define FIFFV_COORD_MRI_DISPLAY
#define FIFFV_MNE_COORD_MRI_VOXEL
#define FIFFV_MNE_COORD_CTF_HEAD
#define FIFFV_COORD_HPI
#define FIFFV_COORD_HEAD
#define FIFFV_COORD_MRI
#define FIFFV_COORD_ISOTRAK
#define FIFFV_COORD_UNKNOWN
#define FIFFV_MNE_COORD_FS_TAL_GTZ
#define FIFFV_MNE_COORD_RAS
FiffStream class declaration.
FiffDirNode class declaration, which provides fiff dir tree processing methods.
#define FIFF_COORD_TRANS
Definition fiff_file.h:475
#define FIFFT_COORD_TRANS_STRUCT
Definition fiff_file.h:252
FiffCoordTrans class declaration.
FIFF file I/O and data structures (raw, epochs, evoked, covariance, forward).
qint32 fiff_int_t
Definition fiff_types.h:89
static FiffCoordTrans readTransformFromNode(FiffStream::SPtr &stream, const FiffDirNode::SPtr &node, int from, int to)
static FiffCoordTrans combine(int from, int to, const FiffCoordTrans &t1, const FiffCoordTrans &t2)
static FiffCoordTrans identity(int from, int to)
static FiffCoordTrans readMriTransform(const QString &name)
FiffCoordTrans inverted() const
static FiffCoordTrans readMeasTransform(const QString &name)
void write(QIODevice &p_IODevice)
Writes the transformation to file.
static FiffCoordTrans procrustesAlign(int from_frame, int to_frame, float **fromp, float **top, float *w, int np, float max_diff)
float translationTo(Eigen::MatrixX4f mTransDest)
static FiffCoordTrans fromCardinalPoints(int from, int to, const float *rL, const float *rN, const float *rR)
static FiffCoordTrans readTransform(const QString &name, int from, int to)
Eigen::MatrixX3f apply_inverse_trans(const Eigen::MatrixX3f &rr, bool do_move=true) const
static FiffCoordTrans readFShead2mriTransform(const QString &name)
Eigen::MatrixX3f apply_trans(const Eigen::MatrixX3f &rr, bool do_move=true) const
static bool read(QIODevice &p_IODevice, FiffCoordTrans &p_Trans)
float angleTo(Eigen::MatrixX4f mTransDest)
Eigen::Matrix< float, 4, 4, Eigen::DontAlign > trans
static FiffCoordTrans readFromTag(const QSharedPointer< FiffTag > &tag)
void writeToStream(FiffStream *p_pStream)
Writes the transformation to a FIFF stream.
static QString frame_name(int frame)
static bool addInverse(FiffCoordTrans &t)
Eigen::Matrix< float, 4, 4, Eigen::DontAlign > invtrans
static FiffCoordTrans readTransformAscii(const QString &name, int from, int to)
QSharedPointer< FiffDirNode > SPtr
FIFF File I/O routines.
fiff_long_t write_coord_trans(const FiffCoordTrans &trans)
static FiffStream::SPtr start_file(QIODevice &p_IODevice)
QSharedPointer< FiffStream > SPtr
QSharedPointer< FiffTag > SPtr
Definition fiff_tag.h:155