MITK-IGT
IGT Extension of MITK
Loading...
Searching...
No Matches
mitkNavigationDataTest.cpp
Go to the documentation of this file.
1/*============================================================================
2
3The Medical Imaging Interaction Toolkit (MITK)
4
5Copyright (c) German Cancer Research Center (DKFZ)
6All rights reserved.
7
8Use of this source code is governed by a 3-clause BSD license that can be
9found in the LICENSE file.
10
11============================================================================*/
12
13#include "mitkTestingMacros.h"
14#include "mitkNavigationData.h"
15#include "mitkVector.h"
16#include <vnl/vnl_math.h>
17#include <itkIndent.h>
18
19
20using namespace mitk;
21
25static bool AreBasicNavigationMembersEqual(const NavigationData::Pointer nd, const bool dataValid,
26 const NavigationData::TimeStampType timeStamp, const std::string name)
27{
28 bool result = true;
29 result = result && (nd->IsDataValid() == dataValid);
30 result = result && (mitk::Equal(nd->GetIGTTimeStamp(), timeStamp));
31 result = result && (0 == name.compare(nd->GetName()));
32
33 return result;
34}
35
40static bool AreBasicNavigationMembersEqual(const NavigationData::Pointer nd1, const NavigationData::Pointer nd2)
41{
42 return AreBasicNavigationMembersEqual(nd1, nd2->IsDataValid(),
43 nd2->GetIGTTimeStamp(),
44 nd2->GetName());
45}
46
56static bool AreCovarianceNavigationMembersEqual(const NavigationData::Pointer nd, bool hasPosition,
57 bool hasOrientation, NavigationData::CovarianceMatrixType covMatrix)
58{
59 bool result = true;
60 result = result && (nd->GetHasPosition() == hasPosition);
61 result = result && (nd->GetHasOrientation() == hasOrientation);
62 result = result && (mitk::MatrixEqualElementWise(nd->GetCovErrorMatrix(), covMatrix));
63
64 return result;
65}
66
76static bool AreCovarianceNavigationMembersEqual(const NavigationData::Pointer nd1, const NavigationData::Pointer nd2)
77{
78 return AreCovarianceNavigationMembersEqual(nd1,
79 nd2->GetHasPosition(), nd2->GetHasOrientation(), nd2->GetCovErrorMatrix());
80}
81
82
88static mitk::NavigationData::Pointer GetTestData()
89{
90 mitk::NavigationData::Pointer nd = mitk::NavigationData::New();
92 mitk::FillVector3D(p, 44.4, 55.5, 66.66);
93 nd->SetPosition(p);
94 mitk::NavigationData::OrientationType o(1.0, 2.0, 3.0, 4.0);
95 nd->SetOrientation(o);
96 nd->SetDataValid(true);
97 nd->SetIGTTimeStamp(100.111);
98 nd->SetHasPosition(false);
99 nd->SetHasOrientation(false);
101 m.Fill(17.17);
102 m(2, 2) = 1000.01;
103 nd->SetCovErrorMatrix(m);
104 nd->SetName("my NavigationData");
105 nd->SetPositionAccuracy(100.0);
106 nd->SetOrientationAccuracy(10.0);
107 return nd;
108}
109
110
111static void TestInstatiation()
112{
113 // Test instantiation of NavigationData
114 mitk::NavigationData::Pointer nd = mitk::NavigationData::New();
115 MITK_TEST_CONDITION(nd.IsNotNull(),"Test instatiation");
116}
117
118
119static void TestGetterSetter()
120{
121 mitk::NavigationData::Pointer nd = mitk::NavigationData::New();
122
124 mitk::FillVector3D(p, 44.4, 55.5, 66.66);
125 nd->SetPosition(p);
126 MITK_TEST_CONDITION(nd->GetPosition() == p, "Set-/GetPosition()");
127
128 mitk::NavigationData::OrientationType o(1.0, 2.0, 3.0, 4.0);
129 nd->SetOrientation(o);
130 MITK_TEST_CONDITION(nd->GetOrientation() == o, "Set-/GetOrientation()");
131
132 nd->SetDataValid(true);
133 MITK_TEST_CONDITION(nd->IsDataValid() == true, "Set-/IsDataValid()");
134
135 nd->SetIGTTimeStamp(100.111);
136 MITK_TEST_CONDITION(mitk::Equal(nd->GetIGTTimeStamp(), 100.111), "Set-/GetIGTTimeStamp()");
137
138 nd->SetHasPosition(false);
139 MITK_TEST_CONDITION(nd->GetHasPosition() == false, "Set-/GetHasPosition()");
140
141 nd->SetHasOrientation(false);
142 MITK_TEST_CONDITION(nd->GetHasOrientation() == false, "Set-/GetHasOrientation()");
143
145 m.Fill(17.17);
146 m(2, 2) = 1000.01;
147 nd->SetCovErrorMatrix(m);
148 MITK_TEST_CONDITION(nd->GetCovErrorMatrix() == m, "Set-/GetCovErrorMatrix()");
149
150 nd->SetName("my NavigationData");
151 MITK_TEST_CONDITION(std::string(nd->GetName()) == "my NavigationData", "Set-/GetName()");
152
153 nd->SetPositionAccuracy(100.0);
154 mitk::NavigationData::CovarianceMatrixType result = nd->GetCovErrorMatrix();
155 MITK_TEST_CONDITION(mitk::Equal(result(0, 0), 10000.0)
156 && mitk::Equal(result(1, 1), 10000.0)
157 && mitk::Equal(result(2, 2), 10000.0), "SetPositionAccuracy()");
158
159 nd->SetOrientationAccuracy(10.0);
160 mitk::NavigationData::CovarianceMatrixType result2 = nd->GetCovErrorMatrix();
161 MITK_TEST_CONDITION(mitk::Equal(result2(3, 3), 100.0)
162 && mitk::Equal(result2(4, 4), 100.0)
163 && mitk::Equal(result2(5, 5), 100.0), "SetOrientationAccuracy()");
164}
165
166static void TestGraft()
167{
168 //create test data
169 mitk::NavigationData::Pointer nd = GetTestData();
170
171 mitk::NavigationData::Pointer graftedCopy = mitk::NavigationData::New();
172 graftedCopy->Graft(nd);
173
174 bool graftIsEqual = (nd->GetPosition() == graftedCopy->GetPosition())
175 && (nd->GetOrientation() == graftedCopy->GetOrientation())
176 && AreCovarianceNavigationMembersEqual(nd, graftedCopy)
177 && AreBasicNavigationMembersEqual(nd, graftedCopy);
178
179 MITK_TEST_CONDITION(graftIsEqual, "Graft() produces equal NavigationData object");
180}
181
182static void TestPrintSelf()
183{
184 mitk::NavigationData::Pointer nd = GetTestData();
185 itk::Indent myIndent = itk::Indent();
186
187 MITK_TEST_OUTPUT(<<"Testing method PrintSelf(), method output will follow:");
188 bool success = true;
189 try
190 {
191 nd->PrintSelf(std::cout,myIndent);
192 }
193 catch(...)
194 {
195 success = false;
196 }
197 MITK_TEST_CONDITION(success, "Testing method PrintSelf().");
198}
199
200
201static void TestWrongInputs()
202{
203 mitk::NavigationData::Pointer nd = GetTestData();
204
205 // Test CopyInformation
206 bool success = false;
207 try
208 {
209 nd->CopyInformation(nullptr);
210 }
211 catch(itk::ExceptionObject&)
212 {
213 success = true;
214 }
215 MITK_TEST_CONDITION(success, "Testing wrong input for method CopyInformation.");
216
217
218 // Test Graft
219 success = false;
220 try
221 {
222 nd->Graft(nullptr);
223 }
224 catch(itk::ExceptionObject&)
225 {
226 success = true;
227 }
228 MITK_TEST_CONDITION(success, "Testing wrong input for method Graft.");
229}
230
231
232static mitk::Quaternion quaternion;
233static mitk::Quaternion quaternion_realistic;
234static mitk::Vector3D offsetVector;
235static mitk::Point3D offsetPoint;
236static mitk::Matrix3D rotation;
237
238static mitk::Quaternion quaternion2;
239static mitk::Vector3D offsetVector2;
240static mitk::Point3D offsetPoint2;
241static mitk::Matrix3D rotation2;
242
243static mitk::Point3D point;
244
248static void SetupNaviDataTests()
249{
250 // set rotation matrix to
251 /*
252 * 0 -1 0
253 * 1 0 0
254 * 0 0 1
255 */
256 rotation.Fill(0);
257 rotation[0][1] = -1;
258 rotation[1][0] = 1;
259 rotation[2][2] = 1;
260
261 // set quaternion to quaternion equivalent
262 // values calculated with javascript at
263 // https://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/
264 quaternion = mitk::Quaternion(0, 0, 0.7071067811865475, 0.7071067811865476);
265
266 // a more realistic quaternion from real tracking data
267 quaternion_realistic = mitk::Quaternion(-0.57747,0.225593,0.366371,0.693933);
268
269 // set offset to some value. Some tests need vectors, offers points.
270 double offsetArray[3] = {1.0,2.0,3.123456};
271 offsetVector = offsetArray;
272 offsetPoint = offsetArray;
273
274 /***** Second set of data for compose tests ****/
275
276 // set rotation2 matrix to
277 /*
278 * 1 0 0
279 * 0 0 -1
280 * 0 1 0
281 */
282 rotation2.Fill(0);
283 rotation2[0][0] = 1;
284 rotation2[1][2] = -1;
285 rotation2[2][1] = 1;
286
287 quaternion2 = mitk::Quaternion(0.7071067811865475, 0, 0, 0.7071067811865476);
288 mitk::ScalarType offsetArray2[3] = {1, 0, 0};
289 offsetVector2 = offsetArray2;
290 offsetPoint2 = offsetArray2;
291
292 /***** Create a point to be transformed *****/
293 mitk::ScalarType pointArray[] = {1.0, 3.0, 5.0};
294 point = pointArray;
295}
296
300static mitk::NavigationData::Pointer CreateNavidata(mitk::Quaternion quaternion, mitk::Point3D offset)
301{
302 mitk::NavigationData::Pointer navigationData = mitk::NavigationData::New();
303 navigationData->SetOrientation(quaternion);
304 navigationData->SetPosition(offset);
305
306 return navigationData;
307}
308
316static mitk::AffineTransform3D::Pointer CreateAffineTransform(mitk::Matrix3D rotationMatrix, mitk::Vector3D offset)
317{
318 mitk::AffineTransform3D::Pointer affineTransform3D = mitk::AffineTransform3D::New();
319 affineTransform3D->SetOffset(offset);
320 affineTransform3D->SetMatrix(rotationMatrix);
321
322 return affineTransform3D;
323}
324
328/*static void TestInverse()
329{
330 SetupNaviDataTests();
331 mitk::NavigationData::Pointer nd = CreateNavidata(quaternion, offsetPoint);
332
333 mitk::NavigationData::Pointer ndInverse = nd->GetInverse();
334
335 // calculate expected inverted position vector: b2 = -A2b1
336
337 // for -A2b1 we need vnl_vectors.
338 vnl_vector_fixed<mitk::ScalarType, 3> b1;
339 for (int i = 0; i < 3; ++i) {
340 b1[i] = nd->GetPosition()[i];
341 }
342 vnl_vector_fixed<mitk::ScalarType, 3> b2;
343 b2 = -(ndInverse->GetOrientation().rotate(b1));
344
345 // now copy result back into our mitk::Point3D
346 mitk::Point3D invertedPosition;
347 for (int i = 0; i < 3; ++i) {
348 invertedPosition[i] = b2[i];
349 }
350
351 MITK_TEST_CONDITION(mitk::Equal(nd->GetOrientation().inverse(), ndInverse->GetOrientation()),"Testing GetInverse: orientation inverted");
352 MITK_TEST_CONDITION(mitk::Equal(invertedPosition, ndInverse->GetPosition()), "Testing GetInverse: position inverted");
353
354 bool otherFlagsOk = AreBasicNavigationMembersEqual(nd, ndInverse)
355 && AreCovarianceNavigationMembersEqual(ndInverse, false, false, nd->GetCovErrorMatrix());
356 // covariance update mechanism not yet implemented, thus validities are set to false.
357
358
359 MITK_TEST_CONDITION(otherFlagsOk, "Testing GetInverse: other flags are same");
360
361 //########################################################################################
362 //################### Second test with more realistic quaternion #########################
363 //########################################################################################
364
365 //just copy data to be real sure that it is not overwritten during the test
366 mitk::Quaternion referenceQuaternion;
367 referenceQuaternion[0] = quaternion_realistic[0];
368 referenceQuaternion[1] = quaternion_realistic[1];
369 referenceQuaternion[2] = quaternion_realistic[2];
370 referenceQuaternion[3] = quaternion_realistic[3];
371
372 mitk::Point3D referencePoint;
373 referencePoint[0] = offsetPoint[0];
374 referencePoint[1] = offsetPoint[1];
375 referencePoint[2] = offsetPoint[2];
376 referencePoint[3] = offsetPoint[3];
377
378 mitk::NavigationData::Pointer nd2 = CreateNavidata(quaternion_realistic, offsetPoint);
379
380 mitk::NavigationData::Pointer ndInverse2 = nd2->GetInverse();
381 MITK_TEST_CONDITION(mitk::Equal(nd2->GetOrientation(),referenceQuaternion),"Testing if the method GetInverse() modifies the data which should never happen!");
382 MITK_TEST_CONDITION(mitk::Equal(ndInverse2->GetOrientation(),referenceQuaternion.inverse()),"Testing if the Qrientation was inverted correctly with the realistic quaternion");
383}*/
384
388static void TestDoubleInverse()
389{
390 SetupNaviDataTests();
391 mitk::NavigationData::Pointer nd = CreateNavidata(quaternion, offsetPoint);
392
393 mitk::NavigationData::Pointer ndDoubleInverse = nd->GetInverse()->GetInverse();
394
395 MITK_TEST_CONDITION(mitk::Equal(nd->GetOrientation(), ndDoubleInverse->GetOrientation()),"Testing GetInverse double application: orientation preserved");
396 MITK_TEST_CONDITION(mitk::Equal(nd->GetPosition(), ndDoubleInverse->GetPosition()), "Testing GetInverse double application: position preserved");
397}
398
403static void TestInverseError()
404{
405 // initialize empty NavigationData (quaternion is zeroed)
406 mitk::NavigationData::Pointer nd = mitk::NavigationData::New();
407 mitk::Quaternion quaternion;
408 quaternion.fill(0);
409 nd->SetOrientation(quaternion);
410
411 MITK_TEST_FOR_EXCEPTION(mitk::Exception, nd->GetInverse());
412}
413
417static void TestTransform()
418{
419 SetupNaviDataTests();
420 mitk::NavigationData::Pointer navigationData = CreateNavidata(quaternion, offsetPoint);
421
422 point = navigationData->TransformPoint(point);
423
424 mitk::ScalarType resultingPointArray[] = {-2, 3, 8.123456};
425 mitk::Point3D resultingPoint = resultingPointArray;
426 MITK_TEST_CONDITION(mitk::Equal(resultingPoint, point), "Testing point transformation");
427}
428
433static void TestAffineConstructor()
434{
435 SetupNaviDataTests();
436 mitk::AffineTransform3D::Pointer affineTransform3D = CreateAffineTransform(rotation, offsetVector);
437
438 mitk::NavigationData::Pointer navigationData = mitk::NavigationData::New(affineTransform3D);
439
440
441 MITK_TEST_CONDITION(AreBasicNavigationMembersEqual(navigationData, true, 0.0, ""),
442 "Testing affine constructor: dataValid, timeStamp and name have been initialized to default values");
443
444 NavigationData::CovarianceMatrixType covMatrix; // empty covariance matrix
445 MITK_TEST_CONDITION(AreCovarianceNavigationMembersEqual(navigationData, true, true, covMatrix),
446 "Testing affine constructor: covariance matrix values have been correctly initialized"); // TODO: discuss with Alfred
447 // why this is the desired initialization of the covariance information.
448
449 MITK_TEST_CONDITION(mitk::Equal(navigationData->GetPosition(), offsetPoint), "Testing affine constructor: offset");
450 MITK_TEST_CONDITION(mitk::Equal(navigationData->GetOrientation(), quaternion), "Testing affine constructor: quaternion");
451}
452
457static void TestAffineConstructorErrorTransposedNotInverse()
458{
459 SetupNaviDataTests();
460 rotation.SetIdentity();
461 rotation[1][0] = 2; // this matrix has determinant = 1 (triangular matrix with ones in diagonal) but transposed != inverse
462 mitk::AffineTransform3D::Pointer affineTransform3D = CreateAffineTransform(rotation, offsetVector);
463
464 MITK_TEST_FOR_EXCEPTION(mitk::Exception, mitk::NavigationData::New(affineTransform3D));
465}
466
471static void TestAffineConstructorErrorDeterminantNonEqualOne()
472{
473 SetupNaviDataTests();
474 rotation.SetIdentity();
475 rotation[0][0] = 2; // determinant for diagonal matrices is product of diagonal elements => det = 2
476 mitk::AffineTransform3D::Pointer affineTransform3D = CreateAffineTransform(rotation, offsetVector);
477
478 MITK_TEST_FOR_EXCEPTION(mitk::Exception, mitk::NavigationData::New(affineTransform3D));
479}
480
481
486static void TestAffineConstructorErrorCheckingFalse()
487{
488 SetupNaviDataTests();
489 rotation.SetIdentity();
490 rotation[0][0] = 2; // determinant for diagonal matrices is product of diagonal elements => det = 2
491 mitk::AffineTransform3D::Pointer affineTransform3D = CreateAffineTransform(rotation, offsetVector);
492
493 bool exceptionSuppressed = true;
494 try
495 {
496 mitk::NavigationData::New(affineTransform3D, false);
497 }
498 catch (mitk::Exception&)
499 {
500 exceptionSuppressed = false;
501 }
502
503 MITK_TEST_CONDITION(exceptionSuppressed, "Test affine constructor: exception can be suppressed.")
504}
505
509static void TestAffineGetter()
510{
511 SetupNaviDataTests();
512 mitk::NavigationData::Pointer navigationData = CreateNavidata(quaternion, offsetPoint);
513
514 mitk::AffineTransform3D::Pointer affineTransform = navigationData->GetAffineTransform3D();
515
516 MITK_TEST_CONDITION(mitk::Equal(affineTransform->GetOffset(), offsetVector), "Testing AffineTransform3D getter: offset");
517 MITK_TEST_CONDITION(mitk::MatrixEqualElementWise(affineTransform->GetMatrix(), rotation), "Testing AffineTransform3D getter: rotation");
518}
519
523static void TestAffineToNaviDataToAffine()
524{
525 SetupNaviDataTests();
526 mitk::AffineTransform3D::Pointer affineTransform3D = CreateAffineTransform(rotation, offsetVector);
527
528 // there and back again: affineTransform -> NavigationData -> affineTransform
529 mitk::NavigationData::Pointer navigationData = mitk::NavigationData::New(affineTransform3D);
530 mitk::AffineTransform3D::Pointer affineTransform3D_2;
531 affineTransform3D_2 = navigationData->GetAffineTransform3D();
532
533 MITK_TEST_CONDITION(mitk::Equal(affineTransform3D->GetOffset(), affineTransform3D_2->GetOffset()), "Testing affine -> navidata -> affine chain: offset");
534 MITK_TEST_CONDITION(mitk::MatrixEqualElementWise(affineTransform3D->GetMatrix(), affineTransform3D_2->GetMatrix()), "Testing affine -> navidata -> affine chain: rotation");
535}
536
537
538static void TestCompose(bool pre = false)
539{
540 SetupNaviDataTests();
541 mitk::NavigationData::Pointer nd = CreateNavidata(quaternion, offsetPoint);
542 mitk::AffineTransform3D::Pointer at = CreateAffineTransform(rotation, offsetVector);
543 // second transform for composition
544 mitk::NavigationData::Pointer nd2 = CreateNavidata(quaternion2, offsetPoint2);
545 mitk::AffineTransform3D::Pointer at2 = CreateAffineTransform(rotation2, offsetVector2);
546 // save point for affinetransform
547 mitk::Point3D point2 = point;
548
549
550 nd->Compose(nd2, pre);
551 point = nd->TransformPoint(point);
552
553 at->Compose(at2, pre);
554 point2 = at->TransformPoint(point2);
555
556 MITK_TEST_CONDITION(mitk::Equal(point, point2), "Compose pre = " << pre << ": composition works ");
557
558 bool covarianceValidityReset = !nd->GetHasOrientation() && !nd->GetHasPosition();
559
560 MITK_TEST_CONDITION(covarianceValidityReset, "Compose pre = " << pre << ": covariance validities reset because not implemented yet.");
561}
562
563static void TestReverseCompose()
564{
565 TestCompose(true);
566}
567
571static void TestClone()
572{
573 SetupNaviDataTests();
574 mitk::NavigationData::Pointer nd = CreateNavidata(quaternion, offsetPoint);
575 mitk::NavigationData::Pointer myClone = nd->Clone();
576 MITK_TEST_CONDITION(mitk::Equal(*nd,*myClone,mitk::eps,true), "Test if clone is equal to original object.");
577
578 //change clone, original object should not change
579 mitk::Point3D myPoint;
580 mitk::FillVector3D(myPoint,121,32132,433);
581 myClone->SetPosition(myPoint);
582 MITK_TEST_CONDITION(!mitk::Equal(*nd,*myClone), "Test if clone could be modified without changing the original object.");
583}
584
585
591int mitkNavigationDataTest(int /* argc */, char* /*argv*/[])
592{
593 MITK_TEST_BEGIN("NavigationData");
594
595 TestInstatiation();
596 TestGetterSetter();
597 TestGraft();
598 TestPrintSelf();
599 TestWrongInputs();
600
601 TestAffineConstructor();
602 TestAffineConstructorErrorDeterminantNonEqualOne();
603 TestAffineConstructorErrorTransposedNotInverse();
604 TestAffineConstructorErrorCheckingFalse();
605
606 TestAffineGetter();
607 TestAffineToNaviDataToAffine();
608
609 TestTransform();
610
611 //TestInverse(); Fails under MAC, see bug 18306
612 TestDoubleInverse();
613 TestInverseError();
614
615 TestCompose();
616 TestReverseCompose();
617 TestClone();
618
619 MITK_TEST_END();
620}
itk::Matrix< mitk::ScalarType, 6, 6 > CovarianceMatrixType
type that holds the error characterization of the position and orientation measurements
mitk::Quaternion OrientationType
Type that holds the orientation part of the tracking data.
double TimeStampType
type that holds the time at which the data was recorded in milliseconds
mitk::Point3D PositionType
Type that holds the position part of the tracking data.
int mitkNavigationDataTest(int, char *[])
IGT Exceptions.
MITKIGTBASE_EXPORT bool Equal(const mitk::NavigationData &leftHandSide, const mitk::NavigationData &rightHandSide, ScalarType eps=mitk::eps, bool verbose=false)
Equal A function comparing two navigation data objects for beeing equal in meta- and imagedata.