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