diff --git a/Core/Code/DataManagement/mitkMatrixConvert.h b/Core/Code/DataManagement/mitkMatrixConvert.h index dca4ab713d..e7ff867d3d 100644 --- a/Core/Code/DataManagement/mitkMatrixConvert.h +++ b/Core/Code/DataManagement/mitkMatrixConvert.h @@ -1,149 +1,151 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKMATRIXCONVERT_H_HEADER_INCLUDED_C1EBD0AD #define MITKMATRIXCONVERT_H_HEADER_INCLUDED_C1EBD0AD #include "mitkGeometry3D.h" #include "mitkItkMatrixHack.h" #include namespace mitk { template void TransferVtkMatrixToItkTransform(const vtkMatrix4x4* vtkmatrix, TTransformType * itkTransform) { if(itkTransform==NULL) return; typename TTransformType::MatrixType::InternalMatrixType& vnlMatrix = const_cast(itkTransform->GetMatrix().GetVnlMatrix()); for ( int i=0; i < 3; ++i) for( int j=0; j < 3; ++j ) vnlMatrix[i][j] = vtkmatrix->GetElement( i, j ); // *This* ensures m_MatrixMTime.Modified(), which is therewith not equal to // m_InverseMatrixMTime, thus a new inverse will be calculated (when // requested). static_cast*>(itkTransform)->MatrixChanged(); typename TTransformType::OffsetType offset; offset[0] = vtkmatrix->GetElement( 0, 3 ); offset[1] = vtkmatrix->GetElement( 1, 3 ); offset[2] = vtkmatrix->GetElement( 2, 3 ); itkTransform->SetOffset( offset ); } template void TransferItkTransformToVtkMatrix(const TTransformType * itkTransform, vtkMatrix4x4* vtkmatrix) { int i,j; for(i=0;i<3;++i) for(j=0;j<3;++j) vtkmatrix->SetElement(i, j, itkTransform->GetMatrix().GetVnlMatrix().get(i, j)); for(i=0;i<3;++i) vtkmatrix->SetElement(i, 3, itkTransform->GetOffset()[i]); - vtkmatrix->Modified(); + for(i=0;i<3;++i) + vtkmatrix->SetElement(3, i, 0.0); + vtkmatrix->SetElement(3, 3, 1); } template void ConvertItkTransform(const TTransformType1* sourceTransform, TTransformType2* destTransform) { if((sourceTransform==NULL) || (destTransform==NULL)) return; // transfer offset const typename TTransformType1::OutputVectorType& sourceOffset = sourceTransform->GetOffset(); typename TTransformType2::OutputVectorType offset; offset[0] = sourceOffset[0]; offset[1] = sourceOffset[1]; offset[2] = sourceOffset[2]; destTransform->SetOffset( offset ); typename TTransformType1::MatrixType::InternalMatrixType& sourceVnlMatrix = const_cast(sourceTransform->GetMatrix().GetVnlMatrix()); //transfer matrix typename TTransformType2::MatrixType::InternalMatrixType& destVnlMatrix = const_cast(destTransform->GetMatrix().GetVnlMatrix()); for ( int i=0; i < 3; ++i) for( int j=0; j < 3; ++j ) destVnlMatrix[i][j] = sourceVnlMatrix[i][j]; // *This* ensures m_MatrixMTime.Modified(), which is therewith not equal to // m_InverseMatrixMTime, thus a new inverse will be calculated (when // requested). static_cast*>(destTransform)->MatrixChanged(); } template void GetRotation(const mitk::Geometry3D * geometry, TMatrixType& itkmatrix) { const mitk::Vector3D& spacing = geometry->GetSpacing(); typename mitk::Geometry3D::TransformType::MatrixType::InternalMatrixType& geometryVnlMatrix = const_cast(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix()); typename TMatrixType::InternalMatrixType& outputVnlMatrix = const_cast(itkmatrix.GetVnlMatrix()); for ( int i=0; i < 3; ++i) for( int j=0; j < 3; ++j ) outputVnlMatrix [i][j] = geometryVnlMatrix [i][j] / spacing[j]; } template void GetWorldToItkPhysicalTransform(const mitk::Geometry3D * geometry, TTransformType* itkTransform) { if(itkTransform==NULL) return; // get rotation matrix and offset from Geometry and transfer in TTransformType types typename TTransformType::MatrixType rotationMatrix; GetRotation(geometry, rotationMatrix); const typename mitk::Geometry3D::TransformType::OffsetType& geometryOffset = geometry->GetIndexToWorldTransform()->GetOffset(); vnl_vector vnlOffset(3); vnlOffset[0] = geometryOffset[0]; vnlOffset[1] = geometryOffset[1]; vnlOffset[2] = geometryOffset[2]; // do calculations typename TTransformType::MatrixType::InternalMatrixType inverseRotationVnlMatrix = rotationMatrix.GetTranspose(); vnlOffset -= inverseRotationVnlMatrix*vnlOffset; typename TTransformType::OutputVectorType offset;//vnl_vector offset; offset[0] = vnlOffset[0]; offset[1] = vnlOffset[1]; offset[2] = vnlOffset[2]; itkTransform->SetOffset( offset ); // copy in destination itkTransform typename TTransformType::MatrixType::InternalMatrixType& destVnlMatrix = const_cast(itkTransform->GetMatrix().GetVnlMatrix()); for ( int i=0; i < 3; ++i) for( int j=0; j < 3; ++j ) destVnlMatrix[i][j] = inverseRotationVnlMatrix[i][j]; // *This* ensures m_MatrixMTime.Modified(), which is therewith not equal to // m_InverseMatrixMTime, thus a new inverse will be calculated (when // requested). static_cast*>(itkTransform)->MatrixChanged(); } } #endif /* MITKMATRIXCONVERT_H_HEADER_INCLUDED_C1EBD0AD */ diff --git a/Core/Code/Testing/files.cmake b/Core/Code/Testing/files.cmake index c999a6a859..ed032cd1f1 100644 --- a/Core/Code/Testing/files.cmake +++ b/Core/Code/Testing/files.cmake @@ -1,151 +1,152 @@ # tests with no extra command line parameter set(MODULE_TESTS mitkAccessByItkTest.cpp mitkCoreObjectFactoryTest.cpp mitkMaterialTest.cpp mitkActionTest.cpp mitkDispatcherTest.cpp mitkEnumerationPropertyTest.cpp mitkEventTest.cpp mitkFocusManagerTest.cpp mitkGenericPropertyTest.cpp mitkGeometry3DTest.cpp mitkGeometryDataToSurfaceFilterTest.cpp mitkGlobalInteractionTest.cpp mitkImageDataItemTest.cpp #mitkImageMapper2DTest.cpp mitkImageGeneratorTest.cpp mitkBaseDataTest.cpp #mitkImageToItkTest.cpp mitkImportItkImageTest.cpp mitkGrabItkImageMemoryTest.cpp mitkInstantiateAccessFunctionTest.cpp mitkInteractorTest.cpp #mitkITKThreadingTest.cpp mitkLevelWindowTest.cpp mitkMessageTest.cpp #mitkPipelineSmartPointerCorrectnessTest.cpp mitkPixelTypeTest.cpp mitkPlaneGeometryTest.cpp mitkPointSetFileIOTest.cpp mitkPointSetTest.cpp mitkPointSetWriterTest.cpp mitkPointSetReaderTest.cpp mitkPointSetInteractorTest.cpp mitkPropertyTest.cpp mitkPropertyListTest.cpp #mitkRegistrationBaseTest.cpp #mitkSegmentationInterpolationTest.cpp mitkSlicedGeometry3DTest.cpp mitkSliceNavigationControllerTest.cpp mitkStateMachineTest.cpp ##mitkStateMachineContainerTest.cpp ## rewrite test, indirect since no longer exported Bug 14529 mitkStateTest.cpp mitkSurfaceTest.cpp mitkSurfaceToSurfaceFilterTest.cpp mitkTimeSlicedGeometryTest.cpp mitkTransitionTest.cpp mitkUndoControllerTest.cpp mitkVtkWidgetRenderingTest.cpp mitkVerboseLimitedLinearUndoTest.cpp mitkWeakPointerTest.cpp mitkTransferFunctionTest.cpp #mitkAbstractTransformGeometryTest.cpp mitkStepperTest.cpp itkTotalVariationDenoisingImageFilterTest.cpp mitkRenderingManagerTest.cpp vtkMitkThickSlicesFilterTest.cpp mitkNodePredicateSourceTest.cpp mitkVectorTest.cpp mitkClippedSurfaceBoundsCalculatorTest.cpp mitkExceptionTest.cpp mitkExtractSliceFilterTest.cpp mitkLogTest.cpp mitkImageDimensionConverterTest.cpp mitkLoggingAdapterTest.cpp mitkUIDGeneratorTest.cpp mitkShaderRepositoryTest.cpp mitkPlanePositionManagerTest.cpp + mitkAffineTransformBaseTest.cpp ) # test with image filename as an extra command line parameter set(MODULE_IMAGE_TESTS mitkImageTimeSelectorTest.cpp #only runs on images mitkImageAccessorTest.cpp #only runs on images mitkDataNodeFactoryTest.cpp #runs on all types of data ) set(MODULE_SURFACE_TESTS mitkSurfaceVtkWriterTest.cpp #only runs on surfaces mitkDataNodeFactoryTest.cpp #runs on all types of data ) # list of images for which the tests are run set(MODULE_TESTIMAGES US4DCyl.nrrd Pic3D.nrrd Pic2DplusT.nrrd BallBinary30x30x30.nrrd Png2D-bw.png ) set(MODULE_TESTSURFACES binary.stl ball.stl ) set(MODULE_CUSTOM_TESTS #mitkLabeledImageToSurfaceFilterTest.cpp #mitkExternalToolsTest.cpp mitkDataStorageTest.cpp mitkDataNodeTest.cpp mitkDicomSeriesReaderTest.cpp mitkDICOMLocaleTest.cpp mitkEventMapperTest.cpp mitkEventConfigTest.cpp mitkNodeDependentPointSetInteractorTest.cpp mitkStateMachineFactoryTest.cpp mitkPointSetLocaleTest.cpp mitkImageTest.cpp mitkImageWriterTest.cpp mitkImageVtkMapper2DTest.cpp mitkImageVtkMapper2DLevelWindowTest.cpp mitkImageVtkMapper2DOpacityTest.cpp mitkImageVtkMapper2DResliceInterpolationPropertyTest.cpp mitkImageVtkMapper2DColorTest.cpp mitkImageVtkMapper2DSwivelTest.cpp mitkImageVtkMapper2DTransferFunctionTest.cpp mitkIOUtilTest.cpp mitkSurfaceVtkMapper3DTest mitkSurfaceVtkMapper3DTexturedSphereTest.cpp mitkSurfaceGLMapper2DColorTest.cpp mitkSurfaceGLMapper2DOpacityTest.cpp mitkVolumeCalculatorTest.cpp mitkLevelWindowManagerTest.cpp mitkPointSetVtkMapper2DTest.cpp mitkPointSetVtkMapper2DImageTest.cpp mitkPointSetVtkMapper2DGlyphTypeTest.cpp ) set(MODULE_RESOURCE_FILES Interactions/AddAndRemovePoints.xml Interactions/globalConfig.xml Interactions/StatemachineTest.xml Interactions/StatemachineConfigTest.xml ) # Create an artificial module initializing class for # the usServiceListenerTest.cpp usFunctionGenerateExecutableInit(testdriver_init_file IDENTIFIER ${MODULE_NAME}TestDriver ) # Embed the resources set(testdriver_resources ) usFunctionEmbedResources(testdriver_resources EXECUTABLE_NAME ${MODULE_NAME}TestDriver ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Resources FILES ${MODULE_RESOURCE_FILES} ) set(TEST_CPP_FILES ${testdriver_init_file} ${testdriver_resources}) diff --git a/Core/Code/Testing/mitkAffineTransformBaseTest.cpp b/Core/Code/Testing/mitkAffineTransformBaseTest.cpp new file mode 100644 index 0000000000..9f257dd1bf --- /dev/null +++ b/Core/Code/Testing/mitkAffineTransformBaseTest.cpp @@ -0,0 +1,185 @@ + + +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + + +#include "mitkVector.h" +#include "itkScalableAffineTransform.h" +#include "mitkMatrixConvert.h" + +#include "mitkTestingMacros.h" + +using namespace mitk; + +static Vector3D offset; +static Matrix3D rotation; +static Point3D originalPoint; +static double originalPointDouble[4]; + +static vtkMatrix4x4* homogenMatrix; + + +static vtkMatrix4x4* expectedHomogenousMatrix; +static const double expectedPointAfterTransformation[] = {2, 4, 4, 1}; + +static void Setup() +{ + originalPoint[0] = 1.0; originalPoint[1] = 0.0; originalPoint[2] = 0.0; + + for (int i = 0; i < 3; i++) + originalPointDouble[i] = originalPoint[i]; // same point as the Point3D version + originalPointDouble[3] = 1; // homogenous extension + + offset[0] = 2.0; offset[1] = 3.0; offset[2] = 4.0; + + // 90° rotation + rotation.Fill(0); + rotation[0][1] = -1; + rotation[1][0] = 1; + + // prepare a Matrix which shall be set later to a specific + // homogen matrix by TransferItkTransformToVtkMatrix + // this makes sure, that the initialization to the identity does not + // overshadow any bugs in TransferItkTransformToVtkMatrix + // (it actually did that by "helping out" TransferItkTransformToVtkMatrix + // by setting the (3,3) element to 1). + homogenMatrix = vtkMatrix4x4::New(); + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + homogenMatrix->SetElement(i,j, i+j*j); // just some not trivial value + + // set the expected homogenous matrix result + expectedHomogenousMatrix = vtkMatrix4x4::New(); + expectedHomogenousMatrix->Zero(); + expectedHomogenousMatrix->SetElement(0,1,-1); + expectedHomogenousMatrix->SetElement(1,0,1); + expectedHomogenousMatrix->SetElement(0,3,2); + expectedHomogenousMatrix->SetElement(1,3,3); + expectedHomogenousMatrix->SetElement(2,3,4); + expectedHomogenousMatrix->SetElement(3,3,1); +} + +/** +* This first test basically assures that we understand the usage of AffineTransform3D correct. +* Meaning that the rotation is set by SetMatrix and the translation is set by SetOffset +*/ +static void testIfPointIsTransformedAsExpected(void) +{ + Setup(); + + /** construct the transformation */ + AffineTransform3D::Pointer transform = AffineTransform3D::New(); + + transform->SetOffset(offset); + transform->SetMatrix(rotation); + + TransferItkTransformToVtkMatrix(transform.GetPointer(), homogenMatrix); + + /** Let a point be transformed by the AffineTransform3D */ + Point3D pointTransformedByAffineTransform3D = transform->TransformPoint(originalPoint); + + /** assert that the transformation was successful */ + bool pointCorrect = true; + for (int i = 0; i < 3; i++) // only first three since no homogenous coordinates + pointCorrect &= Equal(pointTransformedByAffineTransform3D[i], expectedPointAfterTransformation[i]); + + MITK_TEST_CONDITION(pointCorrect, "Point has been correctly transformed by AffineTranform3D") +} + +/** +* This test ensures that the function TransferItkTransformToVtkMatrix translates the AffineTransform3D +* correctly to a VtkMatrix4x4 +*/ +static void testTransferItkTransformToVtkMatrix(void) +{ + Setup(); + + AffineTransform3D::Pointer transform = AffineTransform3D::New(); + + transform->SetOffset(offset); + transform->SetMatrix(rotation); + + + TransferItkTransformToVtkMatrix(transform.GetPointer(), homogenMatrix); + + bool allElementsEqual = true; + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + allElementsEqual &= Equal(homogenMatrix->GetElement(i,j), expectedHomogenousMatrix->GetElement(i,j)); + + MITK_TEST_CONDITION(allElementsEqual, "Homogenous Matrix is set as expected") +} + +/** + * This test basically is just a sanity check and should be PASSED exactly when both + * testTransferItkTransformToVtkMatrix and testIfPointIsTransformedAsExpected are PASSED. + * Tests if we get the same + * result by using the AffineTransform3D to transform a point or the vtkMatrix4x4 which we got by applying + * the TransferItkTransformToVtkMatrix function. This test e.g. ensures we made no mistake in our + * reference results. + * + */ +static void testIfBothTransformationsProduceSameResults(void) +{ + Setup(); + + /** + * create both a AffineTransform3D + * and let this AffineTransform describe also a homogenous 4x4 Matrix vtkMatrix + * by using our transfer method + */ + AffineTransform3D::Pointer transform = AffineTransform3D::New(); + + transform->SetOffset(offset); + transform->SetMatrix(rotation); + + TransferItkTransformToVtkMatrix(transform.GetPointer(), homogenMatrix); + + /** Let a point be transformed by the AffineTransform3D and by homogenMatrix */ + Point3D pointTransformedByAffineTransform3D = transform->TransformPoint(originalPoint); + + double* pointTransformedByHomogenous= homogenMatrix->MultiplyDoublePoint(originalPointDouble); + + /* check if results match */ + + bool pointsMatch = true; + for (int i = 0; i < 3; i++) // only first three since no homogenous coordinates + pointsMatch &= Equal(pointTransformedByAffineTransform3D[i], pointTransformedByHomogenous[i]); + + bool homogenousComponentCorrect = Equal(1, pointTransformedByHomogenous[3]); + + MITK_TEST_CONDITION(pointsMatch + && homogenousComponentCorrect, "Point transformed by AffineTransform and homogenous coordinates match") + +} + +/** + * This test shall ensure and document the basic functionality of the + * itk AffineTransformation functionality and test some basic transformation functionalities provided by mitk. + */ +int mitkAffineTransformBaseTest(int /*argc*/, char* /*argv*/[]) +{ + MITK_TEST_BEGIN("AffineTransformationBaseTest"); + + testIfPointIsTransformedAsExpected(); + + testTransferItkTransformToVtkMatrix(); + + testIfBothTransformationsProduceSameResults(); + + MITK_TEST_END(); +}