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