diff --git a/Core/Code/Algorithms/mitkCompareImageFilter.cpp b/Core/Code/Algorithms/mitkCompareImageFilter.cpp new file mode 100644 index 0000000000..c00f92651d --- /dev/null +++ b/Core/Code/Algorithms/mitkCompareImageFilter.cpp @@ -0,0 +1,106 @@ +/*=================================================================== + +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 "mitkCompareImageFilter.h" +#include "mitkImageAccessByItk.h" +#include "mitkITKImageImport.h" + +#include + +mitk::CompareImageFilter::CompareImageFilter() +{ + this->SetNumberOfRequiredInputs(2); +} + + +void mitk::CompareImageFilter::GenerateData() +{ + // check inputs + + const mitk::Image* input1 = this->GetInput(0); + const mitk::Image* input2 = this->GetInput(1); + + // check input validity + if( input1->GetDimension() == input2->GetDimension() ) + { + AccessByItk_1( input1, EstimateValueDifference, input2); + } +} + +#include "mitkImageCaster.h" + +bool mitk::CompareImageFilter::GetResult(size_t threshold) +{ + if (! m_CompareResult) + { + return false; + } + + if( m_CompareDetails.m_PixelsWithDifference > threshold ) + { + return false; + } + + return true; +} + +template< typename TPixel, unsigned int VImageDimension> +void mitk::CompareImageFilter::EstimateValueDifference(itk::Image< TPixel, VImageDimension>* itkImage1, + const mitk::Image* referenceImage) +{ + + typedef itk::Image< TPixel, VImageDimension> InputImageType; + typedef itk::Image< double, VImageDimension > OutputImageType; + + typename InputImageType::Pointer itk_reference = InputImageType::New(); + + mitk::CastToItkImage( referenceImage, + itk_reference ); + + typedef itk::Testing::ComparisonImageFilter< InputImageType, OutputImageType > CompareFilterType; + typename CompareFilterType::Pointer compare_filter = CompareFilterType::New(); + compare_filter->SetTestInput( itkImage1 ); + compare_filter->SetValidInput( itk_reference ); + + try + { + compare_filter->Update(); + } + catch( const itk::ExceptionObject& e) + { + m_CompareDetails.m_FilterCompleted = false; + m_CompareDetails.m_ExceptionMessage = e.what(); + + MITK_WARN << e.what(); + + m_CompareResult = false; + return; + } + + // the filter has completed the calculation + m_CompareResult = true; + m_CompareDetails.m_FilterCompleted = true; + + m_CompareDetails.m_MaximumDifference = compare_filter->GetMaximumDifference(); + m_CompareDetails.m_MinimumDifference = compare_filter->GetMinimumDifference(); + m_CompareDetails.m_MeanDifference = compare_filter->GetMeanDifference(); + m_CompareDetails.m_TotalDifference = compare_filter->GetTotalDifference(); + m_CompareDetails.m_PixelsWithDifference = compare_filter->GetNumberOfPixelsWithDifferences(); + + mitk::Image::Pointer output = mitk::GrabItkImageMemory( compare_filter->GetOutput() ); + this->SetOutput( MakeNameFromOutputIndex(0), output.GetPointer() ); + + +} diff --git a/Core/Code/Algorithms/mitkCompareImageFilter.h b/Core/Code/Algorithms/mitkCompareImageFilter.h new file mode 100644 index 0000000000..54d166a9fe --- /dev/null +++ b/Core/Code/Algorithms/mitkCompareImageFilter.h @@ -0,0 +1,118 @@ +/*=================================================================== + +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 MITKCOMPAREIMAGEFILTER_H +#define MITKCOMPAREIMAGEFILTER_H + +//MITK +#include "mitkImageToImageFilter.h" +#include "mitkImage.h" + +//ITK +#include + +namespace mitk +{ +/** + * @brief A simple struct to hold the result of the comparison filter. + */ +struct CompareFilterResults +{ + void PrintSelf() + { + if( !m_FilterCompleted ) + { + MITK_INFO << "Comparision filter terminated due to an exception: \n " + << m_ExceptionMessage; + + return; + } + + MITK_INFO << "Min. difference: " << m_MinimumDifference <<"\n" + << "Max. difference: " << m_MaximumDifference <<"\n" + << "Total difference: " << m_TotalDifference <<"\n" + << "Mean difference: " << m_MeanDifference <<"\n" + << "Number of pixels with differences: " << m_PixelsWithDifference; + } + + double m_MinimumDifference; + double m_MaximumDifference; + + double m_TotalDifference; + double m_MeanDifference; + size_t m_PixelsWithDifference; + + bool m_FilterCompleted; + std::string m_ExceptionMessage; + +}; + +/** + * @brief Filter for comparing two mitk::Image objects by pixel values + * + * The comparison is pixel-wise, the filter uses the itk::Testing::ComparisonImageFilter + * to find differences. The filter expects two images as input, provide them by using the SetInput( int, mitk::Image) + * method. + */ +class MITK_CORE_EXPORT CompareImageFilter + : public ImageToImageFilter +{ +public: + mitkClassMacro(CompareImageFilter, + ImageToImageFilter) + + itkSimpleNewMacro(Self) + + /** + * @brief Get the result of the comparision + * + * The method compares only the number of pixels with differences. It returns true if the amount + * is under the specified threshold. To get the complete results, use the GetCompareResults method. + * + * Returns false also if the itk ComparisionImageFilter raises an exception during update. + * + * @param threshold Allowed amount of pixels with differences + */ + bool GetResult(size_t threshold = 0); + + /** + * @brief Get the detailed results of the comparision run + * + * @sa CompareFilterResults + */ + CompareFilterResults GetCompareResults() + { + return m_CompareDetails; + } + +protected: + CompareImageFilter(); + virtual ~CompareImageFilter() {} + + virtual void GenerateData(); + + /** ITK-like method which calls the ComparisionFilter on the two inputs of the filter */ + template< typename TPixel, unsigned int VImageDimension> + void EstimateValueDifference( itk::Image< TPixel, VImageDimension>* itkImage1, + const mitk::Image* referenceImage); + + bool m_CompareResult; + + CompareFilterResults m_CompareDetails; +}; +} // end namespace mitk + +#endif // MITKCOMPAREIMAGEFILTER_H diff --git a/Core/Code/Common/mitkTesting.h b/Core/Code/Common/mitkTesting.h new file mode 100644 index 0000000000..0f10504626 --- /dev/null +++ b/Core/Code/Common/mitkTesting.h @@ -0,0 +1,125 @@ +/*=================================================================== + +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 MITKTESTING_H_HEADER_INCLUDED +#define MITKTESTING_H_HEADER_INCLUDED + +#include +#include +#include + +namespace mitk { + +/** + * @brief Equal Verbose version of the mitk::Equal method in mitkVector.h + * @param scalar1 Scalar value to compare. + * @param scalar2 Scalar value to compare. + * @param eps Tolerance for floating point comparison. + * @param verbose Flag indicating detailed console output. + * @return True if scalars are equal. + */ +inline bool Equal(double scalar1, double scalar2, ScalarType eps, bool verbose) +{ + bool isEqual = mitk::Equal(scalar1, scalar2, eps); + + if(( !isEqual ) && verbose) + { + MITK_INFO << "Scalars not equal. Righthandside " << std::setprecision(12) << scalar1 << " - Lefthandside " << scalar2 << " - epsilon " << eps; + } + return isEqual; +} + +/** + * @brief Equal Verbose version of the mitk::Equal method in mitkVector.h + * @param vector1 Vector to compare. + * @param vector2 Vector to compare. + * @param eps Tolerance for floating point comparison. + * @param verbose Flag indicating detailed console output. + * @return True if vectors are equal. + */ +template +inline bool Equal(const itk::Vector& vector1, const itk::Vector& vector2, TCoordRep eps, bool verbose) +{ + bool isEqual = mitk::Equal( vector1, vector2, eps ); + + if(( !isEqual ) && verbose) + { + MITK_INFO << "Vectors not equal. Righthandside " << std::setprecision(12) << vector1 << " - Lefthandside " << vector2 << " - epsilon " << eps; + } + return isEqual; +} + +/** + * @brief Equal Verbose version of the mitk::Equal method in mitkVector.h + * @param point1 Point to compare. + * @param point2 Point to compare. + * @param eps Tolerance for floating point comparison. + * @param verbose Flag indicating detailed console output. + * @return True if points are equal. + */ +template +inline bool Equal(const itk::Point& point1, const itk::Point& point2, TCoordRep eps, bool verbose) +{ + bool isEqual = mitk::Equal( point1, point2, eps ); + + if(( !isEqual ) && verbose) + { + MITK_INFO << "Points not equal. Righthandside " << std::setprecision(12) << point1 << " - Lefthandside " << point2 << " - epsilon " << eps; + } + return isEqual; +} + +/** + * @brief Equal Verbose version of the mitk::Equal method in mitkVector.h + * @param vector1 Vector to compare. + * @param vector2 Vector to compare. + * @param eps Tolerance for floating point comparison. + * @param verbose Flag indicating detailed console output. + * @return True if vectors are equal. + */ +inline bool Equal(const mitk::VnlVector& vector1, const mitk::VnlVector& vector2, bool verbose, ScalarType eps) +{ + bool isEqual = mitk::Equal( vector1, vector2, eps ); + + if(( !isEqual ) && verbose) + { + MITK_INFO << "Vectors not equal. Righthandside " << std::setprecision(12) << vector1 << " - Lefthandside " << vector2 << " - epsilon " << eps; + } + return isEqual; +} + +/** + * @brief Equal Verbose version of the mitk::Equal method in mitkVector.h + * @param vector1 Vector to compare. + * @param vector2 Vector to compare. + * @param eps Tolerance for floating point comparison. + * @param verbose Flag indicating detailed console output. + * @return True if vectors are equal. + */ +template +inline bool Equal(const vnl_vector_fixed & vector1, const vnl_vector_fixed& vector2, TCoordRep eps, bool verbose) +{ + bool isEqual = mitk::Equal( vector1, vector2, eps ); + + if(( !isEqual ) && verbose) + { + MITK_INFO << "Vectors not equal. Righthandside " << std::setprecision(12) << vector1 << " - Lefthandside " << vector2 << " - epsilon " << eps; + } + return isEqual; +} +} + +#endif //MITKTESTING_H_HEADER_INCLUDED diff --git a/Core/Code/Common/mitkTestingMacros.h b/Core/Code/Common/mitkTestingMacros.h index 8bf909a5cb..a286f1bb35 100644 --- a/Core/Code/Common/mitkTestingMacros.h +++ b/Core/Code/Common/mitkTestingMacros.h @@ -1,139 +1,155 @@ /*=================================================================== 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 #include #include #include #include +#include namespace mitk { /** \brief Indicate a failed test. */ class TestFailedException : public std::exception { public: TestFailedException() {} }; } /** * * \brief Output some text without generating a terminating newline. Include * * */ #define MITK_TEST_OUTPUT_NO_ENDL(x) \ std::cout x ; /** \brief Output some text. */ #define MITK_TEST_OUTPUT(x) \ MITK_TEST_OUTPUT_NO_ENDL(x << "\n") /** \brief Do some general test preparations. Must be called first in the main test function. */ #define MITK_TEST_BEGIN(testName) \ std::string mitkTestName(#testName); \ mitk::TestManager::GetInstance()->Initialize(); \ try { /** \brief Fail and finish test with message MSG */ #define MITK_TEST_FAILED_MSG(MSG) \ MITK_TEST_OUTPUT(MSG) \ throw mitk::TestFailedException(); /** \brief Must be called last in the main test function. */ #define MITK_TEST_END() \ } catch (mitk::TestFailedException ex) { \ MITK_TEST_OUTPUT(<< "Further test execution skipped.") \ mitk::TestManager::GetInstance()->TestFailed(); \ } catch (std::exception ex) { \ MITK_TEST_OUTPUT(<< "std::exception occured " << ex.what()) \ mitk::TestManager::GetInstance()->TestFailed(); \ } \ if (mitk::TestManager::GetInstance()->NumberOfFailedTests() > 0) { \ MITK_TEST_OUTPUT(<< mitkTestName << ": [DONE FAILED] , subtests passed: " << \ mitk::TestManager::GetInstance()->NumberOfPassedTests() << " failed: " << \ mitk::TestManager::GetInstance()->NumberOfFailedTests() ) \ return EXIT_FAILURE; \ } else { \ MITK_TEST_OUTPUT(<< mitkTestName << ": " \ << mitk::TestManager::GetInstance()->NumberOfPassedTests() \ << " tests [DONE PASSED]") \ return EXIT_SUCCESS; \ } \ #define MITK_TEST_CONDITION(COND,MSG) \ MITK_TEST_OUTPUT_NO_ENDL(<< MSG) \ if ( ! (COND) ) { \ mitk::TestManager::GetInstance()->TestFailed(); \ MITK_TEST_OUTPUT(<< " [FAILED]\n" << "In " << __FILE__ \ << ", line " << __LINE__ \ << ": " #COND " : [FAILED]") \ } else { \ MITK_TEST_OUTPUT(<< " [PASSED]") \ mitk::TestManager::GetInstance()->TestPassed(); \ } #define MITK_TEST_CONDITION_REQUIRED(COND,MSG) \ MITK_TEST_OUTPUT_NO_ENDL(<< MSG) \ if ( ! (COND) ) { \ MITK_TEST_FAILED_MSG(<< " [FAILED]\n" << " +--> in " << __FILE__ \ << ", line " << __LINE__ \ << ", expression is false: \"" #COND "\"") \ } else { \ MITK_TEST_OUTPUT(<< " [PASSED]") \ mitk::TestManager::GetInstance()->TestPassed(); \ } /** * \brief Begin block which should be checked for exceptions * * This macro, together with MITK_TEST_FOR_EXCEPTION_END, can be used * to test whether a code block throws an expected exception. The test FAILS if the * exception is NOT thrown. A simple example: * MITK_TEST_FOR_EXCEPTION_BEGIN(itk::ImageFileReaderException) typedef itk::ImageFileReader< itk::Image > ReaderType; ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName("/tmp/not-existing"); reader->Update(); MITK_TEST_FOR_EXCEPTION_END(itk::ImageFileReaderException) * */ #define MITK_TEST_FOR_EXCEPTION_BEGIN(EXCEPTIONCLASS) \ try { #define MITK_TEST_FOR_EXCEPTION_END(EXCEPTIONCLASS) \ mitk::TestManager::GetInstance()->TestFailed(); \ MITK_TEST_OUTPUT( << "Expected an '" << #EXCEPTIONCLASS << "' exception. [FAILED]") \ } \ catch (EXCEPTIONCLASS) { \ MITK_TEST_OUTPUT( << "Caught an expected '" << #EXCEPTIONCLASS \ << "' exception. [PASSED]") \ mitk::TestManager::GetInstance()->TestPassed(); \ } /** * \brief Simplified version of MITK_TEST_FOR_EXCEPTION_BEGIN / END for * a single statement */ #define MITK_TEST_FOR_EXCEPTION(EXCEPTIONCLASS, STATEMENT) \ MITK_TEST_FOR_EXCEPTION_BEGIN(EXCEPTIONCLASS) \ STATEMENT ; \ MITK_TEST_FOR_EXCEPTION_END(EXCEPTIONCLASS) +/** \brief Testing macro to test if two obejcts are (not) equal. + * + * This macro uses mitk::eps and the corresponding mitk::Equal methods for all + * comparisons and will give verbose output on the dashboard/console. + * Feel free to implement mitk::Equal for your own datatype or purpose. + * + * @param OBJ1 First object. + * @param OBJ2 Second object. + * @param MSG Message to appear with the test. + */ +#define MITK_TEST_EQUAL(OBJ1,OBJ2,MSG) \ + MITK_TEST_CONDITION_REQUIRED( mitk::Equal(OBJ1, OBJ2, mitk::eps, true)==true, MSG) +#define MITK_TEST_NOT_EQUAL(OBJ1,OBJ2,MSG) \ + MITK_TEST_CONDITION_REQUIRED( mitk::Equal(OBJ1, OBJ2, mitk::eps, true)==false, MSG) + diff --git a/Core/Code/DataManagement/mitkGeometry3D.cpp b/Core/Code/DataManagement/mitkGeometry3D.cpp index 5dbfecde2a..9097504659 100644 --- a/Core/Code/DataManagement/mitkGeometry3D.cpp +++ b/Core/Code/DataManagement/mitkGeometry3D.cpp @@ -1,771 +1,909 @@ /*=================================================================== 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 +#include #include "mitkGeometry3D.h" #include "mitkMatrixConvert.h" #include "mitkRotationOperation.h" #include "mitkRestorePlanePositionOperation.h" #include "mitkPointOperation.h" #include "mitkInteractionConst.h" -//#include "mitkStatusBar.h" #include #include // Standard constructor for the New() macro. Sets the geometry to 3 dimensions mitk::Geometry3D::Geometry3D() : m_ParametricBoundingBox(NULL), m_ImageGeometry(false), m_Valid(true), m_FrameOfReferenceID(0), m_IndexToWorldTransformLastModified(0) { FillVector3D(m_FloatSpacing, 1,1,1); m_VtkMatrix = vtkMatrix4x4::New(); m_VtkIndexToWorldTransform = vtkMatrixToLinearTransform::New(); m_VtkIndexToWorldTransform->SetInput(m_VtkMatrix); Initialize(); } mitk::Geometry3D::Geometry3D(const Geometry3D& other) : Superclass(), mitk::OperationActor(), m_ParametricBoundingBox(other.m_ParametricBoundingBox),m_TimeBounds(other.m_TimeBounds), m_ImageGeometry(other.m_ImageGeometry), m_Valid(other.m_Valid), m_FrameOfReferenceID(other.m_FrameOfReferenceID), m_IndexToWorldTransformLastModified(other.m_IndexToWorldTransformLastModified), m_RotationQuaternion( other.m_RotationQuaternion ) , m_Origin(other.m_Origin) { // AffineGeometryFrame SetBounds(other.GetBounds()); //SetIndexToObjectTransform(other.GetIndexToObjectTransform()); //SetObjectToNodeTransform(other.GetObjectToNodeTransform()); //SetIndexToWorldTransform(other.GetIndexToWorldTransform()); // this is not used in AffineGeometryFrame of ITK, thus there are not Get and Set methods // m_IndexToNodeTransform = other.m_IndexToNodeTransform; // m_InvertedTransform = TransformType::New(); // m_InvertedTransform = TransformType::New(); // m_InvertedTransform->DeepCopy(other.m_InvertedTransform); m_VtkMatrix = vtkMatrix4x4::New(); m_VtkMatrix->DeepCopy(other.m_VtkMatrix); if (other.m_ParametricBoundingBox.IsNotNull()) { m_ParametricBoundingBox = other.m_ParametricBoundingBox->DeepCopy(); } FillVector3D(m_FloatSpacing,other.m_FloatSpacing[0],other.m_FloatSpacing[1],other.m_FloatSpacing[2]); m_VtkIndexToWorldTransform = vtkMatrixToLinearTransform::New(); m_VtkIndexToWorldTransform->DeepCopy(other.m_VtkIndexToWorldTransform); m_VtkIndexToWorldTransform->SetInput(m_VtkMatrix); other.InitializeGeometry(this); } mitk::Geometry3D::~Geometry3D() { m_VtkMatrix->Delete(); m_VtkIndexToWorldTransform->Delete(); } static void CopySpacingFromTransform(mitk::AffineTransform3D* transform, mitk::Vector3D& spacing, float floatSpacing[3]) { mitk::AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = transform->GetMatrix().GetVnlMatrix(); spacing[0]=vnlmatrix.get_column(0).magnitude(); spacing[1]=vnlmatrix.get_column(1).magnitude(); spacing[2]=vnlmatrix.get_column(2).magnitude(); floatSpacing[0]=spacing[0]; floatSpacing[1]=spacing[1]; floatSpacing[2]=spacing[2]; } void mitk::Geometry3D::Initialize() { float b[6] = {0,1,0,1,0,1}; SetFloatBounds(b); m_IndexToObjectTransform = TransformType::New(); m_ObjectToNodeTransform = TransformType::New(); if(m_IndexToWorldTransform.IsNull()) m_IndexToWorldTransform = TransformType::New(); else m_IndexToWorldTransform->SetIdentity(); CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing, m_FloatSpacing); vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); m_VtkMatrix->Identity(); m_TimeBounds[0]=ScalarTypeNumericTraits::NonpositiveMin(); m_TimeBounds[1]=ScalarTypeNumericTraits::max(); m_FrameOfReferenceID = 0; m_ImageGeometry = false; } void mitk::Geometry3D::TransferItkToVtkTransform() { // copy m_IndexToWorldTransform into m_VtkIndexToWorldTransform TransferItkTransformToVtkMatrix(m_IndexToWorldTransform.GetPointer(), m_VtkMatrix); m_VtkIndexToWorldTransform->Modified(); } void mitk::Geometry3D::TransferVtkToItkTransform() { TransferVtkMatrixToItkTransform(m_VtkMatrix, m_IndexToWorldTransform.GetPointer()); CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing, m_FloatSpacing); vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); } void mitk::Geometry3D::SetIndexToWorldTransformByVtkMatrix(vtkMatrix4x4* vtkmatrix) { m_VtkMatrix->DeepCopy(vtkmatrix); TransferVtkToItkTransform(); } void mitk::Geometry3D::SetTimeBounds(const TimeBounds& timebounds) { if(m_TimeBounds != timebounds) { m_TimeBounds = timebounds; Modified(); } } void mitk::Geometry3D::SetFloatBounds(const float bounds[6]) { mitk::BoundingBox::BoundsArrayType b; const float *input = bounds; int i=0; for(mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); i < 6 ;++i) *it++ = (mitk::ScalarType)*input++; SetBoundsArray(b, m_BoundingBox); } void mitk::Geometry3D::SetFloatBounds(const double bounds[6]) { mitk::BoundingBox::BoundsArrayType b; const double *input = bounds; int i=0; for(mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); i < 6 ;++i) *it++ = (mitk::ScalarType)*input++; SetBoundsArray(b, m_BoundingBox); } void mitk::Geometry3D::SetParametricBounds(const BoundingBox::BoundsArrayType& bounds) { SetBoundsArray(bounds, m_ParametricBoundingBox); } void mitk::Geometry3D::WorldToIndex(const mitk::Point3D &pt_mm, mitk::Point3D &pt_units) const { BackTransform(pt_mm, pt_units); } void mitk::Geometry3D::IndexToWorld(const mitk::Point3D &pt_units, mitk::Point3D &pt_mm) const { pt_mm = m_IndexToWorldTransform->TransformPoint(pt_units); } void mitk::Geometry3D::WorldToIndex(const mitk::Point3D & /*atPt3d_mm*/, const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const { MITK_WARN<<"Warning! Call of the deprecated function Geometry3D::WorldToIndex(point, vec, vec). Use Geometry3D::WorldToIndex(vec, vec) instead!"; //BackTransform(atPt3d_mm, vec_mm, vec_units); this->WorldToIndex(vec_mm, vec_units); } void mitk::Geometry3D::WorldToIndex( const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const { BackTransform( vec_mm, vec_units); } void mitk::Geometry3D::IndexToWorld(const mitk::Point3D &/*atPt3d_units*/, const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const { MITK_WARN<<"Warning! Call of the deprecated function Geometry3D::IndexToWorld(point, vec, vec). Use Geometry3D::IndexToWorld(vec, vec) instead!"; //vec_mm = m_IndexToWorldTransform->TransformVector(vec_units); this->IndexToWorld(vec_units, vec_mm); } void mitk::Geometry3D::IndexToWorld(const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const { vec_mm = m_IndexToWorldTransform->TransformVector(vec_units); } void mitk::Geometry3D::SetIndexToWorldTransform(mitk::AffineTransform3D* transform) { if(m_IndexToWorldTransform.GetPointer() != transform) { Superclass::SetIndexToWorldTransform(transform); CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing, m_FloatSpacing); vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); TransferItkToVtkTransform(); Modified(); } } itk::LightObject::Pointer mitk::Geometry3D::InternalClone() const { Self::Pointer newGeometry = new Self(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } /* void mitk::Geometry3D::InitializeGeometry(Geometry3D * newGeometry) const { Superclass::InitializeGeometry(newGeometry); newGeometry->SetTimeBounds(m_TimeBounds); //newGeometry->GetVtkTransform()->SetMatrix(m_VtkIndexToWorldTransform->GetMatrix()); IW //newGeometry->TransferVtkToItkTransform(); //MH newGeometry->SetFrameOfReferenceID(GetFrameOfReferenceID()); newGeometry->m_ImageGeometry = m_ImageGeometry; } */ void mitk::Geometry3D::SetExtentInMM(int direction, ScalarType extentInMM) { ScalarType len = GetExtentInMM(direction); if(fabs(len - extentInMM)>=mitk::eps) { AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = m_IndexToWorldTransform->GetMatrix().GetVnlMatrix(); if(len>extentInMM) vnlmatrix.set_column(direction, vnlmatrix.get_column(direction)/len*extentInMM); else vnlmatrix.set_column(direction, vnlmatrix.get_column(direction)*extentInMM/len); Matrix3D matrix; matrix = vnlmatrix; m_IndexToWorldTransform->SetMatrix(matrix); Modified(); } } mitk::BoundingBox::Pointer mitk::Geometry3D::CalculateBoundingBoxRelativeToTransform(const mitk::AffineTransform3D* transform) const { mitk::BoundingBox::PointsContainer::Pointer pointscontainer=mitk::BoundingBox::PointsContainer::New(); mitk::BoundingBox::PointIdentifier pointid=0; unsigned char i; if(transform!=NULL) { mitk::AffineTransform3D::Pointer inverse = mitk::AffineTransform3D::New(); transform->GetInverse(inverse); for(i=0; i<8; ++i) pointscontainer->InsertElement( pointid++, inverse->TransformPoint( GetCornerPoint(i) )); } else { for(i=0; i<8; ++i) pointscontainer->InsertElement( pointid++, GetCornerPoint(i) ); } mitk::BoundingBox::Pointer result = mitk::BoundingBox::New(); result->SetPoints(pointscontainer); result->ComputeBoundingBox(); return result; } #include void mitk::Geometry3D::ExecuteOperation(Operation* operation) { vtkTransform *vtktransform = vtkTransform::New(); vtktransform->SetMatrix(m_VtkMatrix); switch (operation->GetOperationType()) { case OpNOTHING: break; case OpMOVE: { mitk::PointOperation *pointOp = dynamic_cast(operation); if (pointOp == NULL) { //mitk::StatusBar::GetInstance()->DisplayText("received wrong type of operation!See mitkAffineInteractor.cpp", 10000); return; } mitk::Point3D newPos = pointOp->GetPoint(); ScalarType data[3]; vtktransform->GetPosition(data); vtktransform->PostMultiply(); vtktransform->Translate(newPos[0], newPos[1], newPos[2]); vtktransform->PreMultiply(); break; } case OpSCALE: { mitk::PointOperation *pointOp = dynamic_cast(operation); if (pointOp == NULL) { //mitk::StatusBar::GetInstance()->DisplayText("received wrong type of operation!See mitkAffineInteractor.cpp", 10000); return; } mitk::Point3D newScale = pointOp->GetPoint(); ScalarType data[3]; /* calculate new scale: newscale = oldscale * (oldscale + scaletoadd)/oldscale */ data[0] = 1 + (newScale[0] / GetMatrixColumn(0).magnitude()); data[1] = 1 + (newScale[1] / GetMatrixColumn(1).magnitude()); data[2] = 1 + (newScale[2] / GetMatrixColumn(2).magnitude()); mitk::Point3D center = const_cast(m_BoundingBox.GetPointer())->GetCenter(); ScalarType pos[3]; vtktransform->GetPosition(pos); vtktransform->PostMultiply(); vtktransform->Translate(-pos[0], -pos[1], -pos[2]); vtktransform->Translate(-center[0], -center[1], -center[2]); vtktransform->PreMultiply(); vtktransform->Scale(data[0], data[1], data[2]); vtktransform->PostMultiply(); vtktransform->Translate(+center[0], +center[1], +center[2]); vtktransform->Translate(pos[0], pos[1], pos[2]); vtktransform->PreMultiply(); break; } case OpROTATE: { mitk::RotationOperation *rotateOp = dynamic_cast(operation); if (rotateOp == NULL) { //mitk::StatusBar::GetInstance()->DisplayText("received wrong type of operation!See mitkAffineInteractor.cpp", 10000); return; } Vector3D rotationVector = rotateOp->GetVectorOfRotation(); Point3D center = rotateOp->GetCenterOfRotation(); ScalarType angle = rotateOp->GetAngleOfRotation(); vtktransform->PostMultiply(); vtktransform->Translate(-center[0], -center[1], -center[2]); vtktransform->RotateWXYZ(angle, rotationVector[0], rotationVector[1], rotationVector[2]); vtktransform->Translate(center[0], center[1], center[2]); vtktransform->PreMultiply(); break; } case OpRESTOREPLANEPOSITION: { //Copy necessary to avoid vtk warning vtkMatrix4x4* matrix = vtkMatrix4x4::New(); TransferItkTransformToVtkMatrix(dynamic_cast(operation)->GetTransform().GetPointer(), matrix); vtktransform->SetMatrix(matrix); break; } default: vtktransform->Delete(); return; } m_VtkMatrix->DeepCopy(vtktransform->GetMatrix()); TransferVtkToItkTransform(); Modified(); vtktransform->Delete(); } void mitk::Geometry3D::BackTransform(const mitk::Point3D &in, mitk::Point3D& out) const { ScalarType temp[3]; unsigned int i, j; const TransformType::OffsetType& offset = m_IndexToWorldTransform->GetOffset(); // Remove offset for (j = 0; j < 3; j++) { temp[j] = in[j] - offset[j]; } // Get WorldToIndex transform if (m_IndexToWorldTransformLastModified != m_IndexToWorldTransform->GetMTime()) { m_InvertedTransform = TransformType::New(); if (!m_IndexToWorldTransform->GetInverse( m_InvertedTransform.GetPointer() )) { itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed." ); } m_IndexToWorldTransformLastModified = m_IndexToWorldTransform->GetMTime(); } // Check for valid matrix inversion const TransformType::MatrixType& inverse = m_InvertedTransform->GetMatrix(); if(inverse.GetVnlMatrix().has_nans()) { itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed. Matrix was: " << std::endl << m_IndexToWorldTransform->GetMatrix() << "Suggested inverted matrix is:" << std::endl << inverse ); } // Transform point for (i = 0; i < 3; i++) { out[i] = 0.0; for (j = 0; j < 3; j++) { out[i] += inverse[i][j]*temp[j]; } } } void mitk::Geometry3D::BackTransform(const mitk::Point3D &/*at*/, const mitk::Vector3D &in, mitk::Vector3D& out) const { MITK_INFO<<"Warning! Call of the deprecated function Geometry3D::BackTransform(point, vec, vec). Use Geometry3D::BackTransform(vec, vec) instead!"; //// Get WorldToIndex transform //if (m_IndexToWorldTransformLastModified != m_IndexToWorldTransform->GetMTime()) //{ // m_InvertedTransform = TransformType::New(); // if (!m_IndexToWorldTransform->GetInverse( m_InvertedTransform.GetPointer() )) // { // itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed." ); // } // m_IndexToWorldTransformLastModified = m_IndexToWorldTransform->GetMTime(); //} //// Check for valid matrix inversion //const TransformType::MatrixType& inverse = m_InvertedTransform->GetMatrix(); //if(inverse.GetVnlMatrix().has_nans()) //{ // itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed. Matrix was: " << std::endl // << m_IndexToWorldTransform->GetMatrix() << "Suggested inverted matrix is:" << std::endl // << inverse ); //} //// Transform vector //for (unsigned int i = 0; i < 3; i++) //{ // out[i] = 0.0; // for (unsigned int j = 0; j < 3; j++) // { // out[i] += inverse[i][j]*in[j]; // } //} this->BackTransform(in, out); } void mitk::Geometry3D::BackTransform(const mitk::Vector3D& in, mitk::Vector3D& out) const { // Get WorldToIndex transform if (m_IndexToWorldTransformLastModified != m_IndexToWorldTransform->GetMTime()) { m_InvertedTransform = TransformType::New(); if (!m_IndexToWorldTransform->GetInverse( m_InvertedTransform.GetPointer() )) { itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed." ); } m_IndexToWorldTransformLastModified = m_IndexToWorldTransform->GetMTime(); } // Check for valid matrix inversion const TransformType::MatrixType& inverse = m_InvertedTransform->GetMatrix(); if(inverse.GetVnlMatrix().has_nans()) { itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed. Matrix was: " << std::endl << m_IndexToWorldTransform->GetMatrix() << "Suggested inverted matrix is:" << std::endl << inverse ); } // Transform vector for (unsigned int i = 0; i < 3; i++) { out[i] = 0.0; for (unsigned int j = 0; j < 3; j++) { out[i] += inverse[i][j]*in[j]; } } } const float* mitk::Geometry3D::GetFloatSpacing() const { return m_FloatSpacing; } void mitk::Geometry3D::SetSpacing(const mitk::Vector3D& aSpacing) { if(mitk::Equal(m_Spacing, aSpacing) == false) { assert(aSpacing[0]>0 && aSpacing[1]>0 && aSpacing[2]>0); m_Spacing = aSpacing; AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = m_IndexToWorldTransform->GetMatrix().GetVnlMatrix(); mitk::VnlVector col; col = vnlmatrix.get_column(0); col.normalize(); col*=aSpacing[0]; vnlmatrix.set_column(0, col); col = vnlmatrix.get_column(1); col.normalize(); col*=aSpacing[1]; vnlmatrix.set_column(1, col); col = vnlmatrix.get_column(2); col.normalize(); col*=aSpacing[2]; vnlmatrix.set_column(2, col); Matrix3D matrix; matrix = vnlmatrix; AffineTransform3D::Pointer transform = AffineTransform3D::New(); transform->SetMatrix(matrix); transform->SetOffset(m_IndexToWorldTransform->GetOffset()); SetIndexToWorldTransform(transform.GetPointer()); itk2vtk(m_Spacing, m_FloatSpacing); } } void mitk::Geometry3D::SetOrigin(const Point3D & origin) { if(origin!=GetOrigin()) { m_Origin = origin; m_IndexToWorldTransform->SetOffset(m_Origin.GetVectorFromOrigin()); Modified(); TransferItkToVtkTransform(); } } void mitk::Geometry3D::Translate(const Vector3D & vector) { if((vector[0] != 0) || (vector[1] != 0) || (vector[2] != 0)) { this->SetOrigin(m_Origin + vector); // m_IndexToWorldTransform->SetOffset(m_IndexToWorldTransform->GetOffset()+vector); // TransferItkToVtkTransform(); // Modified(); } } void mitk::Geometry3D::SetIdentity() { m_IndexToWorldTransform->SetIdentity(); m_Origin.Fill(0); Modified(); TransferItkToVtkTransform(); } void mitk::Geometry3D::Compose( const mitk::AffineGeometryFrame3D::TransformType * other, bool pre ) { m_IndexToWorldTransform->Compose(other, pre); CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing, m_FloatSpacing); vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); Modified(); TransferItkToVtkTransform(); } void mitk::Geometry3D::Compose( const vtkMatrix4x4 * vtkmatrix, bool pre ) { mitk::AffineGeometryFrame3D::TransformType::Pointer itkTransform = mitk::AffineGeometryFrame3D::TransformType::New(); TransferVtkMatrixToItkTransform(vtkmatrix, itkTransform.GetPointer()); Compose(itkTransform, pre); } const std::string mitk::Geometry3D::GetTransformAsString( TransformType* transformType ) { std::ostringstream out; out << '['; for( int i=0; i<3; ++i ) { out << '['; for( int j=0; j<3; ++j ) out << transformType->GetMatrix().GetVnlMatrix().get(i, j) << ' '; out << ']'; } out << "]["; for( int i=0; i<3; ++i ) out << transformType->GetOffset()[i] << ' '; out << "]\0"; return out.str(); } void mitk::Geometry3D::PrintSelf(std::ostream& os, itk::Indent indent) const { os << indent << " IndexToWorldTransform: "; if(m_IndexToWorldTransform.IsNull()) os << "NULL" << std::endl; else { // from itk::MatrixOffsetTransformBase unsigned int i, j; os << std::endl; os << indent << "Matrix: " << std::endl; for (i = 0; i < 3; i++) { os << indent.GetNextIndent(); for (j = 0; j < 3; j++) { os << m_IndexToWorldTransform->GetMatrix()[i][j] << " "; } os << std::endl; } os << indent << "Offset: " << m_IndexToWorldTransform->GetOffset() << std::endl; os << indent << "Center: " << m_IndexToWorldTransform->GetCenter() << std::endl; os << indent << "Translation: " << m_IndexToWorldTransform->GetTranslation() << std::endl; os << indent << "Inverse: " << std::endl; for (i = 0; i < 3; i++) { os << indent.GetNextIndent(); for (j = 0; j < 3; j++) { os << m_IndexToWorldTransform->GetInverseMatrix()[i][j] << " "; } os << std::endl; } // from itk::ScalableAffineTransform os << indent << "Scale : "; for (i = 0; i < 3; i++) { os << m_IndexToWorldTransform->GetScale()[i] << " "; } os << std::endl; } os << indent << " BoundingBox: "; if(m_BoundingBox.IsNull()) os << "NULL" << std::endl; else { os << indent << "( "; for (unsigned int i=0; i<3; i++) { os << m_BoundingBox->GetBounds()[2*i] << "," << m_BoundingBox->GetBounds()[2*i+1] << " "; } os << " )" << std::endl; } os << indent << " Origin: " << m_Origin << std::endl; os << indent << " ImageGeometry: " << m_ImageGeometry << std::endl; os << indent << " Spacing: " << m_Spacing << std::endl; os << indent << " TimeBounds: " << m_TimeBounds << std::endl; } mitk::Point3D mitk::Geometry3D::GetCornerPoint(int id) const { assert(id >= 0); assert(m_BoundingBox.IsNotNull()); BoundingBox::BoundsArrayType bounds = m_BoundingBox->GetBounds(); Point3D cornerpoint; switch(id) { case 0: FillVector3D(cornerpoint, bounds[0],bounds[2],bounds[4]); break; case 1: FillVector3D(cornerpoint, bounds[0],bounds[2],bounds[5]); break; case 2: FillVector3D(cornerpoint, bounds[0],bounds[3],bounds[4]); break; case 3: FillVector3D(cornerpoint, bounds[0],bounds[3],bounds[5]); break; case 4: FillVector3D(cornerpoint, bounds[1],bounds[2],bounds[4]); break; case 5: FillVector3D(cornerpoint, bounds[1],bounds[2],bounds[5]); break; case 6: FillVector3D(cornerpoint, bounds[1],bounds[3],bounds[4]); break; case 7: FillVector3D(cornerpoint, bounds[1],bounds[3],bounds[5]); break; default: { itkExceptionMacro(<<"A cube only has 8 corners. These are labeled 0-7."); } } if(m_ImageGeometry) { // Here i have to adjust the 0.5 offset manually, because the cornerpoint is the corner of the // bounding box. The bounding box itself is no image, so it is corner-based FillVector3D(cornerpoint, cornerpoint[0]-0.5, cornerpoint[1]-0.5, cornerpoint[2]-0.5); } return m_IndexToWorldTransform->TransformPoint(cornerpoint); } mitk::Point3D mitk::Geometry3D::GetCornerPoint(bool xFront, bool yFront, bool zFront) const { assert(m_BoundingBox.IsNotNull()); BoundingBox::BoundsArrayType bounds = m_BoundingBox->GetBounds(); Point3D cornerpoint; cornerpoint[0] = (xFront ? bounds[0] : bounds[1]); cornerpoint[1] = (yFront ? bounds[2] : bounds[3]); cornerpoint[2] = (zFront ? bounds[4] : bounds[5]); if(m_ImageGeometry) { // Here i have to adjust the 0.5 offset manually, because the cornerpoint is the corner of the // bounding box. The bounding box itself is no image, so it is corner-based FillVector3D(cornerpoint, cornerpoint[0]-0.5, cornerpoint[1]-0.5, cornerpoint[2]-0.5); } return m_IndexToWorldTransform->TransformPoint(cornerpoint); } void mitk::Geometry3D::ResetSubTransforms() { } void mitk::Geometry3D::ChangeImageGeometryConsideringOriginOffset( const bool isAnImageGeometry ) { // If Geometry is switched to ImageGeometry, you have to put an offset to the origin, because // imageGeometries origins are pixel-center-based // ... and remove the offset, if you switch an imageGeometry back to a normal geometry // For more information please see the Geometry documentation page if(m_ImageGeometry == isAnImageGeometry) return; const BoundingBox::BoundsArrayType& boundsarray = this->GetBoundingBox()->GetBounds(); Point3D originIndex; FillVector3D(originIndex, boundsarray[0], boundsarray[2], boundsarray[4]); if(isAnImageGeometry == true) FillVector3D( originIndex, originIndex[0] + 0.5, originIndex[1] + 0.5, originIndex[2] + 0.5 ); else FillVector3D( originIndex, originIndex[0] - 0.5, originIndex[1] - 0.5, originIndex[2] - 0.5 ); Point3D originWorld; originWorld = GetIndexToWorldTransform() ->TransformPoint( originIndex ); // instead could as well call IndexToWorld(originIndex,originWorld); SetOrigin(originWorld); this->SetImageGeometry(isAnImageGeometry); } - bool mitk::Geometry3D::Is2DConvertable() { bool isConvertableWithoutLoss = true; do { if (this->GetSpacing()[2] != 1) { isConvertableWithoutLoss = false; break; } if (this->GetOrigin()[2] != 0) { isConvertableWithoutLoss = false; break; } mitk::Vector3D col0, col1, col2; col0.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0)); col1.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1)); col2.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)); if ((col0[2] != 0) || (col1[2] != 0) || (col2[0] != 0) || (col2[1] != 0) || (col2[2] != 1)) { isConvertableWithoutLoss = false; break; } } while (0); return isConvertableWithoutLoss; } + +bool mitk::Equal( const mitk::Geometry3D::BoundingBoxType *rightHandSide, const mitk::Geometry3D::BoundingBoxType *leftHandSide, ScalarType eps, bool verbose ) +{ + bool result = true; + if(( rightHandSide == NULL ) ) + { + if(verbose) + MITK_INFO << "[( Geometry3D::BoundingBoxType )] rightHandSide NULL."; + return false; + } + if(( leftHandSide == NULL ) ) + { + if(verbose) + MITK_INFO << "[( Geometry3D::BoundingBoxType )] leftHandSide NULL."; + return false; + } + + Geometry3D::BoundsArrayType rightBounds = rightHandSide->GetBounds(); + Geometry3D::BoundsArrayType leftBounds = leftHandSide->GetBounds(); + Geometry3D::BoundsArrayType::Iterator itLeft = leftBounds.Begin(); + for( Geometry3D::BoundsArrayType::Iterator itRight = rightBounds.Begin(); itRight != rightBounds.End(); ++itRight) + { + if(( !mitk::Equal( *itRight, *itLeft, eps )) ) + { + if(verbose) + { + MITK_INFO << "[( Geometry3D::BoundingBoxType )] bounds are not equal."; + MITK_INFO << "rightHandSide is " << setprecision(12) << *itRight << " : leftHandSide is " << *itLeft << " and tolerance is " << eps; + } + result = false; + } + itLeft++; + } + return result; +} + +bool mitk::Equal(const mitk::Geometry3D *rightHandSide, const mitk::Geometry3D *leftHandSide, ScalarType eps, bool verbose) +{ + bool result = true; + + if(( rightHandSide == NULL ) ) + { + if(verbose) + MITK_INFO << "[( Geometry3D )] rightHandSide NULL."; + return false; + } + if(( leftHandSide == NULL) ) + { + if(verbose) + MITK_INFO << "[( Geometry3D )] leftHandSide NULL."; + return false; + } + + //Compare spacings + if(( !mitk::Equal( rightHandSide->GetSpacing(), leftHandSide->GetSpacing(), eps )) ) + { + if(verbose) + { + MITK_INFO << "[( Geometry3D )] Spacing differs."; + MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide->GetSpacing() << " : leftHandSide is " << leftHandSide->GetSpacing() << " and tolerance is " << eps; + } + result = false; + } + + //Compare Origins + if(( !mitk::Equal( rightHandSide->GetOrigin(), leftHandSide->GetOrigin(), eps )) ) + { + if(verbose) + { + MITK_INFO << "[( Geometry3D )] Origin differs."; + MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide->GetOrigin() << " : leftHandSide is " << leftHandSide->GetOrigin() << " and tolerance is " << eps; + } + result = false; + } + + //Compare Axis and Extents + for( unsigned int i=0; i<3; ++i) + { + if( !mitk::Equal( rightHandSide->GetAxisVector(i), leftHandSide->GetAxisVector(i), eps)) + { + if(verbose) + { + MITK_INFO << "[( Geometry3D )] AxisVector #" << i << " differ"; + MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide->GetAxisVector(i) << " : leftHandSide is " << leftHandSide->GetAxisVector(i) << " and tolerance is " << eps; + } + result = false; + } + + if(( !mitk::Equal( rightHandSide->GetExtent(i), leftHandSide->GetExtent(i), eps) ) ) + { + if(verbose) + { + MITK_INFO << "[( Geometry3D )] Extent #" << i << " differ"; + MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide->GetExtent(i) << " : leftHandSide is " << leftHandSide->GetExtent(i) << " and tolerance is " << eps; + } + result = false; + } + } + + //Compare ImageGeometry Flag + if(( rightHandSide->GetImageGeometry() != leftHandSide->GetImageGeometry()) ) + { + if(verbose) + { + MITK_INFO << "[( Geometry3D )] GetImageGeometry is different."; + MITK_INFO << "rightHandSide is " << rightHandSide->GetImageGeometry() << " : leftHandSide is " << leftHandSide->GetImageGeometry(); + } + result = false; + } + + //Compare BoundingBoxes + if(( !mitk::Equal( rightHandSide->GetBoundingBox(), leftHandSide->GetBoundingBox(), eps, verbose)) ) + { + result = false; + } + + //Compare IndexToWorldTransform Matrix + if(( !mitk::Equal( rightHandSide->GetIndexToWorldTransform(), rightHandSide->GetIndexToWorldTransform(), eps, verbose)) ) + { + result = false; + } + return result; +} + +bool mitk::Equal( const Geometry3D::TransformType *rightHandSide, const Geometry3D::TransformType *leftHandSide, ScalarType eps, bool verbose ) +{ + //Compare IndexToWorldTransform Matrix + if(( !mitk::MatrixEqualElementWise( rightHandSide->GetMatrix(), + leftHandSide->GetMatrix()) ) ) + { + if(verbose) + { + MITK_INFO << "[( Geometry3D::TransformType )] Index to World Transformation matrix differs."; + MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide->GetMatrix() << " : leftHandSide is " << leftHandSide->GetMatrix() << " and tolerance is " << eps; + } + return false; + } + return true; +} diff --git a/Core/Code/DataManagement/mitkGeometry3D.h b/Core/Code/DataManagement/mitkGeometry3D.h index 3f5bdd424e..260136206b 100644 --- a/Core/Code/DataManagement/mitkGeometry3D.h +++ b/Core/Code/DataManagement/mitkGeometry3D.h @@ -1,669 +1,717 @@ /*=================================================================== 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 GEOMETRY3D_H_HEADER_INCLUDED_C1EBD0AD #define GEOMETRY3D_H_HEADER_INCLUDED_C1EBD0AD #include #include #include "mitkVector.h" #include "mitkOperationActor.h" #include #include #include #include class vtkLinearTransform; class vtkMatrixToLinearTransform; class vtkMatrix4x4; namespace mitk { //##Documentation //## @brief Standard 3D-BoundingBox typedef //## //## Standard 3D-BoundingBox typedef to get rid of template arguments (3D, type). typedef itk::BoundingBox BoundingBox; //##Documentation //## @brief Standard typedef for time-bounds typedef itk::FixedArray TimeBounds; typedef itk::FixedArray FixedArrayType; typedef itk::AffineGeometryFrame AffineGeometryFrame3D; //##Documentation //## @brief Describes the geometry of a data object //## //## At least, it can return the bounding box of the data object. //## //## The class holds //## \li a bounding box which is axes-parallel in intrinsic coordinates //## (often integer indices of pixels), to be accessed by //## GetBoundingBox() //## \li a transform to convert intrinsic coordinates into a //## world-coordinate system with coordinates in millimeters //## and milliseconds (all are floating point values), to //## be accessed by GetIndexToWorldTransform() //## \li a life span, i.e. a bounding box in time in ms (with //## start and end time), to be accessed by GetTimeBounds(). //## The default is minus infinity to plus infinity. //## //## Geometry3D and its sub-classes allow converting between //## intrinsic coordinates (called index or unit coordinates) //## and world-coordinates (called world or mm coordinates), //## e.g. WorldToIndex. //## In case you need integer index coordinates, provide an //## mitk::Index3D (or itk::Index) as target variable to //## WorldToIndex, otherwise you will get a continuous index //## (floating point values). //## //## An important sub-class is SlicedGeometry3D, which descibes //## data objects consisting of slices, e.g., objects of type Image. //## Conversions between world coordinates (in mm) and unit coordinates //## (e.g., pixels in the case of an Image) can be performed. //## //## For more information on related classes, see \ref Geometry. //## //## Geometry3D instances referring to an Image need a slightly //## different definition of corners, see SetImageGeometry. This //## is usualy automatically called by Image. //## //## Geometry3D have to be initialized in the method GenerateOutputInformation() //## of BaseProcess (or CopyInformation/ UpdateOutputInformation of BaseData, //## if possible, e.g., by analyzing pic tags in Image) subclasses. See also //## itk::ProcessObject::GenerateOutputInformation(), //## itk::DataObject::CopyInformation() and //## itk::DataObject::UpdateOutputInformation(). //## //## Rule: everything is in mm (ms) if not stated otherwise. //## @ingroup Geometry class MITK_CORE_EXPORT Geometry3D : public AffineGeometryFrame3D, public OperationActor { public: mitkClassMacro(Geometry3D, AffineGeometryFrame3D); typedef itk::QuaternionRigidTransform< ScalarType > QuaternionTransformType; typedef QuaternionTransformType::VnlQuaternionType VnlQuaternionType; /** Method for creation through the object factory. */ itkNewMacro(Self); // a bit of a misuse, but we want only doxygen to see the following: #ifdef DOXYGEN_SKIP //##Documentation //## @brief Get the transformation used to convert from index //## to world coordinates itkGetObjectMacro(IndexToWorldTransform, AffineTransform3D); #endif //## @brief Set the transformation used to convert from index //## to world coordinates virtual void SetIndexToWorldTransform(mitk::AffineTransform3D* transform); //##Documentation //## @brief Convenience method for setting the ITK transform //## (m_IndexToWorldTransform) via an vtkMatrix4x4 //## \sa SetIndexToWorldTransform virtual void SetIndexToWorldTransformByVtkMatrix(vtkMatrix4x4* vtkmatrix); #ifdef DOXYGEN_SKIP //##Documentation //## @brief Get bounding box (in index/unit coordinates) itkGetConstObjectMacro(BoundingBox, BoundingBoxType); //##Documentation //## @brief Get bounding box (in index/unit coordinates) as a BoundsArrayType const BoundsArrayType GetBounds() const { assert(m_BoundingBox.IsNotNull()); return m_BoundingBox->GetBounds(); } //##Documentation //## \brief Set the bounding box (in index/unit coordinates) //## //## Only possible via the BoundsArray to make clear that a //## copy of the bounding-box is stored, not a reference to it. virtual void SetBounds(const BoundsArrayType& bounds); #endif //##Documentation //## @brief Set the bounding box (in index/unit coordinates) via a float array virtual void SetFloatBounds(const float bounds[6]); //##Documentation //## @brief Set the bounding box (in index/unit coordinates) via a double array virtual void SetFloatBounds(const double bounds[6]); //##Documentation //## @brief When switching from an Image Geometry to a normal Geometry (and the other way around), you have to change the origin as well (See Geometry Documentation)! This function will change the "isImageGeometry" bool flag and changes the origin respectively. virtual void ChangeImageGeometryConsideringOriginOffset( const bool isAnImageGeometry ); //##Documentation //## @brief Checks, if the given geometry can be converted to 2D without information loss //## e.g. when a 2D image is saved, the matrix is usually cropped to 2x2, and when you load it back to MITK //## it will be filled with standard values. This function checks, if information would be lost during this //## procedure virtual bool Is2DConvertable(); //##Documentation //## @brief Get the time bounds (in ms) itkGetConstReferenceMacro(TimeBounds, TimeBounds); //##Documentation //## @brief Set the time bounds (in ms) virtual void SetTimeBounds(const TimeBounds& timebounds); //##Documentation //## @brief Get the position of the corner number \a id (in world coordinates) //## //## See SetImageGeometry for how a corner is defined on images. Point3D GetCornerPoint(int id) const; //##Documentation //## @brief Get the position of a corner (in world coordinates) //## //## See SetImageGeometry for how a corner is defined on images. Point3D GetCornerPoint(bool xFront=true, bool yFront=true, bool zFront=true) const; //##Documentation //## @brief Get vector along bounding-box in the specified @a direction in mm //## //## The length of the vector is the size of the bounding-box in the //## specified @a direction in mm //## \sa GetMatrixColumn Vector3D GetAxisVector(unsigned int direction) const { Vector3D frontToBack; frontToBack.SetVnlVector(m_IndexToWorldTransform->GetMatrix().GetVnlMatrix().get_column(direction)); frontToBack *= GetExtent(direction); return frontToBack; } //##Documentation //## @brief Get the center of the bounding-box in mm //## Point3D GetCenter() const { assert(m_BoundingBox.IsNotNull()); return m_IndexToWorldTransform->TransformPoint(m_BoundingBox->GetCenter()); } //##Documentation //## @brief Get the squared length of the diagonal of the bounding-box in mm //## double GetDiagonalLength2() const { Vector3D diagonalvector = GetCornerPoint()-GetCornerPoint(false, false, false); return diagonalvector.GetSquaredNorm(); } //##Documentation //## @brief Get the length of the diagonal of the bounding-box in mm //## double GetDiagonalLength() const { return sqrt(GetDiagonalLength2()); } //##Documentation //## @brief Get a VnlVector along bounding-box in the specified //## @a direction, length is spacing //## //## \sa GetAxisVector VnlVector GetMatrixColumn(unsigned int direction) const { return m_IndexToWorldTransform->GetMatrix().GetVnlMatrix().get_column(direction); } #ifdef DOXYGEN_SKIP //##Documentation //## @brief Get the extent of the bounding box (in index/unit coordinates) //## //## To access the extent in mm use GetExtentInMM ScalarType GetExtent(unsigned int direction) const; #endif //##Documentation //## @brief Get the extent of the bounding-box in the specified @a direction in mm //## //## Equals length of GetAxisVector(direction). ScalarType GetExtentInMM(int direction) const { return m_IndexToWorldTransform->GetMatrix().GetVnlMatrix().get_column(direction).magnitude()*GetExtent(direction); } //##Documentation //## @brief Set the extent of the bounding-box in the specified @a direction in mm //## //## @note This changes the matrix in the transform, @a not the bounds, which are given in units! virtual void SetExtentInMM(int direction, ScalarType extentInMM); //##Documentation //## @brief Get the m_IndexToWorldTransform as a vtkLinearTransform vtkLinearTransform* GetVtkTransform() const { return (vtkLinearTransform*)m_VtkIndexToWorldTransform; } //##Documentation //## @brief Set the origin, i.e. the upper-left corner of the plane //## virtual void SetOrigin(const Point3D& origin); //##Documentation //## @brief Translate the origin by a vector //## virtual void Translate(const Vector3D& vector); //##Documentation //## @brief Set the transform to identity //## virtual void SetIdentity(); //##Documentation //## @brief Compose new IndexToWorldTransform with a given transform. //## //## This method composes m_IndexToWorldTransform with another transform, //## modifying self to be the composition of self and other. //## If the argument pre is true, then other is precomposed with self; //## that is, the resulting transformation consists of first applying //## other to the source, followed by self. If pre is false or omitted, //## then other is post-composed with self; that is the resulting //## transformation consists of first applying self to the source, //## followed by other. virtual void Compose( const AffineGeometryFrame3D::TransformType * other, bool pre = 0 ); //##Documentation //## @brief Compose new IndexToWorldTransform with a given vtkMatrix4x4. //## //## Converts the vtkMatrix4x4 into a itk-transform and calls the previous method. virtual void Compose( const vtkMatrix4x4 * vtkmatrix, bool pre = 0 ); //##Documentation //## @brief Get the origin, e.g. the upper-left corner of the plane const Point3D& GetOrigin() const { return m_Origin; } //##Documentation //## @brief Get the origin as VnlVector //## //## \sa GetOrigin VnlVector GetOriginVnl() const { return const_cast(this)->m_Origin.GetVnlVector(); } //##Documentation //## @brief Convert world coordinates (in mm) of a \em point to (continuous!) index coordinates //## \warning If you need (discrete) integer index coordinates (e.g., for iterating easily over an image), //## use WorldToIndex(const mitk::Point3D& pt_mm, itk::Index &index). //## For further information about coordinates types, please see the Geometry documentation void WorldToIndex(const mitk::Point3D& pt_mm, mitk::Point3D& pt_units) const; //##Documentation //## @brief Convert (continuous or discrete) index coordinates of a \em point to world coordinates (in mm) //## For further information about coordinates types, please see the Geometry documentation void IndexToWorld(const mitk::Point3D& pt_units, mitk::Point3D& pt_mm) const; //##Documentation //## @brief Convert world coordinates (in mm) of a \em vector //## \a vec_mm to (continuous!) index coordinates. //## @deprecated First parameter (Point3D) is not used. If possible, please use void WorldToIndex(const mitk::Vector3D& vec_mm, mitk::Vector3D& vec_units) const. //## For further information about coordinates types, please see the Geometry documentation void WorldToIndex(const mitk::Point3D& atPt3d_mm, const mitk::Vector3D& vec_mm, mitk::Vector3D& vec_units) const; //##Documentation //## @brief Convert world coordinates (in mm) of a \em vector //## \a vec_mm to (continuous!) index coordinates. //## For further information about coordinates types, please see the Geometry documentation void WorldToIndex(const mitk::Vector3D& vec_mm, mitk::Vector3D& vec_units) const; //##Documentation //## @brief Convert (continuous or discrete) index coordinates of a \em vector //## \a vec_units to world coordinates (in mm) //## @deprecated First parameter (Point3D) is not used. If possible, please use void IndexToWorld(const mitk::Vector3D& vec_units, mitk::Vector3D& vec_mm) const. //## For further information about coordinates types, please see the Geometry documentation void IndexToWorld(const mitk::Point3D& atPt3d_units, const mitk::Vector3D& vec_units, mitk::Vector3D& vec_mm) const; //##Documentation //## @brief Convert (continuous or discrete) index coordinates of a \em vector //## \a vec_units to world coordinates (in mm) //## For further information about coordinates types, please see the Geometry documentation void IndexToWorld(const mitk::Vector3D& vec_units, mitk::Vector3D& vec_mm) const; //##Documentation //## @brief Convert world coordinates (in mm) of a \em point to (discrete!) index coordinates. //## This method rounds to integer indices! //## For further information about coordinates types, please see the Geometry documentation template void WorldToIndex(const mitk::Point3D& pt_mm, itk::Index &index) const { typedef itk::Index IndexType; mitk::Point3D pt_units; this->WorldToIndex(pt_mm, pt_units); int i, dim=index.GetIndexDimension(); if(dim>3) { index.Fill(0); dim=3; } for(i=0;i( pt_units[i] ); } } //##Documentation //## @brief Deprecated for use with ITK version 3.10 or newer. //## Convert world coordinates (in mm) of a \em point to //## ITK physical coordinates (in mm, but without a possible rotation) //## //## This method is useful if you have want to access an mitk::Image //## via an itk::Image. ITK v3.8 and older did not support rotated (tilted) //## images, i.e., ITK images are always parallel to the coordinate axes. //## When accessing a (possibly rotated) mitk::Image via an itk::Image //## the rotational part of the transformation in the Geometry3D is //## simply discarded; in other word: only the origin and spacing is //## used by ITK, not the complete matrix available in MITK. //## With WorldToItkPhysicalPoint you can convert an MITK world //## coordinate (including the rotation) into a coordinate that //## can be used with the ITK image as a ITK physical coordinate //## (excluding the rotation). template void WorldToItkPhysicalPoint(const mitk::Point3D& pt_mm, itk::Point& itkPhysicalPoint) const { mitk::vtk2itk(pt_mm, itkPhysicalPoint); } //##Documentation //## @brief Deprecated for use with ITK version 3.10 or newer. //## Convert ITK physical coordinates of a \em point (in mm, //## but without a rotation) into MITK world coordinates (in mm) //## //## For more information, see WorldToItkPhysicalPoint. template void ItkPhysicalPointToWorld(const itk::Point& itkPhysicalPoint, mitk::Point3D& pt_mm) const { mitk::vtk2itk(itkPhysicalPoint, pt_mm); } //##Documentation //## @brief Initialize the Geometry3D virtual void Initialize(); //##Documentation //## @brief Is this an ImageGeometry? //## //## For more information, see SetImageGeometry itkGetConstMacro(ImageGeometry, bool); //##Documentation //## @brief Define that this Geometry3D is refering to an Image //## //## A geometry referring to an Image needs a slightly different //## definition of the position of the corners (see GetCornerPoint). //## The position of a voxel is defined by the position of its center. //## If we would use the origin (position of the (center of) the first //## voxel) as a corner and display this point, it would seem to be //## \em not at the corner but a bit within the image. Even worse for //## the opposite corner of the image: here the corner would appear //## outside the image (by half of the voxel diameter). Thus, we have //## to correct for this and to be able to do that, we need to know //## that the Geometry3D is referring to an Image. itkSetMacro(ImageGeometry, bool); itkBooleanMacro(ImageGeometry); //##Documentation //## @brief Is this Geometry3D in a state that is valid? virtual bool IsValid() const { return m_Valid; } //##Documentation //## @brief Test whether the point \a p (world coordinates in mm) is //## inside the bounding box bool IsInside(const mitk::Point3D& p) const { mitk::Point3D index; WorldToIndex(p, index); return IsIndexInside(index); } //##Documentation //## @brief Test whether the point \a p ((continous!)index coordinates in units) is //## inside the bounding box bool IsIndexInside(const mitk::Point3D& index) const { bool inside = false; //if it is an image geometry, we need to convert the index to discrete values //this is done by applying the rounding function also used in WorldToIndex (see line 323) if (m_ImageGeometry) { mitk::Point3D discretIndex; discretIndex[0]=itk::Math::RoundHalfIntegerUp( index[0] ); discretIndex[1]=itk::Math::RoundHalfIntegerUp( index[1] ); discretIndex[2]=itk::Math::RoundHalfIntegerUp( index[2] ); inside = m_BoundingBox->IsInside(discretIndex); //we have to check if the index is at the upper border of each dimension, // because the boundingbox is not centerbased if (inside) { const BoundingBox::BoundsArrayType& bounds = m_BoundingBox->GetBounds(); if((discretIndex[0] == bounds[1]) || (discretIndex[1] == bounds[3]) || (discretIndex[2] == bounds[5])) inside = false; } } else inside = m_BoundingBox->IsInside(index); return inside; } //##Documentation //## @brief Convenience method for working with ITK indices template bool IsIndexInside(const itk::Index &index) const { int i, dim=index.GetIndexDimension(); Point3D pt_index; pt_index.Fill(0); for ( i = 0; i < dim; ++i ) { pt_index[i] = index[i]; } return IsIndexInside(pt_index); } //##Documentation //## @brief Get the spacing (size of a pixel). //## itkGetConstReferenceMacro(Spacing, mitk::Vector3D); //##Documentation //## @brief Get the spacing as a float[3] array. const float* GetFloatSpacing() const; //##Documentation //## @brief Set the spacing (m_Spacing) virtual void SetSpacing(const mitk::Vector3D& aSpacing); //##Documentation //## @brief Get the DICOM FrameOfReferenceID referring to the //## used world coordinate system itkGetConstMacro(FrameOfReferenceID, unsigned int); //##Documentation //## @brief Set the DICOM FrameOfReferenceID referring to the //## used world coordinate system itkSetMacro(FrameOfReferenceID, unsigned int); //##Documentation //## @brief Copy the ITK transform //## (m_IndexToWorldTransform) to the VTK transform //## \sa SetIndexToWorldTransform void TransferItkToVtkTransform(); //##Documentation //## @brief Copy the VTK transform //## to the ITK transform (m_IndexToWorldTransform) //## \sa SetIndexToWorldTransform void TransferVtkToItkTransform(); //##Documentation //## @brief Get the parametric bounding-box //## //## See AbstractTransformGeometry for an example usage of this. itkGetConstObjectMacro(ParametricBoundingBox, BoundingBox); //##Documentation //## @brief Get the parametric bounds //## //## See AbstractTransformGeometry for an example usage of this. const BoundingBox::BoundsArrayType& GetParametricBounds() const { assert(m_ParametricBoundingBox.IsNotNull()); return m_ParametricBoundingBox->GetBounds(); } //##Documentation //## @brief Get the parametric extent //## //## See AbstractTransformGeometry for an example usage of this. mitk::ScalarType GetParametricExtent(int direction) const { assert(direction>=0 && direction<3); assert(m_ParametricBoundingBox.IsNotNull()); BoundingBoxType::BoundsArrayType bounds = m_ParametricBoundingBox->GetBounds(); return bounds[direction*2+1]-bounds[direction*2]; } //##Documentation //## @brief Get the parametric extent in mm //## //## See AbstractTransformGeometry for an example usage of this. virtual mitk::ScalarType GetParametricExtentInMM(int direction) const { return GetExtentInMM(direction); } //##Documentation //## @brief Get the parametric transform //## //## See AbstractTransformGeometry for an example usage of this. virtual const Transform3D* GetParametricTransform() const { return m_IndexToWorldTransform; } //##Documentation //## @brief Calculates a bounding-box around the geometry relative //## to a coordinate system defined by a transform //## mitk::BoundingBox::Pointer CalculateBoundingBoxRelativeToTransform(const mitk::AffineTransform3D* transform) const; //##Documentation //## @brief clones the geometry //## //## Overwrite in all sub-classes. //## Normally looks like: //## \code //## Self::Pointer newGeometry = new Self(*this); //## newGeometry->UnRegister(); //## return newGeometry.GetPointer(); //## \endcode virtual itk::LightObject::Pointer InternalClone() const; //##Documentation //##@brief executes affine operations (translate, rotate, scale) virtual void ExecuteOperation(Operation* operation); protected: Geometry3D(); Geometry3D(const Geometry3D& other); static const std::string GetTransformAsString( TransformType* transformType ); virtual ~Geometry3D(); virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; virtual void BackTransform(const mitk::Point3D& in, mitk::Point3D& out) const; //##Documentation //## @brief Deprecated virtual void BackTransform(const mitk::Point3D& at, const mitk::Vector3D& in, mitk::Vector3D& out) const; //Without redundant parameter Point3D virtual void BackTransform(const mitk::Vector3D& in, mitk::Vector3D& out) const; //##Documentation //## @brief Set the parametric bounds //## //## Protected in this class, made public in some sub-classes, e.g., //## ExternAbstractTransformGeometry. virtual void SetParametricBounds(const BoundingBox::BoundsArrayType& bounds); /** Resets sub-transforms that compose m_IndexToWorldTransform, by using * the current value of m_IndexToWorldTransform and setting the rotation * component to zero. */ virtual void ResetSubTransforms(); mutable mitk::BoundingBox::Pointer m_ParametricBoundingBox; mutable mitk::TimeBounds m_TimeBounds; vtkMatrix4x4* m_VtkMatrix; bool m_ImageGeometry; //##Documentation //## @brief Spacing of the data. Only significant if the geometry describes //## an Image (m_ImageGeometry==true). mitk::Vector3D m_Spacing; bool m_Valid; unsigned int m_FrameOfReferenceID; static const std::string INDEX_TO_OBJECT_TRANSFORM; static const std::string OBJECT_TO_NODE_TRANSFORM; static const std::string INDEX_TO_NODE_TRANSFORM; static const std::string INDEX_TO_WORLD_TRANSFORM; private: mutable TransformType::Pointer m_InvertedTransform; mutable unsigned long m_IndexToWorldTransformLastModified; VnlQuaternionType m_RotationQuaternion; float m_FloatSpacing[3]; vtkMatrixToLinearTransform* m_VtkIndexToWorldTransform; //##Documentation //## @brief Origin, i.e. upper-left corner of the plane //## Point3D m_Origin; }; +// +// Static compare functions mainly for testing +// +/** + * @brief Equal A function comparing two geometries for beeing identical. + * + * The function compares the spacing, origin, axisvectors, extents, the matrix of the + * IndexToWorldTransform (elementwise), the bounding (elementwise) and the ImageGeometry flag. + * + * The parameter eps is a tolarence value for all methods which are internally used for comparion. + * If you want to use different tolarance values for different parts of the geometry, feel free to use + * the other comparison methods and write your own implementation of Equal. + * @param rightHandSide Compare this against leftHandSide. + * @param leftHandSide Compare this against rightHandSide. + * @param eps Tolarence for comparison. You can use mitk::eps in most cases. + * @param verbose Flag indicating if the user wants detailed console output or not. + * @return True, if all comparison are true. False in any other case. + */ +MITK_CORE_EXPORT bool Equal(const mitk::Geometry3D* rightHandSide, const mitk::Geometry3D* leftHandSide, ScalarType eps, bool verbose); + +/** + * @brief Equal A function comparing two transforms (TransformType) for beeing identical. + * + * The function compares the IndexToWorldTransform (elementwise). + * + * The parameter eps is a tolarence value for all methods which are internally used for comparion. + * @param rightHandSide Compare this against leftHandSide. + * @param leftHandSide Compare this against rightHandSide. + * @param eps Tolarence for comparison. You can use mitk::eps in most cases. + * @param verbose Flag indicating if the user wants detailed console output or not. + * @return True, if all comparison are true. False in any other case. + */ +MITK_CORE_EXPORT bool Equal( const mitk::Geometry3D::TransformType *rightHandSide, const mitk::Geometry3D::TransformType *leftHandSide, ScalarType eps, bool verbose); + +/** + * @brief Equal A function comparing two bounding boxes (BoundingBoxType) for beeing identical. + * + * The function compares the bounds (elementwise). + * + * The parameter eps is a tolarence value for all methods which are internally used for comparion. + * @param rightHandSide Compare this against leftHandSide. + * @param leftHandSide Compare this against rightHandSide. + * @param eps Tolarence for comparison. You can use mitk::eps in most cases. + * @param verbose Flag indicating if the user wants detailed console output or not. + * @return True, if all comparison are true. False in any other case. + */ +MITK_CORE_EXPORT bool Equal( const mitk::Geometry3D::BoundingBoxType *rightHandSide, const mitk::Geometry3D::BoundingBoxType *leftHandSide, ScalarType eps, bool verbose); + } // namespace mitk #endif /* GEOMETRY3D_H_HEADER_INCLUDED_C1EBD0AD */ diff --git a/Core/Code/DataManagement/mitkImage.cpp b/Core/Code/DataManagement/mitkImage.cpp index 9071d900a3..c50ddb9970 100644 --- a/Core/Code/DataManagement/mitkImage.cpp +++ b/Core/Code/DataManagement/mitkImage.cpp @@ -1,1285 +1,1374 @@ /*=================================================================== 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. ===================================================================*/ +//MITK #include "mitkImage.h" - #include "mitkImageStatisticsHolder.h" #include "mitkPixelTypeMultiplex.h" +#include "mitkCompareImageFilter.h" +//VTK #include +//Other #include #define FILL_C_ARRAY( _arr, _size, _value) for(unsigned int i=0u; i<_size; i++) \ { _arr[i] = _value; } mitk::Image::Image() : -m_Dimension(0), m_Dimensions(NULL), m_ImageDescriptor(NULL), m_OffsetTable(NULL), m_CompleteData(NULL), + m_Dimension(0), m_Dimensions(NULL), m_ImageDescriptor(NULL), m_OffsetTable(NULL), m_CompleteData(NULL), m_ImageStatistics(NULL) { - m_Dimensions = new unsigned int[MAX_IMAGE_DIMENSIONS]; - FILL_C_ARRAY( m_Dimensions, MAX_IMAGE_DIMENSIONS, 0u); + m_Dimensions = new unsigned int[MAX_IMAGE_DIMENSIONS]; + FILL_C_ARRAY( m_Dimensions, MAX_IMAGE_DIMENSIONS, 0u); - m_Initialized = false; + m_Initialized = false; } mitk::Image::Image(const Image &other) : SlicedData(other), m_Dimension(0), m_Dimensions(NULL), -m_ImageDescriptor(NULL), m_OffsetTable(NULL), m_CompleteData(NULL), m_ImageStatistics(NULL) + m_ImageDescriptor(NULL), m_OffsetTable(NULL), m_CompleteData(NULL), m_ImageStatistics(NULL) { m_Dimensions = new unsigned int[MAX_IMAGE_DIMENSIONS]; FILL_C_ARRAY( m_Dimensions, MAX_IMAGE_DIMENSIONS, 0u); this->Initialize( other.GetPixelType(), other.GetDimension(), other.GetDimensions()); //Since the above called "Initialize" method doesn't take the geometry into account we need to set it //here manually this->SetGeometry(dynamic_cast(other.GetTimeSlicedGeometry()->Clone().GetPointer())); if (this->GetDimension() > 3) { const unsigned int time_steps = this->GetDimension(3); for (unsigned int i = 0u; i < time_steps; ++i) { ImageDataItemPointer volume = const_cast(other).GetVolumeData(i); this->SetVolume(volume->GetData(), i); } } else { ImageDataItemPointer volume = const_cast(other).GetVolumeData(0); this->SetVolume(volume->GetData(), 0); } } mitk::Image::~Image() { Clear(); m_ReferenceCountLock.Lock(); m_ReferenceCount = 3; m_ReferenceCountLock.Unlock(); m_ReferenceCountLock.Lock(); m_ReferenceCount = 0; m_ReferenceCountLock.Unlock(); if(m_OffsetTable != NULL) delete [] m_OffsetTable; if(m_ImageStatistics != NULL) delete m_ImageStatistics; } const mitk::PixelType mitk::Image::GetPixelType(int n) const { return this->m_ImageDescriptor->GetChannelTypeById(n); } unsigned int mitk::Image::GetDimension() const { return m_Dimension; } unsigned int mitk::Image::GetDimension(int i) const { if((i>=0) && (i<(int)m_Dimension)) return m_Dimensions[i]; return 1; } void* mitk::Image::GetData() { if(m_Initialized==false) { if(GetSource().IsNull()) return NULL; if(GetSource()->Updating()==false) GetSource()->UpdateOutputInformation(); } m_CompleteData=GetChannelData(); // update channel's data // if data was not available at creation point, the m_Data of channel descriptor is NULL // if data present, it won't be overwritten m_ImageDescriptor->GetChannelDescriptor(0).SetData(m_CompleteData->GetData()); return m_CompleteData->GetData(); } template void AccessPixel( const mitk::PixelType ptype, void* data, const unsigned int offset, double& value ) { value = 0.0; if( data == NULL ) return; if(ptype.GetBpe() != 24) { value = (double) (((T*) data)[ offset ]); - } + } else { const unsigned int rgboffset = 3 * offset; double returnvalue = (((T*) data)[rgboffset ]); returnvalue += (((T*) data)[rgboffset + 1]); returnvalue += (((T*) data)[rgboffset + 2]); value = returnvalue; } } double mitk::Image::GetPixelValueByIndex(const mitk::Index3D &position, unsigned int timestep) { double value = 0; if (this->GetTimeSteps() < timestep) { timestep = this->GetTimeSteps(); } value = 0.0; const unsigned int* imageDims = this->m_ImageDescriptor->GetDimensions(); const mitk::PixelType ptype = this->m_ImageDescriptor->GetChannelTypeById(0); // Comparison ?>=0 not needed since all position[i] and timestep are unsigned int // (position[0]>=0 && position[1] >=0 && position[2]>=0 && timestep>=0) // bug-11978 : we still need to catch index with negative values if ( position[0] < 0 || position[1] < 0 || position[2] < 0 ) { MITK_WARN << "Given position ("<< position << ") is out of image range, returning 0." ; } // check if the given position is inside the index range of the image, the 3rd dimension needs to be compared only if the dimension is not 0 else if ( (unsigned int)position[0] >= imageDims[0] || (unsigned int)position[1] >= imageDims[1] || ( imageDims[2] && (unsigned int)position[2] >= imageDims[2] )) { MITK_WARN << "Given position ("<< position << ") is out of image range, returning 0." ; } else { const unsigned int offset = position[0] + position[1]*imageDims[0] + position[2]*imageDims[0]*imageDims[1] + timestep*imageDims[0]*imageDims[1]*imageDims[2]; mitkPixelTypeMultiplex3( AccessPixel, ptype, this->GetData(), offset, value ); } return value; } double mitk::Image::GetPixelValueByWorldCoordinate(const mitk::Point3D& position, unsigned int timestep) { double value = 0.0; if (this->GetTimeSteps() < timestep) { timestep = this->GetTimeSteps(); } Index3D itkIndex; this->GetGeometry()->WorldToIndex(position, itkIndex); value = this->GetPixelValueByIndex( itkIndex, timestep); return value; } mitk::ImageVtkAccessor* mitk::Image::GetVtkImageData(int t, int n) { if(m_Initialized==false) { if(GetSource().IsNull()) return NULL; if(GetSource()->Updating()==false) GetSource()->UpdateOutputInformation(); } ImageDataItemPointer volume=GetVolumeData(t, n); if(volume.GetPointer()==NULL || volume->GetVtkImageData(this) == NULL) return NULL; float *fspacing = const_cast(GetSlicedGeometry(t)->GetFloatSpacing()); double dspacing[3] = {fspacing[0],fspacing[1],fspacing[2]}; volume->GetVtkImageData(this)->SetSpacing( dspacing ); return volume->GetVtkImageData(this); } mitk::Image::ImageDataItemPointer mitk::Image::GetSliceData(int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) { if(IsValidSlice(s,t,n)==false) return NULL; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // slice directly available? int pos=GetSliceIndex(s,t,n); if(m_Slices[pos].GetPointer()!=NULL) return m_Slices[pos]; // is slice available as part of a volume that is available? ImageDataItemPointer sl, ch, vol; vol=m_Volumes[GetVolumeIndex(t,n)]; if((vol.GetPointer()!=NULL) && (vol->IsComplete())) { sl=new ImageDataItem(*vol, m_ImageDescriptor, 2, data, importMemoryManagement == ManageMemory, ((size_t) s)*m_OffsetTable[2]*(ptypeSize)); sl->SetComplete(true); return m_Slices[pos]=sl; } // is slice available as part of a channel that is available? ch=m_Channels[n]; if((ch.GetPointer()!=NULL) && (ch->IsComplete())) { sl=new ImageDataItem(*ch, m_ImageDescriptor, 2, data, importMemoryManagement == ManageMemory, (((size_t) s)*m_OffsetTable[2]+((size_t) t)*m_OffsetTable[3])*(ptypeSize)); sl->SetComplete(true); return m_Slices[pos]=sl; } // slice is unavailable. Can we calculate it? if((GetSource().IsNotNull()) && (GetSource()->Updating()==false)) { // ... wir mussen rechnen!!! .... m_RequestedRegion.SetIndex(0, 0); m_RequestedRegion.SetIndex(1, 0); m_RequestedRegion.SetIndex(2, s); m_RequestedRegion.SetIndex(3, t); m_RequestedRegion.SetIndex(4, n); m_RequestedRegion.SetSize(0, m_Dimensions[0]); m_RequestedRegion.SetSize(1, m_Dimensions[1]); m_RequestedRegion.SetSize(2, 1); m_RequestedRegion.SetSize(3, 1); m_RequestedRegion.SetSize(4, 1); m_RequestedRegionInitialized=true; GetSource()->Update(); if(IsSliceSet(s,t,n)) //yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetSliceData(s,t,n,data,importMemoryManagement); else return NULL; } else { ImageDataItemPointer item = AllocateSliceData(s,t,n,data,importMemoryManagement); item->SetComplete(true); return item; } } mitk::Image::ImageDataItemPointer mitk::Image::GetVolumeData(int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) { if(IsValidVolume(t,n)==false) return NULL; ImageDataItemPointer ch, vol; // volume directly available? int pos=GetVolumeIndex(t,n); vol=m_Volumes[pos]; if((vol.GetPointer()!=NULL) && (vol->IsComplete())) return vol; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // is volume available as part of a channel that is available? ch=m_Channels[n]; if((ch.GetPointer()!=NULL) && (ch->IsComplete())) { vol=new ImageDataItem(*ch, m_ImageDescriptor, 3, data, importMemoryManagement == ManageMemory, (((size_t) t)*m_OffsetTable[3])*(ptypeSize)); vol->SetComplete(true); return m_Volumes[pos]=vol; } // let's see if all slices of the volume are set, so that we can (could) combine them to a volume bool complete=true; unsigned int s; for(s=0;sSetComplete(true); } else { mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(n); vol=m_Volumes[pos]; // ok, let's combine the slices! if(vol.GetPointer()==NULL) vol=new ImageDataItem( chPixelType, 3, m_Dimensions, NULL, true); vol->SetComplete(true); size_t size=m_OffsetTable[2]*(ptypeSize); for(s=0;sGetParent()!=vol) { // copy data of slices in volume size_t offset = ((size_t) s)*size; std::memcpy(static_cast(vol->GetData())+offset, sl->GetData(), size); // FIXME mitkIpPicDescriptor * pic = sl->GetPicDescriptor(); // replace old slice with reference to volume sl=new ImageDataItem(*vol, m_ImageDescriptor, 2, data, importMemoryManagement == ManageMemory, ((size_t) s)*size); sl->SetComplete(true); //mitkIpFuncCopyTags(sl->GetPicDescriptor(), pic); m_Slices[posSl]=sl; } } //if(vol->GetPicDescriptor()->info->tags_head==NULL) // mitkIpFuncCopyTags(vol->GetPicDescriptor(), m_Slices[GetSliceIndex(0,t,n)]->GetPicDescriptor()); } return m_Volumes[pos]=vol; } // volume is unavailable. Can we calculate it? if((GetSource().IsNotNull()) && (GetSource()->Updating()==false)) { // ... wir muessen rechnen!!! .... m_RequestedRegion.SetIndex(0, 0); m_RequestedRegion.SetIndex(1, 0); m_RequestedRegion.SetIndex(2, 0); m_RequestedRegion.SetIndex(3, t); m_RequestedRegion.SetIndex(4, n); m_RequestedRegion.SetSize(0, m_Dimensions[0]); m_RequestedRegion.SetSize(1, m_Dimensions[1]); m_RequestedRegion.SetSize(2, m_Dimensions[2]); m_RequestedRegion.SetSize(3, 1); m_RequestedRegion.SetSize(4, 1); m_RequestedRegionInitialized=true; GetSource()->Update(); if(IsVolumeSet(t,n)) //yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetVolumeData(t,n,data,importMemoryManagement); else return NULL; } else { ImageDataItemPointer item = AllocateVolumeData(t,n,data,importMemoryManagement); item->SetComplete(true); return item; } } mitk::Image::ImageDataItemPointer mitk::Image::GetChannelData(int n, void *data, ImportMemoryManagementType importMemoryManagement) { if(IsValidChannel(n)==false) return NULL; ImageDataItemPointer ch, vol; ch=m_Channels[n]; if((ch.GetPointer()!=NULL) && (ch->IsComplete())) return ch; // let's see if all volumes are set, so that we can (could) combine them to a channel if(IsChannelSet(n)) { // if there is only one time frame we do not need to combine anything if(m_Dimensions[3]<=1) { vol=GetVolumeData(0,n,data,importMemoryManagement); ch=new ImageDataItem(*vol, m_ImageDescriptor, m_ImageDescriptor->GetNumberOfDimensions(), data, importMemoryManagement == ManageMemory); ch->SetComplete(true); } else { const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ch=m_Channels[n]; // ok, let's combine the volumes! if(ch.GetPointer()==NULL) ch=new ImageDataItem(this->m_ImageDescriptor, NULL, true); ch->SetComplete(true); size_t size=m_OffsetTable[m_Dimension-1]*(ptypeSize); unsigned int t; ImageDataItemPointerArray::iterator slicesIt = m_Slices.begin()+n*m_Dimensions[2]*m_Dimensions[3]; for(t=0;tGetParent()!=ch) { // copy data of volume in channel size_t offset = ((size_t) t)*m_OffsetTable[3]*(ptypeSize); std::memcpy(static_cast(ch->GetData())+offset, vol->GetData(), size); // REVEIW FIX mitkIpPicDescriptor * pic = vol->GetPicDescriptor(); // replace old volume with reference to channel vol=new ImageDataItem(*ch, m_ImageDescriptor, 3, data, importMemoryManagement == ManageMemory, offset); vol->SetComplete(true); //mitkIpFuncCopyTags(vol->GetPicDescriptor(), pic); m_Volumes[posVol]=vol; // get rid of slices - they may point to old volume ImageDataItemPointer dnull=NULL; for(unsigned int i = 0; i < m_Dimensions[2]; ++i, ++slicesIt) { assert(slicesIt != m_Slices.end()); *slicesIt = dnull; } } } - // REVIEW FIX - // if(ch->GetPicDescriptor()->info->tags_head==NULL) - // mitkIpFuncCopyTags(ch->GetPicDescriptor(), m_Volumes[GetVolumeIndex(0,n)]->GetPicDescriptor()); + // REVIEW FIX + // if(ch->GetPicDescriptor()->info->tags_head==NULL) + // mitkIpFuncCopyTags(ch->GetPicDescriptor(), m_Volumes[GetVolumeIndex(0,n)]->GetPicDescriptor()); } return m_Channels[n]=ch; } // channel is unavailable. Can we calculate it? if((GetSource().IsNotNull()) && (GetSource()->Updating()==false)) { // ... wir muessen rechnen!!! .... m_RequestedRegion.SetIndex(0, 0); m_RequestedRegion.SetIndex(1, 0); m_RequestedRegion.SetIndex(2, 0); m_RequestedRegion.SetIndex(3, 0); m_RequestedRegion.SetIndex(4, n); m_RequestedRegion.SetSize(0, m_Dimensions[0]); m_RequestedRegion.SetSize(1, m_Dimensions[1]); m_RequestedRegion.SetSize(2, m_Dimensions[2]); m_RequestedRegion.SetSize(3, m_Dimensions[3]); m_RequestedRegion.SetSize(4, 1); m_RequestedRegionInitialized=true; GetSource()->Update(); // did it work? if(IsChannelSet(n)) //yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetChannelData(n,data,importMemoryManagement); else return NULL; } else { ImageDataItemPointer item = AllocateChannelData(n,data,importMemoryManagement); item->SetComplete(true); return item; } } bool mitk::Image::IsSliceSet(int s, int t, int n) const { if(IsValidSlice(s,t,n)==false) return false; if(m_Slices[GetSliceIndex(s,t,n)].GetPointer()!=NULL) return true; ImageDataItemPointer ch, vol; vol=m_Volumes[GetVolumeIndex(t,n)]; if((vol.GetPointer()!=NULL) && (vol->IsComplete())) return true; ch=m_Channels[n]; if((ch.GetPointer()!=NULL) && (ch->IsComplete())) return true; return false; } bool mitk::Image::IsVolumeSet(int t, int n) const { if(IsValidVolume(t,n)==false) return false; ImageDataItemPointer ch, vol; // volume directly available? vol=m_Volumes[GetVolumeIndex(t,n)]; if((vol.GetPointer()!=NULL) && (vol->IsComplete())) return true; // is volume available as part of a channel that is available? ch=m_Channels[n]; if((ch.GetPointer()!=NULL) && (ch->IsComplete())) return true; // let's see if all slices of the volume are set, so that we can (could) combine them to a volume unsigned int s; for(s=0;sIsComplete())) return true; // let's see if all volumes are set, so that we can (could) combine them to a channel unsigned int t; for(t=0;t(data), s, t, n, CopyMemory); } bool mitk::Image::SetVolume(const void *data, int t, int n) { // const_cast is no risk for ImportMemoryManagementType == CopyMemory return SetImportVolume(const_cast(data), t, n, CopyMemory); } bool mitk::Image::SetChannel(const void *data, int n) { // const_cast is no risk for ImportMemoryManagementType == CopyMemory return SetImportChannel(const_cast(data), n, CopyMemory); } bool mitk::Image::SetImportSlice(void *data, int s, int t, int n, ImportMemoryManagementType importMemoryManagement) { if(IsValidSlice(s,t,n)==false) return false; ImageDataItemPointer sl; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); if(IsSliceSet(s,t,n)) { sl=GetSliceData(s,t,n,data,importMemoryManagement); if(sl->GetManageMemory()==false) { sl=AllocateSliceData(s,t,n,data,importMemoryManagement); if(sl.GetPointer()==NULL) return false; } if ( sl->GetData() != data ) std::memcpy(sl->GetData(), data, m_OffsetTable[2]*(ptypeSize)); sl->Modified(); //we have changed the data: call Modified()! Modified(); } else { sl=AllocateSliceData(s,t,n,data,importMemoryManagement); if(sl.GetPointer()==NULL) return false; if ( sl->GetData() != data ) std::memcpy(sl->GetData(), data, m_OffsetTable[2]*(ptypeSize)); //we just added a missing slice, which is not regarded as modification. //Therefore, we do not call Modified()! } return true; } bool mitk::Image::SetImportVolume(void *data, int t, int n, ImportMemoryManagementType importMemoryManagement) { if(IsValidVolume(t,n)==false) return false; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ImageDataItemPointer vol; if(IsVolumeSet(t,n)) { vol=GetVolumeData(t,n,data,importMemoryManagement); if(vol->GetManageMemory()==false) { vol=AllocateVolumeData(t,n,data,importMemoryManagement); if(vol.GetPointer()==NULL) return false; } if ( vol->GetData() != data ) std::memcpy(vol->GetData(), data, m_OffsetTable[3]*(ptypeSize)); vol->Modified(); vol->SetComplete(true); //we have changed the data: call Modified()! Modified(); } else { vol=AllocateVolumeData(t,n,data,importMemoryManagement); if(vol.GetPointer()==NULL) return false; if ( vol->GetData() != data ) { std::memcpy(vol->GetData(), data, m_OffsetTable[3]*(ptypeSize)); } vol->SetComplete(true); this->m_ImageDescriptor->GetChannelDescriptor(n).SetData( vol->GetData() ); //we just added a missing Volume, which is not regarded as modification. //Therefore, we do not call Modified()! } return true; } bool mitk::Image::SetImportChannel(void *data, int n, ImportMemoryManagementType importMemoryManagement) { if(IsValidChannel(n)==false) return false; // channel descriptor const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ImageDataItemPointer ch; if(IsChannelSet(n)) { ch=GetChannelData(n,data,importMemoryManagement); if(ch->GetManageMemory()==false) { ch=AllocateChannelData(n,data,importMemoryManagement); if(ch.GetPointer()==NULL) return false; } if ( ch->GetData() != data ) std::memcpy(ch->GetData(), data, m_OffsetTable[4]*(ptypeSize)); ch->Modified(); ch->SetComplete(true); //we have changed the data: call Modified()! Modified(); } else { ch=AllocateChannelData(n,data,importMemoryManagement); if(ch.GetPointer()==NULL) return false; if ( ch->GetData() != data ) std::memcpy(ch->GetData(), data, m_OffsetTable[4]*(ptypeSize)); ch->SetComplete(true); this->m_ImageDescriptor->GetChannelDescriptor(n).SetData( ch->GetData() ); //we just added a missing Channel, which is not regarded as modification. //Therefore, we do not call Modified()! } return true; } void mitk::Image::Initialize() { ImageDataItemPointerArray::iterator it, end; for( it=m_Slices.begin(), end=m_Slices.end(); it!=end; ++it ) { (*it)=NULL; } for( it=m_Volumes.begin(), end=m_Volumes.end(); it!=end; ++it ) { (*it)=NULL; } for( it=m_Channels.begin(), end=m_Channels.end(); it!=end; ++it ) { (*it)=NULL; } m_CompleteData = NULL; if( m_ImageStatistics == NULL) { m_ImageStatistics = new mitk::ImageStatisticsHolder( this ); } SetRequestedRegionToLargestPossibleRegion(); } void mitk::Image::Initialize(const mitk::ImageDescriptor::Pointer inDesc) { // store the descriptor this->m_ImageDescriptor = inDesc; // initialize image this->Initialize( inDesc->GetChannelDescriptor(0).GetPixelType(), inDesc->GetNumberOfDimensions(), inDesc->GetDimensions(), 1 ); } void mitk::Image::Initialize(const mitk::PixelType& type, unsigned int dimension, const unsigned int *dimensions, unsigned int channels) { Clear(); m_Dimension=dimension; if(!dimensions) itkExceptionMacro(<< "invalid zero dimension image"); unsigned int i; for(i=0;im_ImageDescriptor = mitk::ImageDescriptor::New(); this->m_ImageDescriptor->Initialize( this->m_Dimensions, this->m_Dimension ); for(i=0;i<4;++i) { m_LargestPossibleRegion.SetIndex(i, 0); m_LargestPossibleRegion.SetSize (i, m_Dimensions[i]); } m_LargestPossibleRegion.SetIndex(i, 0); m_LargestPossibleRegion.SetSize(i, channels); if(m_LargestPossibleRegion.GetNumberOfPixels()==0) { delete [] m_Dimensions; m_Dimensions = NULL; return; } for( unsigned int i=0u; im_ImageDescriptor->AddNewChannel( type ); } PlaneGeometry::Pointer planegeometry = PlaneGeometry::New(); planegeometry->InitializeStandardPlane(m_Dimensions[0], m_Dimensions[1]); SlicedGeometry3D::Pointer slicedGeometry = SlicedGeometry3D::New(); slicedGeometry->InitializeEvenlySpaced(planegeometry, m_Dimensions[2]); if(dimension>=4) { TimeBounds timebounds; timebounds[0] = 0.0; timebounds[1] = 1.0; slicedGeometry->SetTimeBounds(timebounds); } TimeSlicedGeometry::Pointer timeSliceGeometry = TimeSlicedGeometry::New(); timeSliceGeometry->InitializeEvenlyTimed(slicedGeometry, m_Dimensions[3]); timeSliceGeometry->ImageGeometryOn(); SetGeometry(timeSliceGeometry); ImageDataItemPointer dnull=NULL; m_Channels.assign(GetNumberOfChannels(), dnull); m_Volumes.assign(GetNumberOfChannels()*m_Dimensions[3], dnull); m_Slices.assign(GetNumberOfChannels()*m_Dimensions[3]*m_Dimensions[2], dnull); ComputeOffsetTable(); Initialize(); m_Initialized = true; } void mitk::Image::Initialize(const mitk::PixelType& type, const mitk::Geometry3D& geometry, unsigned int channels, int tDim ) { unsigned int dimensions[5]; dimensions[0] = (unsigned int)(geometry.GetExtent(0)+0.5); dimensions[1] = (unsigned int)(geometry.GetExtent(1)+0.5); dimensions[2] = (unsigned int)(geometry.GetExtent(2)+0.5); dimensions[3] = 0; dimensions[4] = 0; unsigned int dimension = 2; if ( dimensions[2] > 1 ) dimension = 3; if ( tDim > 0) { dimensions[3] = tDim; } else { const mitk::TimeSlicedGeometry* timeGeometry = dynamic_cast(&geometry); if ( timeGeometry != NULL ) { dimensions[3] = timeGeometry->GetTimeSteps(); } } if ( dimensions[3] > 1 ) dimension = 4; Initialize( type, dimension, dimensions, channels ); SetGeometry(static_cast(geometry.Clone().GetPointer())); mitk::BoundingBox::BoundsArrayType bounds = geometry.GetBoundingBox()->GetBounds(); if( (bounds[0] != 0.0) || (bounds[2] != 0.0) || (bounds[4] != 0.0) ) { SlicedGeometry3D* slicedGeometry = GetSlicedGeometry(0); mitk::Point3D origin; origin.Fill(0.0); slicedGeometry->IndexToWorld(origin, origin); bounds[1]-=bounds[0]; bounds[3]-=bounds[2]; bounds[5]-=bounds[4]; bounds[0] = 0.0; bounds[2] = 0.0; bounds[4] = 0.0; -this->m_ImageDescriptor->Initialize( this->m_Dimensions, this->m_Dimension ); + this->m_ImageDescriptor->Initialize( this->m_Dimensions, this->m_Dimension ); slicedGeometry->SetBounds(bounds); slicedGeometry->GetIndexToWorldTransform()->SetOffset(origin.GetVnlVector().data_block()); GetTimeSlicedGeometry()->InitializeEvenlyTimed(slicedGeometry, m_Dimensions[3]); } } void mitk::Image::Initialize(const mitk::PixelType& type, int sDim, const mitk::Geometry2D& geometry2d, bool flipped, unsigned int channels, int tDim ) { SlicedGeometry3D::Pointer slicedGeometry = SlicedGeometry3D::New(); slicedGeometry->InitializeEvenlySpaced(static_cast(geometry2d.Clone().GetPointer()), sDim, flipped); Initialize(type, *slicedGeometry, channels, tDim); } void mitk::Image::Initialize(const mitk::Image* image) { Initialize(image->GetPixelType(), *image->GetTimeSlicedGeometry()); } void mitk::Image::Initialize(vtkImageData* vtkimagedata, int channels, int tDim, int sDim, int pDim) { if(vtkimagedata==NULL) return; m_Dimension=vtkimagedata->GetDataDimension(); unsigned int i, *tmpDimensions=new unsigned int[m_Dimension>4?m_Dimension:4]; for(i=0;iGetDimensions()[i]; if(m_Dimension<4) { unsigned int *p; for(i=0,p=tmpDimensions+m_Dimension;i<4-m_Dimension;++i, ++p) *p=1; } if(pDim>=0) { - tmpDimensions[1]=pDim; - if(m_Dimension < 2) - m_Dimension = 2; + tmpDimensions[1]=pDim; + if(m_Dimension < 2) + m_Dimension = 2; } if(sDim>=0) { tmpDimensions[2]=sDim; if(m_Dimension < 3) m_Dimension = 3; } if(tDim>=0) { tmpDimensions[3]=tDim; if(m_Dimension < 4) m_Dimension = 4; } switch ( vtkimagedata->GetScalarType() ) { case VTK_BIT: case VTK_CHAR: //pixelType.Initialize(typeid(char), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_UNSIGNED_CHAR: //pixelType.Initialize(typeid(unsigned char), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_SHORT: //pixelType.Initialize(typeid(short), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_UNSIGNED_SHORT: //pixelType.Initialize(typeid(unsigned short), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_INT: //pixelType.Initialize(typeid(int), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_UNSIGNED_INT: //pixelType.Initialize(typeid(unsigned int), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_LONG: //pixelType.Initialize(typeid(long), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_UNSIGNED_LONG: //pixelType.Initialize(typeid(unsigned long), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_FLOAT: //pixelType.Initialize(typeid(float), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_DOUBLE: //pixelType.Initialize(typeid(double), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; default: break; } /* Initialize(pixelType, m_Dimension, tmpDimensions, channels); */ const double *spacinglist = vtkimagedata->GetSpacing(); Vector3D spacing; FillVector3D(spacing, spacinglist[0], 1.0, 1.0); if(m_Dimension>=2) spacing[1]=spacinglist[1]; if(m_Dimension>=3) spacing[2]=spacinglist[2]; // access origin of vtkImage - Point3D origin; + Point3D origin; vtkFloatingPointType vtkorigin[3]; vtkimagedata->GetOrigin(vtkorigin); FillVector3D(origin, vtkorigin[0], 0.0, 0.0); if(m_Dimension>=2) origin[1]=vtkorigin[1]; if(m_Dimension>=3) origin[2]=vtkorigin[2]; SlicedGeometry3D* slicedGeometry = GetSlicedGeometry(0); // re-initialize PlaneGeometry with origin and direction PlaneGeometry* planeGeometry = static_cast(slicedGeometry->GetGeometry2D(0)); planeGeometry->SetOrigin(origin); // re-initialize SlicedGeometry3D slicedGeometry->SetOrigin(origin); slicedGeometry->SetSpacing(spacing); GetTimeSlicedGeometry()->InitializeEvenlyTimed(slicedGeometry, m_Dimensions[3]); delete [] tmpDimensions; } bool mitk::Image::IsValidSlice(int s, int t, int n) const { if(m_Initialized) return ((s>=0) && (s<(int)m_Dimensions[2]) && (t>=0) && (t< (int) m_Dimensions[3]) && (n>=0) && (n< (int)GetNumberOfChannels())); else return false; } bool mitk::Image::IsValidVolume(int t, int n) const { if(m_Initialized) return IsValidSlice(0, t, n); else return false; } bool mitk::Image::IsValidChannel(int n) const { if(m_Initialized) return IsValidSlice(0, 0, n); else return false; } void mitk::Image::ComputeOffsetTable() { if(m_OffsetTable!=NULL) delete [] m_OffsetTable; m_OffsetTable=new size_t[m_Dimension>4 ? m_Dimension+1 : 4+1]; unsigned int i; size_t num=1; m_OffsetTable[0] = 1; for (i=0; i < m_Dimension; ++i) { num *= m_Dimensions[i]; m_OffsetTable[i+1] = num; } for (;i < 4; ++i) m_OffsetTable[i+1] = num; } bool mitk::Image::IsValidTimeStep(int t) const { return ( ( m_Dimension >= 4 && t <= (int)m_Dimensions[3] && t > 0 ) || (t == 0) ); } void mitk::Image::Expand(unsigned int timeSteps) { if(timeSteps < 1) itkExceptionMacro(<< "Invalid timestep in Image!"); Superclass::Expand(timeSteps); } int mitk::Image::GetSliceIndex(int s, int t, int n) const { if(IsValidSlice(s,t,n)==false) return false; return ((size_t)s)+((size_t) t)*m_Dimensions[2]+((size_t) n)*m_Dimensions[3]*m_Dimensions[2]; //?? } int mitk::Image::GetVolumeIndex(int t, int n) const { if(IsValidVolume(t,n)==false) return false; return ((size_t)t)+((size_t) n)*m_Dimensions[3]; //?? } mitk::Image::ImageDataItemPointer mitk::Image::AllocateSliceData(int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) { int pos; pos=GetSliceIndex(s,t,n); const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // is slice available as part of a volume that is available? ImageDataItemPointer sl, ch, vol; vol=m_Volumes[GetVolumeIndex(t,n)]; if(vol.GetPointer()!=NULL) { sl=new ImageDataItem(*vol, m_ImageDescriptor, 2, data, importMemoryManagement == ManageMemory, ((size_t) s)*m_OffsetTable[2]*(ptypeSize)); sl->SetComplete(true); return m_Slices[pos]=sl; } // is slice available as part of a channel that is available? ch=m_Channels[n]; if(ch.GetPointer()!=NULL) { sl=new ImageDataItem(*ch, m_ImageDescriptor, 2, data, importMemoryManagement == ManageMemory, (((size_t) s)*m_OffsetTable[2]+((size_t) t)*m_OffsetTable[3])*(ptypeSize)); sl->SetComplete(true); return m_Slices[pos]=sl; } // allocate new volume (instead of a single slice to keep data together!) m_Volumes[GetVolumeIndex(t,n)]=vol=AllocateVolumeData(t,n,NULL,importMemoryManagement); sl=new ImageDataItem(*vol, m_ImageDescriptor, 2, data, importMemoryManagement == ManageMemory, ((size_t) s)*m_OffsetTable[2]*(ptypeSize)); sl->SetComplete(true); return m_Slices[pos]=sl; ////ALTERNATIVE: //// allocate new slice //sl=new ImageDataItem(*m_PixelType, 2, m_Dimensions); //m_Slices[pos]=sl; //return vol; } mitk::Image::ImageDataItemPointer mitk::Image::AllocateVolumeData(int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) { int pos; pos=GetVolumeIndex(t,n); const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // is volume available as part of a channel that is available? ImageDataItemPointer ch, vol; ch=m_Channels[n]; if(ch.GetPointer()!=NULL) { vol=new ImageDataItem(*ch, m_ImageDescriptor, 3, data,importMemoryManagement == ManageMemory, (((size_t) t)*m_OffsetTable[3])*(ptypeSize)); return m_Volumes[pos]=vol; } mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(n); // allocate new volume if(importMemoryManagement == CopyMemory) { vol=new ImageDataItem( chPixelType, 3, m_Dimensions, NULL, true); if(data != NULL) std::memcpy(vol->GetData(), data, m_OffsetTable[3]*(ptypeSize)); } else { vol=new ImageDataItem( chPixelType, 3, m_Dimensions, data, importMemoryManagement == ManageMemory); } m_Volumes[pos]=vol; return vol; } mitk::Image::ImageDataItemPointer mitk::Image::AllocateChannelData(int n, void *data, ImportMemoryManagementType importMemoryManagement) { ImageDataItemPointer ch; // allocate new channel if(importMemoryManagement == CopyMemory) { const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ch=new ImageDataItem(this->m_ImageDescriptor, NULL, true); if(data != NULL) std::memcpy(ch->GetData(), data, m_OffsetTable[4]*(ptypeSize)); } else { ch=new ImageDataItem(this->m_ImageDescriptor, data, importMemoryManagement == ManageMemory); } m_Channels[n]=ch; return ch; } unsigned int* mitk::Image::GetDimensions() const { return m_Dimensions; } void mitk::Image::Clear() { Superclass::Clear(); delete [] m_Dimensions; m_Dimensions = NULL; } void mitk::Image::SetGeometry(Geometry3D* aGeometry3D) { // Please be aware of the 0.5 offset/pixel-center issue! See Geometry documentation for further information if(aGeometry3D->GetImageGeometry()==false) { MITK_INFO << "WARNING: Applied a non-image geometry onto an image. Please be SURE that this geometry is pixel-center-based! If it is not, you need to call Geometry3D->ChangeImageGeometryConsideringOriginOffset(true) before calling image->setGeometry(..)\n"; } Superclass::SetGeometry(aGeometry3D); GetTimeSlicedGeometry()->ImageGeometryOn(); } void mitk::Image::PrintSelf(std::ostream& os, itk::Indent indent) const { unsigned char i; if(m_Initialized) { os << indent << " Dimension: " << m_Dimension << std::endl; os << indent << " Dimensions: "; for(i=0; i < m_Dimension; ++i) os << GetDimension(i) << " "; os << std::endl; for(unsigned int ch=0; ch < this->m_ImageDescriptor->GetNumberOfChannels(); ch++) { mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(ch); os << indent << " Channel: " << this->m_ImageDescriptor->GetChannelName(ch) << std::endl; os << indent << " PixelType: " << chPixelType.GetPixelTypeAsString() << std::endl; os << indent << " BytesPerElement: " << chPixelType.GetSize() << std::endl; os << indent << " ComponentType: " << chPixelType.GetComponentTypeAsString() << std::endl; os << indent << " NumberOfComponents: " << chPixelType.GetNumberOfComponents() << std::endl; os << indent << " BitsPerComponent: " << chPixelType.GetBitsPerComponent() << std::endl; } } else { os << indent << " Image not initialized: m_Initialized: false" << std::endl; } Superclass::PrintSelf(os,indent); } bool mitk::Image::IsRotated() const { const mitk::Geometry3D* geo = this->GetGeometry(); bool ret = false; if(geo) { const vnl_matrix_fixed & mx = geo->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix(); float ref = 0; for(short k = 0; k < 3; ++k) ref += mx[k][k]; ref/=1000; // Arbitrary value; if a non-diagonal (nd) element is bigger then this, matrix is considered nd. for(short i = 0; i < 3; ++i) { for(short j = 0; j < 3; ++j) { if(i != j) { if(std::abs(mx[i][j]) > ref) // matrix is nd ret = true; } } } } return ret; } -#include "mitkImageStatisticsHolder.h" - -//##Documentation mitk::ScalarType mitk::Image::GetScalarValueMin(int t) const { return m_ImageStatistics->GetScalarValueMin(t); } -//##Documentation //## \brief Get the maximum for scalar images mitk::ScalarType mitk::Image::GetScalarValueMax(int t) const { return m_ImageStatistics->GetScalarValueMax(t); } -//##Documentation //## \brief Get the second smallest value for scalar images mitk::ScalarType mitk::Image::GetScalarValue2ndMin(int t) const { return m_ImageStatistics->GetScalarValue2ndMin(t); } mitk::ScalarType mitk::Image::GetScalarValueMinNoRecompute( unsigned int t ) const { return m_ImageStatistics->GetScalarValueMinNoRecompute(t); } mitk::ScalarType mitk::Image::GetScalarValue2ndMinNoRecompute( unsigned int t ) const { return m_ImageStatistics->GetScalarValue2ndMinNoRecompute(t); } mitk::ScalarType mitk::Image::GetScalarValue2ndMax(int t) const { return m_ImageStatistics->GetScalarValue2ndMax(t); } mitk::ScalarType mitk::Image::GetScalarValueMaxNoRecompute( unsigned int t) const { return m_ImageStatistics->GetScalarValueMaxNoRecompute(t); } mitk::ScalarType mitk::Image::GetScalarValue2ndMaxNoRecompute( unsigned int t ) const { return m_ImageStatistics->GetScalarValue2ndMaxNoRecompute(t); } mitk::ScalarType mitk::Image::GetCountOfMinValuedVoxels(int t ) const { return m_ImageStatistics->GetCountOfMinValuedVoxels(t); } mitk::ScalarType mitk::Image::GetCountOfMaxValuedVoxels(int t) const { return m_ImageStatistics->GetCountOfMaxValuedVoxels(t); } unsigned int mitk::Image::GetCountOfMaxValuedVoxelsNoRecompute( unsigned int t ) const { return m_ImageStatistics->GetCountOfMaxValuedVoxelsNoRecompute(t); } unsigned int mitk::Image::GetCountOfMinValuedVoxelsNoRecompute( unsigned int t ) const { return m_ImageStatistics->GetCountOfMinValuedVoxelsNoRecompute(t); } +bool mitk::Equal(const mitk::Image* rightHandSide, const mitk::Image* leftHandSide, ScalarType eps, bool verbose) +{ + bool returnValue = true; + if(( rightHandSide == NULL ) ) + { + if(verbose) + MITK_INFO << "[( Image )] rightHandSide is NULL."; + return false; + } + + if(( leftHandSide == NULL ) ) + { + if(verbose) + MITK_INFO << "[( Image )] leftHandSide is NULL."; + return false; + } + + // Dimensionality + if(( rightHandSide->GetDimension() != leftHandSide->GetDimension() ) ) + { + if(verbose) + { + MITK_INFO << "[( Image )] Dimensionality differs."; + MITK_INFO << "rightHandSide is " << rightHandSide->GetDimension() + << "leftHandSide is " << leftHandSide->GetDimension(); + } + returnValue = false; + } + + // Pair-wise dimension (size) comparison + unsigned int minDimensionality = std::min(rightHandSide->GetDimension(),leftHandSide->GetDimension()); + for( unsigned int i=0; i< minDimensionality; ++i) + { + if(( rightHandSide->GetDimension(i) != leftHandSide->GetDimension(i) ) ) + { + returnValue = false; + if(verbose) + { + MITK_INFO << "[( Image )] dimension differs."; + MITK_INFO << "rightHandSide->GetDimension("<GetDimension(i) + << "leftHandSide->GetDimension("<GetDimension(i); + } + } + } + + // Pixeltype + mitk::PixelType pixelTypeRightHandSide = rightHandSide->GetPixelType(); + mitk::PixelType pixelTypeLeftHandSide = leftHandSide->GetPixelType(); + if(( !( pixelTypeRightHandSide == pixelTypeLeftHandSide ) ) ) + { + if(verbose) + { + MITK_INFO << "[( Image )] PixelType differs."; + MITK_INFO << "rightHandSide is " << pixelTypeRightHandSide.GetTypeAsString() + << "leftHandSide is " << pixelTypeLeftHandSide.GetTypeAsString(); + } + returnValue = false; + } + + // Geometries + if(( !mitk::Equal( rightHandSide->GetGeometry(), + leftHandSide->GetGeometry(), eps, verbose) ) ) + { + if(verbose) + { + MITK_INFO << "[( Image )] Geometries differ."; + } + returnValue = false; + } + + // Pixel values - default mode [ 0 threshold in difference ] + // compare only if all previous checks were successfull, otherwise the ITK filter will throw an exception + if( returnValue ) + { + mitk::CompareImageFilter::Pointer compareFilter = mitk::CompareImageFilter::New(); + compareFilter->SetInput(0, rightHandSide); + compareFilter->SetInput(1, leftHandSide); + compareFilter->Update(); + + if(( !compareFilter->GetResult() ) ) + { + returnValue = false; + if(verbose) + { + MITK_INFO << "[(Image)] Pixel values differ: "; + } + compareFilter->GetCompareResults().PrintSelf(); + } + } + return returnValue; +} diff --git a/Core/Code/DataManagement/mitkImage.h b/Core/Code/DataManagement/mitkImage.h index 3693b593f7..65ba71500f 100644 --- a/Core/Code/DataManagement/mitkImage.h +++ b/Core/Code/DataManagement/mitkImage.h @@ -1,671 +1,690 @@ /*=================================================================== 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 MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2 #define MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2 #include #include "mitkSlicedData.h" #include "mitkBaseData.h" #include "mitkLevelWindow.h" #include "mitkPlaneGeometry.h" #include "mitkImageDataItem.h" #include "mitkImageDescriptor.h" #include "mitkImageAccessorBase.h" #include "mitkImageVtkAccessor.h" #ifndef __itkHistogram_h #include #endif class vtkImageData; namespace mitk { class SubImageSelector; class ImageTimeSelector; class ImageStatisticsHolder; //##Documentation //## @brief Image class for storing images //## //## Can be asked for header information, the data vector, //## the mitkIpPicDescriptor struct or vtkImageData objects. If not the complete //## data is required, the appropriate SubImageSelector class should be used //## for access. //## Image organizes sets of slices (s x 2D), volumes (t x 3D) and channels (n //## x ND). Channels are for different kind of data, e.g., morphology in //## channel 0, velocities in channel 1. All channels must have the same Geometry! In //## particular, the dimensions of all channels are the same, only the pixel-type //## may differ between channels. //## //## For importing ITK images use of mitk::ITKImageImport is recommended, see //## \ref Adaptor. //## //## For ITK v3.8 and older: Converting coordinates from the ITK physical //## coordinate system (which does not support rotated images) to the MITK world //## coordinate system should be performed via the Geometry3D of the Image, see //## Geometry3D::WorldToItkPhysicalPoint. //## @ingroup Data class MITK_CORE_EXPORT Image : public SlicedData { friend class SubImageSelector; friend class ImageAccessorBase; friend class ImageVtkAccessor; friend class ImageReadAccessor; friend class ImageWriteAccessor; public: mitkClassMacro(Image, SlicedData); itkNewMacro(Self); mitkCloneMacro(Image); /** Smart Pointer type to a ImageDataItem. */ typedef itk::SmartPointer ImageDataItemPointer; typedef itk::Statistics::Histogram HistogramType; typedef mitk::ImageStatisticsHolder* StatisticsHolderPointer; //## @param ImportMemoryManagementType This parameter is evaluated when setting new data to an image. //## The different options are: //## CopyMemory: Data to be set is copied and assigned to a new memory block. Data memory block will be freed on deletion of mitk::Image. //## MamageMemory: Data to be set will be referenced, and Data memory block will be freed on deletion of mitk::Image. //## Reference Memory: Data to be set will be referenced, but Data memory block will not be freed on deletion of mitk::Image. //## DontManageMemory = ReferenceMemory. enum ImportMemoryManagementType { CopyMemory, ManageMemory, ReferenceMemory, DontManageMemory = ReferenceMemory }; //##Documentation //## @brief Vector container of SmartPointers to ImageDataItems; //## Class is only for internal usage to allow convenient access to all slices over iterators; //## See documentation of ImageDataItem for details. typedef std::vector ImageDataItemPointerArray; public: //##Documentation //## @brief Returns the PixelType of channel @a n. const mitk::PixelType GetPixelType(int n = 0) const; //##Documentation //## @brief Get dimension of the image //## unsigned int GetDimension() const; //##Documentation //## @brief Get the size of dimension @a i (e.g., i=0 results in the number of pixels in x-direction). //## //## @sa GetDimensions() unsigned int GetDimension(int i) const; /** @brief Get the data vector of the complete image, i.e., of all channels linked together. If you only want to access a slice, volume at a specific time or single channel use one of the SubImageSelector classes. \deprecatedSince{2012_09} Please use image accessors instead: See Doxygen/Related-Pages/Concepts/Image. This method can be replaced by ImageWriteAccessor::GetData() or ImageReadAccessor::GetData() */ DEPRECATED(virtual void* GetData()); public: /** @brief Get the pixel value at one specific index position. The pixel type is always being converted to double. \deprecatedSince{2012_09} Please use image accessors instead: See Doxygen/Related-Pages/Concepts/Image. This method can be replaced by a method from ImagePixelWriteAccessor or ImagePixelReadAccessor */ DEPRECATED(double GetPixelValueByIndex(const mitk::Index3D& position, unsigned int timestep = 0)); /** @brief Get the pixel value at one specific world position. The pixel type is always being converted to double. \deprecatedSince{2012_09} Please use image accessors instead: See Doxygen/Related-Pages/Concepts/Image. This method can be replaced by a method from ImagePixelWriteAccessor or ImagePixelReadAccessor */ DEPRECATED(double GetPixelValueByWorldCoordinate(const mitk::Point3D& position, unsigned int timestep = 0)); //##Documentation //## @brief Get a volume at a specific time @a t of channel @a n as a vtkImageData. virtual ImageVtkAccessor* GetVtkImageData(int t = 0, int n = 0); //##Documentation //## @brief Get the complete image, i.e., all channels linked together, as a @a mitkIpPicDescriptor. //## //## If you only want to access a slice, volume at a specific time or single channel //## use one of the SubImageSelector classes. //virtual mitkIpPicDescriptor* GetPic(); //##Documentation //## @brief Check whether slice @a s at time @a t in channel @a n is set virtual bool IsSliceSet(int s = 0, int t = 0, int n = 0) const; //##Documentation //## @brief Check whether volume at time @a t in channel @a n is set virtual bool IsVolumeSet(int t = 0, int n = 0) const; //##Documentation //## @brief Check whether the channel @a n is set virtual bool IsChannelSet(int n = 0) const; //##Documentation //## @brief Set @a data as slice @a s at time @a t in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a slice (at least is not smaller than a slice), since there is //## no chance to check this. //## //## The data is copied to an array managed by the image. If the image shall //## reference the data, use SetImportSlice with ImportMemoryManagementType //## set to ReferenceMemory. For importing ITK images use of mitk:: //## ITKImageImport is recommended. //## @sa SetPicSlice, SetImportSlice, SetImportVolume virtual bool SetSlice(const void *data, int s = 0, int t = 0, int n = 0); //##Documentation //## @brief Set @a data as volume at time @a t in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a volume (at least is not smaller than a volume), since there is //## no chance to check this. //## //## The data is copied to an array managed by the image. If the image shall //## reference the data, use SetImportVolume with ImportMemoryManagementType //## set to ReferenceMemory. For importing ITK images use of mitk:: //## ITKImageImport is recommended. //## @sa SetPicVolume, SetImportVolume virtual bool SetVolume(const void *data, int t = 0, int n = 0); //##Documentation //## @brief Set @a data in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a channel (at least is not smaller than a channel), since there is //## no chance to check this. //## //## The data is copied to an array managed by the image. If the image shall //## reference the data, use SetImportChannel with ImportMemoryManagementType //## set to ReferenceMemory. For importing ITK images use of mitk:: //## ITKImageImport is recommended. //## @sa SetPicChannel, SetImportChannel virtual bool SetChannel(const void *data, int n = 0); //##Documentation //## @brief Set @a data as slice @a s at time @a t in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a slice (at least is not smaller than a slice), since there is //## no chance to check this. //## //## The data is managed according to the parameter \a importMemoryManagement. //## @sa SetPicSlice virtual bool SetImportSlice(void *data, int s = 0, int t = 0, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory ); //##Documentation //## @brief Set @a data as volume at time @a t in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a volume (at least is not smaller than a volume), since there is //## no chance to check this. //## //## The data is managed according to the parameter \a importMemoryManagement. //## @sa SetPicVolume virtual bool SetImportVolume(void *data, int t = 0, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory ); //##Documentation //## @brief Set @a data in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a channel (at least is not smaller than a channel), since there is //## no chance to check this. //## //## The data is managed according to the parameter \a importMemoryManagement. //## @sa SetPicChannel virtual bool SetImportChannel(void *data, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory ); //##Documentation //## initialize new (or re-initialize) image information //## @warning Initialize() by pic assumes a plane, evenly spaced geometry starting at (0,0,0). virtual void Initialize(const mitk::PixelType& type, unsigned int dimension, const unsigned int *dimensions, unsigned int channels = 1); //##Documentation //## initialize new (or re-initialize) image information by a Geometry3D //## //## @param tDim override time dimension (@a n[3]) if @a geometry is a TimeSlicedGeometry (if >0) virtual void Initialize(const mitk::PixelType& type, const mitk::Geometry3D& geometry, unsigned int channels = 1, int tDim=-1); //##Documentation //## initialize new (or re-initialize) image information by a Geometry2D and number of slices //## //## Initializes the bounding box according to the width/height of the //## Geometry2D and @a sDim via SlicedGeometry3D::InitializeEvenlySpaced. //## The spacing is calculated from the Geometry2D. //## @param tDim override time dimension (@a n[3]) if @a geometry is a TimeSlicedGeometry (if >0) //## \sa SlicedGeometry3D::InitializeEvenlySpaced virtual void Initialize(const mitk::PixelType& type, int sDim, const mitk::Geometry2D& geometry2d, bool flipped = false, unsigned int channels = 1, int tDim=-1); //##Documentation //## initialize new (or re-initialize) image information by another //## mitk-image. //## Only the header is used, not the data vector! //## virtual void Initialize(const mitk::Image* image); virtual void Initialize(const mitk::ImageDescriptor::Pointer inDesc); //##Documentation //## initialize new (or re-initialize) image information by @a pic. //## Dimensions and @a Geometry3D /@a Geometry2D are set according //## to the tags in @a pic. //## Only the header is used, not the data vector! Use SetPicVolume(pic) //## to set the data vector. //## //## @param tDim override time dimension (@a n[3]) in @a pic (if >0) //## @param sDim override z-space dimension (@a n[2]) in @a pic (if >0) //## @warning Initialize() by pic assumes a plane, evenly spaced geometry starting at (0,0,0). //virtual void Initialize(const mitkIpPicDescriptor* pic, int channels = 1, int tDim = -1, int sDim = -1); //##Documentation //## initialize new (or re-initialize) image information by @a vtkimagedata, //## a vtk-image. //## Only the header is used, not the data vector! Use //## SetVolume(vtkimage->GetScalarPointer()) to set the data vector. //## //## @param tDim override time dimension in @a vtkimagedata (if >0 and <) //## @param sDim override z-space dimension in @a vtkimagedata (if >0 and <) //## @param pDim override y-space dimension in @a vtkimagedata (if >0 and <) virtual void Initialize(vtkImageData* vtkimagedata, int channels = 1, int tDim = -1, int sDim = -1, int pDim = -1); //##Documentation //## initialize new (or re-initialize) image information by @a itkimage, //## a templated itk-image. //## Only the header is used, not the data vector! Use //## SetVolume(itkimage->GetBufferPointer()) to set the data vector. //## //## @param tDim override time dimension in @a itkimage (if >0 and <) //## @param sDim override z-space dimension in @a itkimage (if >0 and <) template void InitializeByItk(const itkImageType* itkimage, int channels = 1, int tDim = -1, int sDim=-1) { if(itkimage==NULL) return; MITK_DEBUG << "Initializing MITK image from ITK image."; // build array with dimensions in each direction with at least 4 entries m_Dimension=itkimage->GetImageDimension(); unsigned int i, *tmpDimensions=new unsigned int[m_Dimension>4?m_Dimension:4]; for(i=0;iGetLargestPossibleRegion().GetSize().GetSize()[i]; if(m_Dimension<4) { unsigned int *p; for(i=0,p=tmpDimensions+m_Dimension;i<4-m_Dimension;++i, ++p) *p=1; } // overwrite number of slices if sDim is set if((m_Dimension>2) && (sDim>=0)) tmpDimensions[2]=sDim; // overwrite number of time points if tDim is set if((m_Dimension>3) && (tDim>=0)) tmpDimensions[3]=tDim; // rough initialization of Image // mitk::PixelType importType = ImportItkPixelType( itkimage::PixelType ); Initialize(MakePixelType(), m_Dimension, tmpDimensions, channels); const typename itkImageType::SpacingType & itkspacing = itkimage->GetSpacing(); MITK_DEBUG << "ITK spacing " << itkspacing; // access spacing of itk::Image Vector3D spacing; FillVector3D(spacing, itkspacing[0], 1.0, 1.0); if(m_Dimension >= 2) spacing[1]=itkspacing[1]; if(m_Dimension >= 3) spacing[2]=itkspacing[2]; // access origin of itk::Image Point3D origin; const typename itkImageType::PointType & itkorigin = itkimage->GetOrigin(); MITK_DEBUG << "ITK origin " << itkorigin; FillVector3D(origin, itkorigin[0], 0.0, 0.0); if(m_Dimension>=2) origin[1]=itkorigin[1]; if(m_Dimension>=3) origin[2]=itkorigin[2]; // access direction of itk::Imagm_PixelType = new mitk::PixelType(type);e and include spacing const typename itkImageType::DirectionType & itkdirection = itkimage->GetDirection(); MITK_DEBUG << "ITK direction " << itkdirection; mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (m_Dimension >= 3? 3 : m_Dimension); // check if spacing has no zero entry and itkdirection has no zero columns bool itkdirectionOk = true; mitk::ScalarType columnSum; for( j=0; j < itkDimMax3; ++j ) { columnSum = 0.0; for ( i=0; i < itkDimMax3; ++i) { columnSum += fabs(itkdirection[i][j]); } if(columnSum < mitk::eps) { itkdirectionOk = false; } if ( (spacing[j] < - mitk::eps) // (normally sized) negative value && (j==2) && (m_Dimensions[2] == 1) ) { // Negative spacings can occur when reading single DICOM slices with ITK via GDCMIO // In these cases spacing is not determind by ITK correctly (because it distinguishes correctly // between slice thickness and inter slice distance -- slice distance is meaningless for // single slices). // I experienced that ITK produced something meaningful nonetheless because is is // evaluating the tag "(0018,0088) Spacing between slices" as a fallback. This tag is not // reliable (http://www.itk.org/pipermail/insight-users/2005-September/014711.html) // but gives at least a hint. // In real world cases I experienced that this tag contained the correct inter slice distance // with a negative sign, so we just invert such negative spacings. MITK_WARN << "Illegal value of itk::Image::GetSpacing()[" << j <<"]=" << spacing[j] << ". Using inverted value " << -spacing[j]; spacing[j] = -spacing[j]; } else if (spacing[j] < mitk::eps) // value near zero { MITK_ERROR << "Illegal value of itk::Image::GetSpacing()[" << j <<"]=" << spacing[j] << ". Using 1.0 instead."; spacing[j] = 1.0; } } if(itkdirectionOk == false) { MITK_ERROR << "Illegal matrix returned by itk::Image::GetDirection():" << itkdirection << " Using identity instead."; for ( i=0; i < itkDimMax3; ++i) for( j=0; j < itkDimMax3; ++j ) if ( i == j ) matrix[i][j] = spacing[j]; else matrix[i][j] = 0.0; } else { for ( i=0; i < itkDimMax3; ++i) for( j=0; j < itkDimMax3; ++j ) matrix[i][j] = itkdirection[i][j]*spacing[j]; } // re-initialize PlaneGeometry with origin and direction PlaneGeometry* planeGeometry = static_cast(GetSlicedGeometry(0)->GetGeometry2D(0)); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D* slicedGeometry = GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, m_Dimensions[2]); slicedGeometry->SetSpacing(spacing); // re-initialize TimeSlicedGeometry GetTimeSlicedGeometry()->InitializeEvenlyTimed(slicedGeometry, m_Dimensions[3]); // clean-up delete [] tmpDimensions; this->Initialize(); }; //##Documentation //## @brief Check whether slice @a s at time @a t in channel @a n is valid, i.e., //## is (or can be) inside of the image virtual bool IsValidSlice(int s = 0, int t = 0, int n = 0) const; //##Documentation //## @brief Check whether volume at time @a t in channel @a n is valid, i.e., //## is (or can be) inside of the image virtual bool IsValidVolume(int t = 0, int n = 0) const; //##Documentation //## @brief Check whether the channel @a n is valid, i.e., //## is (or can be) inside of the image virtual bool IsValidChannel(int n = 0) const; //##Documentation //## @brief Returns true if an image is rotated, i.e. its geometry's //## transformation matrix has nonzero elements besides the diagonal. //## Non-diagonal elements are checked if larger then 1/1000 of the matrix' trace. bool IsRotated() const; //##Documentation //## @brief Get the sizes of all dimensions as an integer-array. //## //## @sa GetDimension(int i); unsigned int* GetDimensions() const; ImageDescriptor::Pointer GetImageDescriptor() const { return m_ImageDescriptor; } ChannelDescriptor GetChannelDescriptor( int id = 0 ) const { return m_ImageDescriptor->GetChannelDescriptor(id); } /** \brief Sets a geometry to an image. */ virtual void SetGeometry(Geometry3D* aGeometry3D); /** * @warning for internal use only */ virtual ImageDataItemPointer GetSliceData(int s = 0, int t = 0, int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); /** * @warning for internal use only */ virtual ImageDataItemPointer GetVolumeData(int t = 0, int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); /** * @warning for internal use only */ virtual ImageDataItemPointer GetChannelData(int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); /** \brief (DEPRECATED) Get the minimum for scalar images */ DEPRECATED (ScalarType GetScalarValueMin(int t=0) const); /** \brief (DEPRECATED) Get the maximum for scalar images \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetScalarValueMax(int t=0) const); /** \brief (DEPRECATED) Get the second smallest value for scalar images \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetScalarValue2ndMin(int t=0) const); /** \brief (DEPRECATED) Get the smallest value for scalar images, but do not recompute it first \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetScalarValueMinNoRecompute( unsigned int t = 0 ) const); /** \brief (DEPRECATED) Get the second smallest value for scalar images, but do not recompute it first \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetScalarValue2ndMinNoRecompute( unsigned int t = 0 ) const); /** \brief (DEPRECATED) Get the second largest value for scalar images \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetScalarValue2ndMax(int t=0) const); /** \brief (DEPRECATED) Get the largest value for scalar images, but do not recompute it first \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetScalarValueMaxNoRecompute( unsigned int t = 0 ) const ); /** \brief (DEPRECATED) Get the second largest value for scalar images, but do not recompute it first \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetScalarValue2ndMaxNoRecompute( unsigned int t = 0 ) const); /** \brief (DEPRECATED) Get the count of voxels with the smallest scalar value in the dataset \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetCountOfMinValuedVoxels(int t = 0) const); /** \brief (DEPRECATED) Get the count of voxels with the largest scalar value in the dataset \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetCountOfMaxValuedVoxels(int t = 0) const); /** \brief (DEPRECATED) Get the count of voxels with the largest scalar value in the dataset \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (unsigned int GetCountOfMaxValuedVoxelsNoRecompute( unsigned int t = 0 ) const); /** \brief (DEPRECATED) Get the count of voxels with the smallest scalar value in the dataset \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (unsigned int GetCountOfMinValuedVoxelsNoRecompute( unsigned int t = 0 ) const); /** \brief Returns a pointer to the ImageStatisticsHolder object that holds all statistics information for the image. All Get-methods for statistics properties formerly accessible directly from an Image object are now moved to the new \a ImageStatisticsHolder object. */ StatisticsHolderPointer GetStatistics() const { return m_ImageStatistics; } protected: int GetSliceIndex(int s = 0, int t = 0, int n = 0) const; int GetVolumeIndex(int t = 0, int n = 0) const; void ComputeOffsetTable(); virtual bool IsValidTimeStep(int t) const; virtual void Expand( unsigned int timeSteps ); virtual ImageDataItemPointer AllocateSliceData(int s = 0, int t = 0, int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); virtual ImageDataItemPointer AllocateVolumeData(int t = 0, int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); virtual ImageDataItemPointer AllocateChannelData(int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); Image(); Image(const Image &other); virtual ~Image(); virtual void Clear(); //## @warning Has to be called by every Initialize method! virtual void Initialize(); virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; mutable ImageDataItemPointerArray m_Channels; mutable ImageDataItemPointerArray m_Volumes; mutable ImageDataItemPointerArray m_Slices; unsigned int m_Dimension; unsigned int* m_Dimensions; ImageDescriptor::Pointer m_ImageDescriptor; size_t *m_OffsetTable; ImageDataItemPointer m_CompleteData; // Image statistics Holder replaces the former implementation directly inside this class friend class ImageStatisticsHolder; StatisticsHolderPointer m_ImageStatistics; private: /** Stores all existing ImageReadAccessors */ std::vector m_Readers; /** Stores all existing ImageWriteAccessors */ std::vector m_Writers; /** Stores all existing ImageVtkAccessors */ std::vector m_VtkReaders; /** A mutex, which needs to be locked to manage m_Readers and m_Writers */ itk::SimpleFastMutexLock m_ReadWriteLock; /** A mutex, which needs to be locked to manage m_VtkReaders */ itk::SimpleFastMutexLock m_VtkReadersLock; }; + /** + * @brief Equal A function comparing two images for beeing equal in meta- and imagedata + * + * Following aspects are tested for equality: + * - dimension of the images + * - size of the images + * - pixel type + * - pixel values : pixel values are expected to be identical at each position ( for other options see mitk::CompareImageFilter ) + * + * @param rightHandSide An image to be compared + * @param leftHandSide An image to be compared + * @param eps Tolarence for comparison. You can use mitk::eps in most cases. + * @param verbose Flag indicating if the user wants detailed console output or not. + * @return true, if all subsequent comparisons are true, false otherwise + */ +MITK_CORE_EXPORT bool Equal( const mitk::Image* rightHandSide, const mitk::Image* leftHandSide, ScalarType eps, bool verbose ); + + +//} //##Documentation //## @brief Cast an itk::Image (with a specific type) to an mitk::Image. //## //## CastToMitkImage does not cast pixel types etc., just image data //## Needs "mitkImage.h" header included. //## If you get a compile error, try image.GetPointer(); //## @ingroup Adaptor //## \sa mitkITKImageImport template void CastToMitkImage(const itk::SmartPointer& itkimage, itk::SmartPointer& mitkoutputimage) { if(mitkoutputimage.IsNull()) { mitkoutputimage = mitk::Image::New(); } mitkoutputimage->InitializeByItk(itkimage.GetPointer()); mitkoutputimage->SetChannel(itkimage->GetBufferPointer()); } //##Documentation //## @brief Cast an itk::Image (with a specific type) to an mitk::Image. //## //## CastToMitkImage does not cast pixel types etc., just image data //## Needs "mitkImage.h" header included. //## If you get a compile error, try image.GetPointer(); //## @ingroup Adaptor //## \sa mitkITKImageImport template void CastToMitkImage(const ItkOutputImageType* itkimage, itk::SmartPointer& mitkoutputimage) { if(mitkoutputimage.IsNull()) { mitkoutputimage = mitk::Image::New(); } mitkoutputimage->InitializeByItk(itkimage); mitkoutputimage->SetChannel(itkimage->GetBufferPointer()); } } // namespace mitk #endif /* MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2 */ diff --git a/Core/Code/DataManagement/mitkPointSet.cpp b/Core/Code/DataManagement/mitkPointSet.cpp index 64a6e20e42..6f3bf1fdf1 100755 --- a/Core/Code/DataManagement/mitkPointSet.cpp +++ b/Core/Code/DataManagement/mitkPointSet.cpp @@ -1,819 +1,885 @@ /*=================================================================== 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 "mitkPointSet.h" #include "mitkPointOperation.h" #include "mitkInteractionConst.h" +#include +#include mitk::PointSet::PointSet() { this->InitializeEmpty(); } mitk::PointSet::PointSet(const PointSet &other) : BaseData(other) , m_PointSetSeries(other.GetPointSetSeriesSize()) , m_CalculateBoundingBox(true) { // Copy points for (std::size_t t = 0; t < m_PointSetSeries.size(); ++t) { m_PointSetSeries[t] = DataType::New(); DataType::Pointer otherPts = other.GetPointSet(t); for (PointsConstIterator i = other.Begin(t); i != other.End(t); ++i) { m_PointSetSeries[t]->SetPoint(i.Index(), i.Value()); PointDataType pointData; if (otherPts->GetPointData(i.Index(), &pointData)) { m_PointSetSeries[t]->SetPointData(i.Index(), pointData); } } } } mitk::PointSet::~PointSet() { this->ClearData(); } void mitk::PointSet::ClearData() { m_PointSetSeries.clear(); Superclass::ClearData(); } void mitk::PointSet::InitializeEmpty() { m_PointSetSeries.resize( 1 ); m_PointSetSeries[0] = DataType::New(); PointDataContainer::Pointer pointData = PointDataContainer::New(); m_PointSetSeries[0]->SetPointData( pointData ); m_CalculateBoundingBox = false; Superclass::InitializeTimeSlicedGeometry(1); m_Initialized = true; } bool mitk::PointSet::IsEmptyTimeStep(unsigned int t) const { return IsInitialized() && (GetSize(t) == 0); } void mitk::PointSet::Expand( unsigned int timeSteps ) { // Check if the vector is long enough to contain the new element // at the given position. If not, expand it with sufficient pre-initialized // elements. // // NOTE: This method will never REDUCE the vector size; it should only // be used to make sure that the vector has enough elements to include the // specified time step. unsigned int oldSize = m_PointSetSeries.size(); if ( timeSteps > oldSize ) { Superclass::Expand( timeSteps ); m_PointSetSeries.resize( timeSteps ); for ( unsigned int i = oldSize; i < timeSteps; ++i ) { m_PointSetSeries[i] = DataType::New(); PointDataContainer::Pointer pointData = PointDataContainer::New(); m_PointSetSeries[i]->SetPointData( pointData ); } //if the size changes, then compute the bounding box m_CalculateBoundingBox = true; this->InvokeEvent( PointSetExtendTimeRangeEvent() ); } } unsigned int mitk::PointSet::GetPointSetSeriesSize() const { return m_PointSetSeries.size(); } int mitk::PointSet::GetSize( unsigned int t ) const { if ( t < m_PointSetSeries.size() ) { return m_PointSetSeries[t]->GetNumberOfPoints(); } else { return 0; } } mitk::PointSet::DataType::Pointer mitk::PointSet::GetPointSet( int t ) const { if ( t < (int)m_PointSetSeries.size() ) { return m_PointSetSeries[t]; } else { return NULL; } } mitk::PointSet::PointsIterator mitk::PointSet::Begin( int t ) { if (t >= 0 && t < static_cast(m_PointSetSeries.size())) { return m_PointSetSeries[t]->GetPoints()->Begin(); } return PointsIterator(); } mitk::PointSet::PointsConstIterator mitk::PointSet::Begin(int t) const { if (t >= 0 && t < static_cast(m_PointSetSeries.size())) { return m_PointSetSeries[t]->GetPoints()->Begin(); } return PointsConstIterator(); } mitk::PointSet::PointsIterator mitk::PointSet::End( int t ) { if (t >= 0 && t < static_cast(m_PointSetSeries.size())) { return m_PointSetSeries[t]->GetPoints()->End(); } return PointsIterator(); } mitk::PointSet::PointsConstIterator mitk::PointSet::End(int t) const { if (t >= 0 && t < static_cast(m_PointSetSeries.size())) { return m_PointSetSeries[t]->GetPoints()->End(); } return PointsConstIterator(); } int mitk::PointSet::SearchPoint( Point3D point, float distance, int t ) const { if ( t >= (int)m_PointSetSeries.size() ) { return -1; } // Out is the point which is checked to be the searched point PointType out; out.Fill( 0 ); PointType indexPoint; this->GetGeometry( t )->WorldToIndex(point, indexPoint); // Searching the first point in the Set, that is +- distance far away fro // the given point unsigned int i; PointsContainer::Iterator it, end; end = m_PointSetSeries[t]->GetPoints()->End(); int bestIndex = -1; distance = distance * distance; // To correct errors from converting index to world and world to index if (distance == 0.0) { distance = 0.000001; } ScalarType bestDist = distance; ScalarType dist, tmp; for ( it = m_PointSetSeries[t]->GetPoints()->Begin(), i = 0; it != end; ++it, ++i ) { bool ok = m_PointSetSeries[t]->GetPoints() ->GetElementIfIndexExists( it->Index(), &out ); if ( !ok ) { return -1; } else if ( indexPoint == out ) //if totally equal { return it->Index(); } //distance calculation tmp = out[0] - indexPoint[0]; dist = tmp * tmp; tmp = out[1] - indexPoint[1]; dist += tmp * tmp; tmp = out[2] - indexPoint[2]; dist += tmp * tmp; if ( dist < bestDist ) { bestIndex = it->Index(); bestDist = dist; } } return bestIndex; } mitk::PointSet::PointType mitk::PointSet::GetPoint( PointIdentifier id, int t ) const { PointType out; out.Fill(0); if ( (unsigned int) t >= m_PointSetSeries.size() ) { return out; } if ( m_PointSetSeries[t]->GetPoints()->IndexExists(id) ) { m_PointSetSeries[t]->GetPoint( id, &out ); this->GetGeometry(t)->IndexToWorld( out, out ); return out; } else { return out; } } bool mitk::PointSet ::GetPointIfExists( PointIdentifier id, PointType* point, int t ) const { if ( (unsigned int) t >= m_PointSetSeries.size() ) { return false; } if ( m_PointSetSeries[t]->GetPoints()->GetElementIfIndexExists(id, point) ) { this->GetGeometry( t )->IndexToWorld( *point, *point ); return true; } else { return false; } } void mitk::PointSet::SetPoint( PointIdentifier id, PointType point, int t ) { // Adapt the size of the data vector if necessary this->Expand( t+1 ); mitk::Point3D indexPoint; this->GetGeometry( t )->WorldToIndex( point, indexPoint ); m_PointSetSeries[t]->SetPoint( id, indexPoint ); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = mitk::PTUNDEFINED; m_PointSetSeries[t]->SetPointData( id, defaultPointData ); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); } void mitk::PointSet::SetPoint( PointIdentifier id, PointType point, PointSpecificationType spec, int t ) { // Adapt the size of the data vector if necessary this->Expand( t+1 ); mitk::Point3D indexPoint; this->GetGeometry( t )->WorldToIndex( point, indexPoint ); m_PointSetSeries[t]->SetPoint( id, indexPoint ); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = spec; m_PointSetSeries[t]->SetPointData( id, defaultPointData ); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); } void mitk::PointSet::InsertPoint( PointIdentifier id, PointType point, int t ) { this->InsertPoint(id, point, mitk::PTUNDEFINED, t); } void mitk::PointSet::InsertPoint( PointIdentifier id, PointType point, PointSpecificationType spec, int t ) { if ( (unsigned int) t < m_PointSetSeries.size() ) { mitk::Point3D indexPoint; mitk::Geometry3D* tempGeometry = this->GetGeometry( t ); if (tempGeometry == NULL) { MITK_INFO<< __FILE__ << ", l." << __LINE__ << ": GetGeometry of "<< t <<" returned NULL!" << std::endl; return; } tempGeometry->WorldToIndex( point, indexPoint ); m_PointSetSeries[t]->GetPoints()->InsertElement( id, indexPoint ); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = spec; m_PointSetSeries[t]->GetPointData()->InsertElement(id, defaultPointData); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); } } bool mitk::PointSet::SwapPointPosition( PointIdentifier id, bool moveUpwards, int t ) { if(IndexExists(id, t) ) { PointType point = GetPoint(id,t); if(moveUpwards) {//up if(IndexExists(id-1,t)) { InsertPoint(id, GetPoint(id - 1, t), t); InsertPoint(id-1,point,t); this->Modified(); return true; } } else {//down if(IndexExists(id+1,t)) { InsertPoint(id, GetPoint(id + 1, t), t); InsertPoint(id+1,point,t); this->Modified(); return true; } } } return false; } bool mitk::PointSet::IndexExists( int position, int t ) const { if ( (unsigned int) t < m_PointSetSeries.size() ) { return m_PointSetSeries[t]->GetPoints()->IndexExists( position ); } else { return false; } } bool mitk::PointSet::GetSelectInfo( int position, int t ) const { if ( this->IndexExists( position, t ) ) { PointDataType pointData = { 0, false, PTUNDEFINED }; m_PointSetSeries[t]->GetPointData( position, &pointData ); return pointData.selected; } else { return false; } } void mitk::PointSet::SetSelectInfo( int position, bool selected, int t ) { if ( this->IndexExists( position, t ) ) { // timeStep to ms ScalarType timeInMS = this->GetTimeSlicedGeometry()->TimeStepToMS( t ); // point Point3D point = this->GetPoint( position, t ); std::auto_ptr op; if (selected) { op.reset(new mitk::PointOperation(OpSELECTPOINT, timeInMS, point, position )); } else { op.reset(new mitk::PointOperation(OpDESELECTPOINT, timeInMS, point, position )); } this->ExecuteOperation( op.get() ); } } mitk::PointSpecificationType mitk::PointSet::GetSpecificationTypeInfo( int position, int t ) const { if ( this->IndexExists( position, t ) ) { PointDataType pointData = { 0, false, PTUNDEFINED }; m_PointSetSeries[t]->GetPointData( position, &pointData ); return pointData.pointSpec; } else { return PTUNDEFINED; } } int mitk::PointSet::GetNumberOfSelected( int t ) const { if ( (unsigned int) t >= m_PointSetSeries.size() ) { return 0; } int numberOfSelected = 0; PointDataIterator it; for ( it = m_PointSetSeries[t]->GetPointData()->Begin(); it != m_PointSetSeries[t]->GetPointData()->End(); it++ ) { if (it->Value().selected == true) { ++numberOfSelected; } } return numberOfSelected; } int mitk::PointSet::SearchSelectedPoint( int t ) const { if ( (unsigned int) t >= m_PointSetSeries.size() ) { return -1; } PointDataIterator it; for ( it = m_PointSetSeries[t]->GetPointData()->Begin(); it != m_PointSetSeries[t]->GetPointData()->End(); it++ ) { if ( it->Value().selected == true ) { return it->Index(); } } return -1; } void mitk::PointSet::ExecuteOperation( Operation* operation ) { int timeStep = -1; mitkCheckOperationTypeMacro(PointOperation, operation, pointOp); if ( pointOp ) { timeStep = this->GetTimeSlicedGeometry() ->MSToTimeStep( pointOp->GetTimeInMS() ); } if ( timeStep < 0 ) { MITK_ERROR << "Time step (" << timeStep << ") outside of PointSet time bounds" << std::endl; return; } switch (operation->GetOperationType()) { case OpNOTHING: break; case OpINSERT://inserts the point at the given position and selects it. { int position = pointOp->GetIndex(); PointType pt; pt.CastFrom(pointOp->GetPoint()); //transfer from world to index coordinates mitk::Geometry3D* geometry = this->GetGeometry( timeStep ); if (geometry == NULL) { MITK_INFO<<"GetGeometry returned NULL!\n"; return; } geometry->WorldToIndex(pt, pt); m_PointSetSeries[timeStep]->GetPoints()->InsertElement(position, pt); PointDataType pointData = { static_cast(pointOp->GetIndex()), pointOp->GetSelected(), pointOp->GetPointType() }; m_PointSetSeries[timeStep]->GetPointData() ->InsertElement(position, pointData); this->Modified(); //boundingbox has to be computed m_CalculateBoundingBox = true; this->InvokeEvent( PointSetAddEvent() ); this->OnPointSetChange(); } break; case OpMOVE://moves the point given by index { PointType pt; pt.CastFrom(pointOp->GetPoint()); //transfer from world to index coordinates this->GetGeometry( timeStep )->WorldToIndex(pt, pt); // Copy new point into container m_PointSetSeries[timeStep]->SetPoint(pointOp->GetIndex(), pt); // Insert a default point data object to keep the containers in sync // (if no point data object exists yet) PointDataType pointData; if ( !m_PointSetSeries[timeStep]->GetPointData( pointOp->GetIndex(), &pointData ) ) { m_PointSetSeries[timeStep]->SetPointData( pointOp->GetIndex(), pointData ); } this->OnPointSetChange(); this->Modified(); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->InvokeEvent( PointSetMoveEvent() ); } break; case OpREMOVE://removes the point at given by position { m_PointSetSeries[timeStep]->GetPoints()->DeleteIndex((unsigned)pointOp->GetIndex()); m_PointSetSeries[timeStep]->GetPointData()->DeleteIndex((unsigned)pointOp->GetIndex()); this->OnPointSetChange(); this->Modified(); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->InvokeEvent( PointSetRemoveEvent() ); } break; case OpSELECTPOINT://select the given point { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData); pointData.selected = true; m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); this->Modified(); } break; case OpDESELECTPOINT://unselect the given point { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData); pointData.selected = false; m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); this->Modified(); } break; case OpSETPOINTTYPE: { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData); pointData.pointSpec = pointOp->GetPointType(); m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); this->Modified(); } break; case OpMOVEPOINTUP: // swap content of point with ID pointOp->GetIndex() with the point preceding it in the container // move point position within the pointset { PointIdentifier currentID = pointOp->GetIndex(); /* search for point with this id and point that precedes this one in the data container */ PointsContainer::STLContainerType points = m_PointSetSeries[timeStep]->GetPoints()->CastToSTLContainer(); PointsContainer::STLContainerType::iterator it = points.find(currentID); if (it == points.end()) // ID not found break; if (it == points.begin()) // we are at the first element, there is no previous element break; /* get and cache current point & pointdata and previous point & pointdata */ --it; PointIdentifier prevID = it->first; if (this->SwapPointContents(prevID, currentID, timeStep) == true) this->Modified(); } break; case OpMOVEPOINTDOWN: // move point position within the pointset { PointIdentifier currentID = pointOp->GetIndex(); /* search for point with this id and point that succeeds this one in the data container */ PointsContainer::STLContainerType points = m_PointSetSeries[timeStep]->GetPoints()->CastToSTLContainer(); PointsContainer::STLContainerType::iterator it = points.find(currentID); if (it == points.end()) // ID not found break; ++it; if (it == points.end()) // ID is already the last element, there is no succeeding element break; /* get and cache current point & pointdata and previous point & pointdata */ PointIdentifier nextID = it->first; if (this->SwapPointContents(nextID, currentID, timeStep) == true) this->Modified(); } break; default: itkWarningMacro("mitkPointSet could not understrand the operation. Please check!"); break; } //to tell the mappers, that the data is modified and has to be updated //only call modified if anything is done, so call in cases //this->Modified(); mitk::OperationEndEvent endevent(operation); ((const itk::Object*)this)->InvokeEvent(endevent); //*todo has to be done here, cause of update-pipeline not working yet // As discussed lately, don't mess with the rendering from inside data structures //mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::PointSet::UpdateOutputInformation() { if ( this->GetSource( ) ) { this->GetSource( )->UpdateOutputInformation( ); } // // first make sure, that the associated time sliced geometry has // the same number of geometry 3d's as PointSets are present // mitk::TimeSlicedGeometry* timeGeometry = GetTimeSlicedGeometry(); if ( timeGeometry->GetTimeSteps() != m_PointSetSeries.size() ) { itkExceptionMacro(<<"timeGeometry->GetTimeSteps() != m_PointSetSeries.size() -- use Initialize(timeSteps) with correct number of timeSteps!"); } // This is needed to detect zero objects mitk::ScalarType nullpoint[]={0,0,0,0,0,0}; BoundingBox::BoundsArrayType itkBoundsNull(nullpoint); // // Iterate over the PointSets and update the Geometry // information of each of the items. // if (m_CalculateBoundingBox) { for ( unsigned int i = 0 ; i < m_PointSetSeries.size() ; ++i ) { const DataType::BoundingBoxType *bb = m_PointSetSeries[i]->GetBoundingBox(); BoundingBox::BoundsArrayType itkBounds = bb->GetBounds(); if ( m_PointSetSeries[i].IsNull() || (m_PointSetSeries[i]->GetNumberOfPoints() == 0) || (itkBounds == itkBoundsNull) ) { itkBounds = itkBoundsNull; continue; } // Ensure minimal bounds of 1.0 in each dimension for ( unsigned int j = 0; j < 3; ++j ) { if ( itkBounds[j*2+1] - itkBounds[j*2] < 1.0 ) { BoundingBox::CoordRepType center = (itkBounds[j*2] + itkBounds[j*2+1]) / 2.0; itkBounds[j*2] = center - 0.5; itkBounds[j*2+1] = center + 0.5; } } this->GetGeometry(i)->SetBounds(itkBounds); } m_CalculateBoundingBox = false; } this->GetTimeSlicedGeometry()->UpdateInformation(); } void mitk::PointSet::SetRequestedRegionToLargestPossibleRegion() { } bool mitk::PointSet::RequestedRegionIsOutsideOfTheBufferedRegion() { return false; } bool mitk::PointSet::VerifyRequestedRegion() { return true; } void mitk::PointSet::SetRequestedRegion(const DataObject * ) { } void mitk::PointSet::PrintSelf( std::ostream& os, itk::Indent indent ) const { Superclass::PrintSelf(os, indent); os << indent << "Number timesteps: " << m_PointSetSeries.size() << "\n"; unsigned int i = 0; for (PointSetSeries::const_iterator it = m_PointSetSeries.begin(); it != m_PointSetSeries.end(); ++it) { os << indent << "Timestep " << i++ << ": \n"; MeshType::Pointer ps = *it; itk::Indent nextIndent = indent.GetNextIndent(); ps->Print(os, nextIndent); MeshType::PointsContainer* points = ps->GetPoints(); MeshType::PointDataContainer* datas = ps->GetPointData(); MeshType::PointDataContainer::Iterator dataIterator = datas->Begin(); for (MeshType::PointsContainer::Iterator pointIterator = points->Begin(); pointIterator != points->End(); ++pointIterator, ++dataIterator) { os << nextIndent << "Point " << pointIterator->Index() << ": ["; os << pointIterator->Value().GetElement(0); for (unsigned int i = 1; i < PointType::GetPointDimension(); ++i) { os << ", " << pointIterator->Value().GetElement(i); } os << "]"; os << ", selected: " << dataIterator->Value().selected << ", point spec: " << dataIterator->Value().pointSpec << "\n"; } } } bool mitk::PointSet::SwapPointContents(PointIdentifier id1, PointIdentifier id2, int timeStep) { /* search and cache contents */ PointType p1; if (m_PointSetSeries[timeStep]->GetPoint(id1, &p1) == false) return false; PointDataType data1; if (m_PointSetSeries[timeStep]->GetPointData(id1, &data1) == false) return false; PointType p2; if (m_PointSetSeries[timeStep]->GetPoint(id2, &p2) == false) return false; PointDataType data2; if (m_PointSetSeries[timeStep]->GetPointData(id2, &data2) == false) return false; /* now swap contents */ m_PointSetSeries[timeStep]->SetPoint(id1, p2); m_PointSetSeries[timeStep]->SetPointData(id1, data2); m_PointSetSeries[timeStep]->SetPoint(id2, p1); m_PointSetSeries[timeStep]->SetPointData(id2, data1); return true; } bool mitk::PointSet::PointDataType::operator ==(const mitk::PointSet::PointDataType &other) const { return id == other.id && selected == other.selected && pointSpec == other.pointSpec; } + +bool mitk::Equal( const mitk::PointSet* leftHandSide, const mitk::PointSet* rightHandSide, mitk::ScalarType eps, bool verbose ) +{ + bool result = true; + + if( rightHandSide == NULL ) + { + if(verbose) + { + MITK_INFO << "[( PointSet )] rightHandSide NULL."; + } + return false; + } + if( leftHandSide == NULL ) + { + if(verbose) + MITK_INFO << "[( PointSet )] leftHandSide NULL."; + return false; + } + + if( !mitk::Equal(leftHandSide->GetGeometry(), rightHandSide->GetGeometry(), eps, verbose) ) + { + if(verbose) + MITK_INFO << "[( PointSet )] Geometries differ."; + result = false; + } + + if ( leftHandSide->GetSize() != rightHandSide->GetSize()) + { + if(verbose) + MITK_INFO << "[( PointSet )] Number of points differ."; + result = false; + } + else + { + //if the size is equal, we compare the point values + mitk::Point3D pointLeftHandSide; + mitk::Point3D pointRightHandSide; + + int numberOfIncorrectPoints = 0; + + //Iterate over both pointsets in order to compare all points pair-wise + mitk::PointSet::PointsConstIterator end = leftHandSide->End(); + for( mitk::PointSet::PointsConstIterator pointSetIteratorLeft = leftHandSide->Begin(), pointSetIteratorRight = rightHandSide->Begin(); + pointSetIteratorLeft != end; ++pointSetIteratorLeft, ++pointSetIteratorRight) //iterate simultaneously over both sets + { + pointLeftHandSide = pointSetIteratorLeft.Value(); + pointRightHandSide = pointSetIteratorRight.Value(); + if( !mitk::Equal( pointLeftHandSide, pointRightHandSide, eps, verbose ) ) + { + if(verbose) + MITK_INFO << "[( PointSet )] Point values are different."; + result = false; + numberOfIncorrectPoints++; + } + } + + if((numberOfIncorrectPoints > 0) && verbose) + { + MITK_INFO << numberOfIncorrectPoints <<" of a total of " << leftHandSide->GetSize() << " points are different."; + } + } + return result; +} diff --git a/Core/Code/DataManagement/mitkPointSet.h b/Core/Code/DataManagement/mitkPointSet.h index 7177cdd61b..77785a403e 100755 --- a/Core/Code/DataManagement/mitkPointSet.h +++ b/Core/Code/DataManagement/mitkPointSet.h @@ -1,284 +1,298 @@ /*=================================================================== 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 MITKPointSet_H_HEADER_INCLUDED #define MITKPointSet_H_HEADER_INCLUDED #include "mitkBaseData.h" #include #include namespace mitk { /** * \brief Data structure which stores a set of points. Superclass of * mitk::Mesh. * * 3D points are grouped within a point set; for time resolved usage, one point * set is created and maintained per time step. A point entry consists of the * point coordinates and point data. * * The point data includes a point ID (unique identifier to address this point * within the point set), the selection state of the point and the type of * the point. * * For further information about different point types see * mitk::PointSpecificationType in mitkVector.h. * * Inserting a point is accompanied by an event, containing an index. The new * point is inserted into the list at the specified position. At the same time * an internal ID is generated and stored for the point. Points at specific time * steps are accessed by specifying the time step number (which defaults to 0). * * The points of itk::PointSet stores the points in a pointContainer * (MapContainer). The points are best accessed by using a ConstIterator (as * defined in MapContainer); avoid access via index. * * The class internally uses an itk::Mesh for each time step, because * mitk::Mesh is derived from mitk::PointSet and needs the itk::Mesh structure * which is also derived from itk::PointSet. Thus several typedefs which seem * to be in wrong place, are declared here (for example SelectedLinesType). * * \section mitkPointSetDisplayOptions * * The default mappers for this data structure are mitk::PointSetGLMapper2D and * mitk::PointSetVtkMapper3D. See these classes for display options which can * can be set via properties. * * \section Events * * PointSet issues the following events, for which observers can register * (the below events are grouped into a class hierarchy as indicated by * identation level; e.g. PointSetSizeChangeEvent comprises PointSetAddEvent * and PointSetRemoveEvent): * * * PointSetEvent subsumes all PointSet events * PointSetMoveEvent issued when a point of the PointSet is moved * PointSetSizeChangeEvent subsumes add and remove events * PointSetAddEvent issued when a point is added to the PointSet * PointSetRemoveEvent issued when a point is removed from the PointSet * * \ingroup PSIO * \ingroup Data */ class MITK_CORE_EXPORT PointSet : public BaseData { public: mitkClassMacro(PointSet, BaseData); itkNewMacro(Self); mitkCloneMacro(PointSet); typedef mitk::ScalarType CoordinateType; typedef mitk::ScalarType InterpolationWeightType; static const unsigned int PointDimension = 3; static const unsigned int MaxTopologicalDimension = 3; /** * \brief struct for data of a point */ struct MITK_CORE_EXPORT PointDataType { unsigned int id; //to give the point a special ID bool selected; //information about if the point is selected mitk::PointSpecificationType pointSpec; //specifies the type of the point bool operator==(const PointDataType& other) const; }; /** * \brief cellDataType, that stores all indexes of the lines, that are * selected e.g.: points A,B and C.Between A and B there is a line with * index 0. If vector of cellData contains 1 and 2, then the lines between * B and C and C and A is selected. */ typedef std::vector SelectedLinesType; typedef SelectedLinesType::iterator SelectedLinesIter; struct CellDataType { //used to set the whole cell on selected bool selected; //indexes of selected lines. 0 is between pointId 0 and 1 SelectedLinesType selectedLines; //is the polygon already finished and closed bool closed; }; typedef itk::DefaultDynamicMeshTraits< PointDataType, PointDimension, MaxTopologicalDimension, CoordinateType, InterpolationWeightType, CellDataType > MeshTraits; typedef itk::Mesh MeshType; typedef MeshType DataType; typedef DataType::PointType PointType; typedef DataType::PointIdentifier PointIdentifier; typedef DataType::PointsContainer PointsContainer; typedef DataType::PointsContainerIterator PointsIterator; typedef DataType::PointsContainer::ConstIterator PointsConstIterator; typedef DataType::PointDataContainer PointDataContainer; typedef DataType::PointDataContainerIterator PointDataIterator; typedef DataType::PointDataContainerIterator PointDataConstIterator; virtual void Expand( unsigned int timeSteps ); /** \brief executes the given Operation */ virtual void ExecuteOperation(Operation* operation); /** \brief returns the current size of the point-list */ virtual int GetSize( unsigned int t = 0 ) const; virtual unsigned int GetPointSetSeriesSize() const; /** \brief returns the pointset */ virtual DataType::Pointer GetPointSet( int t = 0 ) const; PointsIterator Begin( int t = 0 ); PointsConstIterator Begin( int t = 0 ) const; PointsIterator End( int t = 0 ); PointsConstIterator End( int t = 0 ) const; /** * \brief Get the point with ID id in world coordinates * * check if the ID exists. If it doesn't exist, then return 0,0,0 */ PointType GetPoint( PointIdentifier id, int t = 0 ) const; /** * \brief Get the point with ID id in world coordinates * * If a point exists for the ID id, the point is returned in the parameter point * and the method returns true. If the ID does not exist, the method returns false */ bool GetPointIfExists( PointIdentifier id, PointType* point, int t = 0 ) const; /** * \brief Set the given point in world coordinate system into the itkPointSet. */ void SetPoint( PointIdentifier id, PointType point, int t = 0 ); /** * \brief Set the given point in world coordinate system with the given PointSpecificationType */ void SetPoint( PointIdentifier id, PointType point, PointSpecificationType spec, int t = 0 ); /** * \brief Set the given point in world coordinate system into the itkPointSet. */ void InsertPoint( PointIdentifier id, PointType point, int t = 0 ); /** * \brief Set the given point in world coordinate system with given PointSpecificationType */ void InsertPoint( PointIdentifier id, PointType point, PointSpecificationType spec, int t ); /** * \brief Swap a point at the given position (id) with the upper point (moveUpwards=true) or with the lower point (moveUpwards=false). * If upper or lower index does not exist false is returned, if swap was successful true. */ bool SwapPointPosition( PointIdentifier id, bool moveUpwards, int t = 0 ); /** * \brief searches a selected point and returns the id of that point. * If no point is found, then -1 is returned */ virtual int SearchSelectedPoint( int t = 0 ) const; /** \brief returns true if a point exists at this position */ virtual bool IndexExists( int position, int t = 0 ) const; /** \brief to get the state selected/unselected of the point on the * position */ virtual bool GetSelectInfo( int position, int t = 0 ) const; virtual void SetSelectInfo( int position, bool selected, int t = 0 ); /** \brief to get the type of the point at the position and the moment */ virtual PointSpecificationType GetSpecificationTypeInfo( int position, int t ) const; /** \brief returns the number of selected points */ virtual int GetNumberOfSelected( int t = 0 ) const; /** * \brief searches a point in the list == point +/- distance * * \param point is in world coordinates. * \param distance is in mm. * returns -1 if no point is found * or the position in the list of the first match */ int SearchPoint( Point3D point, float distance, int t = 0 ) const; virtual bool IsEmptyTimeStep(unsigned int t) const; //virtual methods, that need to be implemented virtual void UpdateOutputInformation(); virtual void SetRequestedRegionToLargestPossibleRegion(); virtual bool RequestedRegionIsOutsideOfTheBufferedRegion(); virtual bool VerifyRequestedRegion(); virtual void SetRequestedRegion(const itk::DataObject *data); //Method for subclasses virtual void OnPointSetChange(){}; protected: PointSet(); PointSet(const PointSet &other); virtual ~PointSet(); virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; ///< print content of the object to os virtual void ClearData(); virtual void InitializeEmpty(); /** \brief swaps point coordinates and point data of the points with identifiers id1 and id2 */ bool SwapPointContents(PointIdentifier id1, PointIdentifier id2, int t = 0 ); typedef std::vector< DataType::Pointer > PointSetSeries; PointSetSeries m_PointSetSeries; /** * @brief flag to indicate the right time to call SetBounds **/ bool m_CalculateBoundingBox; }; +/** + * @brief Equal A function comparing two pointsets for beeing identical. + * + * The function compares the Geometry, the size and all points element-wise. + * The parameter eps is a tolarence value for all methods which are internally used for comparion. + * + * @param rightHandSide Compare this against leftHandSide. + * @param leftHandSide Compare this against rightHandSide. + * @param eps Tolarence for comparison. You can use mitk::eps in most cases. + * @param verbose Flag indicating if the user wants detailed console output or not. + * @return True, if all subsequent comparisons are true, false otherwise + */ +MITK_CORE_EXPORT bool Equal( const mitk::PointSet* leftHandSide, const mitk::PointSet* rightHandSide, mitk::ScalarType eps, bool verbose ); + #pragma GCC visibility push(default) itkEventMacro( PointSetEvent, itk::AnyEvent ); itkEventMacro( PointSetMoveEvent, PointSetEvent ); itkEventMacro( PointSetSizeChangeEvent, PointSetEvent ); itkEventMacro( PointSetAddEvent, PointSetSizeChangeEvent ); itkEventMacro( PointSetRemoveEvent, PointSetSizeChangeEvent ); itkEventMacro( PointSetExtendTimeRangeEvent, PointSetEvent ); #pragma GCC visibility pop } // namespace mitk #endif /* MITKPointSet_H_HEADER_INCLUDED */ diff --git a/Core/Code/DataManagement/mitkSurface.cpp b/Core/Code/DataManagement/mitkSurface.cpp index 81dfb66e43..b6b0a69195 100644 --- a/Core/Code/DataManagement/mitkSurface.cpp +++ b/Core/Code/DataManagement/mitkSurface.cpp @@ -1,386 +1,540 @@ /*=================================================================== 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 "mitkSurface.h" #include "mitkInteractionConst.h" #include "mitkSurfaceOperation.h" #include #include +#include static vtkPolyData* DeepCopy(vtkPolyData* other) { if (other == NULL) return NULL; vtkPolyData* copy = vtkPolyData::New(); copy->DeepCopy(other); return copy; } static void Delete(vtkPolyData* polyData) { if (polyData != NULL) polyData->Delete(); } static void Update(vtkPolyData* polyData) { if (polyData != NULL) polyData->Update(); } mitk::Surface::Surface() : m_CalculateBoundingBox(false) { this->InitializeEmpty(); } mitk::Surface::Surface(const mitk::Surface& other) : BaseData(other), m_LargestPossibleRegion(other.m_LargestPossibleRegion), m_RequestedRegion(other.m_RequestedRegion), m_CalculateBoundingBox(other.m_CalculateBoundingBox) { if(!other.m_PolyDatas.empty()) { m_PolyDatas.resize(other.m_PolyDatas.size()); std::transform(other.m_PolyDatas.begin(), other.m_PolyDatas.end(), m_PolyDatas.begin(), DeepCopy); } else { this->InitializeEmpty(); } } void mitk::Surface::Swap(mitk::Surface& other) { std::swap(m_PolyDatas, other.m_PolyDatas); std::swap(m_LargestPossibleRegion, other.m_LargestPossibleRegion); std::swap(m_RequestedRegion, other.m_RequestedRegion); std::swap(m_CalculateBoundingBox, other.m_CalculateBoundingBox); } mitk::Surface& mitk::Surface::operator=(Surface other) { this->Swap(other); return *this; } mitk::Surface::~Surface() { this->ClearData(); } void mitk::Surface::ClearData() { using ::Delete; std::for_each(m_PolyDatas.begin(), m_PolyDatas.end(), Delete); m_PolyDatas.clear(); Superclass::ClearData(); } const mitk::Surface::RegionType& mitk::Surface::GetLargestPossibleRegion() const { m_LargestPossibleRegion.SetIndex(3, 0); m_LargestPossibleRegion.SetSize(3, GetTimeSlicedGeometry()->GetTimeSteps()); return m_LargestPossibleRegion; } const mitk::Surface::RegionType& mitk::Surface::GetRequestedRegion() const { return m_RequestedRegion; } void mitk::Surface::InitializeEmpty() { if (!m_PolyDatas.empty()) this->ClearData(); Superclass::InitializeTimeSlicedGeometry(); m_PolyDatas.push_back(NULL); m_Initialized = true; } void mitk::Surface::SetVtkPolyData(vtkPolyData* polyData, unsigned int t) { this->Expand(t + 1); if (m_PolyDatas[t] != NULL) { if (m_PolyDatas[t] == polyData) return; m_PolyDatas[t]->Delete(); } m_PolyDatas[t] = polyData; if(polyData != NULL) polyData->Register(NULL); m_CalculateBoundingBox = true; this->Modified(); this->UpdateOutputInformation(); } bool mitk::Surface::IsEmptyTimeStep(unsigned int t) const { if(!IsInitialized()) return false; vtkPolyData* polyData = const_cast(this)->GetVtkPolyData(t); return polyData == NULL || ( polyData->GetNumberOfLines() == 0 && polyData->GetNumberOfPolys() == 0 && polyData->GetNumberOfStrips() == 0 && polyData->GetNumberOfVerts() == 0 ); } vtkPolyData* mitk::Surface::GetVtkPolyData(unsigned int t) { if (t < m_PolyDatas.size()) { if(m_PolyDatas[t] == NULL && this->GetSource().IsNotNull()) { RegionType requestedRegion; requestedRegion.SetIndex(3, t); requestedRegion.SetSize(3, 1); this->SetRequestedRegion(&requestedRegion); this->GetSource()->Update(); } return m_PolyDatas[t]; } return NULL; } void mitk::Surface::UpdateOutputInformation() { if (this->GetSource().IsNotNull()) this->GetSource()->UpdateOutputInformation(); if (m_CalculateBoundingBox == true && !m_PolyDatas.empty()) this->CalculateBoundingBox(); else this->GetTimeSlicedGeometry()->UpdateInformation(); } void mitk::Surface::CalculateBoundingBox() { mitk::TimeSlicedGeometry* timeSlicedGeometry = this->GetTimeSlicedGeometry(); if (timeSlicedGeometry->GetTimeSteps() != m_PolyDatas.size()) mitkThrow() << "Number of geometry time steps is inconsistent with number of poly data pointers."; for (unsigned int i = 0; i < m_PolyDatas.size(); ++i) { vtkPolyData* polyData = m_PolyDatas[i]; vtkFloatingPointType bounds[6] = {0}; if (polyData != NULL && polyData->GetNumberOfPoints() > 0) { polyData->Update(); polyData->ComputeBounds(); polyData->GetBounds(bounds); } mitk::Geometry3D::Pointer geometry = timeSlicedGeometry->GetGeometry3D(i); if (geometry.IsNull()) mitkThrow() << "Time-sliced geometry is invalid (equals NULL)."; geometry->SetFloatBounds(bounds); } timeSlicedGeometry->UpdateInformation(); m_CalculateBoundingBox = false; } void mitk::Surface::SetRequestedRegionToLargestPossibleRegion() { m_RequestedRegion = GetLargestPossibleRegion(); } bool mitk::Surface::RequestedRegionIsOutsideOfTheBufferedRegion() { RegionType::IndexValueType end = m_RequestedRegion.GetIndex(3) + m_RequestedRegion.GetSize(3); if(static_cast(m_PolyDatas.size()) < end) return true; for(RegionType::IndexValueType t = m_RequestedRegion.GetIndex(3); t < end; ++t) { if(m_PolyDatas[t] == NULL) return true; } return false; } bool mitk::Surface::VerifyRequestedRegion() { if(m_RequestedRegion.GetIndex(3) >= 0 && m_RequestedRegion.GetIndex(3) + m_RequestedRegion.GetSize(3) <= m_PolyDatas.size()) return true; return false; } void mitk::Surface::SetRequestedRegion(const itk::DataObject* data ) { const mitk::Surface *surface = dynamic_cast(data); if (surface != NULL) m_RequestedRegion = surface->GetRequestedRegion(); else mitkThrow() << "Data object used to get requested region is not a mitk::Surface."; } void mitk::Surface::SetRequestedRegion(Surface::RegionType* region) { if (region == NULL) mitkThrow() << "Requested region is invalid (equals NULL)"; m_RequestedRegion = *region; } void mitk::Surface::CopyInformation(const itk::DataObject* data) { Superclass::CopyInformation(data); const mitk::Surface* surface = dynamic_cast(data); if (surface == NULL) mitkThrow() << "Data object used to get largest possible region is not a mitk::Surface."; m_LargestPossibleRegion = surface->GetLargestPossibleRegion(); } void mitk::Surface::Update() { using ::Update; if (this->GetSource().IsNull()) std::for_each(m_PolyDatas.begin(), m_PolyDatas.end(), Update); Superclass::Update(); } void mitk::Surface::Expand(unsigned int timeSteps) { if (timeSteps > m_PolyDatas.size()) { Superclass::Expand(timeSteps); m_PolyDatas.resize(timeSteps); m_CalculateBoundingBox = true; } } void mitk::Surface::ExecuteOperation(Operation* operation) { switch (operation->GetOperationType()) { case OpSURFACECHANGED: { mitk::SurfaceOperation* surfaceOperation = dynamic_cast(operation); if(surfaceOperation == NULL) break; unsigned int timeStep = surfaceOperation->GetTimeStep(); if(m_PolyDatas[timeStep] != NULL) { vtkPolyData* updatedPolyData = surfaceOperation->GetVtkPolyData(); if(updatedPolyData != NULL) { this->SetVtkPolyData(updatedPolyData, timeStep); this->CalculateBoundingBox(); this->Modified(); } } break; } default: return; } } unsigned int mitk::Surface::GetSizeOfPolyDataSeries() const { return m_PolyDatas.size(); } void mitk::Surface::Graft(const DataObject* data) { const Surface* surface = dynamic_cast(data); if(surface == NULL) mitkThrow() << "Data object used to graft surface is not a mitk::Surface."; this->CopyInformation(data); m_PolyDatas.clear(); for (unsigned int i = 0; i < surface->GetSizeOfPolyDataSeries(); ++i) { m_PolyDatas.push_back(vtkPolyData::New()); m_PolyDatas.back()->DeepCopy(const_cast(surface)->GetVtkPolyData(i)); } } void mitk::Surface::PrintSelf(std::ostream& os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "\nNumber PolyDatas: " << m_PolyDatas.size() << "\n"; unsigned int count = 0; for (std::vector::const_iterator it = m_PolyDatas.begin(); it != m_PolyDatas.end(); ++it) { os << "\n"; if(*it != NULL) { os << indent << "PolyData at time step " << count << ":\n"; os << indent << "Number of cells: " << (*it)->GetNumberOfCells() << "\n"; os << indent << "Number of points: " << (*it)->GetNumberOfPoints() << "\n\n"; os << indent << "VTKPolyData:\n"; (*it)->Print(os); } else { os << indent << "Empty PolyData at time step " << count << "\n"; } ++count; } } + +bool mitk::Equal( vtkPolyData* rightHandSide, vtkPolyData* leftHandSide, mitk::ScalarType eps, bool verbose ) +{ + bool noDifferenceFound = true; + + if(( rightHandSide == NULL ) ) + { + if(verbose) + { + MITK_INFO << "[Equal( vtkPolyData*, vtkPolyData* )] rightHandSide NULL."; + } + return false; + } + + if(( leftHandSide == NULL ) ) + { + if(verbose) + { + MITK_INFO << "[Equal( vtkPolyData*, vtkPolyData* )] leftHandSide NULL."; + } + return false; + } + + if( ! mitk::Equal( rightHandSide->GetNumberOfCells(), leftHandSide->GetNumberOfCells(), eps, verbose ) ) + { + if(verbose) + MITK_INFO << "[Equal( vtkPolyData*, vtkPolyData* )] Number of cells not equal"; + noDifferenceFound = false; + } + + if( ! mitk::Equal( rightHandSide->GetNumberOfVerts(), leftHandSide->GetNumberOfVerts(), eps, verbose )) + { + if(verbose) + MITK_INFO << "[Equal( vtkPolyData*, vtkPolyData* )] Number of vertices not equal"; + noDifferenceFound = false; + } + + if( ! mitk::Equal( rightHandSide->GetNumberOfLines(), leftHandSide->GetNumberOfLines(), eps, verbose ) ) + { + if(verbose) + MITK_INFO << "[Equal( vtkPolyData*, vtkPolyData* )] Number of lines not equal"; + noDifferenceFound = false; + } + + if( ! mitk::Equal( rightHandSide->GetNumberOfPolys(), leftHandSide->GetNumberOfPolys(), eps, verbose ) ) + { + if(verbose) + MITK_INFO << "[Equal( vtkPolyData*, vtkPolyData* )] Number of polys not equal"; + noDifferenceFound = false; + } + + if( ! mitk::Equal( rightHandSide->GetNumberOfStrips(), leftHandSide->GetNumberOfStrips(), eps, verbose ) ) + { + if(verbose) + MITK_INFO << "[Equal( vtkPolyData*, vtkPolyData* )] Number of strips not equal"; + noDifferenceFound = false; + } + + { + unsigned int numberOfPointsRight = rightHandSide->GetPoints()->GetNumberOfPoints(); + unsigned int numberOfPointsLeft = leftHandSide->GetPoints()->GetNumberOfPoints(); + if(! mitk::Equal( numberOfPointsRight, numberOfPointsLeft, eps, verbose )) + { + if(verbose) + MITK_INFO << "[Equal( vtkPolyData*, vtkPolyData* )] Number of points not equal"; + noDifferenceFound = false; + } + else + { + for( unsigned int i( 0 ); i < numberOfPointsRight; i++ ) + { + bool pointFound = false; + double pointOne[3]; + rightHandSide->GetPoints()->GetPoint(i, pointOne); + + for( unsigned int j( 0 ); j < numberOfPointsLeft; j++ ) + { + double pointTwo[3]; + leftHandSide->GetPoints()->GetPoint(j, pointTwo); + + double x = pointOne[0] - pointTwo[0]; + double y = pointOne[1] - pointTwo[1]; + double z = pointOne[2] - pointTwo[2]; + double distance = x*x + y*y + z*z; + + if( distance < eps ) + { + pointFound = true; + break; + } + } + if( !pointFound ) + { + if(verbose) + { + MITK_INFO << "[Equal( vtkPolyData*, vtkPolyData* )] Right hand side point with id " << i << " and coordinates ( " + << std::setprecision(12) + << pointOne[0] << " ; " << pointOne[1] << " ; " << pointOne[2] + << " ) could not be found in left hand side with epsilon " << eps << "."; + } + noDifferenceFound = false; + break; + } + } + } + } + return noDifferenceFound; +} + +bool mitk::Equal( mitk::Surface* rightHandSide, mitk::Surface* leftHandSide, mitk::ScalarType eps, bool verbose ) +{ + bool noDifferenceFound = true; + + if( rightHandSide == NULL ) + { + if(verbose) + MITK_INFO << "[Equal( mitk::surface*, mitk::surface* )] rightHandSide NULL."; + return false; + } + + if( leftHandSide == NULL ) + { + if(verbose) + MITK_INFO << "[Equal( mitk::surface*, mitk::surface* )] leftHandSide NULL."; + return false; + } + + if( ! mitk::Equal( rightHandSide->GetSizeOfPolyDataSeries(), leftHandSide->GetSizeOfPolyDataSeries(), eps, verbose ) ) + { + if(verbose) + MITK_INFO << "[Equal( mitk::surface*, mitk::surface* )] Size of PolyData series not equal."; + noDifferenceFound = false; + } + + if( ! mitk::Equal( rightHandSide->GetTimeSlicedGeometry(), leftHandSide->GetTimeSlicedGeometry(), eps, verbose ) ) + { + if(verbose) + MITK_INFO << "[Equal( mitk::surface*, mitk::surface* )] Time sliced geometries not equal"; + noDifferenceFound = false; + } + + for( unsigned int i( 0 ); i < rightHandSide->GetSizeOfPolyDataSeries(); i++ ) + { + if( ! mitk::Equal( rightHandSide->GetVtkPolyData( i ), leftHandSide->GetVtkPolyData( i ), eps, verbose ) ) + { + if(verbose) + MITK_INFO << "[Equal( mitk::surface*, mitk::surface* )] Poly datas not equal."; + noDifferenceFound = false; + } + } + + return noDifferenceFound; +} diff --git a/Core/Code/DataManagement/mitkSurface.h b/Core/Code/DataManagement/mitkSurface.h index 8c406d34e6..d580ec1339 100644 --- a/Core/Code/DataManagement/mitkSurface.h +++ b/Core/Code/DataManagement/mitkSurface.h @@ -1,79 +1,104 @@ /*=================================================================== 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 mitkSurface_h #define mitkSurface_h #include "mitkBaseData.h" #include "itkImageRegion.h" class vtkPolyData; namespace mitk { /** * \brief Class for storing surfaces (vtkPolyData). * \ingroup Data */ class MITK_CORE_EXPORT Surface : public BaseData { public: typedef itk::ImageRegion<5> RegionType; mitkClassMacro(Surface, BaseData); itkNewMacro(Self); mitkCloneMacro(Surface); void CalculateBoundingBox(); virtual void CopyInformation(const itk::DataObject *data); virtual void ExecuteOperation(Operation *operation); virtual void Expand( unsigned int timeSteps = 1 ); const RegionType& GetLargestPossibleRegion() const; virtual const RegionType& GetRequestedRegion() const; unsigned int GetSizeOfPolyDataSeries() const; virtual vtkPolyData* GetVtkPolyData(unsigned int t = 0); virtual void Graft( const DataObject* data ); virtual bool IsEmptyTimeStep(unsigned int t) const; virtual void PrintSelf( std::ostream& os, itk::Indent indent ) const; virtual bool RequestedRegionIsOutsideOfTheBufferedRegion(); virtual void SetRequestedRegion(const itk::DataObject *data); virtual void SetRequestedRegion(Surface::RegionType *region); virtual void SetRequestedRegionToLargestPossibleRegion(); virtual void SetVtkPolyData(vtkPolyData* polydata, unsigned int t = 0); virtual void Swap(Surface& other); virtual void Update(); virtual void UpdateOutputInformation(); virtual bool VerifyRequestedRegion(); + protected: Surface(); virtual ~Surface(); Surface(const Surface& other); Surface& operator=(Surface other); virtual void ClearData(); virtual void InitializeEmpty(); private: std::vector m_PolyDatas; mutable RegionType m_LargestPossibleRegion; RegionType m_RequestedRegion; bool m_CalculateBoundingBox; }; + + /** + * @brief Equal Compare two surfaces for equality, returns true if found equal. + * @param rightHandSide Surface to compare. + * @param leftHandSide Surface to compare. + * @param eps Epsilon to use for floating point comparison. Most of the time mitk::eps will be sufficient. + * @param verbose Flag indicating if the method should give a detailed console output. + * @return True if every comparison is true, false in any other case. + */ +MITK_CORE_EXPORT bool Equal( mitk::Surface* rightHandSide, mitk::Surface* leftHandSide, mitk::ScalarType eps, bool verbose); + + /** + * @brief Equal Compare two vtk PolyDatas for equality, returns true if found equal. + * @param rightHandSide Surface to compare. + * @param leftHandSide Surface to compare. + * @param eps Epsilon to use for floating point comparison. Most of the time mitk::eps will be sufficient. + * @param verbose Flag indicating if the method should give a detailed console output. + * @return True if every comparison is true, false in any other case. + * + * This will only check if the number of cells, vertices, polygons, stripes and lines is the same and whether + * all the two poly datas have the same number of points with the same coordinates. It is not checked whether + * all points are correctly connected. + */ +MITK_CORE_EXPORT bool Equal( vtkPolyData* rightHandSide, vtkPolyData* leftHandSide, mitk::ScalarType eps, bool verbose); } #endif diff --git a/Core/Code/DataManagement/mitkVector.h b/Core/Code/DataManagement/mitkVector.h index d5d3ee8728..760d2437d7 100644 --- a/Core/Code/DataManagement/mitkVector.h +++ b/Core/Code/DataManagement/mitkVector.h @@ -1,457 +1,458 @@ /*=================================================================== 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 MITKVECTOR_H_HEADER_INCLUDED_C1EBD0AD #define MITKVECTOR_H_HEADER_INCLUDED_C1EBD0AD // this is needed for memcopy in ITK // can be removed when fixed in ITK #include #include #include #include #include #include #include #include #include #include #ifndef DOXYGEN_SKIP namespace mitk { typedef float ScalarType; typedef itk::Matrix Matrix3D; typedef itk::Matrix Matrix4D; typedef vnl_matrix_fixed VnlMatrix3D; typedef itk::Transform Transform3D; typedef vnl_vector VnlVector; typedef vnl_vector_ref VnlVectorRef; typedef itk::Point Point2D; typedef itk::Point Point3D; typedef itk::Point Point4D; typedef itk::Point Point2I; typedef itk::Point Point3I; typedef itk::Point Point4I; typedef itk::Vector Vector2D; typedef itk::Vector Vector3D; typedef itk::Index<3> Index3D; typedef itk::ContinuousIndex ContinuousIndex3D; typedef vnl_quaternion Quaternion; //##Documentation //##@brief enumeration of the type a point can be enum PointSpecificationType { PTUNDEFINED = 0, PTSTART, PTCORNER, PTEDGE, PTEND }; typedef itk::NumericTraits ScalarTypeNumericTraits; MITK_CORE_EXPORT extern const ScalarType eps; MITK_CORE_EXPORT extern const ScalarType sqrteps; MITK_CORE_EXPORT extern const double large; template class VectorTraits { public: typedef T ValueType; }; template <> class VectorTraits { public: typedef ScalarType ValueType; }; template<> class VectorTraits { public: typedef double ValueType; }; template<> class VectorTraits< itk::Index<5> > { public: typedef itk::Index<5>::IndexValueType ValueType; }; template<> class VectorTraits< itk::Index<3> > { public: typedef itk::Index<3>::IndexValueType ValueType; }; template<> class VectorTraits< long int [3]> { public: typedef long int ValueType; }; template<> class VectorTraits< float [3]> { public: typedef float ValueType; }; template<> class VectorTraits< double [3]> { public: typedef double ValueType; }; template<> class VectorTraits< vnl_vector_fixed > { public: typedef ScalarType ValueType; }; template<> class VectorTraits< long unsigned int[3]> { public: typedef long unsigned int ValueType; }; template<> class VectorTraits< unsigned int *> { public: typedef unsigned int ValueType; }; template<> class VectorTraits< ScalarType[4] > { public: typedef ScalarType ValueType; }; template<> class VectorTraits< itk::Vector > { public: typedef float ValueType; }; template<> class VectorTraits< itk::Point > { public: typedef float ValueType; }; template<> class VectorTraits< itk::Point > { public: typedef float ValueType; }; template<> class VectorTraits< itk::Vector > { public: typedef double ValueType; }; template<> class VectorTraits< itk::Point > { public: typedef double ValueType; }; template<> class VectorTraits< itk::Vector > { public: typedef int ValueType; }; template<> class VectorTraits< itk::Point > { public: typedef int ValueType; }; template inline void itk2vtk(const Tin& in, Tout& out) { out[0]=(typename VectorTraits::ValueType)(in[0]); out[1]=(typename VectorTraits::ValueType)(in[1]); out[2]=(typename VectorTraits::ValueType)(in[2]); } template inline void vtk2itk(const Tin& in, Tout& out) { out[0]=(typename VectorTraits::ValueType)(in[0]); out[1]=(typename VectorTraits::ValueType)(in[1]); out[2]=(typename VectorTraits::ValueType)(in[2]); } template inline void FillVector3D(Tout& out, ScalarType x, ScalarType y, ScalarType z) { out[0] = (typename VectorTraits::ValueType)x; out[1] = (typename VectorTraits::ValueType)y; out[2] = (typename VectorTraits::ValueType)z; } template inline void FillVector4D(Tout& out, ScalarType x, ScalarType y, ScalarType z, ScalarType t) { out[0] = (typename VectorTraits::ValueType)x; out[1] = (typename VectorTraits::ValueType)y; out[2] = (typename VectorTraits::ValueType)z; out[3] = (typename VectorTraits::ValueType)t; } template inline void vnl2vtk(const vnl_vector& in, Tout *out) { unsigned int i; for(i=0; i inline void vtk2vnl(const Tin *in, vnl_vector& out) { unsigned int i; for(i=0; i inline void vtk2vnlref(const Tin *in, vnl_vector_ref& out) { unsigned int i; for(i=0; i inline void vnl2vtk(const vnl_vector_fixed& in, Tout *out) { unsigned int i; for(i=0; i inline void vtk2vnl(const Tin *in, vnl_vector_fixed& out) { unsigned int i; for(i=0; i itk::Vector operator+(const itk::Vector &vector, const itk::Point &point) { itk::Vector sub; for( unsigned int i=0; i inline itk::Vector& operator+=(itk::Vector &vector, const itk::Point &point) { for( unsigned int i=0; i itk::Vector operator-(const itk::Vector &vector, const itk::Point &point) { itk::Vector sub; for( unsigned int i=0; i inline itk::Vector& operator-=(itk::Vector &vector, const itk::Point &point) { for( unsigned int i=0; i inline bool MatrixEqualRMS(const vnl_matrix_fixed& matrix1,const vnl_matrix_fixed& matrix2,mitk::ScalarType epsilon=mitk::eps) { if ( (matrix1.rows() == matrix2.rows()) && (matrix1.cols() == matrix2.cols()) ) { vnl_matrix_fixed differenceMatrix = matrix1-matrix2; if (differenceMatrix.rms() inline bool MatrixEqualRMS(const itk::Matrix& matrix1,const itk::Matrix& matrix2,mitk::ScalarType epsilon=mitk::eps) { return mitk::MatrixEqualRMS(matrix1.GetVnlMatrix(),matrix2.GetVnlMatrix(),epsilon); } /*! \brief Check for element-wise matrix equality with a user defined accuracy. \param matrix1 first vnl matrix \param matrix2 second vnl matrix \param epsilon user defined accuracy bounds */ template inline bool MatrixEqualElementWise(const vnl_matrix_fixed& matrix1,const vnl_matrix_fixed& matrix2,mitk::ScalarType epsilon=mitk::eps) { if ( (matrix1.rows() == matrix2.rows()) && (matrix1.cols() == matrix2.cols()) ) { for( unsigned int r=0; repsilon) { return false; } } } return true; } else { return false; } } /*! \brief Check for element-wise matrix equality with a user defined accuracy. \param matrix1 first itk matrix \param matrix2 second itk matrix \param epsilon user defined accuracy bounds */ template inline bool MatrixEqualElementWise(const itk::Matrix& matrix1,const itk::Matrix& matrix2,mitk::ScalarType epsilon=mitk::eps) { return mitk::MatrixEqualElementWise(matrix1.GetVnlMatrix(),matrix2.GetVnlMatrix(),epsilon); } template inline bool Equal(const itk::Vector& vector1, const itk::Vector& vector2, TCoordRep eps=mitk::eps) { typename itk::Vector::VectorType diff = vector1-vector2; for (unsigned int i=0; ieps || diff[i]<-eps) return false; return true; } template inline bool Equal(const itk::Point& vector1, const itk::Point& vector2, TCoordRep eps=mitk::eps) { typename itk::Point::VectorType diff = vector1-vector2; for (unsigned int i=0; ieps || diff[i]<-eps) return false; return true; } inline bool Equal(const mitk::VnlVector& vector1, const mitk::VnlVector& vector2, ScalarType eps=mitk::eps) { mitk::VnlVector diff = vector1-vector2; for (unsigned int i=0; ieps || diff[i]<-eps) return false; return true; } inline bool Equal(double scalar1, double scalar2, ScalarType eps=mitk::eps) { - return fabs(scalar1-scalar2) < eps; + bool isEqual( fabs(scalar1-scalar2) < eps ); + return isEqual; } template inline bool Equal(const vnl_vector_fixed & vector1, const vnl_vector_fixed& vector2, TCoordRep eps=mitk::eps) { vnl_vector_fixed diff = vector1-vector2; bool returnValue = true; for( unsigned int i=0; ieps || diff[i]<-eps) { returnValue = false; } } return returnValue; } template inline void TransferMatrix(const itk::Matrix& in, itk::Matrix& out) { for (unsigned int i = 0; i < in.RowDimensions; ++i) for (unsigned int j = 0; j < in.ColumnDimensions; ++j) out[i][j] = in[i][j]; } } // namespace mitk #endif //DOXYGEN_SKIP /* * This part of the code has been shifted here to avoid compiler clashes * caused by including before the declaration of * the Equal() methods above. This problem occurs when using MSVC and is * probably related to a compiler bug. */ #include namespace mitk { typedef itk::AffineGeometryFrame::TransformType AffineTransform3D; } #define mitkSetConstReferenceMacro(name,type) \ virtual void Set##name (const type & _arg) \ { \ itkDebugMacro("setting " << #name " to " << _arg ); \ if (this->m_##name != _arg) \ { \ this->m_##name = _arg; \ this->Modified(); \ } \ } #define mitkSetVectorMacro(name,type) \ mitkSetConstReferenceMacro(name,type) #define mitkGetVectorMacro(name,type) \ itkGetConstReferenceMacro(name,type) #endif /* MITKVECTOR_H_HEADER_INCLUDED_C1EBD0AD */ diff --git a/Core/Code/Testing/files.cmake b/Core/Code/Testing/files.cmake index 1d25bb196b..fd5290f73e 100644 --- a/Core/Code/Testing/files.cmake +++ b/Core/Code/Testing/files.cmake @@ -1,163 +1,167 @@ # 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 + mitkGeometry3DEqualTest.cpp mitkGeometryDataToSurfaceFilterTest.cpp mitkGlobalInteractionTest.cpp + mitkImageEqualTest.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 + mitkPointSetEqualTest.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 + mitkSurfaceEqualTest.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 mitkPropertyAliasesTest.cpp mitkPropertyDescriptionsTest.cpp mitkPropertyExtensionsTest.cpp mitkPropertyFiltersTest.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 mitkLabelOverlay3DRendering2DTest.cpp mitkLabelOverlay3DRendering3DTest.cpp mitkTextOverlay2DRenderingTest.cpp mitkTextOverlay2DLayouterRenderingTest.cpp mitkTextOverlay3DRendering2DTest.cpp mitkTextOverlay3DRendering3DTest.cpp mitkTextOverlay3DColorRenderingTest.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/mitkGeometry3DEqualTest.cpp b/Core/Code/Testing/mitkGeometry3DEqualTest.cpp new file mode 100644 index 0000000000..f36606c2ff --- /dev/null +++ b/Core/Code/Testing/mitkGeometry3DEqualTest.cpp @@ -0,0 +1,126 @@ +/*=================================================================== + +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 "mitkGeometry3D.h" +#include "mitkTestingMacros.h" + +/** Members used inside the different (sub-)tests. All members are initialized via Setup().*/ +mitk::Geometry3D::Pointer m_Geometry3D; +mitk::Geometry3D::Pointer m_AnotherGeometry3D; + +/** +* @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used members for a new test case. (If the members are not used in a test, the method does not need to be called). +*/ +static void Setup() +{ + m_Geometry3D = mitk::Geometry3D::New(); + m_Geometry3D->Initialize(); + m_AnotherGeometry3D = m_Geometry3D->Clone(); +} + +static void Equal_CloneAndOriginal_ReturnsTrue() +{ + Setup(); + MITK_TEST_EQUAL( m_Geometry3D, m_AnotherGeometry3D, "A clone should be equal to its original."); +} + +static void Equal_DifferentOrigin_ReturnsFalse() +{ + Setup(); + + mitk::Point3D origin; + origin[0] = 0.0; + origin[1] = 0.0; + origin[2] = 1.0 + 2*mitk::eps; + m_AnotherGeometry3D->SetOrigin(origin); + + MITK_TEST_NOT_EQUAL( m_Geometry3D, m_AnotherGeometry3D, "Origin was modified. Result should be false."); +} + +static void Equal_DifferentIndexToWorldTransform_ReturnsFalse() +{ + Setup(); + + //Create another index to world transform and make it different somehow + mitk::AffineTransform3D::Pointer differentIndexToWorldTransform = mitk::AffineTransform3D::New(); + + mitk::AffineTransform3D::MatrixType differentMatrix; + differentMatrix.SetIdentity(); + differentMatrix(1,1) = 2; + + differentIndexToWorldTransform->SetMatrix( differentMatrix ); + m_AnotherGeometry3D->SetIndexToWorldTransform(differentIndexToWorldTransform); + + MITK_TEST_NOT_EQUAL( m_Geometry3D, m_AnotherGeometry3D, "IndexToWorldTransform was modified. Result should be false."); +} + +static void Equal_DifferentSpacing_ReturnsFalse() +{ + Setup(); + + mitk::Vector3D differentSpacing; + differentSpacing[0] = 1.0; + differentSpacing[1] = 1.0 + 2*mitk::eps; + differentSpacing[2] = 1.0; + + m_AnotherGeometry3D->SetSpacing(differentSpacing); + + MITK_TEST_NOT_EQUAL( m_Geometry3D, m_AnotherGeometry3D, "Spacing was modified. Result should be false."); +} + +static void Equal_InputIsNull_ReturnsFalse() +{ + mitk::Geometry3D::Pointer geometry3D = NULL; + MITK_TEST_NOT_EQUAL( geometry3D, geometry3D, "Input is NULL. Result should be false."); +} + +static void Equal_DifferentImageGeometry_ReturnsFalse() +{ + Setup(); + + m_AnotherGeometry3D->SetImageGeometry(true); + + MITK_TEST_NOT_EQUAL( m_Geometry3D, m_AnotherGeometry3D, "One Geometry is image, the other is not. Result should be false."); +} + +static void Equal_DifferentBoundingBox_ReturnsFalse() +{ + Setup(); + + //create different bounds to make the comparison false + float bounds[ ] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + m_AnotherGeometry3D->SetBounds(bounds); + + MITK_TEST_NOT_EQUAL( m_Geometry3D, m_AnotherGeometry3D, "Bounds are different. Result should be false."); +} + +/** + * @brief mitkGeometry3DEqualTest A test class for Equal methods in mitk::Geometry3D. + */ +int mitkGeometry3DEqualTest(int /*argc*/, char* /*argv*/[]) +{ + MITK_TEST_BEGIN(mitkGeometry3DEqualTest); + + Equal_CloneAndOriginal_ReturnsTrue(); + Equal_InputIsNull_ReturnsFalse(); + Equal_DifferentSpacing_ReturnsFalse(); + Equal_DifferentImageGeometry_ReturnsFalse(); + Equal_DifferentOrigin_ReturnsFalse(); + Equal_DifferentIndexToWorldTransform_ReturnsFalse(); + Equal_DifferentBoundingBox_ReturnsFalse(); + + MITK_TEST_END(); +} diff --git a/Core/Code/Testing/mitkImageEqualTest.cpp b/Core/Code/Testing/mitkImageEqualTest.cpp new file mode 100644 index 0000000000..f4cf2f1612 --- /dev/null +++ b/Core/Code/Testing/mitkImageEqualTest.cpp @@ -0,0 +1,119 @@ +/*=================================================================== + +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 "mitkImage.h" +#include "mitkImageGenerator.h" +#include "mitkTestingMacros.h" +#include "mitkImageSliceSelector.h" + +/** Members used inside the different (sub-)tests. All members are initialized via Setup().*/ +mitk::Image::Pointer m_Image; +mitk::Image::Pointer m_AnotherImage; + +/** +* @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used members for a new test case. (If the members are not used in a test, the method does not need to be called). +*/ +static void Setup() +{ + //generate a gradient test image + m_Image = mitk::ImageGenerator::GenerateGradientImage(3u, 3u, 1u); + m_AnotherImage = m_Image->Clone(); +} + +void Equal_CloneAndOriginal_ReturnsTrue() +{ + Setup(); + MITK_TEST_EQUAL( m_Image, m_AnotherImage, "A clone should be equal to its original."); +} + +static void Equal_InputIsNull_ReturnsFalse() +{ + mitk::Image::Pointer image = NULL; + MITK_TEST_NOT_EQUAL( image, image, "Input is NULL. Result should be false."); +} + +static void Equal_DifferentImageGeometry_ReturnsFalse() +{ + Setup(); + + mitk::Point3D origin; + origin[0] = 0.0; + origin[1] = 0.0; + origin[2] = mitk::eps * 1.01; + + m_AnotherImage->GetGeometry()->SetOrigin(origin); + + MITK_TEST_NOT_EQUAL( m_Image, m_AnotherImage, "One origin was modified. Result should be false."); +} + +static void Equal_DifferentPixelTypes_ReturnsFalse() +{ + Setup(); + + m_AnotherImage = mitk::ImageGenerator::GenerateGradientImage(3u, 3u, 1u); + + MITK_TEST_NOT_EQUAL( m_Image, m_AnotherImage, "One pixel type is float, the other unsigned char. Result should be false."); +} + +static void Equal_DifferentDimensions_ReturnsFalse() +{ + Setup(); + + m_AnotherImage = mitk::ImageGenerator::GenerateGradientImage(5u, 7u, 3u); + + MITK_TEST_NOT_EQUAL( m_Image, m_AnotherImage, "Dimensions of first image are: (3, 3, 1). Dimensions of second image are: (5, 7, 3). Result should be false."); +} + +static void Equal_DifferentDimensionalities_ReturnsFalse() +{ + Setup(); + + //Select the first slice of a 2D image and compare it to the 3D original + mitk::ImageSliceSelector::Pointer sliceSelector = mitk::ImageSliceSelector::New(); + sliceSelector->SetInput( m_Image ); + sliceSelector->SetSliceNr( 0 ); + sliceSelector->Update(); + m_AnotherImage = sliceSelector->GetOutput(); + + MITK_TEST_NOT_EQUAL( m_Image, m_AnotherImage, "First image is 3D. Second image is 2D. Result should be false."); +} + +static void Equal_DifferentPixelValues_ReturnsFalse() +{ + //todo: Replace the random images via simpler images with fixed values. + m_Image = mitk::ImageGenerator::GenerateRandomImage(3u, 3u); + m_AnotherImage = mitk::ImageGenerator::GenerateRandomImage(3u, 3u); + + MITK_TEST_NOT_EQUAL( m_Image, m_AnotherImage, "We compare two random images. Result should be false."); +} + +/** + * @brief mitkImageAreEqualTest A test class for Equal methods in mitk::Image. + */ +int mitkImageEqualTest(int /*argc*/, char* /*argv*/[]) +{ + MITK_TEST_BEGIN(mitkImageAreEqualTest); + + Equal_CloneAndOriginal_ReturnsTrue(); + Equal_InputIsNull_ReturnsFalse(); + Equal_DifferentImageGeometry_ReturnsFalse(); + Equal_DifferentPixelTypes_ReturnsFalse(); + Equal_DifferentDimensions_ReturnsFalse(); + Equal_DifferentDimensionalities_ReturnsFalse(); + Equal_DifferentPixelValues_ReturnsFalse(); + + MITK_TEST_END(); +} diff --git a/Core/Code/Testing/mitkImageTest.cpp b/Core/Code/Testing/mitkImageTest.cpp index 5e6f003e50..e91fada8c4 100644 --- a/Core/Code/Testing/mitkImageTest.cpp +++ b/Core/Code/Testing/mitkImageTest.cpp @@ -1,497 +1,499 @@ /*=================================================================== 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. ===================================================================*/ // mitk includes #include #include #include #include "mitkItkImageFileReader.h" #include #include #include "mitkImageGenerator.h" #include "mitkImageReadAccessor.h" #include "mitkException.h" +#include "mitkImageSliceSelector.h" + // itk includes #include #include // stl includes #include // vtk includes #include // Checks if reference count is correct after using GetVtkImageData() bool ImageVtkDataReferenceCheck(const char* fname) { const std::string filename = std::string(fname); mitk::ItkImageFileReader::Pointer imageReader = mitk::ItkImageFileReader::New(); try { imageReader->SetFileName(filename); imageReader->Update(); } catch(...) { MITK_TEST_FAILED_MSG(<< "Could not read file for testing: " << filename); return false; } { mitk::Image::Pointer image = imageReader->GetOutput(); vtkImageData* vtk = image->GetVtkImageData(); if(vtk == NULL) return false; } return true; } class mitkImageTestClass { public: void SetClonedGeometry_None_ClonedEqualInput() { mitk::Image::Pointer image = mitk::ImageGenerator::GenerateRandomImage(100, 100, 100, 1, 0.2, 0.3, 0.4); //----------------- // geometry information for image mitk::Point3D origin; mitk::Vector3D right, bottom; mitk::Vector3D spacing; mitk::FillVector3D(origin, 17.0, 19.92, 7.83); mitk::FillVector3D(right, 1.0, 2.0, 3.0); mitk::FillVector3D(bottom, 0.0, -3.0, 2.0); mitk::FillVector3D(spacing, 0.78, 0.91, 2.23); //InitializeStandardPlane(rightVector, downVector, spacing) mitk::PlaneGeometry::Pointer planegeometry = mitk::PlaneGeometry::New(); planegeometry->InitializeStandardPlane(100, 100, right, bottom, &spacing); planegeometry->SetOrigin(origin); planegeometry->ChangeImageGeometryConsideringOriginOffset(true); image->SetClonedGeometry(planegeometry); mitk::Geometry3D::Pointer imageGeometry = image->GetGeometry(); itk::ScalableAffineTransform* frameNew = imageGeometry->GetIndexToWorldTransform(); itk::ScalableAffineTransform* frameOld = planegeometry->GetIndexToWorldTransform(); bool matrixEqual = true; for (int i = 0; i < 16; ++i) { double valueNew = *(frameNew->GetMatrix()[i]); double valueOld = *(frameOld->GetMatrix()[i]); //MITK_INFO << "Index: " << i << " Old: " << valueOld << " New: " << valueNew << " Difference:" << valueOld-valueNew<< std::endl; matrixEqual = matrixEqual && mitk::Equal(valueNew, valueOld, mitk::eps); } // Disabled because this test fails on the dashboard. Does not fail on my machine. // See Bug 6505 // MITK_TEST_CONDITION(matrixEqual, "Matrix elements of cloned matrix equal original matrix"); } }; int mitkImageTest(int argc, char* argv[]) { MITK_TEST_BEGIN(mitkImageTest); mitkImageTestClass tester; tester.SetClonedGeometry_None_ClonedEqualInput(); //Create Image out of nowhere mitk::Image::Pointer imgMem = mitk::Image::New(); mitk::PixelType pt = mitk::MakeScalarPixelType(); unsigned int dim[]={100,100,20}; MITK_TEST_CONDITION_REQUIRED( imgMem.IsNotNull(), "An image was created. "); // Initialize image imgMem->Initialize( pt, 3, dim); MITK_TEST_CONDITION_REQUIRED( imgMem->IsInitialized(), "Image::IsInitialized() ?"); MITK_TEST_CONDITION_REQUIRED( imgMem->GetPixelType() == pt, "PixelType was set correctly."); int *p; int *p2; try { mitk::ImageReadAccessor imgMemAcc(imgMem); p = (int*)imgMemAcc.GetData(); } catch (mitk::Exception& e) { MITK_ERROR << e.what(); } MITK_TEST_CONDITION( p != NULL, "GetData() returned not-NULL pointer."); // filling image const unsigned int size = dim[0]*dim[1]*dim[2]; for(unsigned int i=0; iGetSliceData(dim[2]/2)); p2 = (int*)imgMemAcc.GetData(); } catch (mitk::Exception& e) { MITK_ERROR << e.what(); } MITK_TEST_CONDITION_REQUIRED( p2 != NULL, "Valid slice data returned"); unsigned int xy_size = dim[0]*dim[1]; unsigned int start_mid_slice = (dim[2]/2)*xy_size; isEqual = true; for(unsigned int i=0; i(); imgMem->Initialize( pType , 3, dim); MITK_TEST_CONDITION_REQUIRED(imgMem->GetDimension()== 3, "Testing initialization parameter dimension!"); MITK_TEST_CONDITION_REQUIRED(imgMem->GetPixelType() == pType, "Testing initialization parameter pixeltype!"); MITK_TEST_CONDITION_REQUIRED(imgMem->GetDimension(0) == dim[0] && imgMem->GetDimension(1)== dim[1] && imgMem->GetDimension(2)== dim[2], "Testing initialization of dimensions!"); MITK_TEST_CONDITION( imgMem->IsInitialized(), "Image is initialized."); // Setting volume again: try { mitk::ImageReadAccessor imgMemAcc(imgMem); imgMem->SetVolume(imgMemAcc.GetData()); } catch (mitk::Exception& e) { MITK_ERROR << e.what(); } //----------------- // geometry information for image mitk::Point3D origin; mitk::Vector3D right, bottom; mitk::Vector3D spacing; mitk::FillVector3D(origin, 17.0, 19.92, 7.83); mitk::FillVector3D(right, 1.0, 2.0, 3.0); mitk::FillVector3D(bottom, 0.0, -3.0, 2.0); mitk::FillVector3D(spacing, 0.78, 0.91, 2.23); //InitializeStandardPlane(rightVector, downVector, spacing) mitk::PlaneGeometry::Pointer planegeometry = mitk::PlaneGeometry::New(); planegeometry->InitializeStandardPlane(100, 100, right, bottom, &spacing); planegeometry->SetOrigin(origin); // Testing Initialize(const mitk::PixelType& type, const mitk::Geometry3D& geometry, unsigned int slices) with PlaneGeometry and GetData(): "; imgMem->Initialize( mitk::MakePixelType(), *planegeometry); MITK_TEST_CONDITION_REQUIRED( imgMem->GetGeometry()->GetOrigin() == static_cast(planegeometry)->GetOrigin(), "Testing correct setting of geometry via initialize!"); try { mitk::ImageReadAccessor imgMemAcc(imgMem); p = (int*)imgMemAcc.GetData(); } catch (mitk::Exception& e) { MITK_ERROR << e.what(); } MITK_TEST_CONDITION_REQUIRED( p!=NULL, "GetData() returned valid pointer."); // Testing Initialize(const mitk::PixelType& type, int sDim, const mitk::PlaneGeometry& geometry) and GetData(): "; imgMem->Initialize( mitk::MakePixelType() , 40, *planegeometry); try { mitk::ImageReadAccessor imgMemAcc(imgMem); p = (int*)imgMemAcc.GetData(); } catch (mitk::Exception& e) { MITK_ERROR << e.what(); } MITK_TEST_CONDITION_REQUIRED( p != NULL, "GetData() returned valid pointer."); //----------------- // testing origin information and methods MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetGeometry()->GetOrigin(), origin), "Testing correctness of origin via GetGeometry()->GetOrigin(): "); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetTimeSlicedGeometry()->GetOrigin(), origin), "Testing correctness of origin via GetTimeSlicedGeometry()->GetOrigin(): "); // Setting origin via SetOrigin(origin): "; mitk::FillVector3D(origin, 37.0, 17.92, 27.83); imgMem->SetOrigin(origin); // Test origin MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetGeometry()->GetOrigin(), origin), "Testing correctness of changed origin via GetGeometry()->GetOrigin(): "); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetTimeSlicedGeometry()->GetOrigin(), origin), "Testing correctness of changed origin via GetTimeSlicedGeometry()->GetOrigin(): "); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetSlicedGeometry()->GetGeometry2D(0)->GetOrigin(), origin), "Testing correctness of changed origin via GetSlicedGeometry()->GetGeometry2D(0)->GetOrigin(): "); //----------------- - // testing spacing information and methods + // testing spacing information and methodsunsigned int dim[]={100,100,20}; MITK_TEST_CONDITION_REQUIRED(mitk::Equal(imgMem->GetGeometry()->GetSpacing(), spacing), "Testing correct spacing from Geometry3D!"); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(imgMem->GetTimeSlicedGeometry()->GetSpacing(), spacing), "Testing correctspacing from TimeSlicedGeometry!"); mitk::FillVector3D(spacing, 7.0, 0.92, 1.83); imgMem->SetSpacing(spacing); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetGeometry()->GetSpacing(), spacing), "Testing correctness of changed spacing via GetGeometry()->GetSpacing(): "); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetTimeSlicedGeometry()->GetSpacing(), spacing), "Testing correctness of changed spacing via GetTimeSlicedGeometry()->GetSpacing(): "); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetSlicedGeometry()->GetGeometry2D(0)->GetSpacing(), spacing), "Testing correctness of changed spacing via GetSlicedGeometry()->GetGeometry2D(0)->GetSpacing(): "); mitk::Image::Pointer vecImg = mitk::Image::New(); try { mitk::ImageReadAccessor imgMemAcc(imgMem); vecImg->Initialize( imgMem->GetPixelType(), *imgMem->GetGeometry(), 2 /* #channels */, 0 /*tDim*/ ); vecImg->SetImportChannel(const_cast(imgMemAcc.GetData()), 0, mitk::Image::CopyMemory ); vecImg->SetImportChannel(const_cast(imgMemAcc.GetData()), 1, mitk::Image::CopyMemory ); mitk::ImageReadAccessor vecImgAcc(vecImg); mitk::ImageReadAccessor vecImgAcc0(vecImg, vecImg->GetChannelData(0)); mitk::ImageReadAccessor vecImgAcc1(vecImg, vecImg->GetChannelData(1)); MITK_TEST_CONDITION_REQUIRED(vecImgAcc0.GetData() != NULL && vecImgAcc1.GetData() != NULL, "Testing set and return of channel data!"); MITK_TEST_CONDITION_REQUIRED( vecImg->IsValidSlice(0,0,1) , ""); MITK_TEST_OUTPUT(<< " Testing whether CopyMemory worked"); MITK_TEST_CONDITION_REQUIRED(imgMemAcc.GetData() != vecImgAcc.GetData(), ""); MITK_TEST_OUTPUT(<< " Testing destruction after SetImportChannel"); vecImg = NULL; MITK_TEST_CONDITION_REQUIRED(vecImg.IsNull() , "testing destruction!"); } catch (mitk::Exception& e) { MITK_ERROR << e.what(); } //----------------- MITK_TEST_OUTPUT(<< "Testing initialization via vtkImageData"); MITK_TEST_OUTPUT(<< " Setting up vtkImageData"); vtkImageData* vtkimage = vtkImageData::New(); vtkimage->Initialize(); vtkimage->SetDimensions( 2, 3, 4); double vtkorigin[] = {-350,-358.203, -1363.5}; vtkimage->SetOrigin(vtkorigin); mitk::Point3D vtkoriginAsMitkPoint; mitk::vtk2itk(vtkorigin, vtkoriginAsMitkPoint); double vtkspacing[] = {1.367, 1.367, 2}; vtkimage->SetSpacing(vtkspacing); vtkimage->SetScalarType( VTK_SHORT ); vtkimage->AllocateScalars(); std::cout<<"[PASSED]"<Initialize(vtkimage); MITK_TEST_CONDITION_REQUIRED(mitkByVtkImage->IsInitialized(), ""); vtkimage->Delete(); MITK_TEST_OUTPUT(<< " Testing whether spacing has been correctly initialized from vtkImageData"); mitk::Vector3D spacing2 = mitkByVtkImage->GetGeometry()->GetSpacing(); mitk::Vector3D vtkspacingAsMitkVector; mitk::vtk2itk(vtkspacing, vtkspacingAsMitkVector); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(spacing2,vtkspacingAsMitkVector), ""); MITK_TEST_OUTPUT(<< " Testing whether GetSlicedGeometry(0)->GetOrigin() has been correctly initialized from vtkImageData"); mitk::Point3D origin2 = mitkByVtkImage->GetSlicedGeometry(0)->GetOrigin(); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(origin2,vtkoriginAsMitkPoint), ""); MITK_TEST_OUTPUT(<< " Testing whether GetGeometry()->GetOrigin() has been correctly initialized from vtkImageData"); origin2 = mitkByVtkImage->GetGeometry()->GetOrigin(); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(origin2,vtkoriginAsMitkPoint), ""); MITK_TEST_OUTPUT(<< " Testing whether GetTimeSlicedGeometry()->GetOrigin() has been correctly initialized from vtkImageData"); origin2 = mitkByVtkImage->GetTimeSlicedGeometry()->GetOrigin(); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(origin2,vtkoriginAsMitkPoint), ""); // TODO test the following initializers on channel-incorporation // void mitk::Image::Initialize(const mitk::PixelType& type, unsigned int dimension, unsigned int *dimensions, unsigned int channels) // void mitk::Image::Initialize(const mitk::PixelType& type, int sDim, const mitk::Geometry2D& geometry2d, bool flipped, unsigned int channels, int tDim ) // void mitk::Image::Initialize(const mitk::Image* image) // void mitk::Image::Initialize(const mitkIpPicDescriptor* pic, int channels, int tDim, int sDim) //mitk::Image::Pointer vecImg = mitk::Image::New(); //vecImg->Initialize(PixelType(typeid(float), 6, itk::ImageIOBase::SYMMETRICSECONDRANKTENSOR), *imgMem->GetGeometry(), 2 /* #channels */, 0 /*tDim*/, false /*shiftBoundingBoxMinimumToZero*/ ); //vecImg->Initialize(PixelType(typeid(itk::Vector)), *imgMem->GetGeometry(), 2 /* #channels */, 0 /*tDim*/, false /*shiftBoundingBoxMinimumToZero*/ ); // testing access by index coordinates and by world coordinates MITK_TEST_CONDITION_REQUIRED(argc == 2, "Check if test image is accessible!"); const std::string filename = std::string(argv[1]); mitk::ItkImageFileReader::Pointer imageReader = mitk::ItkImageFileReader::New(); try { imageReader->SetFileName(filename); imageReader->Update(); } catch(...) { MITK_TEST_FAILED_MSG(<< "Could not read file for testing: " << filename); return 0; } mitk::Image::Pointer image = imageReader->GetOutput(); // generate a random point in world coordinates mitk::Point3D xMax, yMax, zMax, xMaxIndex, yMaxIndex, zMaxIndex; xMaxIndex.Fill(0.0f); yMaxIndex.Fill(0.0f); zMaxIndex.Fill(0.0f); xMaxIndex[0] = image->GetLargestPossibleRegion().GetSize()[0]; yMaxIndex[1] = image->GetLargestPossibleRegion().GetSize()[1]; zMaxIndex[2] = image->GetLargestPossibleRegion().GetSize()[2]; image->GetGeometry()->IndexToWorld(xMaxIndex, xMax); image->GetGeometry()->IndexToWorld(yMaxIndex, yMax); image->GetGeometry()->IndexToWorld(zMaxIndex, zMax); MITK_INFO << "Origin " << image->GetGeometry()->GetOrigin()[0] << " "<< image->GetGeometry()->GetOrigin()[1] << " "<< image->GetGeometry()->GetOrigin()[2] << ""; MITK_INFO << "MaxExtend " << xMax[0] << " "<< yMax[1] << " "<< zMax[2] << ""; mitk::Point3D point; itk::Statistics::MersenneTwisterRandomVariateGenerator::Pointer randomGenerator = itk::Statistics::MersenneTwisterRandomVariateGenerator::New(); randomGenerator->Initialize( std::rand() ); // initialize with random value, to get sensible random points for the image point[0] = randomGenerator->GetUniformVariate( image->GetGeometry()->GetOrigin()[0], xMax[0]); point[1] = randomGenerator->GetUniformVariate( image->GetGeometry()->GetOrigin()[1], yMax[1]); point[2] = randomGenerator->GetUniformVariate( image->GetGeometry()->GetOrigin()[2], zMax[2]); MITK_INFO << "RandomPoint " << point[0] << " "<< point[1] << " "<< point[2] << ""; // test values and max/min mitk::ScalarType imageMin = image->GetStatistics()->GetScalarValueMin(); mitk::ScalarType imageMax = image->GetStatistics()->GetScalarValueMax(); mitk::ScalarType value = image->GetPixelValueByWorldCoordinate(point); MITK_INFO << imageMin << " "<< imageMax << " "<< value << ""; MITK_TEST_CONDITION( (value >= imageMin && value <= imageMax), "Value returned is between max/min"); // test accessing PixelValue with coordinate leading to a negative index const mitk::Point3D geom_origin = image->GetGeometry()->GetOrigin(); const mitk::Point3D geom_center = image->GetGeometry()->GetCenter(); const unsigned int timestep = 0; // shift position from origin outside of the image ( in the opposite direction to [center-origin] vector which points in the inside) mitk::Point3D position = geom_origin + (geom_origin - geom_center); MITK_TEST_CONDITION_REQUIRED( image->GetPixelValueByWorldCoordinate(position, timestep) == 0, "Test access to the outside of the image") // testing the clone method of mitk::Image mitk::Image::Pointer cloneImage = image->Clone(); MITK_TEST_CONDITION_REQUIRED(cloneImage->GetDimension() == image->GetDimension(), "Clone (testing dimension)"); MITK_TEST_CONDITION_REQUIRED(cloneImage->GetPixelType() == image->GetPixelType(), "Clone (testing pixel type)"); // After cloning an image the geometry of both images should be equal too MITK_TEST_CONDITION_REQUIRED(cloneImage->GetGeometry()->GetOrigin() == image->GetGeometry()->GetOrigin(), "Clone (testing origin)"); MITK_TEST_CONDITION_REQUIRED(cloneImage->GetGeometry()->GetSpacing() == image->GetGeometry()->GetSpacing(), "Clone (testing spacing)"); MITK_TEST_CONDITION_REQUIRED(mitk::MatrixEqualElementWise(cloneImage->GetGeometry()->GetIndexToWorldTransform()->GetMatrix(), image->GetGeometry()->GetIndexToWorldTransform()->GetMatrix()), "Clone (testing transformation matrix)"); MITK_TEST_CONDITION_REQUIRED(mitk::MatrixEqualElementWise(cloneImage->GetTimeSlicedGeometry()->GetGeometry3D(cloneImage->GetDimension(3)-1)->GetIndexToWorldTransform()->GetMatrix(), cloneImage->GetTimeSlicedGeometry()->GetGeometry3D(image->GetDimension(3)-1)->GetIndexToWorldTransform()->GetMatrix()), "Clone(testing time sliced geometry)"); for (unsigned int i = 0u; i < cloneImage->GetDimension(); ++i) { MITK_TEST_CONDITION_REQUIRED(cloneImage->GetDimension(i) == image->GetDimension(i), "Clone (testing dimension " << i << ")"); } //access via itk if(image->GetDimension()> 3) // CastToItk only works with 3d images so we need to check for 4d images { mitk::ImageTimeSelector::Pointer selector = mitk::ImageTimeSelector::New(); selector->SetTimeNr(0); selector->SetInput(image); selector->Update(); image = selector->GetOutput(); } if(image->GetDimension()==3) { typedef itk::Image ItkFloatImage3D; ItkFloatImage3D::Pointer itkimage; mitk::CastToItkImage(image, itkimage); MITK_TEST_CONDITION_REQUIRED(itkimage.IsNotNull(), "Test conversion to itk::Image!"); mitk::Point3D itkPhysicalPoint; image->GetGeometry()->WorldToItkPhysicalPoint(point, itkPhysicalPoint); MITK_INFO << "ITKPoint " << itkPhysicalPoint[0] << " "<< itkPhysicalPoint[1] << " "<< itkPhysicalPoint[2] << ""; mitk::Point3D backTransformedPoint; image->GetGeometry()->ItkPhysicalPointToWorld(itkPhysicalPoint, backTransformedPoint); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(point,backTransformedPoint), "Testing world->itk-physical->world consistency"); itk::Index<3> idx; bool status = itkimage->TransformPhysicalPointToIndex(itkPhysicalPoint, idx); MITK_INFO << "ITK Index " << idx[0] << " "<< idx[1] << " "<< idx[2] << ""; if(status) { float valByItk = itkimage->GetPixel(idx); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(valByItk, value), "Compare value of pixel returned by mitk in comparison to itk"); } else { MITK_WARN<< "Index is out buffered region!"; } } else { MITK_INFO << "Image does not contain three dimensions, some test cases are skipped!"; } // clone generated 3D image with one slice in z direction (cf. bug 11058) unsigned int* threeDdim = new unsigned int[3]; threeDdim[0] = 100; threeDdim[1] = 200; threeDdim[2] = 1; mitk::Image::Pointer threeDImage = mitk::Image::New(); threeDImage->Initialize(mitk::MakeScalarPixelType(), 3, threeDdim); mitk::Image::Pointer cloneThreeDImage = threeDImage->Clone(); // check that the clone image has the same dimensionality as the source image MITK_TEST_CONDITION_REQUIRED( cloneThreeDImage->GetDimension() == 3, "Testing if the clone image initializes with 3D!"); MITK_TEST_CONDITION_REQUIRED( ImageVtkDataReferenceCheck(argv[1]), "Checking reference count of Image after using GetVtkImageData()"); MITK_TEST_END(); } diff --git a/Core/Code/Testing/mitkPointSetEqualTest.cpp b/Core/Code/Testing/mitkPointSetEqualTest.cpp new file mode 100644 index 0000000000..bf98632e93 --- /dev/null +++ b/Core/Code/Testing/mitkPointSetEqualTest.cpp @@ -0,0 +1,105 @@ +/*=================================================================== + +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 "mitkTestingMacros.h" +#include + +/** Members used inside the different (sub-)tests. All members are initialized via Setup().*/ +mitk::PointSet::Pointer m_PointSet; +mitk::PointSet::Pointer m_AnotherPointSet; + +/** +* @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used members for a new test case. (If the members are not used in a test, the method does not need to be called). +*/ +static void Setup() +{ + m_PointSet = mitk::PointSet::New(); + m_AnotherPointSet = m_PointSet->Clone(); +} + +static void Equal_CloneAndOriginal_ReturnsTrue() +{ + Setup(); + MITK_TEST_EQUAL( m_PointSet, m_AnotherPointSet, "A clone should be equal to its original."); +} + +static void Equal_DifferentGeometries_ReturnsFalse() +{ + Setup(); + + mitk::Point3D origin; + origin[0] = 0.0; + origin[1] = 0.0; + origin[2] = 1.0 + 2*mitk::eps; + m_AnotherPointSet->GetGeometry()->SetOrigin(origin); + + MITK_TEST_NOT_EQUAL( m_PointSet, m_AnotherPointSet, "Origin was modified. Result should be false."); +} + +static void Equal_InputIsNull_ReturnsFalse() +{ + mitk::PointSet::Pointer pointSet = NULL; + MITK_TEST_NOT_EQUAL( pointSet, pointSet, "Input is NULL. Result should be false."); +} + +static void Equal_DifferentNumberOfPoints_ReturnsFalse() +{ + Setup(); + mitk::Point3D tmpPoint; + tmpPoint[0] = 1.0; + tmpPoint[1] = 1.0; + tmpPoint[2] = 1.0; + + m_PointSet->InsertPoint( 1, tmpPoint ); + m_PointSet->InsertPoint( 2, tmpPoint ); + + m_AnotherPointSet->InsertPoint( 1, tmpPoint ); + + MITK_TEST_NOT_EQUAL( m_PointSet, m_AnotherPointSet, "One pointset has two points the other has one. Result should be false."); +} + +static void Equal_DifferentPoints_ReturnsFalse() +{ + Setup(); + + mitk::Point3D tmpPoint; + tmpPoint[0] = 1.0; + tmpPoint[1] = 1.0; + tmpPoint[2] = 1.0; + + m_PointSet->InsertPoint( 1, tmpPoint ); + + tmpPoint[0] = 1.0 + 2*mitk::eps; + m_AnotherPointSet->InsertPoint( 1, tmpPoint ); + + MITK_TEST_NOT_EQUAL( m_PointSet, m_AnotherPointSet, "Two pointsets with different points. Result should be false."); +} + +/** + * @brief mitkPointSetEqualTest A test class for Equal methods in mitk::PointSet. + */ +int mitkPointSetEqualTest(int /*argc*/, char* /*argv*/[]) +{ + MITK_TEST_BEGIN(mitkPointSetEqualTest); + + Equal_CloneAndOriginal_ReturnsTrue(); + Equal_InputIsNull_ReturnsFalse(); + Equal_DifferentGeometries_ReturnsFalse(); + Equal_DifferentNumberOfPoints_ReturnsFalse(); + Equal_DifferentPoints_ReturnsFalse(); + + MITK_TEST_END(); +} diff --git a/Core/Code/Testing/mitkSurfaceEqualTest.cpp b/Core/Code/Testing/mitkSurfaceEqualTest.cpp new file mode 100644 index 0000000000..072abba9f6 --- /dev/null +++ b/Core/Code/Testing/mitkSurfaceEqualTest.cpp @@ -0,0 +1,181 @@ +/*=================================================================== + +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 "mitkSurface.h" +#include "mitkTestingMacros.h" + +#include +#include +#include +#include +#include + +/** Members used inside the different (sub-)tests. All members are initialized via Setup().*/ +mitk::Surface::Pointer m_Surface3D; +mitk::Surface::Pointer m_Surface3DTwoTimeSteps; + +vtkSmartPointer m_PointsOne; +vtkSmartPointer m_PointsTwo; +vtkSmartPointer m_PolygonArrayTwo; +vtkSmartPointer m_PolyDataOne; + +/** +* @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used members for a new test case. (If the members are not used in a test, the method does not need to be called). +*/ +static void Setup() +{ + //generate two sets of points + m_PointsOne = vtkSmartPointer::New(); + m_PointsOne->InsertNextPoint( 0.0, 0.0, 0.0 ); + m_PointsOne->InsertNextPoint( 1.0, 0.0, 0.0 ); + m_PointsOne->InsertNextPoint( 0.0, 1.0, 0.0 ); + m_PointsOne->InsertNextPoint( 1.0, 1.0, 0.0 ); + + m_PointsTwo = vtkSmartPointer::New(); + m_PointsTwo->InsertNextPoint( 0.0, 0.0, 0.0 ); + m_PointsTwo->InsertNextPoint( 0.0, 0.0, 2.0 ); + m_PointsTwo->InsertNextPoint( 0.0, 1.0, 0.0 ); + m_PointsTwo->InsertNextPoint( 0.0, 1.0, 2.0 ); + + //generate two polygons + vtkSmartPointer polygonOne = vtkSmartPointer::New(); + polygonOne->GetPointIds()->SetNumberOfIds(4); + polygonOne->GetPointIds()->SetId(0,0); + polygonOne->GetPointIds()->SetId(1,1); + polygonOne->GetPointIds()->SetId(2,2); + polygonOne->GetPointIds()->SetId(3,3); + + vtkSmartPointer polygonTwo = vtkSmartPointer::New(); + polygonTwo->GetPointIds()->SetNumberOfIds(4); + polygonTwo->GetPointIds()->SetId(0,3); + polygonTwo->GetPointIds()->SetId(1,2); + polygonTwo->GetPointIds()->SetId(2,0); + polygonTwo->GetPointIds()->SetId(3,1); + + //generate polydatas + vtkSmartPointer polygonArrayOne = vtkSmartPointer::New(); + polygonArrayOne->InsertNextCell(polygonOne); + + m_PolyDataOne = vtkSmartPointer::New(); + m_PolyDataOne->SetPoints(m_PointsOne); + m_PolyDataOne->SetPolys(polygonArrayOne); + + m_PolygonArrayTwo = vtkSmartPointer::New(); + m_PolygonArrayTwo->InsertNextCell(polygonTwo); + + vtkSmartPointer polyDataTwo = vtkSmartPointer::New(); + polyDataTwo->SetPoints(m_PointsOne); + polyDataTwo->SetPolys(m_PolygonArrayTwo); + + //generate surfaces + m_Surface3D = mitk::Surface::New(); + m_Surface3D->SetVtkPolyData( m_PolyDataOne ); + + m_Surface3DTwoTimeSteps = mitk::Surface::New(); + m_Surface3DTwoTimeSteps->SetVtkPolyData( m_PolyDataOne, 0 ); + m_Surface3DTwoTimeSteps->SetVtkPolyData( polyDataTwo, 1 ); +} + +static void Equal_InputIsNull_ReturnsFalse() +{ + mitk::Surface::Pointer surface = NULL; + MITK_TEST_NOT_EQUAL( surface, surface, "Input is NULL. Result should be false."); +} + +static void Equal_CloneAndOriginalOneTimestep_ReturnsTrue() +{ + Setup(); + MITK_TEST_EQUAL( m_Surface3D, m_Surface3D->Clone(), "A one timestep clone should be equal to its original."); +} + +static void Equal_CloneAndOriginalTwoTimesteps_ReturnsTrue() +{ + Setup(); + MITK_TEST_EQUAL( m_Surface3DTwoTimeSteps, m_Surface3DTwoTimeSteps->Clone(), "A two timestep clone should be equal to its original."); +} + +static void Equal_OneTimeStepVSTwoTimeStep_ReturnsFalse() +{ + Setup(); + MITK_TEST_NOT_EQUAL( m_Surface3D, m_Surface3DTwoTimeSteps, "A one timestep and two timestep surface should not be equal."); +} + +static void Equal_TwoTimeStepsDifferentPoints_ReturnsFalse() +{ + Setup(); + + vtkSmartPointer polyDataDifferentPoints = vtkSmartPointer::New(); + polyDataDifferentPoints->SetPoints(m_PointsTwo); + polyDataDifferentPoints->SetPolys(m_PolygonArrayTwo); + + mitk::Surface::Pointer surface3DTwoTimeStepsDifferentPoints = mitk::Surface::New(); + surface3DTwoTimeStepsDifferentPoints->SetVtkPolyData( m_PolyDataOne, 0 ); + surface3DTwoTimeStepsDifferentPoints->SetVtkPolyData( polyDataDifferentPoints, 1 ); + + //The geometry also changes, because the second pointset has a different geometry/extent. + MITK_TEST_NOT_EQUAL( surface3DTwoTimeStepsDifferentPoints, m_Surface3DTwoTimeSteps, "A surface with the same timesteps and different points should not be equal."); +} + +static void Equal_SurfaceWithPolygonSurfaceWithPolyLine_ReturnsFalse() +{ + Setup(); + + //generate a line + vtkSmartPointer polyLineOne = vtkSmartPointer::New(); + polyLineOne->GetPointIds()->SetNumberOfIds(2); + polyLineOne->GetPointIds()->SetId(0,0); + polyLineOne->GetPointIds()->SetId(1,1); + + vtkSmartPointer polyLineArrayOne = vtkSmartPointer::New(); + polyLineArrayOne->InsertNextCell(polyLineOne); + + vtkSmartPointer polyDataLine = vtkSmartPointer::New(); + polyDataLine->SetPoints(m_PointsOne); + polyDataLine->SetLines(polyLineArrayOne); + + mitk::Surface::Pointer surface3DLine = mitk::Surface::New(); + surface3DLine->SetVtkPolyData( polyDataLine ); + + MITK_TEST_NOT_EQUAL( m_Surface3D, surface3DLine, "A surface with the same timesteps and points and the same number of cells, but different types of cells should not be equal."); +} + +static void Equal_DifferentPoints_ReturnsFalse() +{ + Setup(); + mitk::Surface::Pointer surfaceWithADifferentPoint = m_Surface3D->Clone(); + //modify points. m_Surface3D contains m_PointsOne + surfaceWithADifferentPoint->GetVtkPolyData()->SetPoints( m_PointsTwo ); + + MITK_TEST_NOT_EQUAL( m_Surface3D, surfaceWithADifferentPoint, "A surface with a single timestep and different points should not be equal."); +} + +/** +* @brief mitkSurfaceEqualTest A test class for Equal methods in mitk::Surface. +*/ +int mitkSurfaceEqualTest(int /*argc*/, char* /*argv*/[]) +{ + MITK_TEST_BEGIN(mitkSurfaceEqualTest); + + Equal_InputIsNull_ReturnsFalse(); + Equal_CloneAndOriginalOneTimestep_ReturnsTrue(); + Equal_CloneAndOriginalTwoTimesteps_ReturnsTrue(); + Equal_OneTimeStepVSTwoTimeStep_ReturnsFalse(); + Equal_TwoTimeStepsDifferentPoints_ReturnsFalse(); + Equal_DifferentPoints_ReturnsFalse(); + Equal_SurfaceWithPolygonSurfaceWithPolyLine_ReturnsFalse(); + + MITK_TEST_END(); +} diff --git a/Core/Code/Testing/mitkVectorTest.cpp b/Core/Code/Testing/mitkVectorTest.cpp index f5ed207d5c..d6e1fc020b 100644 --- a/Core/Code/Testing/mitkVectorTest.cpp +++ b/Core/Code/Testing/mitkVectorTest.cpp @@ -1,137 +1,137 @@ /*=================================================================== 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 #include int mitkVectorTest(int /*argc*/, char* /*argv*/[]) { MITK_TEST_BEGIN("mitkVector"); // test itk vector equality methods itk::Vector itkVector_1; itkVector_1[0] = 4.6; itkVector_1[1] = 9.76543; itkVector_1[2] = 746.09; itk::Vector itkVector_2; itk::Vector itkVector_3; for (int i=0; i<3; i++) { itkVector_2[i] = itkVector_1[i] - mitk::eps*1.1; itkVector_3[i] = itkVector_1[i] - mitk::eps*0.9; } MITK_TEST_CONDITION(mitk::Equal(itkVector_1,itkVector_1), "Test vector equality using the same vector with mitk::eps"); MITK_TEST_CONDITION(!mitk::Equal(itkVector_1,itkVector_2), "Test vector equality using different vectors with an element-wise difference greater than mitk::eps"); MITK_TEST_CONDITION( mitk::Equal(itkVector_1, itkVector_2, mitk::eps*1.2f), "Vectors are equal for higher epsilon tolerance ( 1.2 * mitk::eps )"); MITK_TEST_CONDITION(mitk::Equal(itkVector_1,itkVector_3), "Test vector equality using different vectors with an element-wise difference less than mitk::eps"); // test itk point equality methods itk::Point itkPoint_1; itk::Point itkPoint_2; itk::Point itkPoint_3; for (int i=0; i<3; i++) { itkPoint_1[i] = itkVector_1[i]; itkPoint_2[i] = itkVector_2[i]; itkPoint_3[i] = itkVector_3[i]; } MITK_TEST_CONDITION(mitk::Equal(itkPoint_1,itkPoint_1), "Test point equality using the same point with mitk::eps"); MITK_TEST_CONDITION(!mitk::Equal(itkPoint_1,itkPoint_2), "Test point equality using different points with an element-wise difference greater than mitk::eps"); MITK_TEST_CONDITION( mitk::Equal(itkPoint_1, itkPoint_2, mitk::eps * 1.2f), "Points are equal for higher epsilon tolerance ( 1.2 * mitk::eps )"); MITK_TEST_CONDITION(mitk::Equal(itkPoint_1,itkPoint_3), "Test point equality using different points with an element-wise difference less than mitk::eps"); // test mitk vnl vector equality methods mitk::VnlVector mitk_vnl_vector_1(3); mitk::VnlVector mitk_vnl_vector_2(3); mitk::VnlVector mitk_vnl_vector_3(3); for (int i=0; i<3; i++) { mitk_vnl_vector_1.put(i,itkVector_1[i]); mitk_vnl_vector_2.put(i,itkVector_2[i]); mitk_vnl_vector_3.put(i,itkVector_1[i]); } MITK_TEST_CONDITION(mitk::Equal(mitk_vnl_vector_1,mitk_vnl_vector_1), "Test mitk vnl vector equality using the same mitk vnl vector with mitk::eps"); MITK_TEST_CONDITION(!mitk::Equal(mitk_vnl_vector_1,mitk_vnl_vector_2), "Test mitk vnl vector equality using different mitk vnl vectors with an element-wise difference greater than mitk::eps"); - MITK_TEST_CONDITION( mitk::Equal(mitk_vnl_vector_1, mitk_vnl_vector_2, mitk::eps*1.2), "Vnl vectors are equal for higher epsilon tolerance ( 1.2 * mitk::eps )"); + MITK_TEST_CONDITION( mitk::Equal(mitk_vnl_vector_1, mitk_vnl_vector_2, true, mitk::eps*1.2), "Vnl vectors are equal for higher epsilon tolerance ( 1.2 * mitk::eps )"); MITK_TEST_CONDITION(mitk::Equal(mitk_vnl_vector_1,mitk_vnl_vector_3), "Test mitk vnl vector equality using different mitk vnl vectors with an element-wise difference less than mitk::eps"); // test vnl_vector equality method typedef mitk::ScalarType VnlValueType; vnl_vector_fixed vnlVector_1; vnlVector_1[3] = 56.98; vnlVector_1[4] = 22.32; vnlVector_1[5] = 1.00; vnlVector_1[6] = 746.09; vnl_vector_fixed vnlVector_2; vnl_vector_fixed vnlVector_3; for (int i=0; i<7; i++) { if (i<3) { vnlVector_1.put(i,itkVector_1[i]); } vnlVector_2[i] = vnlVector_1[i] - mitk::eps * 1.1f; vnlVector_3[i] = vnlVector_1[i] - mitk::eps * 0.9f; } MITK_TEST_CONDITION( (mitk::Equal(vnlVector_1,vnlVector_1)), "vnl_fixed : v_1 == v_1 "); // the v_2 is constructed so that the equality test fails for mitk::eps, the norm of the difference between the vectors is 7 * eps/6.9 MITK_TEST_CONDITION(!(mitk::Equal(vnlVector_1,vnlVector_2)), "vnl_fixed : v_1 != v_2 with mitk::eps "); // increase the epsilon value used for testing equality - should now pass ( 1.2 * mitk::eps > 7 * mitk::eps/6.9 ) MITK_TEST_CONDITION( (mitk::Equal(vnlVector_1,vnlVector_2, mitk::eps*1.2f)) , "vnl_fixed : v_1 == v_2 with eps = 1.2 * mitk::eps "); MITK_TEST_CONDITION( (mitk::Equal(vnlVector_1,vnlVector_3, mitk::eps)), "vnl_fixed : v_1 == v_3 with eps = 0.8 * mitk::eps "); MITK_TEST_CONDITION(!(mitk::Equal(vnlVector_1,vnlVector_3, mitk::eps*0.8f)), "vnl_fixed : v_1 != v_3 with eps = 0.8 * mitk::eps "); // test scalar equality method double scalar1 = 0.5689; double scalar2 = scalar1 + mitk::eps; double scalar3 = scalar1 + mitk::eps*0.95; MITK_TEST_CONDITION(mitk::Equal(scalar1,scalar1), "Test scalar equality using the same scalar with mitk::eps"); MITK_TEST_CONDITION(!mitk::Equal(scalar1,scalar2), "Test scalar equality using the different scalars with a difference greater than mitk::eps"); MITK_TEST_CONDITION(mitk::Equal(scalar1,scalar3), "Test scalar equality using the different scalars with a difference less than mitk::eps"); // test matrix equality methods vnl_matrix_fixed vnlMatrix3x3_1; vnlMatrix3x3_1(0,0) = 1.1; vnlMatrix3x3_1(0,1) = 0.4; vnlMatrix3x3_1(0,2) = 5.3; vnlMatrix3x3_1(1,0) = 2.7; vnlMatrix3x3_1(1,1) = 3578.56418; vnlMatrix3x3_1(1,2) = 123.56; vnlMatrix3x3_1(2,0) = 546.89; vnlMatrix3x3_1(2,1) = 0.0001; vnlMatrix3x3_1(2,2) = 1.0; vnl_matrix_fixed vnlMatrix3x3_2; vnlMatrix3x3_2(0,0) = 1.1000009; vnlMatrix3x3_2(0,1) = 0.4000009; vnlMatrix3x3_2(0,2) = 5.3000009; vnlMatrix3x3_2(1,0) = 2.7000009; vnlMatrix3x3_2(1,1) = 3578.5641809; vnlMatrix3x3_2(1,2) = 123.5600009; vnlMatrix3x3_2(2,0) = 546.8900009; vnlMatrix3x3_2(2,1) = 0.0001009; vnlMatrix3x3_2(2,2) = 1.0000009; mitk::ScalarType epsilon = 0.000001; MITK_TEST_CONDITION(mitk::MatrixEqualElementWise(vnlMatrix3x3_1,vnlMatrix3x3_1,0.0),"Test for matrix equality with given epsilon=0.0 and exactly the same matrix elements"); MITK_TEST_CONDITION(!mitk::MatrixEqualElementWise(vnlMatrix3x3_1,vnlMatrix3x3_2,0.0),"Test for matrix equality with given epsilon=0.0 and slightly different matrix elements"); MITK_TEST_CONDITION(mitk::MatrixEqualElementWise(vnlMatrix3x3_1,vnlMatrix3x3_2,epsilon),"Test for matrix equality with given epsilon and slightly different matrix elements"); MITK_TEST_CONDITION(!mitk::MatrixEqualRMS(vnlMatrix3x3_1,vnlMatrix3x3_2,0.0),"Test for matrix equality with given epsilon=0.0 and slightly different matrix elements"); MITK_TEST_CONDITION(mitk::MatrixEqualRMS(vnlMatrix3x3_1,vnlMatrix3x3_2,epsilon),"Test for matrix equality with given epsilon and slightly different matrix elements"); MITK_TEST_END(); } diff --git a/Core/Code/files.cmake b/Core/Code/files.cmake index f8b02aa3a4..37d254ccec 100644 --- a/Core/Code/files.cmake +++ b/Core/Code/files.cmake @@ -1,394 +1,396 @@ set(H_FILES Algorithms/itkImportMitkImageContainer.h Algorithms/itkImportMitkImageContainer.txx Algorithms/itkLocalVariationImageFilter.h Algorithms/itkLocalVariationImageFilter.txx Algorithms/itkMITKScalarImageToHistogramGenerator.h Algorithms/itkMITKScalarImageToHistogramGenerator.txx Algorithms/itkTotalVariationDenoisingImageFilter.h Algorithms/itkTotalVariationDenoisingImageFilter.txx Algorithms/itkTotalVariationSingleIterationImageFilter.h Algorithms/itkTotalVariationSingleIterationImageFilter.txx Algorithms/mitkBilateralFilter.h Algorithms/mitkBilateralFilter.cpp Algorithms/mitkInstantiateAccessFunctions.h Algorithms/mitkPixelTypeList.h Algorithms/mitkPPArithmeticDec.h Algorithms/mitkPPArgCount.h Algorithms/mitkPPCat.h Algorithms/mitkPPConfig.h Algorithms/mitkPPControlExprIIf.h Algorithms/mitkPPControlIf.h Algorithms/mitkPPControlIIf.h Algorithms/mitkPPDebugError.h Algorithms/mitkPPDetailAutoRec.h Algorithms/mitkPPDetailDMCAutoRec.h Algorithms/mitkPPExpand.h Algorithms/mitkPPFacilitiesEmpty.h Algorithms/mitkPPFacilitiesExpand.h Algorithms/mitkPPLogicalBool.h Algorithms/mitkPPRepetitionDetailDMCFor.h Algorithms/mitkPPRepetitionDetailEDGFor.h Algorithms/mitkPPRepetitionDetailFor.h Algorithms/mitkPPRepetitionDetailMSVCFor.h Algorithms/mitkPPRepetitionFor.h Algorithms/mitkPPSeqElem.h Algorithms/mitkPPSeqForEach.h Algorithms/mitkPPSeqForEachProduct.h Algorithms/mitkPPSeq.h Algorithms/mitkPPSeqEnum.h Algorithms/mitkPPSeqSize.h Algorithms/mitkPPSeqToTuple.h Algorithms/mitkPPStringize.h Algorithms/mitkPPTupleEat.h Algorithms/mitkPPTupleElem.h Algorithms/mitkPPTupleRem.h Algorithms/mitkClippedSurfaceBoundsCalculator.h Algorithms/mitkExtractSliceFilter.h Algorithms/mitkConvert2Dto3DImageFilter.h Algorithms/mitkPlaneClipping.h Common/mitkExceptionMacro.h Common/mitkServiceBaseObject.h Common/mitkTestingMacros.h + Common/mitkTesting.h DataManagement/mitkImageAccessByItk.h DataManagement/mitkImageCast.h DataManagement/mitkImagePixelAccessor.h DataManagement/mitkImagePixelReadAccessor.h DataManagement/mitkImagePixelWriteAccessor.h DataManagement/mitkImageReadAccessor.h DataManagement/mitkImageWriteAccessor.h DataManagement/mitkITKImageImport.h DataManagement/mitkITKImageImport.txx DataManagement/mitkImageToItk.h DataManagement/mitkImageToItk.txx Interactions/mitkEventMapperAddOn.h Interfaces/mitkIDataNodeReader.h Rendering/mitkLocalStorageHandler.h IO/mitkPixelTypeTraits.h ) set(CPP_FILES Algorithms/mitkBaseDataSource.cpp + Algorithms/mitkCompareImageFilter.cpp Algorithms/mitkDataNodeSource.cpp Algorithms/mitkGeometry2DDataToSurfaceFilter.cpp Algorithms/mitkHistogramGenerator.cpp Algorithms/mitkImageChannelSelector.cpp Algorithms/mitkImageSliceSelector.cpp Algorithms/mitkImageSource.cpp Algorithms/mitkImageTimeSelector.cpp Algorithms/mitkImageToImageFilter.cpp Algorithms/mitkImageToSurfaceFilter.cpp Algorithms/mitkPointSetSource.cpp Algorithms/mitkPointSetToPointSetFilter.cpp Algorithms/mitkRGBToRGBACastImageFilter.cpp Algorithms/mitkSubImageSelector.cpp Algorithms/mitkSurfaceSource.cpp Algorithms/mitkSurfaceToImageFilter.cpp Algorithms/mitkSurfaceToSurfaceFilter.cpp Algorithms/mitkUIDGenerator.cpp Algorithms/mitkVolumeCalculator.cpp Algorithms/mitkClippedSurfaceBoundsCalculator.cpp Algorithms/mitkExtractSliceFilter.cpp Algorithms/mitkConvert2Dto3DImageFilter.cpp Controllers/mitkBaseController.cpp Controllers/mitkCallbackFromGUIThread.cpp Controllers/mitkCameraController.cpp Controllers/mitkCameraRotationController.cpp Controllers/mitkCoreActivator.cpp Controllers/mitkFocusManager.cpp Controllers/mitkLimitedLinearUndo.cpp Controllers/mitkOperationEvent.cpp Controllers/mitkPlanePositionManager.cpp Controllers/mitkProgressBar.cpp Controllers/mitkRenderingManager.cpp Controllers/mitkSliceNavigationController.cpp Controllers/mitkSlicesCoordinator.cpp Controllers/mitkSlicesRotator.cpp Controllers/mitkSlicesSwiveller.cpp Controllers/mitkStatusBar.cpp Controllers/mitkStepper.cpp Controllers/mitkTestManager.cpp Controllers/mitkUndoController.cpp Controllers/mitkVerboseLimitedLinearUndo.cpp Controllers/mitkVtkInteractorCameraController.cpp Controllers/mitkVtkLayerController.cpp DataManagement/mitkAbstractTransformGeometry.cpp DataManagement/mitkAnnotationProperty.cpp DataManagement/mitkApplicationCursor.cpp DataManagement/mitkBaseData.cpp DataManagement/mitkBaseProperty.cpp DataManagement/mitkClippingProperty.cpp DataManagement/mitkChannelDescriptor.cpp DataManagement/mitkColorProperty.cpp DataManagement/mitkDataStorage.cpp # DataManagement/mitkDataTree.cpp DataManagement/mitkDataNode.cpp DataManagement/mitkDataNodeFactory.cpp # DataManagement/mitkDataTreeStorage.cpp DataManagement/mitkDisplayGeometry.cpp DataManagement/mitkEnumerationProperty.cpp DataManagement/mitkGeometry2D.cpp DataManagement/mitkGeometry2DData.cpp DataManagement/mitkGeometry3D.cpp DataManagement/mitkGeometryData.cpp DataManagement/mitkGroupTagProperty.cpp DataManagement/mitkImage.cpp DataManagement/mitkImageAccessorBase.cpp DataManagement/mitkImageCaster.cpp DataManagement/mitkImageCastPart1.cpp DataManagement/mitkImageCastPart2.cpp DataManagement/mitkImageCastPart3.cpp DataManagement/mitkImageCastPart4.cpp DataManagement/mitkImageDataItem.cpp DataManagement/mitkImageDescriptor.cpp DataManagement/mitkImageVtkAccessor.cpp DataManagement/mitkImageStatisticsHolder.cpp DataManagement/mitkLandmarkBasedCurvedGeometry.cpp DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.cpp DataManagement/mitkLandmarkProjector.cpp DataManagement/mitkLevelWindow.cpp DataManagement/mitkLevelWindowManager.cpp DataManagement/mitkLevelWindowPreset.cpp DataManagement/mitkLevelWindowProperty.cpp DataManagement/mitkLookupTable.cpp DataManagement/mitkLookupTables.cpp # specializations of GenericLookupTable DataManagement/mitkMemoryUtilities.cpp DataManagement/mitkModalityProperty.cpp DataManagement/mitkModeOperation.cpp DataManagement/mitkNodePredicateAnd.cpp DataManagement/mitkNodePredicateBase.cpp DataManagement/mitkNodePredicateCompositeBase.cpp DataManagement/mitkNodePredicateData.cpp DataManagement/mitkNodePredicateDataType.cpp DataManagement/mitkNodePredicateDimension.cpp DataManagement/mitkNodePredicateFirstLevel.cpp DataManagement/mitkNodePredicateNot.cpp DataManagement/mitkNodePredicateOr.cpp DataManagement/mitkNodePredicateProperty.cpp DataManagement/mitkNodePredicateSource.cpp DataManagement/mitkPlaneOrientationProperty.cpp DataManagement/mitkPlaneGeometry.cpp DataManagement/mitkPlaneOperation.cpp DataManagement/mitkPointOperation.cpp DataManagement/mitkPointSet.cpp DataManagement/mitkProperties.cpp DataManagement/mitkPropertyList.cpp DataManagement/mitkRestorePlanePositionOperation.cpp DataManagement/mitkRotationOperation.cpp DataManagement/mitkSlicedData.cpp DataManagement/mitkSlicedGeometry3D.cpp DataManagement/mitkSmartPointerProperty.cpp DataManagement/mitkStandaloneDataStorage.cpp DataManagement/mitkStateTransitionOperation.cpp DataManagement/mitkStringProperty.cpp DataManagement/mitkSurface.cpp DataManagement/mitkSurfaceOperation.cpp DataManagement/mitkThinPlateSplineCurvedGeometry.cpp DataManagement/mitkTimeSlicedGeometry.cpp DataManagement/mitkTransferFunction.cpp DataManagement/mitkTransferFunctionProperty.cpp DataManagement/mitkTransferFunctionInitializer.cpp DataManagement/mitkVector.cpp DataManagement/mitkVtkInterpolationProperty.cpp DataManagement/mitkVtkRepresentationProperty.cpp DataManagement/mitkVtkResliceInterpolationProperty.cpp DataManagement/mitkVtkScalarModeProperty.cpp DataManagement/mitkVtkVolumeRenderingProperty.cpp DataManagement/mitkWeakPointerProperty.cpp DataManagement/mitkRenderingModeProperty.cpp DataManagement/mitkShaderProperty.cpp DataManagement/mitkResliceMethodProperty.cpp DataManagement/mitkMaterial.cpp DataManagement/mitkPointSetShapeProperty.cpp DataManagement/mitkFloatPropertyExtension.cpp DataManagement/mitkIntPropertyExtension.cpp DataManagement/mitkPropertyExtension.cpp DataManagement/mitkPropertyFilter.cpp DataManagement/mitkPropertyAliases.cpp DataManagement/mitkPropertyDescriptions.cpp DataManagement/mitkPropertyExtensions.cpp DataManagement/mitkPropertyFilters.cpp Interactions/mitkAction.cpp Interactions/mitkAffineInteractor.cpp Interactions/mitkBindDispatcherInteractor.cpp Interactions/mitkCoordinateSupplier.cpp Interactions/mitkDataInteractor.cpp Interactions/mitkDispatcher.cpp Interactions/mitkDisplayCoordinateOperation.cpp Interactions/mitkDisplayInteractor.cpp Interactions/mitkDisplayPositionEvent.cpp # Interactions/mitkDisplayVectorInteractorLevelWindow.cpp # legacy, prob even now unneeded # Interactions/mitkDisplayVectorInteractorScroll.cpp Interactions/mitkEvent.cpp Interactions/mitkEventConfig.cpp Interactions/mitkEventDescription.cpp Interactions/mitkEventFactory.cpp Interactions/mitkInteractionEventHandler.cpp Interactions/mitkEventMapper.cpp Interactions/mitkEventStateMachine.cpp Interactions/mitkGlobalInteraction.cpp Interactions/mitkInteractor.cpp Interactions/mitkInternalEvent.cpp Interactions/mitkInteractionEvent.cpp Interactions/mitkInteractionEventConst.cpp Interactions/mitkInteractionPositionEvent.cpp Interactions/mitkInteractionKeyEvent.cpp Interactions/mitkMousePressEvent.cpp Interactions/mitkMouseMoveEvent.cpp Interactions/mitkMouseReleaseEvent.cpp Interactions/mitkMouseWheelEvent.cpp Interactions/mitkMouseDoubleClickEvent.cpp Interactions/mitkMouseModeSwitcher.cpp Interactions/mitkMouseMovePointSetInteractor.cpp Interactions/mitkMoveBaseDataInteractor.cpp Interactions/mitkNodeDepententPointSetInteractor.cpp Interactions/mitkPointSetDataInteractor.cpp Interactions/mitkPointSetInteractor.cpp Interactions/mitkPositionEvent.cpp Interactions/mitkPositionTracker.cpp Interactions/mitkStateMachineAction.cpp Interactions/mitkStateMachineCondition.cpp Interactions/mitkStateMachineState.cpp Interactions/mitkStateMachineTransition.cpp Interactions/mitkState.cpp Interactions/mitkStateMachineContainer.cpp Interactions/mitkStateEvent.cpp Interactions/mitkStateMachine.cpp Interactions/mitkStateMachineFactory.cpp Interactions/mitkTransition.cpp Interactions/mitkWheelEvent.cpp Interactions/mitkKeyEvent.cpp Interactions/mitkVtkEventAdapter.cpp Interactions/mitkVtkInteractorStyle.cxx Interactions/mitkCrosshairPositionEvent.cpp Interfaces/mitkInteractionEventObserver.cpp Interfaces/mitkIShaderRepository.cpp Interfaces/mitkIPropertyAliases.cpp Interfaces/mitkIPropertyDescriptions.cpp Interfaces/mitkIPropertyExtensions.cpp Interfaces/mitkIPropertyFilters.cpp IO/mitkBaseDataIOFactory.cpp IO/mitkCoreDataNodeReader.cpp IO/mitkDicomSeriesReader.cpp IO/mitkFileReader.cpp IO/mitkFileSeriesReader.cpp IO/mitkFileWriter.cpp # IO/mitkIpPicGet.c IO/mitkImageGenerator.cpp IO/mitkImageWriter.cpp IO/mitkImageWriterFactory.cpp IO/mitkItkImageFileIOFactory.cpp IO/mitkItkImageFileReader.cpp IO/mitkItkLoggingAdapter.cpp IO/mitkItkPictureWrite.cpp IO/mitkIOUtil.cpp IO/mitkLookupTableProperty.cpp IO/mitkOperation.cpp # IO/mitkPicFileIOFactory.cpp # IO/mitkPicFileReader.cpp # IO/mitkPicFileWriter.cpp # IO/mitkPicHelper.cpp # IO/mitkPicVolumeTimeSeriesIOFactory.cpp # IO/mitkPicVolumeTimeSeriesReader.cpp IO/mitkPixelType.cpp IO/mitkPointSetIOFactory.cpp IO/mitkPointSetReader.cpp IO/mitkPointSetWriter.cpp IO/mitkPointSetWriterFactory.cpp IO/mitkRawImageFileReader.cpp IO/mitkStandardFileLocations.cpp IO/mitkSTLFileIOFactory.cpp IO/mitkSTLFileReader.cpp IO/mitkSurfaceVtkWriter.cpp IO/mitkSurfaceVtkWriterFactory.cpp IO/mitkVtkLoggingAdapter.cpp IO/mitkVtiFileIOFactory.cpp IO/mitkVtiFileReader.cpp IO/mitkVtkImageIOFactory.cpp IO/mitkVtkImageReader.cpp IO/mitkVtkSurfaceIOFactory.cpp IO/mitkVtkSurfaceReader.cpp IO/vtkPointSetXMLParser.cpp IO/mitkLog.cpp Rendering/mitkBaseRenderer.cpp Rendering/mitkVtkMapper.cpp Rendering/mitkRenderWindowFrame.cpp Rendering/mitkGeometry2DDataMapper2D.cpp Rendering/mitkGeometry2DDataVtkMapper3D.cpp Rendering/mitkGLMapper.cpp Rendering/mitkGradientBackground.cpp Rendering/mitkManufacturerLogo.cpp Rendering/mitkMapper.cpp Rendering/mitkPointSetGLMapper2D.cpp Rendering/mitkPointSetVtkMapper2D.cpp Rendering/mitkPointSetVtkMapper3D.cpp Rendering/mitkPolyDataGLMapper2D.cpp Rendering/mitkSurfaceGLMapper2D.cpp Rendering/mitkSurfaceVtkMapper3D.cpp Rendering/mitkVolumeDataVtkMapper3D.cpp Rendering/mitkVtkPropRenderer.cpp Rendering/mitkVtkWidgetRendering.cpp Rendering/vtkMitkRectangleProp.cpp Rendering/vtkMitkRenderProp.cpp Rendering/mitkVtkEventProvider.cpp Rendering/mitkRenderWindow.cpp Rendering/mitkRenderWindowBase.cpp Rendering/mitkShaderRepository.cpp Rendering/mitkImageVtkMapper2D.cpp Rendering/vtkMitkThickSlicesFilter.cpp Rendering/vtkMitkLevelWindowFilter.cpp Rendering/vtkNeverTranslucentTexture.cpp Rendering/mitkRenderingTestHelper.cpp Rendering/mitkOverlay.cpp Rendering/mitkVtkOverlay.cpp Rendering/mitkVtkOverlay2D.cpp Rendering/mitkVtkOverlay3D.cpp Rendering/mitkOverlayManager.cpp Rendering/mitkAbstractOverlayLayouter.cpp Rendering/mitkTextOverlay2D.cpp Rendering/mitkTextOverlay3D.cpp Rendering/mitkLabelOverlay3D.cpp Rendering/mitkOverlay2DLayouter.cpp Common/mitkException.cpp Common/mitkCommon.h Common/mitkCoreObjectFactoryBase.cpp Common/mitkCoreObjectFactory.cpp Common/mitkCoreServices.cpp ) list(APPEND CPP_FILES ${CppMicroServices_SOURCES}) set(RESOURCE_FILES Interactions/globalConfig.xml Interactions/DisplayInteraction.xml Interactions/DisplayConfig.xml Interactions/DisplayConfigPACS.xml Interactions/DisplayConfigPACSPan.xml Interactions/DisplayConfigPACSScroll.xml Interactions/DisplayConfigPACSZoom.xml Interactions/DisplayConfigPACSLevelWindow.xml Interactions/DisplayConfigMITK.xml Interactions/PointSet.xml Interactions/Legacy/StateMachine.xml Interactions/Legacy/DisplayConfigMITKTools.xml Interactions/PointSetConfig.xml Shaders/mitkShaderLighting.xml mitkLevelWindowPresets.xml )