diff --git a/Modules/Core/include/mitkBaseGeometry.h b/Modules/Core/include/mitkBaseGeometry.h index 272521a625..8d2e4feb85 100644 --- a/Modules/Core/include/mitkBaseGeometry.h +++ b/Modules/Core/include/mitkBaseGeometry.h @@ -1,786 +1,763 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef BaseGeometry_H_HEADER_INCLUDED #define BaseGeometry_H_HEADER_INCLUDED #include "mitkOperationActor.h" #include #include #include "itkScalableAffineTransform.h" #include "mitkNumericTypes.h" #include #include #include #include #include #include #include class vtkMatrix4x4; class vtkMatrixToLinearTransform; class vtkLinearTransform; 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 BaseGeometry Describes the geometry of a 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 an origin and spacing to define the geometry //## //## BaseGeometry 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. //## //## BaseGeometry instances referring to an Image need a slightly //## different definition of corners, see SetImageGeometry. This //## is usualy automatically called by Image. //## //## BaseGeometry 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(). //## //## At least, it can return the bounding box of the data object. //## //## The BaseGeometry class is an abstract class. The most simple implementation //## is the sublass Geometry3D. //## //## Rule: everything is in mm (ms) if not stated otherwise. //## @ingroup Geometry class MITKCORE_EXPORT BaseGeometry : public itk::Object, public OperationActor { public: mitkClassMacroItkParent(BaseGeometry, itk::Object); itkCloneMacro(Self); // ********************************** TypeDef ********************************** typedef GeometryTransformHolder::TransformType TransformType; typedef itk::BoundingBox BoundingBoxType; typedef BoundingBoxType::BoundsArrayType BoundsArrayType; typedef BoundingBoxType::Pointer BoundingBoxPointer; // ********************************** Origin, Spacing ********************************** //##Documentation //## @brief Get the origin, e.g. the upper-left corner of the plane const Point3D GetOrigin() const; //##Documentation //## @brief Set the origin, i.e. the upper-left corner of the plane //## void SetOrigin(const Point3D &origin); //##Documentation //## @brief Get the spacing (size of a pixel). //## const mitk::Vector3D GetSpacing() const; //##Documentation //## @brief Set the spacing (m_Spacing). //## //##The spacing is also changed in the IndexToWorldTransform. void SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing = false); //##Documentation //## @brief Get the origin as VnlVector //## //## \sa GetOrigin VnlVector GetOriginVnl() const; // ********************************** other functions ********************************** //##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); itkGetConstMacro(IndexToWorldTransformLastModified, unsigned long); //##Documentation //## @brief Overload of function Modified() to prohibit several calls of Modified() using the ModifiedLock class. //## //## For the use of Modified(), see class ModifiedLock. void Modified() const override; friend class ModifiedLock; //##Documentation //## @brief Is this BaseGeometry in a state that is valid? //## //## This function returns always true in the BaseGeometry class. Other implementations are possible in subclasses. virtual bool IsValid() const; // ********************************** Initialize ********************************** //##Documentation //## @brief Initialize the BaseGeometry void Initialize(); void InitializeGeometry(Self *newGeometry) const; // ********************************** Transformations Set/Get ********************************** //##Documentation //## @brief Get the transformation used to convert from index //## to world coordinates mitk::AffineTransform3D *GetIndexToWorldTransform(); //##Documentation //## @brief Get the transformation used to convert from index //## to world coordinates const mitk::AffineTransform3D *GetIndexToWorldTransform() const; //## @brief Set the transformation used to convert from index //## to world coordinates. The spacing of the new transform is //## copied to m_spacing. void SetIndexToWorldTransform(mitk::AffineTransform3D *transform); //##Documentation //## @brief Convenience method for setting the ITK transform //## (m_IndexToWorldTransform) via an vtkMatrix4x4.The spacing of //## the new transform is copied to m_spacing. //## \sa SetIndexToWorldTransform void SetIndexToWorldTransformByVtkMatrix(vtkMatrix4x4 *vtkmatrix); //## @brief Set the transformation used to convert from index //## to world coordinates.This function keeps the original spacing. void SetIndexToWorldTransformWithoutChangingSpacing(mitk::AffineTransform3D *transform); //##Documentation //## @brief Convenience method for setting the ITK transform //## (m_IndexToWorldTransform) via an vtkMatrix4x4. This function keeps the original spacing. //## \sa SetIndexToWorldTransform void SetIndexToWorldTransformByVtkMatrixWithoutChangingSpacing(vtkMatrix4x4 *vtkmatrix); //## Get the Vtk Matrix which describes the transform. vtkMatrix4x4 *GetVtkMatrix(); //##Documentation //## @brief Get the m_IndexToWorldTransform as a vtkLinearTransform vtkLinearTransform *GetVtkTransform() const; //##Documentation //## @brief Set the transform to identity, the spacing to 1 and origin to 0 //## void SetIdentity(); // ********************************** Transformations ********************************** //##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. //## This method also changes m_spacing. void Compose(const TransformType *other, bool pre = false); //##Documentation //## @brief Compose new IndexToWorldTransform with a given vtkMatrix4x4. //## //## Converts the vtkMatrix4x4 into a itk-transform and calls the previous method. void Compose(const vtkMatrix4x4 *vtkmatrix, bool pre = false); //##Documentation //## @brief Translate the origin by a vector //## void Translate(const Vector3D &vector); //##Documentation //##@brief executes affine operations (translate, rotate, scale) void ExecuteOperation(Operation *operation) override; //##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 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 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 < dim; ++i) { index[i] = itk::Math::RoundHalfIntegerUp(pt_units[i]); } } //##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 (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 (discrete) index coordinates of a \em point to world coordinates (in mm) //## For further information about coordinates types, please see the Geometry documentation template void IndexToWorld(const itk::Index &index, mitk::Point3D &pt_mm) const { mitk::Point3D pt_units; pt_units.Fill(0); int i, dim = index.GetIndexDimension(); if (dim > 3) { dim = 3; } for (i = 0; i < dim; ++i) { pt_units[i] = index[i]; } IndexToWorld(pt_units, pt_mm); } //##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 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 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 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 BaseGeometry 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); } // ********************************** BoundingBox ********************************** /** Get the bounding box */ itkGetConstObjectMacro(BoundingBox, BoundingBoxType); // a bit of a misuse, but we want only doxygen to see the following: #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; #endif const BoundsArrayType GetBounds() const; //##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. void SetBounds(const BoundsArrayType &bounds); //##Documentation //## @brief Set the bounding box (in index/unit coordinates) via a float array void SetFloatBounds(const float bounds[6]); //##Documentation //## @brief Set the bounding box (in index/unit coordinates) via a double array void SetFloatBounds(const double bounds[6]); //##Documentation //## @brief Get a VnlVector along bounding-box in the specified //## @a direction, length is spacing //## //## \sa GetAxisVector VnlVector GetMatrixColumn(unsigned int direction) const; //##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 Set the time bounds (in ms) // void SetTimeBounds(const TimeBounds& timebounds); // ********************************** Geometry ********************************** #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 /** Get the extent of the bounding box */ ScalarType GetExtent(unsigned int direction) const; //##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; //##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; //##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 center of the bounding-box in mm //## Point3D GetCenter() const; //##Documentation //## @brief Get the squared length of the diagonal of the bounding-box in mm //## double GetDiagonalLength2() const; //##Documentation //## @brief Get the length of the diagonal of the bounding-box in mm //## double GetDiagonalLength() const; //##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 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! void SetExtentInMM(int direction, ScalarType extentInMM); //##Documentation //## @brief Test whether the point \a p (world coordinates in mm) is //## inside the bounding box bool IsInside(const mitk::Point3D &p) const; //##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; //##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); } // ********************************* Image Geometry ******************************** //##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 Is this an ImageGeometry? //## //## For more information, see SetImageGeometry itkGetConstMacro(ImageGeometry, bool) //##Documentation //## @brief Define that this BaseGeometry 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 BaseGeometry is referring to an Image. itkSetMacro(ImageGeometry, bool); itkBooleanMacro(ImageGeometry); const GeometryTransformHolder *GetGeometryTransformHolder() const; protected: // ********************************** Constructor ********************************** BaseGeometry(); BaseGeometry(const BaseGeometry &other); ~BaseGeometry() override; //##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 itk::LightObject::Pointer InternalClone() const override = 0; void PrintSelf(std::ostream &os, itk::Indent indent) const override; static const std::string GetTransformAsString(TransformType *transformType); itkGetConstMacro(NDimensions, unsigned int); bool IsBoundingBoxNull() const; bool IsIndexToWorldTransformNull() const; void SetVtkMatrixDeepCopy(vtkTransform *vtktransform); void _SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing = false); //##Documentation //## @brief PreSetSpacing //## //## These virtual function allows a different beahiour in subclasses. //## Do implement them in every subclass of BaseGeometry. If not needed, use //## {Superclass::PreSetSpacing();}; virtual void PreSetSpacing(const mitk::Vector3D & /*aSpacing*/){}; //##Documentation //## @brief CheckBounds //## //## This function is called in SetBounds. Assertions can be implemented in this function (see PlaneGeometry.cpp). //## If you implement this function in a subclass, make sure, that all classes were your class inherits from //## have an implementation of CheckBounds //## (e.g. inheritance BaseGeometry <- A <- B. Implementation of CheckBounds in class B needs implementation in A as // well!) virtual void CheckBounds(const BoundsArrayType & /*bounds*/){}; //##Documentation //## @brief CheckIndexToWorldTransform //## //## This function is called in SetIndexToWorldTransform. Assertions can be implemented in this function (see // PlaneGeometry.cpp). //## In Subclasses of BaseGeometry, implement own conditions or call Superclass::CheckBounds(bounds);. virtual void CheckIndexToWorldTransform(mitk::AffineTransform3D * /*transform*/){}; private: GeometryTransformHolder *m_GeometryTransform; void InitializeGeometryTransformHolder(const BaseGeometry *otherGeometry); //##Documentation //## @brief Bounding Box, which is axes-parallel in intrinsic coordinates //## (often integer indices of pixels) BoundingBoxPointer m_BoundingBox; unsigned int m_FrameOfReferenceID; // mitk::TimeBounds m_TimeBounds; static const unsigned int m_NDimensions = 3; mutable TransformType::Pointer m_InvertedTransform; mutable unsigned long m_IndexToWorldTransformLastModified; bool m_ImageGeometry; //##Documentation //## @brief ModifiedLockFlag is used to prohibit the call of Modified() //## //## For the use of this Flag, see class ModifiedLock. This flag should only be set //## by the ModifiedLock class! bool m_ModifiedLockFlag; //##Documentation //## @brief ModifiedcalledFlag is used to collect calls of Modified(). //## //## For the use of this Flag, see class ModifiedLock. This flag should only be set //## by the Modified() function! mutable bool m_ModifiedCalledFlag; }; // ********************************** Equal Functions ********************************** // // Static compare functions mainly for testing // /** * @brief Equal A function comparing two geometries for beeing identical. - * @warning This method is deprecated and will not be available in the future. Use the \a bool mitk::Equal(const - * mitk::mitk::BaseGeometry& g1, const mitk::BaseGeometry& g2) instead. * * @ingroup MITKTestingAPI * * 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 + * If you want to use different tolerance 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 coordinateEps Tolerance for comparison of all spatial aspects (spacing, origin and grid alignment). + * You can use mitk::eps in most cases. + * @param directionEps Tolerance for comparison of all directional aspects (axis). 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. */ - DEPRECATED(MITKCORE_EXPORT bool Equal( - const mitk::BaseGeometry *leftHandSide, const mitk::BaseGeometry *rightHandSide, ScalarType eps, bool verbose)); + MITKCORE_EXPORT bool Equal(const mitk::BaseGeometry& leftHandSide, + const mitk::BaseGeometry& rightHandSide, + ScalarType coordinateEps, + ScalarType directionEps, + bool verbose = false); /** * @brief Equal A function comparing two geometries for beeing identical. * * @ingroup MITKTestingAPI * - * 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. + * @overload This is an overloaded version that uses a single tolerance for spatial and directional aspects. For more details, + * see the other overloaded version. * @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. */ MITKCORE_EXPORT bool Equal(const mitk::BaseGeometry &leftHandSide, const mitk::BaseGeometry &rightHandSide, - ScalarType eps, - bool verbose); - - /** - * @brief Equal A function comparing two transforms (TransformType) for beeing identical. - * @warning This method is deprecated and will not be available in the future. Use the \a bool mitk::Equal(const - * mitk::mitk::BaseGeometry::TransformType& t1, const mitk::BaseGeometry::TransformType& t2) instead. - * - * @ingroup MITKTestingAPI - * - * 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. - */ - DEPRECATED(MITKCORE_EXPORT bool Equal(const mitk::BaseGeometry::TransformType *leftHandSide, - const mitk::BaseGeometry::TransformType *rightHandSide, - ScalarType eps, - bool verbose)); + ScalarType eps = mitk::eps, + bool verbose = false); /** * @brief Equal A function comparing two transforms (TransformType) for beeing identical. * * @ingroup MITKTestingAPI * * 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. */ MITKCORE_EXPORT bool Equal(const mitk::BaseGeometry::TransformType &leftHandSide, const mitk::BaseGeometry::TransformType &rightHandSide, ScalarType eps, bool verbose); - /** - * @brief Equal A function comparing two bounding boxes (BoundingBoxType) for beeing identical. - * @warning This method is deprecated and will not be available in the future. Use the \a bool mitk::Equal(const - * mitk::mitk::BaseGeometry::BoundingBoxType& b1, const mitk::BaseGeometry::BoundingBoxType& b2) instead. - * - * @ingroup MITKTestingAPI - * - * 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. - */ - DEPRECATED(MITKCORE_EXPORT bool Equal(const mitk::BaseGeometry::BoundingBoxType *leftHandSide, - const mitk::BaseGeometry::BoundingBoxType *rightHandSide, - ScalarType eps, - bool verbose)); - /** * @brief Equal A function comparing two bounding boxes (BoundingBoxType) for beeing identical. * * @ingroup MITKTestingAPI * * 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. */ MITKCORE_EXPORT bool Equal(const mitk::BaseGeometry::BoundingBoxType &leftHandSide, const mitk::BaseGeometry::BoundingBoxType &rightHandSide, ScalarType eps, bool verbose); /** * @brief A function checks if a test geometry is a sub geometry of * a given reference geometry. * * Sub geometry means that both geometries have the same voxel grid (same spacing, same axes, * orgin is on voxel grid), but the bounding box of the checked geometry is contained or equal * to the bounding box of the reference geometry.\n * By this definition equal geometries are always sub geometries of each other. * * The function checks the spacing, origin, axis vectors, 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 comparison. + * The parameter eps is a tolerance value for all methods which are internally used for comparison. * @param testGeo Geometry that should be checked if it is a sub geometry of referenceGeo. * @param referenceGeo Geometry that should contain testedGeometry as sub geometry. - * @param eps Tolarence for comparison. You can use mitk::eps in most cases. + * @param coordinateEps Tolerance for comparison of all spatial aspects (spacing, origin and grid alignment). + * You can use mitk::eps in most cases. + * @param directionEps Tolerance for comparison of all directional aspects (axis). 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 comparisons are true. False otherwise. + */ + MITKCORE_EXPORT bool IsSubGeometry(const mitk::BaseGeometry& testGeo, + const mitk::BaseGeometry& referenceGeo, + ScalarType coordinateEps, + ScalarType directionEps, + bool verbose = false); + + /** + * @brief A function checks if a test geometry is a sub geometry of + * a given reference geometry. + * + * @overload This is a overloaded version that uses a single tolerance for spatial and directional aspects. For more details, + * see the other overloaded version. + * @param testGeo Geometry that should be checked if it is a sub geometry of referenceGeo. + * @param referenceGeo Geometry that should contain testedGeometry as sub geometry. + * @param eps Tolarence for comparison (both spatial and directional). 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 otherwise. */ MITKCORE_EXPORT bool IsSubGeometry(const mitk::BaseGeometry& testGeo, const mitk::BaseGeometry& referenceGeo, - ScalarType eps, - bool verbose); + ScalarType eps = mitk::eps, + bool verbose = false); } // namespace mitk #endif /* BaseGeometry_H_HEADER_INCLUDED */ diff --git a/Modules/Core/include/mitkNodePredicateGeometry.h b/Modules/Core/include/mitkNodePredicateGeometry.h index 626da5b3ac..42c8fd55c5 100644 --- a/Modules/Core/include/mitkNodePredicateGeometry.h +++ b/Modules/Core/include/mitkNodePredicateGeometry.h @@ -1,69 +1,80 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKNODEPREDICATEGEOMETRY_H_HEADER_INCLUDED_ #define MITKNODEPREDICATEGEOMETRY_H_HEADER_INCLUDED_ #include "mitkNodePredicateBase.h" #include "mitkBaseGeometry.h" #include "mitkTimeGeometry.h" namespace mitk { class BaseData; /**Documentation @brief Predicate that evaluates if the given DataNode's data object has the same geometry (in terms of spacing, origin, orientation) like - the reference geometry. One can either check the whole time geometry of + the reference geometry. + + One can either check the whole time geometry of the date node by defining a referenc time geometry or check against one given reference base geometry. If the predicate should check against a base geometry, you can specify the timepoint of the data's time geometry that should be checked. If no timepoint is defined the predicate will evaluate the data geometry in the first timestep. Evaluates to "false" for unsupported or undefined data objects/geometries. + + One can specify the tolerance/precision of the check via SetCheckPrecision(). + @remark The default tolerance is 1e-6 and therefore not as strict as mitk::eps. The reason is, + that, for the typical use of the node predicate, mitk::eps would be to pedantic. We + often encounter floating point differences and practically it makes no difference e.g. + if two images differ something like 0.000001 mm in size or spacing. @ingroup DataStorage */ class MITKCORE_EXPORT NodePredicateGeometry : public NodePredicateBase { public: mitkClassMacro(NodePredicateGeometry, NodePredicateBase); mitkNewMacro1Param(NodePredicateGeometry, const TimeGeometry*); mitkNewMacro1Param(NodePredicateGeometry, const BaseGeometry*); mitkNewMacro2Param(NodePredicateGeometry, const BaseGeometry*, TimePointType); itkSetMacro(CheckPrecision, mitk::ScalarType); itkGetMacro(CheckPrecision, mitk::ScalarType); ~NodePredicateGeometry() override; bool CheckNode(const mitk::DataNode *node) const override; protected: /**Constructor that is used configures the predicate to check the reference geometry against the first data timepoint.*/ NodePredicateGeometry(const BaseGeometry* refGeometry); /**Constructor allows to define the timepoint that should be evaluated against the reference.*/ NodePredicateGeometry(const BaseGeometry* refGeometry, TimePointType relevantTimePoint); /**Constructor that is used configures the predicate to check against the whole time geometry.*/ NodePredicateGeometry(const TimeGeometry* refGeometry); BaseGeometry::ConstPointer m_RefGeometry; TimeGeometry::ConstPointer m_RefTimeGeometry; TimePointType m_TimePoint; /**Indicates if m_TimePoint should be regarded or always the first timestep should be used.*/ bool m_UseTimePoint; /**Precision that should be used for the equal checks.*/ mitk::ScalarType m_CheckPrecision; }; + + constexpr double NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION = 1e-6; + } // namespace mitk #endif /* MITKNodePredicateGeometry_H_HEADER_INCLUDED_ */ diff --git a/Modules/Core/include/mitkNodePredicateSubGeometry.h b/Modules/Core/include/mitkNodePredicateSubGeometry.h index dbb6dcc602..b777a64099 100644 --- a/Modules/Core/include/mitkNodePredicateSubGeometry.h +++ b/Modules/Core/include/mitkNodePredicateSubGeometry.h @@ -1,68 +1,74 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKNODEPREDICATESUBGEOMETRY_H_HEADER_INCLUDED_ #define MITKNODEPREDICATESUBGEOMETRY_H_HEADER_INCLUDED_ #include "mitkNodePredicateBase.h" #include "mitkBaseGeometry.h" #include "mitkTimeGeometry.h" namespace mitk { class BaseData; /**Documentation @brief Predicate that evaluates if the given DataNode's data object has a geometry that is a sub geomety of the reference geometry. Sub geometry means that both geometries have the same voxel grid (same spacing, same axes, orgin is on voxel grid), but the bounding box of the checked geometry is contained or equal to the bounding box of the reference geometry.\n One can either check the whole time geometry of the data node by defining a referenc time geometry or check against one given2 reference base geometry. If the predicate should check against a base geometry, you can specify the timepoint of the data's time geometry that should be checked. If no timepoint is defined the predicate will evaluate the data geometry in the first timestep. Evaluates to "false" for unsupported or undefined data objects/geometries. + + On can specify the tolerance/precision of the check via SetCheckPrecision(). + @remark The default tolerance is the same as for NodePredicateGeometry and therefore not as strict as mitk::eps. The reason is, + that, for the typical use of the node predicate, mitk::eps would be to pedantic. We + often encounter floating point differences and practically it makes no difference e.g. + if two images differ something like 0.000001 mm in size or spacing. @ingroup DataStorage */ class MITKCORE_EXPORT NodePredicateSubGeometry : public NodePredicateBase { public: mitkClassMacro(NodePredicateSubGeometry, NodePredicateBase); mitkNewMacro1Param(NodePredicateSubGeometry, const BaseGeometry*); mitkNewMacro2Param(NodePredicateSubGeometry, const BaseGeometry*, TimePointType); itkSetMacro(CheckPrecision, mitk::ScalarType); itkGetMacro(CheckPrecision, mitk::ScalarType); ~NodePredicateSubGeometry() override; bool CheckNode(const mitk::DataNode *node) const override; protected: /**Constructor that is used configures the predicate to check the reference geometry against the first data timepoint.*/ NodePredicateSubGeometry(const BaseGeometry* refGeometry); /**Constructor allows to define the timepoint that should be evaluated against the reference.*/ NodePredicateSubGeometry(const BaseGeometry* refGeometry, TimePointType relevantTimePoint); BaseGeometry::ConstPointer m_RefGeometry; TimePointType m_TimePoint; /**Indicates if m_TimePoint should be regarded or always the first timestep should be used.*/ bool m_UseTimePoint; /**Precision that should be used for the equal checks.*/ mitk::ScalarType m_CheckPrecision; }; } // namespace mitk #endif diff --git a/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp b/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp index c2547de34b..b862a7613c 100644 --- a/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp +++ b/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp @@ -1,1105 +1,1080 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include #include #include "mitkApplyTransformMatrixOperation.h" #include "mitkBaseGeometry.h" #include "mitkGeometryTransformHolder.h" #include "mitkInteractionConst.h" #include "mitkMatrixConvert.h" #include "mitkModifiedLock.h" #include "mitkPointOperation.h" #include "mitkRestorePlanePositionOperation.h" #include "mitkRotationOperation.h" #include "mitkScaleOperation.h" #include "mitkVector.h" #include "mitkMatrix.h" mitk::BaseGeometry::BaseGeometry() : Superclass(), mitk::OperationActor(), m_FrameOfReferenceID(0), m_IndexToWorldTransformLastModified(0), m_ImageGeometry(false), m_ModifiedLockFlag(false), m_ModifiedCalledFlag(false) { m_GeometryTransform = new GeometryTransformHolder(); Initialize(); } mitk::BaseGeometry::BaseGeometry(const BaseGeometry &other) : Superclass(), mitk::OperationActor(), m_FrameOfReferenceID(other.m_FrameOfReferenceID), m_IndexToWorldTransformLastModified(other.m_IndexToWorldTransformLastModified), m_ImageGeometry(other.m_ImageGeometry), m_ModifiedLockFlag(false), m_ModifiedCalledFlag(false) { m_GeometryTransform = new GeometryTransformHolder(*other.GetGeometryTransformHolder()); other.InitializeGeometry(this); } mitk::BaseGeometry::~BaseGeometry() { delete m_GeometryTransform; } void mitk::BaseGeometry::SetVtkMatrixDeepCopy(vtkTransform *vtktransform) { m_GeometryTransform->SetVtkMatrixDeepCopy(vtktransform); } const mitk::Point3D mitk::BaseGeometry::GetOrigin() const { return m_GeometryTransform->GetOrigin(); } void mitk::BaseGeometry::SetOrigin(const Point3D &origin) { mitk::ModifiedLock lock(this); if (origin != GetOrigin()) { m_GeometryTransform->SetOrigin(origin); Modified(); } } const mitk::Vector3D mitk::BaseGeometry::GetSpacing() const { return m_GeometryTransform->GetSpacing(); } void mitk::BaseGeometry::Initialize() { float b[6] = {0, 1, 0, 1, 0, 1}; SetFloatBounds(b); m_GeometryTransform->Initialize(); m_FrameOfReferenceID = 0; m_ImageGeometry = false; } void mitk::BaseGeometry::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++; SetBounds(b); } void mitk::BaseGeometry::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++; SetBounds(b); } /** Initialize the geometry */ void mitk::BaseGeometry::InitializeGeometry(BaseGeometry *newGeometry) const { newGeometry->SetBounds(m_BoundingBox->GetBounds()); newGeometry->SetFrameOfReferenceID(GetFrameOfReferenceID()); newGeometry->InitializeGeometryTransformHolder(this); newGeometry->m_ImageGeometry = m_ImageGeometry; } void mitk::BaseGeometry::InitializeGeometryTransformHolder(const BaseGeometry *otherGeometry) { this->m_GeometryTransform->Initialize(otherGeometry->GetGeometryTransformHolder()); } /** Set the bounds */ void mitk::BaseGeometry::SetBounds(const BoundsArrayType &bounds) { mitk::ModifiedLock lock(this); this->CheckBounds(bounds); m_BoundingBox = BoundingBoxType::New(); BoundingBoxType::PointsContainer::Pointer pointscontainer = BoundingBoxType::PointsContainer::New(); BoundingBoxType::PointType p; BoundingBoxType::PointIdentifier pointid; for (pointid = 0; pointid < 2; ++pointid) { unsigned int i; for (i = 0; i < m_NDimensions; ++i) { p[i] = bounds[2 * i + pointid]; } pointscontainer->InsertElement(pointid, p); } m_BoundingBox->SetPoints(pointscontainer); m_BoundingBox->ComputeBoundingBox(); this->Modified(); } void mitk::BaseGeometry::SetIndexToWorldTransform(mitk::AffineTransform3D *transform) { mitk::ModifiedLock lock(this); CheckIndexToWorldTransform(transform); m_GeometryTransform->SetIndexToWorldTransform(transform); Modified(); } void mitk::BaseGeometry::SetIndexToWorldTransformWithoutChangingSpacing(mitk::AffineTransform3D *transform) { // security check mitk::Vector3D originalSpacing = this->GetSpacing(); mitk::ModifiedLock lock(this); CheckIndexToWorldTransform(transform); m_GeometryTransform->SetIndexToWorldTransformWithoutChangingSpacing(transform); Modified(); // Security check. Spacig must not have changed if (!mitk::Equal(originalSpacing, this->GetSpacing())) { MITK_WARN << "Spacing has changed in a method, where the spacing must not change."; assert(false); } } const mitk::BaseGeometry::BoundsArrayType mitk::BaseGeometry::GetBounds() const { assert(m_BoundingBox.IsNotNull()); return m_BoundingBox->GetBounds(); } bool mitk::BaseGeometry::IsValid() const { return true; } void mitk::BaseGeometry::SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing) { PreSetSpacing(aSpacing); _SetSpacing(aSpacing, enforceSetSpacing); } void mitk::BaseGeometry::_SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing) { m_GeometryTransform->SetSpacing(aSpacing, enforceSetSpacing); } mitk::Vector3D mitk::BaseGeometry::GetAxisVector(unsigned int direction) const { Vector3D frontToBack; frontToBack.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(direction)); frontToBack *= GetExtent(direction); return frontToBack; } mitk::ScalarType mitk::BaseGeometry::GetExtent(unsigned int direction) const { assert(m_BoundingBox.IsNotNull()); if (direction >= m_NDimensions) mitkThrow() << "Direction is too big. This geometry is for 3D Data"; BoundsArrayType bounds = m_BoundingBox->GetBounds(); return bounds[direction * 2 + 1] - bounds[direction * 2]; } bool mitk::BaseGeometry::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 (false); return isConvertableWithoutLoss; } mitk::Point3D mitk::BaseGeometry::GetCenter() const { assert(m_BoundingBox.IsNotNull()); Point3D c = m_BoundingBox->GetCenter(); if (m_ImageGeometry) { // Get Center returns the middel of min and max pixel index. In corner based images, this is the right position. // In center based images (imageGeometry == true), the index needs to be shifted back. c[0] -= 0.5; c[1] -= 0.5; c[2] -= 0.5; } this->IndexToWorld(c, c); return c; } double mitk::BaseGeometry::GetDiagonalLength2() const { Vector3D diagonalvector = GetCornerPoint() - GetCornerPoint(false, false, false); return diagonalvector.GetSquaredNorm(); } double mitk::BaseGeometry::GetDiagonalLength() const { return sqrt(GetDiagonalLength2()); } mitk::Point3D mitk::BaseGeometry::GetCornerPoint(int id) const { assert(id >= 0); assert(this->IsBoundingBoxNull() == false); BoundingBox::BoundsArrayType bounds = this->GetBoundingBox()->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 this->GetIndexToWorldTransform()->TransformPoint(cornerpoint); } mitk::Point3D mitk::BaseGeometry::GetCornerPoint(bool xFront, bool yFront, bool zFront) const { assert(this->IsBoundingBoxNull() == false); BoundingBox::BoundsArrayType bounds = this->GetBoundingBox()->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 this->GetIndexToWorldTransform()->TransformPoint(cornerpoint); } mitk::ScalarType mitk::BaseGeometry::GetExtentInMM(int direction) const { return this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(direction).magnitude() * GetExtent(direction); } void mitk::BaseGeometry::SetExtentInMM(int direction, ScalarType extentInMM) { mitk::ModifiedLock lock(this); ScalarType len = GetExtentInMM(direction); if (fabs(len - extentInMM) >= mitk::eps) { AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = m_GeometryTransform->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_GeometryTransform->SetMatrix(matrix); Modified(); } } bool mitk::BaseGeometry::IsInside(const mitk::Point3D &p) const { mitk::Point3D index; WorldToIndex(p, index); return IsIndexInside(index); } bool mitk::BaseGeometry::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 = this->GetBoundingBox()->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 = this->GetBoundingBox()->GetBounds(); if ((discretIndex[0] == bounds[1]) || (discretIndex[1] == bounds[3]) || (discretIndex[2] == bounds[5])) inside = false; } } else inside = this->GetBoundingBox()->IsInside(index); return inside; } void mitk::BaseGeometry::WorldToIndex(const mitk::Point3D &pt_mm, mitk::Point3D &pt_units) const { mitk::Vector3D tempIn, tempOut; const TransformType::OffsetType &offset = this->GetIndexToWorldTransform()->GetOffset(); tempIn = pt_mm.GetVectorFromOrigin() - offset; WorldToIndex(tempIn, tempOut); pt_units = tempOut; } void mitk::BaseGeometry::WorldToIndex(const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const { // Get WorldToIndex transform if (m_IndexToWorldTransformLastModified != this->GetIndexToWorldTransform()->GetMTime()) { if (!m_InvertedTransform) { m_InvertedTransform = TransformType::New(); } if (!this->GetIndexToWorldTransform()->GetInverse(m_InvertedTransform.GetPointer())) { itkExceptionMacro("Internal ITK matrix inversion error, cannot proceed."); } m_IndexToWorldTransformLastModified = this->GetIndexToWorldTransform()->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 << this->GetIndexToWorldTransform()->GetMatrix() << "Suggested inverted matrix is:" << std::endl << inverse); } vec_units = inverse * vec_mm; } void mitk::BaseGeometry::WorldToIndex(const mitk::Point3D & /*atPt3d_mm*/, const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const { MITK_WARN << "Warning! Call of the deprecated function BaseGeometry::WorldToIndex(point, vec, vec). Use " "BaseGeometry::WorldToIndex(vec, vec) instead!"; this->WorldToIndex(vec_mm, vec_units); } mitk::VnlVector mitk::BaseGeometry::GetOriginVnl() const { return GetOrigin().GetVnlVector(); } vtkLinearTransform *mitk::BaseGeometry::GetVtkTransform() const { return m_GeometryTransform->GetVtkTransform(); } void mitk::BaseGeometry::SetIdentity() { mitk::ModifiedLock lock(this); m_GeometryTransform->SetIdentity(); Modified(); } void mitk::BaseGeometry::Compose(const mitk::BaseGeometry::TransformType *other, bool pre) { mitk::ModifiedLock lock(this); m_GeometryTransform->Compose(other, pre); Modified(); } void mitk::BaseGeometry::Compose(const vtkMatrix4x4 *vtkmatrix, bool pre) { mitk::BaseGeometry::TransformType::Pointer itkTransform = mitk::BaseGeometry::TransformType::New(); TransferVtkMatrixToItkTransform(vtkmatrix, itkTransform.GetPointer()); Compose(itkTransform, pre); } void mitk::BaseGeometry::Translate(const Vector3D &vector) { if ((vector[0] != 0) || (vector[1] != 0) || (vector[2] != 0)) { this->SetOrigin(this->GetOrigin() + vector); } } void mitk::BaseGeometry::IndexToWorld(const mitk::Point3D &pt_units, mitk::Point3D &pt_mm) const { pt_mm = this->GetIndexToWorldTransform()->TransformPoint(pt_units); } void mitk::BaseGeometry::IndexToWorld(const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const { vec_mm = this->GetIndexToWorldTransform()->TransformVector(vec_units); } void mitk::BaseGeometry::ExecuteOperation(Operation *operation) { mitk::ModifiedLock lock(this); vtkTransform *vtktransform = vtkTransform::New(); vtktransform->SetMatrix(this->GetVtkMatrix()); switch (operation->GetOperationType()) { case OpNOTHING: break; case OpMOVE: { auto *pointOp = dynamic_cast(operation); if (pointOp == nullptr) { MITK_ERROR << "Point move operation is null!"; 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: { auto *scaleOp = dynamic_cast(operation); if (scaleOp == nullptr) { MITK_ERROR << "Scale operation is null!"; return; } mitk::Point3D newScale = scaleOp->GetScaleFactor(); ScalarType scalefactor[3]; scalefactor[0] = 1 + (newScale[0] / GetMatrixColumn(0).magnitude()); scalefactor[1] = 1 + (newScale[1] / GetMatrixColumn(1).magnitude()); scalefactor[2] = 1 + (newScale[2] / GetMatrixColumn(2).magnitude()); mitk::Point3D anchor = scaleOp->GetScaleAnchorPoint(); vtktransform->PostMultiply(); vtktransform->Translate(-anchor[0], -anchor[1], -anchor[2]); vtktransform->Scale(scalefactor[0], scalefactor[1], scalefactor[2]); vtktransform->Translate(anchor[0], anchor[1], anchor[2]); break; } case OpROTATE: { auto *rotateOp = dynamic_cast(operation); if (rotateOp == nullptr) { MITK_ERROR << "Rotation operation is null!"; 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); matrix->Delete(); break; } case OpAPPLYTRANSFORMMATRIX: { auto *applyMatrixOp = dynamic_cast(operation); vtktransform->SetMatrix(applyMatrixOp->GetMatrix()); break; } default: vtktransform->Delete(); return; } this->SetVtkMatrixDeepCopy(vtktransform); Modified(); vtktransform->Delete(); } mitk::VnlVector mitk::BaseGeometry::GetMatrixColumn(unsigned int direction) const { return this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(direction); } mitk::BoundingBox::Pointer mitk::BaseGeometry::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 != nullptr) { 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; } const std::string mitk::BaseGeometry::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::BaseGeometry::SetIndexToWorldTransformByVtkMatrix(vtkMatrix4x4 *vtkmatrix) { m_GeometryTransform->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); } void mitk::BaseGeometry::SetIndexToWorldTransformByVtkMatrixWithoutChangingSpacing(vtkMatrix4x4 *vtkmatrix) { m_GeometryTransform->SetIndexToWorldTransformByVtkMatrixWithoutChangingSpacing(vtkmatrix); } void mitk::BaseGeometry::IndexToWorld(const mitk::Point3D & /*atPt3d_units*/, const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const { MITK_WARN << "Warning! Call of the deprecated function BaseGeometry::IndexToWorld(point, vec, vec). Use " "BaseGeometry::IndexToWorld(vec, vec) instead!"; // vec_mm = m_IndexToWorldTransform->TransformVector(vec_units); this->IndexToWorld(vec_units, vec_mm); } vtkMatrix4x4 *mitk::BaseGeometry::GetVtkMatrix() { return m_GeometryTransform->GetVtkMatrix(); } bool mitk::BaseGeometry::IsBoundingBoxNull() const { return m_BoundingBox.IsNull(); } bool mitk::BaseGeometry::IsIndexToWorldTransformNull() const { return m_GeometryTransform->IsIndexToWorldTransformNull(); } void mitk::BaseGeometry::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); } void mitk::BaseGeometry::PrintSelf(std::ostream &os, itk::Indent indent) const { os << indent << " IndexToWorldTransform: "; if (this->IsIndexToWorldTransformNull()) os << "nullptr" << 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 << this->GetIndexToWorldTransform()->GetMatrix()[i][j] << " "; } os << std::endl; } os << indent << "Offset: " << this->GetIndexToWorldTransform()->GetOffset() << std::endl; os << indent << "Center: " << this->GetIndexToWorldTransform()->GetCenter() << std::endl; os << indent << "Translation: " << this->GetIndexToWorldTransform()->GetTranslation() << std::endl; os << indent << "Inverse: " << std::endl; for (i = 0; i < 3; i++) { os << indent.GetNextIndent(); for (j = 0; j < 3; j++) { os << this->GetIndexToWorldTransform()->GetInverseMatrix()[i][j] << " "; } os << std::endl; } // from itk::ScalableAffineTransform os << indent << "Scale : "; for (i = 0; i < 3; i++) { os << this->GetIndexToWorldTransform()->GetScale()[i] << " "; } os << std::endl; } os << indent << " BoundingBox: "; if (this->IsBoundingBoxNull()) os << "nullptr" << std::endl; else { os << indent << "( "; for (unsigned int i = 0; i < 3; i++) { os << this->GetBoundingBox()->GetBounds()[2 * i] << "," << this->GetBoundingBox()->GetBounds()[2 * i + 1] << " "; } os << " )" << std::endl; } os << indent << " Origin: " << this->GetOrigin() << std::endl; os << indent << " ImageGeometry: " << this->GetImageGeometry() << std::endl; os << indent << " Spacing: " << this->GetSpacing() << std::endl; } void mitk::BaseGeometry::Modified() const { if (!m_ModifiedLockFlag) Superclass::Modified(); else m_ModifiedCalledFlag = true; } mitk::AffineTransform3D *mitk::BaseGeometry::GetIndexToWorldTransform() { return m_GeometryTransform->GetIndexToWorldTransform(); } const mitk::AffineTransform3D *mitk::BaseGeometry::GetIndexToWorldTransform() const { return m_GeometryTransform->GetIndexToWorldTransform(); } const mitk::GeometryTransformHolder *mitk::BaseGeometry::GetGeometryTransformHolder() const { return m_GeometryTransform; } -bool mitk::Equal(const mitk::BaseGeometry::BoundingBoxType *leftHandSide, - const mitk::BaseGeometry::BoundingBoxType *rightHandSide, - ScalarType eps, - bool verbose) -{ - if ((leftHandSide == nullptr) || (rightHandSide == nullptr)) - { - MITK_ERROR << "mitk::Equal( const mitk::Geometry3D::BoundingBoxType *leftHandSide, const " - "mitk::Geometry3D::BoundingBoxType *rightHandSide, ScalarType eps, bool verbose ) does not with nullptr " - "pointer input."; - return false; - } - return Equal(*leftHandSide, *rightHandSide, eps, verbose); -} - bool mitk::Equal(const mitk::BaseGeometry::BoundingBoxType &leftHandSide, const mitk::BaseGeometry::BoundingBoxType &rightHandSide, ScalarType eps, bool verbose) { bool result = true; BaseGeometry::BoundsArrayType rightBounds = rightHandSide.GetBounds(); BaseGeometry::BoundsArrayType leftBounds = leftHandSide.GetBounds(); BaseGeometry::BoundsArrayType::Iterator itLeft = leftBounds.Begin(); for (BaseGeometry::BoundsArrayType::Iterator itRight = rightBounds.Begin(); itRight != rightBounds.End(); ++itRight) { if ((!mitk::Equal(*itLeft, *itRight, 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::BaseGeometry *leftHandSide, - const mitk::BaseGeometry *rightHandSide, - ScalarType eps, - bool verbose) -{ - if ((leftHandSide == nullptr) || (rightHandSide == nullptr)) - { - MITK_ERROR << "mitk::Equal(const mitk::Geometry3D *leftHandSide, const mitk::Geometry3D *rightHandSide, ScalarType " - "eps, bool verbose) does not with nullptr pointer input."; - return false; - } - return Equal(*leftHandSide, *rightHandSide, eps, verbose); -} - bool mitk::Equal(const mitk::BaseGeometry &leftHandSide, const mitk::BaseGeometry &rightHandSide, - ScalarType eps, + ScalarType coordinateEps, + ScalarType directionEps, bool verbose) { bool result = true; // Compare spacings - if (!mitk::Equal(leftHandSide.GetSpacing(), rightHandSide.GetSpacing(), eps)) + if (!mitk::Equal(leftHandSide.GetSpacing(), rightHandSide.GetSpacing(), coordinateEps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] Spacing differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetSpacing() << " : leftHandSide is " - << leftHandSide.GetSpacing() << " and tolerance is " << eps; + << leftHandSide.GetSpacing() << " and tolerance is " << coordinateEps; } result = false; } // Compare Origins - if (!mitk::Equal(leftHandSide.GetOrigin(), rightHandSide.GetOrigin(), eps)) + if (!mitk::Equal(leftHandSide.GetOrigin(), rightHandSide.GetOrigin(), coordinateEps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] Origin differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetOrigin() << " : leftHandSide is " - << leftHandSide.GetOrigin() << " and tolerance is " << eps; + << leftHandSide.GetOrigin() << " and tolerance is " << coordinateEps; } result = false; } // Compare Axis and Extents for (unsigned int i = 0; i < 3; ++i) { - if (!mitk::Equal(leftHandSide.GetAxisVector(i), rightHandSide.GetAxisVector(i), eps)) + if (!mitk::Equal(leftHandSide.GetAxisVector(i), rightHandSide.GetAxisVector(i), directionEps)) { 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; + << leftHandSide.GetAxisVector(i) << " and tolerance is " << directionEps; } result = false; } - if (!mitk::Equal(leftHandSide.GetExtent(i), rightHandSide.GetExtent(i), eps)) + if (!mitk::Equal(leftHandSide.GetExtent(i), rightHandSide.GetExtent(i), coordinateEps)) { 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; + << leftHandSide.GetExtent(i) << " and tolerance is " << coordinateEps; } 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 FrameOfReference ID if (rightHandSide.GetFrameOfReferenceID() != leftHandSide.GetFrameOfReferenceID()) { if (verbose) { MITK_INFO << "[( Geometry3D )] GetFrameOfReferenceID is different."; MITK_INFO << "rightHandSide is " << rightHandSide.GetFrameOfReferenceID() << " : leftHandSide is " << leftHandSide.GetFrameOfReferenceID(); } result = false; } // Compare BoundingBoxes - if (!mitk::Equal(*leftHandSide.GetBoundingBox(), *rightHandSide.GetBoundingBox(), eps, verbose)) + if (!mitk::Equal(*leftHandSide.GetBoundingBox(), *rightHandSide.GetBoundingBox(), coordinateEps, verbose)) { result = false; } // Compare IndexToWorldTransform Matrix - if (!mitk::Equal(*leftHandSide.GetIndexToWorldTransform(), *rightHandSide.GetIndexToWorldTransform(), eps, verbose)) + if (!mitk::Equal(*leftHandSide.GetIndexToWorldTransform(), *rightHandSide.GetIndexToWorldTransform(), directionEps, verbose)) { result = false; } return result; } -bool mitk::Equal(const mitk::BaseGeometry::TransformType *leftHandSide, - const mitk::BaseGeometry::TransformType *rightHandSide, - ScalarType eps, - bool verbose) +bool mitk::Equal(const mitk::BaseGeometry& leftHandSide, + const mitk::BaseGeometry& rightHandSide, + ScalarType eps, + bool verbose) { - if ((leftHandSide == nullptr) || (rightHandSide == nullptr)) - { - MITK_ERROR << "mitk::Equal(const Geometry3D::TransformType *leftHandSide, const Geometry3D::TransformType " - "*rightHandSide, ScalarType eps, bool verbose ) does not with nullptr pointer input."; - return false; - } - return Equal(*leftHandSide, *rightHandSide, eps, verbose); + return Equal(leftHandSide, rightHandSide, eps, eps, verbose); } bool mitk::Equal(const mitk::BaseGeometry::TransformType &leftHandSide, const mitk::BaseGeometry::TransformType &rightHandSide, ScalarType eps, bool verbose) { // Compare IndexToWorldTransform Matrix if (!mitk::MatrixEqualElementWise(leftHandSide.GetMatrix(), rightHandSide.GetMatrix(), eps)) { 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; } bool mitk::IsSubGeometry(const mitk::BaseGeometry& testGeo, const mitk::BaseGeometry& referenceGeo, - ScalarType eps, + ScalarType coordinateEps, + ScalarType directionEps, bool verbose) { bool result = true; // Compare spacings (must be equal) const auto testedSpacing = testGeo.GetSpacing(); - if (!mitk::Equal(testedSpacing, referenceGeo.GetSpacing(), eps)) + if (!mitk::Equal(testedSpacing, referenceGeo.GetSpacing(), coordinateEps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] Spacing differs."; MITK_INFO << "testedGeometry is " << setprecision(12) << testedSpacing << " : referenceGeometry is " - << referenceGeo.GetSpacing() << " and tolerance is " << eps; + << referenceGeo.GetSpacing() << " and tolerance is " << coordinateEps; } result = false; } // Compare ImageGeometry Flag (must be equal) if (referenceGeo.GetImageGeometry() != testGeo.GetImageGeometry()) { if (verbose) { MITK_INFO << "[( Geometry3D )] GetImageGeometry is different."; MITK_INFO << "referenceGeo is " << referenceGeo.GetImageGeometry() << " : testGeo is " << testGeo.GetImageGeometry(); } result = false; } // Compare IndexToWorldTransform Matrix (must be equal -> same axis directions) - if (!Equal(*(testGeo.GetIndexToWorldTransform()), *(referenceGeo.GetIndexToWorldTransform()), eps, verbose)) + if (!Equal(*(testGeo.GetIndexToWorldTransform()), *(referenceGeo.GetIndexToWorldTransform()), directionEps, verbose)) { result = false; } // Compare BoundingBoxes (must be <=, thus corners of the tests geom must be inside the reference) for (int i = 0; i<8; ++i) { auto testCorner = testGeo.GetCornerPoint(i); if (testGeo.GetImageGeometry()) { //if it is an image geometry the upper bounds indicate the first index that is not inside //the geometry. Thus we will get the lower corener of the "first" voxel outside, //but we need the corner of the "last" voxel inside the test geometry. //To achive that we supstract one spacing from each index elment that is constructed //by an upper bound value (see implementation of BaseGeometry::GetCorner(). std::bitset bs(i); if (bs.test(0)) { testCorner[2] -= testedSpacing[2]; } if (bs.test(1)) { testCorner[1] -= testedSpacing[1]; } if (bs.test(2)) { testCorner[0] -= testedSpacing[0]; } } if (!referenceGeo.IsInside(testCorner)) { if (verbose) { MITK_INFO << "[( Geometry3D )] corner point is not inside. "; MITK_INFO << "referenceGeo is " << setprecision(12) << referenceGeo << " : tested corner is " << testGeo.GetCornerPoint(i); } result = false; } } // check grid of test geometry is on the grid of the reference geometry. This is important as the // boundingbox is only checked for containing the tested geometry, but if a corner (one is enough // as we know that axis and spacing are equal) of the tested geometry is on the grid it is really // a sub geometry (as they have the same spacing and axis). auto cornerOffset = testGeo.GetCornerPoint(0) - referenceGeo.GetCornerPoint(0); for (unsigned int i = 0; i < 3; ++i) { auto pixelCountContinous = cornerOffset[i] / testedSpacing[i]; auto pixelCount = std::round(pixelCountContinous); - if (std::abs(pixelCount - pixelCountContinous) > eps) + if (std::abs(pixelCount - pixelCountContinous) > coordinateEps) { if (verbose) { MITK_INFO << "[( Geometry3D )] Tested geometry is not on the grid of the reference geometry. "; MITK_INFO << "referenceGeo is " << setprecision(15) << referenceGeo << " : tested corner offset in pixels is " << pixelCountContinous << " for axis "<GetData(); if (data) { if (m_RefGeometry.IsNotNull()) { //check only one time point. mitk::BaseGeometry::Pointer testGeometry = data->GetGeometry(); if (this->m_UseTimePoint) { testGeometry = data->GetTimeGeometry()->GetGeometryForTimePoint(m_TimePoint); } if (testGeometry) { return Equal(*testGeometry, *m_RefGeometry, this->m_CheckPrecision, false); } } else { //check whole time geometry return Equal(*data->GetTimeGeometry(), *m_RefTimeGeometry, this->m_CheckPrecision, false); } } return false; } diff --git a/Modules/Core/src/DataManagement/mitkNodePredicateSubGeometry.cpp b/Modules/Core/src/DataManagement/mitkNodePredicateSubGeometry.cpp index fe55cc35a9..264744e492 100644 --- a/Modules/Core/src/DataManagement/mitkNodePredicateSubGeometry.cpp +++ b/Modules/Core/src/DataManagement/mitkNodePredicateSubGeometry.cpp @@ -1,57 +1,58 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkNodePredicateSubGeometry.h" #include "mitkDataNode.h" #include "mitkImage.h" +#include "mitkNodePredicateGeometry.h" mitk::NodePredicateSubGeometry::NodePredicateSubGeometry(const BaseGeometry* refGeometry, TimePointType relevantTimePoint) - : m_RefGeometry(refGeometry), m_TimePoint(relevantTimePoint), m_UseTimePoint(true), m_CheckPrecision(mitk::eps) + : m_RefGeometry(refGeometry), m_TimePoint(relevantTimePoint), m_UseTimePoint(true), m_CheckPrecision(NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION) { if (m_RefGeometry.IsNull()) mitkThrow() << "Invalid constructor initialization. Reference base geometry instance is nullptr pointer."; } mitk::NodePredicateSubGeometry::NodePredicateSubGeometry(const BaseGeometry* refGeometry) - : m_RefGeometry(refGeometry), m_TimePoint(0), m_UseTimePoint(false), m_CheckPrecision(mitk::eps) + : m_RefGeometry(refGeometry), m_TimePoint(0), m_UseTimePoint(false), m_CheckPrecision(NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION) { if (m_RefGeometry.IsNull()) mitkThrow() << "Invalid constructor initialization. Reference base geometry instance is nullptr pointer."; } mitk::NodePredicateSubGeometry::~NodePredicateSubGeometry() { } bool mitk::NodePredicateSubGeometry::CheckNode(const mitk::DataNode *node) const { if (node == nullptr) mitkThrow() << "NodePredicateSubGeometry: invalid node"; const auto *data = node->GetData(); if (data) { if (nullptr != data && m_RefGeometry.IsNotNull()) { //check only one time point. mitk::BaseGeometry::Pointer testGeometry = data->GetGeometry(); if (this->m_UseTimePoint) { testGeometry = data->GetTimeGeometry()->GetGeometryForTimePoint(m_TimePoint); } if (testGeometry.IsNotNull()) { return IsSubGeometry(*testGeometry, *m_RefGeometry, this->m_CheckPrecision, false); } } } return false; } diff --git a/Modules/Core/test/mitkBaseGeometryTest.cpp b/Modules/Core/test/mitkBaseGeometryTest.cpp index 07b5afaf36..edd73f0f57 100644 --- a/Modules/Core/test/mitkBaseGeometryTest.cpp +++ b/Modules/Core/test/mitkBaseGeometryTest.cpp @@ -1,1518 +1,1518 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkTestingMacros.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class vtkMatrix4x4; class vtkMatrixToLinearTransform; class vtkLinearTransform; typedef itk::BoundingBox BoundingBox; typedef itk::BoundingBox BoundingBoxType; typedef BoundingBoxType::BoundsArrayType BoundsArrayType; typedef BoundingBoxType::Pointer BoundingBoxPointer; // Dummy instance of abstract base class class DummyTestClass : public mitk::BaseGeometry { public: DummyTestClass(){}; DummyTestClass(const DummyTestClass &other) : BaseGeometry(other){}; ~DummyTestClass() override{}; mitkClassMacro(DummyTestClass, mitk::BaseGeometry); itkNewMacro(Self); mitkNewMacro1Param(Self, const Self &); itk::LightObject::Pointer InternalClone() const override { Self::Pointer newGeometry = new Self(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } protected: void PrintSelf(std::ostream & /*os*/, itk::Indent /*indent*/) const override{}; //##Documentation //## @brief Pre- and Post-functions are empty in BaseGeometry //## //## These virtual functions allow for a different beahiour in subclasses. //## Do implement them in every subclass of BaseGeometry. If not needed, use {}. //## If this class is inherited from a subclass of BaseGeometry, call {Superclass::Pre...();};, example: // SlicedGeometry3D class void PreSetSpacing(const mitk::Vector3D &/*aSpacing*/) override{}; }; class mitkBaseGeometryTestSuite : public mitk::TestFixture { // List of Tests CPPUNIT_TEST_SUITE(mitkBaseGeometryTestSuite); // Constructor MITK_TEST(TestConstructors); MITK_TEST(TestInitialize); // Set MITK_TEST(TestSetOrigin); MITK_TEST(TestSetBounds); MITK_TEST(TestSetFloatBounds); MITK_TEST(TestSetFloatBoundsDouble); MITK_TEST(TestSetFrameOfReferenceID); MITK_TEST(TestSetIndexToWorldTransform); MITK_TEST(TestSetIndexToWorldTransformWithoutChangingSpacing); MITK_TEST(TestSetIndexToWorldTransform_WithPointerToSameTransform); MITK_TEST(TestSetSpacing); MITK_TEST(TestTransferItkToVtkTransform); MITK_TEST(TestSetIndexToWorldTransformByVtkMatrix); MITK_TEST(TestSetIdentity); MITK_TEST(TestSetImageGeometry); // Equal MITK_TEST(Equal_CloneAndOriginal_ReturnsTrue); MITK_TEST(Equal_DifferentOrigin_ReturnsFalse); MITK_TEST(Equal_DifferentIndexToWorldTransform_ReturnsFalse); MITK_TEST(Equal_DifferentSpacing_ReturnsFalse); MITK_TEST(Equal_InputIsNull_ReturnsFalse); MITK_TEST(Equal_DifferentBoundingBox_ReturnsFalse); MITK_TEST(Equal_Transforms_MinorDifferences_And_Eps); // other Functions MITK_TEST(TestComposeTransform); MITK_TEST(TestComposeVtkMatrix); MITK_TEST(TestTranslate); MITK_TEST(TestIndexToWorld); MITK_TEST(TestExecuteOperation); MITK_TEST(TestCalculateBoundingBoxRelToTransform); // MITK_TEST(TestSetTimeBounds); MITK_TEST(TestIs2DConvertable); MITK_TEST(TestGetCornerPoint); MITK_TEST(TestExtentInMM); MITK_TEST(TestGetAxisVector); MITK_TEST(TestGetCenter); MITK_TEST(TestGetDiagonalLength); MITK_TEST(TestGetExtent); MITK_TEST(TestIsInside); MITK_TEST(TestGetMatrixColumn); // test IsSubGeometry MITK_TEST(IsSubGeometry_Spacing); MITK_TEST(IsSubGeometry_TransformMatrix); MITK_TEST(IsSubGeometry_Bounds); MITK_TEST(IsSubGeometry_Grid); CPPUNIT_TEST_SUITE_END(); // Used Variables private: mitk::Point3D aPoint; float aFloatSpacing[3]; mitk::Vector3D aSpacing; mitk::AffineTransform3D::Pointer aTransform; BoundingBoxPointer aBoundingBox; mitk::AffineTransform3D::MatrixType aMatrix; mitk::Point3D anotherPoint; mitk::Vector3D anotherSpacing; BoundingBoxPointer anotherBoundingBox; BoundingBoxPointer aThirdBoundingBox; mitk::AffineTransform3D::Pointer anotherTransform; mitk::AffineTransform3D::Pointer aThirdTransform; mitk::AffineTransform3D::MatrixType anotherMatrix; mitk::AffineTransform3D::MatrixType aThirdMatrix; DummyTestClass::Pointer aDummyGeometry; DummyTestClass::Pointer anotherDummyGeometry; public: // Set up for variables void setUp() override { mitk::FillVector3D(aFloatSpacing, 1, 1, 1); mitk::FillVector3D(aSpacing, 1, 1, 1); mitk::FillVector3D(aPoint, 0, 0, 0); // Transform aTransform = mitk::AffineTransform3D::New(); aTransform->SetIdentity(); aMatrix.SetIdentity(); anotherTransform = mitk::AffineTransform3D::New(); anotherMatrix.SetIdentity(); anotherMatrix(1, 1) = 2; anotherTransform->SetMatrix(anotherMatrix); aThirdTransform = mitk::AffineTransform3D::New(); aThirdMatrix.SetIdentity(); aThirdMatrix(1, 1) = 7; aThirdTransform->SetMatrix(aThirdMatrix); // Bounding Box float bounds[6] = {0, 1, 0, 1, 0, 1}; mitk::BoundingBox::BoundsArrayType b; const float *input = bounds; int j = 0; for (mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); j < 6; ++j) *it++ = (mitk::ScalarType)*input++; aBoundingBox = BoundingBoxType::New(); BoundingBoxType::PointsContainer::Pointer pointscontainer = BoundingBoxType::PointsContainer::New(); BoundingBoxType::PointType p; BoundingBoxType::PointIdentifier pointid; for (pointid = 0; pointid < 2; ++pointid) { unsigned int i; for (i = 0; i < 3; ++i) { p[i] = bounds[2 * i + pointid]; } pointscontainer->InsertElement(pointid, p); } aBoundingBox->SetPoints(pointscontainer); aBoundingBox->ComputeBoundingBox(); anotherBoundingBox = BoundingBoxType::New(); p[0] = 11; p[1] = 12; p[2] = 13; pointscontainer->InsertElement(1, p); anotherBoundingBox->SetPoints(pointscontainer); anotherBoundingBox->ComputeBoundingBox(); aThirdBoundingBox = BoundingBoxType::New(); p[0] = 22; p[1] = 23; p[2] = 24; pointscontainer->InsertElement(1, p); aThirdBoundingBox->SetPoints(pointscontainer); aThirdBoundingBox->ComputeBoundingBox(); mitk::FillVector3D(anotherPoint, 2, 3, 4); mitk::FillVector3D(anotherSpacing, 5, 6.5, 7); aDummyGeometry = DummyTestClass::New(); aDummyGeometry->Initialize(); anotherDummyGeometry = aDummyGeometry->Clone(); } void tearDown() override { aDummyGeometry = nullptr; anotherDummyGeometry = nullptr; } // Test functions void TestSetOrigin() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetOrigin(anotherPoint); CPPUNIT_ASSERT(mitk::Equal(anotherPoint, dummy->GetOrigin())); // undo changes, new and changed object need to be the same! dummy->SetOrigin(aPoint); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "TestSetOrigin"); } void TestSetImageGeometry() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetImageGeometry(true); CPPUNIT_ASSERT(dummy->GetImageGeometry()); // undo changes, new and changed object need to be the same! dummy->SetImageGeometry(false); CPPUNIT_ASSERT(dummy->GetImageGeometry() == false); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "TestSetImageGeometry"); } void TestSetFloatBounds() { float bounds[6] = {0, 11, 0, 12, 0, 13}; DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "BoundingBox equality"); // Wrong bounds, test needs to fail bounds[1] = 7; dummy->SetFloatBounds(bounds); MITK_ASSERT_NOT_EQUAL( BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "BoundingBox not equal"); // undo changes, new and changed object need to be the same! float originalBounds[6] = {0, 1, 0, 1, 0, 1}; dummy->SetFloatBounds(originalBounds); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Undo and equal"); } void TestSetBounds() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetBounds(anotherBoundingBox->GetBounds()); MITK_ASSERT_EQUAL(BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "Setting bounds"); // Test needs to fail now dummy->SetBounds(aThirdBoundingBox->GetBounds()); MITK_ASSERT_NOT_EQUAL( BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "Setting unequal bounds"); // undo changes, new and changed object need to be the same! dummy->SetBounds(aBoundingBox->GetBounds()); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Undo set bounds"); } void TestSetFloatBoundsDouble() { double bounds[6] = {0, 11, 0, 12, 0, 13}; DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "Float bounds"); // Test needs to fail now bounds[3] = 7; dummy->SetFloatBounds(bounds); MITK_ASSERT_NOT_EQUAL( BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "Float bounds unequal"); // undo changes, new and changed object need to be the same! double originalBounds[6] = {0, 1, 0, 1, 0, 1}; dummy->SetFloatBounds(originalBounds); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Undo set float bounds"); } void TestSetFrameOfReferenceID() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetFrameOfReferenceID(5); CPPUNIT_ASSERT(dummy->GetFrameOfReferenceID() == 5); // undo changes, new and changed object need to be the same! dummy->SetFrameOfReferenceID(0); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Undo set frame of reference"); } void TestSetIndexToWorldTransform() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); MITK_ASSERT_EQUAL(anotherTransform, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compare IndexToWorldTransform 1"); // Test needs to fail now dummy->SetIndexToWorldTransform(aThirdTransform); MITK_ASSERT_NOT_EQUAL(anotherTransform, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compare IndexToWorldTransform 2"); // undo changes, new and changed object need to be the same! dummy->SetIndexToWorldTransform(aTransform); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Compare IndexToWorldTransform 3"); } void TestSetIndexToWorldTransformWithoutChangingSpacing() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransformWithoutChangingSpacing(anotherTransform); CPPUNIT_ASSERT(mitk::Equal(aSpacing, dummy->GetSpacing(), mitk::eps, true)); // calculate a new version of anotherTransform, so that the spacing should be the same as the original spacing of // aTransform. mitk::AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = anotherTransform->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); mitk::Matrix3D matrix; matrix = vnlmatrix; anotherTransform->SetMatrix(matrix); - CPPUNIT_ASSERT(mitk::Equal(anotherTransform, dummy->GetIndexToWorldTransform(), mitk::eps, true)); + CPPUNIT_ASSERT(mitk::Equal(*anotherTransform, *(dummy->GetIndexToWorldTransform()), mitk::eps, true)); } void TestSetIndexToWorldTransform_WithPointerToSameTransform() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetOrigin(anotherPoint); dummy->SetIndexToWorldTransform(anotherTransform); dummy->SetSpacing(anotherSpacing); mitk::AffineTransform3D::Pointer testTransfrom = dummy->GetIndexToWorldTransform(); mitk::Vector3D modifiedPoint = anotherPoint.GetVectorFromOrigin() * 2.; testTransfrom->SetOffset(modifiedPoint); dummy->SetIndexToWorldTransform(testTransfrom); CPPUNIT_ASSERT(mitk::Equal(modifiedPoint, dummy->GetOrigin().GetVectorFromOrigin())); } void TestSetIndexToWorldTransformByVtkMatrix() { vtkMatrix4x4 *vtkmatrix; vtkmatrix = vtkMatrix4x4::New(); vtkmatrix->Identity(); vtkmatrix->SetElement(1, 1, 2); DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); MITK_ASSERT_EQUAL(anotherTransform, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compare IndexToWorldTransformByVtkMatrix 1"); // test needs to fail now vtkmatrix->SetElement(1, 1, 7); dummy->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); MITK_ASSERT_NOT_EQUAL(anotherTransform, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compare IndexToWorldTransformByVtkMatrix 2"); // undo changes, new and changed object need to be the same! vtkmatrix->SetElement(1, 1, 1); dummy->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); vtkmatrix->Delete(); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Compare IndexToWorldTransformByVtkMatrix 3"); } void TestSetIdentity() { DummyTestClass::Pointer dummy = DummyTestClass::New(); // Change IndextoWorldTransform and Origin dummy->SetIndexToWorldTransform(anotherTransform); dummy->SetOrigin(anotherPoint); // Set Identity should reset ITWT and Origin dummy->SetIdentity(); MITK_ASSERT_EQUAL( aTransform, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Test set identity 1"); CPPUNIT_ASSERT(mitk::Equal(aPoint, dummy->GetOrigin())); CPPUNIT_ASSERT(mitk::Equal(aSpacing, dummy->GetSpacing())); // new and changed object need to be the same! DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Test set identity 2"); } void TestSetSpacing() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetSpacing(anotherSpacing); CPPUNIT_ASSERT(mitk::Equal(anotherSpacing, dummy->GetSpacing())); // undo changes, new and changed object need to be the same! dummy->SetSpacing(aSpacing); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy spacing"); } void TestTransferItkToVtkTransform() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); // calls TransferItkToVtkTransform mitk::AffineTransform3D::Pointer dummyTransform = dummy->GetIndexToWorldTransform(); CPPUNIT_ASSERT(mitk::MatrixEqualElementWise(anotherMatrix, dummyTransform->GetMatrix())); } void TestConstructors() { // test standard constructor DummyTestClass::Pointer dummy1 = DummyTestClass::New(); bool test = dummy1->IsValid(); CPPUNIT_ASSERT(test == true); CPPUNIT_ASSERT(dummy1->GetFrameOfReferenceID() == 0); CPPUNIT_ASSERT(dummy1->GetIndexToWorldTransformLastModified() == 0); CPPUNIT_ASSERT(mitk::Equal(dummy1->GetSpacing(), aSpacing)); CPPUNIT_ASSERT(mitk::Equal(dummy1->GetOrigin(), aPoint)); CPPUNIT_ASSERT(dummy1->GetImageGeometry() == false); MITK_ASSERT_EQUAL( mitk::AffineTransform3D::Pointer(dummy1->GetIndexToWorldTransform()), aTransform, "Contructor test 1"); MITK_ASSERT_EQUAL( mitk::BaseGeometry::BoundingBoxType::ConstPointer(dummy1->GetBoundingBox()), aBoundingBox, "Constructor test 2"); DummyTestClass::Pointer dummy2 = DummyTestClass::New(); dummy2->SetOrigin(anotherPoint); float bounds[6] = {0, 11, 0, 12, 0, 13}; dummy2->SetFloatBounds(bounds); dummy2->SetIndexToWorldTransform(anotherTransform); dummy2->SetSpacing(anotherSpacing); DummyTestClass::Pointer dummy3 = DummyTestClass::New(*dummy2); MITK_ASSERT_EQUAL(dummy3, dummy2, "Dummy contructor"); } // Equal Tests void Equal_CloneAndOriginal_ReturnsTrue() { MITK_ASSERT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Clone test"); } void Equal_DifferentOrigin_ReturnsFalse() { anotherDummyGeometry->SetOrigin(anotherPoint); MITK_ASSERT_NOT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Different origin test"); } void Equal_DifferentIndexToWorldTransform_ReturnsFalse() { anotherDummyGeometry->SetIndexToWorldTransform(anotherTransform); MITK_ASSERT_NOT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Different index to world"); } void Equal_DifferentSpacing_ReturnsFalse() { anotherDummyGeometry->SetSpacing(anotherSpacing); MITK_ASSERT_NOT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Different spacing"); } void Equal_InputIsNull_ReturnsFalse() { DummyTestClass::Pointer geometryNull = nullptr; CPPUNIT_ASSERT_THROW(MITK_ASSERT_EQUAL(geometryNull, anotherDummyGeometry, "Input is null"), mitk::Exception); } void Equal_DifferentBoundingBox_ReturnsFalse() { // create different bounds to make the comparison false mitk::ScalarType bounds[] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; anotherDummyGeometry->SetBounds(bounds); MITK_ASSERT_NOT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Different bounding box"); } void Equal_Transforms_MinorDifferences_And_Eps() { // Verifies that the eps parameter is evaluated properly // when comparing two mitk::BaseGeometry::TransformTypes aMatrix.SetIdentity(); anotherMatrix.SetIdentity(); aMatrix(0, 1) = 0.0002; aTransform->SetMatrix(aMatrix); anotherMatrix(0, 1) = 0.0002; anotherTransform->SetMatrix(anotherMatrix); anotherTransform->SetMatrix(aMatrix); CPPUNIT_ASSERT_MESSAGE("Exact same transforms are mitk::Equal() for eps=mitk::eps", - mitk::Equal(aTransform, anotherTransform, mitk::eps, true)); + mitk::Equal(*aTransform, *anotherTransform, mitk::eps, true)); CPPUNIT_ASSERT_MESSAGE("Exact same transforms are mitk::Equal() for eps=vnl_math::eps", - mitk::Equal(aTransform, anotherTransform, vnl_math::eps, true)); + mitk::Equal(*aTransform, *anotherTransform, vnl_math::eps, true)); anotherMatrix(0, 1) = 0.0002 + mitk::eps; anotherTransform->SetMatrix(anotherMatrix); CPPUNIT_ASSERT_MESSAGE("Transforms of diff mitk::eps are !mitk::Equal() for eps=vnl_math::eps", - !mitk::Equal(aTransform, anotherTransform, vnl_math::eps, true)); + !mitk::Equal(*aTransform, *anotherTransform, vnl_math::eps, true)); CPPUNIT_ASSERT_MESSAGE("Transforms of diff mitk::eps are !mitk::Equal() for eps=mitk::eps-1%", - !mitk::Equal(aTransform, anotherTransform, mitk::eps * 0.99, true)); + !mitk::Equal(*aTransform, *anotherTransform, mitk::eps * 0.99, true)); CPPUNIT_ASSERT_MESSAGE("Transforms of diff mitk::eps _are_ mitk::Equal() for eps=mitk::eps+1%", - mitk::Equal(aTransform, anotherTransform, mitk::eps * 1.01, true)); + mitk::Equal(*aTransform, *anotherTransform, mitk::eps * 1.01, true)); } void TestComposeTransform() { // Create Transformations to set and compare mitk::AffineTransform3D::Pointer transform1; transform1 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix1; matrix1.SetIdentity(); matrix1(1, 1) = 2; transform1->SetMatrix(matrix1); // Spacing = 2 mitk::AffineTransform3D::Pointer transform2; transform2 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix2; matrix2.SetIdentity(); matrix2(1, 1) = 2; transform2->SetMatrix(matrix2); // Spacing = 2 mitk::AffineTransform3D::Pointer transform3; transform3 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix3; matrix3.SetIdentity(); matrix3(1, 1) = 4; transform3->SetMatrix(matrix3); // Spacing = 4 mitk::AffineTransform3D::Pointer transform4; transform4 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix4; matrix4.SetIdentity(); matrix4(1, 1) = 0.25; transform4->SetMatrix(matrix4); // Spacing = 0.25 // Vector to compare spacing mitk::Vector3D expectedSpacing; expectedSpacing.Fill(1.0); expectedSpacing[1] = 4; DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(transform1); // Spacing = 2 dummy->Compose(transform2); // Spacing = 4 CPPUNIT_ASSERT(mitk::Equal(dummy->GetSpacing(), expectedSpacing)); MITK_ASSERT_EQUAL( transform3, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compose transform 2"); // 4=4 // undo changes, new and changed object need to be the same! dummy->Compose(transform4); // Spacing = 1 DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Compose transform 3"); // 1=1 } void TestComposeVtkMatrix() { // Create Transformations to set and compare mitk::AffineTransform3D::Pointer transform1; transform1 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix1; matrix1.SetIdentity(); matrix1(1, 1) = 2; transform1->SetMatrix(matrix1); // Spacing = 2 vtkMatrix4x4 *vtkmatrix2; vtkmatrix2 = vtkMatrix4x4::New(); vtkmatrix2->Identity(); vtkmatrix2->SetElement(1, 1, 2); // Spacing = 2 mitk::AffineTransform3D::Pointer transform3; transform3 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix3; matrix3.SetIdentity(); matrix3(1, 1) = 4; transform3->SetMatrix(matrix3); // Spacing = 4 vtkMatrix4x4 *vtkmatrix4; vtkmatrix4 = vtkMatrix4x4::New(); vtkmatrix4->Identity(); vtkmatrix4->SetElement(1, 1, 0.25); // Spacing = 0.25 // Vector to compare spacing mitk::Vector3D expectedSpacing; expectedSpacing.Fill(1.0); expectedSpacing[1] = 4; DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(transform1); // Spacing = 2 dummy->Compose(vtkmatrix2); // Spacing = 4 vtkmatrix2->Delete(); MITK_ASSERT_EQUAL( transform3, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compose vtk matrix"); // 4=4 CPPUNIT_ASSERT(mitk::Equal(dummy->GetSpacing(), expectedSpacing)); // undo changes, new and changed object need to be the same! dummy->Compose(vtkmatrix4); // Spacing = 1 vtkmatrix4->Delete(); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Compose vtk"); // 1=1 } void TestTranslate() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetOrigin(anotherPoint); CPPUNIT_ASSERT(mitk::Equal(anotherPoint, dummy->GetOrigin())); // use some random values for translation mitk::Vector3D translationVector; translationVector.SetElement(0, 17.5f); translationVector.SetElement(1, -32.3f); translationVector.SetElement(2, 4.0f); // compute ground truth mitk::Point3D tmpResult = anotherPoint + translationVector; dummy->Translate(translationVector); CPPUNIT_ASSERT(mitk::Equal(dummy->GetOrigin(), tmpResult)); // undo changes translationVector *= -1; dummy->Translate(translationVector); CPPUNIT_ASSERT(mitk::Equal(dummy->GetOrigin(), anotherPoint)); // undo changes, new and changed object need to be the same! translationVector.SetElement(0, -1 * anotherPoint[0]); translationVector.SetElement(1, -1 * anotherPoint[1]); translationVector.SetElement(2, -1 * anotherPoint[2]); dummy->Translate(translationVector); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Translate test"); } // a part of the test requires axis-parallel coordinates int testIndexAndWorldConsistency(DummyTestClass::Pointer dummyGeometry) { // Testing consistency of index and world coordinate systems mitk::Point3D origin = dummyGeometry->GetOrigin(); mitk::Point3D dummyPoint; // Testing index->world->index conversion consistency dummyGeometry->WorldToIndex(origin, dummyPoint); dummyGeometry->IndexToWorld(dummyPoint, dummyPoint); CPPUNIT_ASSERT(mitk::EqualArray(dummyPoint, origin, 3, mitk::eps, true)); // Testing WorldToIndex(origin, mitk::Point3D)==(0,0,0) mitk::Point3D globalOrigin; mitk::FillVector3D(globalOrigin, 0, 0, 0); mitk::Point3D originContinuousIndex; dummyGeometry->WorldToIndex(origin, originContinuousIndex); CPPUNIT_ASSERT(mitk::EqualArray(originContinuousIndex, globalOrigin, 3, mitk::eps, true)); // Testing WorldToIndex(origin, itk::Index)==(0,0,0) itk::Index<3> itkindex; dummyGeometry->WorldToIndex(origin, itkindex); itk::Index<3> globalOriginIndex; mitk::vtk2itk(globalOrigin, globalOriginIndex); CPPUNIT_ASSERT(mitk::EqualArray(itkindex, globalOriginIndex, 3, mitk::eps, true)); // Testing WorldToIndex(origin-0.5*spacing, itk::Index)==(0,0,0) mitk::Vector3D halfSpacingStep = dummyGeometry->GetSpacing() * 0.5; mitk::Matrix3D rotation; mitk::Point3D originOffCenter = origin - halfSpacingStep; dummyGeometry->WorldToIndex(originOffCenter, itkindex); CPPUNIT_ASSERT(mitk::EqualArray(itkindex, globalOriginIndex, 3, mitk::eps, true)); // Testing WorldToIndex(origin+0.5*spacing-eps, itk::Index)==(0,0,0) originOffCenter = origin + halfSpacingStep; originOffCenter -= 0.0001; dummyGeometry->WorldToIndex(originOffCenter, itkindex); CPPUNIT_ASSERT(mitk::EqualArray(itkindex, globalOriginIndex, 3, mitk::eps, true)); // Testing WorldToIndex(origin+0.5*spacing, itk::Index)==(1,1,1)"); originOffCenter = origin + halfSpacingStep; itk::Index<3> global111; mitk::FillVector3D(global111, 1, 1, 1); dummyGeometry->WorldToIndex(originOffCenter, itkindex); CPPUNIT_ASSERT(mitk::EqualArray(itkindex, global111, 3, mitk::eps, true)); // Testing WorldToIndex(GetCenter())==BoundingBox.GetCenter mitk::Point3D center = dummyGeometry->GetCenter(); mitk::Point3D centerContIndex; dummyGeometry->WorldToIndex(center, centerContIndex); mitk::BoundingBox::ConstPointer boundingBox = dummyGeometry->GetBoundingBox(); mitk::BoundingBox::PointType centerBounds = boundingBox->GetCenter(); CPPUNIT_ASSERT(mitk::Equal(centerContIndex, centerBounds)); // Testing GetCenter()==IndexToWorld(BoundingBox.GetCenter) center = dummyGeometry->GetCenter(); mitk::Point3D centerBoundsInWorldCoords; dummyGeometry->IndexToWorld(centerBounds, centerBoundsInWorldCoords); CPPUNIT_ASSERT(mitk::Equal(center, centerBoundsInWorldCoords)); // Test using random point, // Testing consistency of index and world coordinate systems mitk::Point3D point; mitk::FillVector3D(point, 3.5, -2, 4.6); // Testing index->world->index conversion consistency dummyGeometry->WorldToIndex(point, dummyPoint); dummyGeometry->IndexToWorld(dummyPoint, dummyPoint); CPPUNIT_ASSERT(mitk::EqualArray(dummyPoint, point, 3, mitk::eps, true)); return EXIT_SUCCESS; } int testIndexAndWorldConsistencyForVectors(DummyTestClass::Pointer dummyGeometry) { // Testing consistency of index and world coordinate systems for vectors mitk::Vector3D xAxisMM = dummyGeometry->GetAxisVector(0); mitk::Vector3D xAxisContinuousIndex; mitk::Point3D p, pIndex, origin; origin = dummyGeometry->GetOrigin(); p[0] = xAxisMM[0] + origin[0]; p[1] = xAxisMM[1] + origin[1]; p[2] = xAxisMM[2] + origin[2]; dummyGeometry->WorldToIndex(p, pIndex); dummyGeometry->WorldToIndex(xAxisMM, xAxisContinuousIndex); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[0], pIndex[0])); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[1], pIndex[1])); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[2], pIndex[2])); dummyGeometry->IndexToWorld(xAxisContinuousIndex, xAxisContinuousIndex); dummyGeometry->IndexToWorld(pIndex, p); CPPUNIT_ASSERT(xAxisContinuousIndex == xAxisMM); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[0], p[0] - origin[0])); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[1], p[1] - origin[1])); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[2], p[2] - origin[2])); // Test consictency for random vector mitk::Vector3D vector; mitk::FillVector3D(vector, 2.5, -3.2, 8.1); mitk::Vector3D vectorContinuousIndex; p[0] = vector[0] + origin[0]; p[1] = vector[1] + origin[1]; p[2] = vector[2] + origin[2]; dummyGeometry->WorldToIndex(p, pIndex); dummyGeometry->WorldToIndex(vector, vectorContinuousIndex); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[0], pIndex[0])); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[1], pIndex[1])); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[2], pIndex[2])); dummyGeometry->IndexToWorld(vectorContinuousIndex, vectorContinuousIndex); dummyGeometry->IndexToWorld(pIndex, p); CPPUNIT_ASSERT(vectorContinuousIndex == vector); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[0], p[0] - origin[0])); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[1], p[1] - origin[1])); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[2], p[2] - origin[2])); return EXIT_SUCCESS; } int testIndexAndWorldConsistencyForIndex(DummyTestClass::Pointer dummyGeometry) { // Testing consistency of index and world coordinate systems // creating testing data itk::Index<4> itkIndex4, itkIndex4b; itk::Index<3> itkIndex3, itkIndex3b; itk::Index<2> itkIndex2, itkIndex2b; itk::Index<3> mitkIndex, mitkIndexb; itkIndex4[0] = itkIndex4[1] = itkIndex4[2] = itkIndex4[3] = 4; itkIndex3[0] = itkIndex3[1] = itkIndex3[2] = 6; itkIndex2[0] = itkIndex2[1] = 2; mitkIndex[0] = mitkIndex[1] = mitkIndex[2] = 13; // check for constistency mitk::Point3D point; dummyGeometry->IndexToWorld(itkIndex2, point); dummyGeometry->WorldToIndex(point, itkIndex2b); CPPUNIT_ASSERT(((itkIndex2b[0] == itkIndex2[0]) && (itkIndex2b[1] == itkIndex2[1]))); // Testing itk::index<2> for IndexToWorld/WorldToIndex consistency dummyGeometry->IndexToWorld(itkIndex3, point); dummyGeometry->WorldToIndex(point, itkIndex3b); CPPUNIT_ASSERT( ((itkIndex3b[0] == itkIndex3[0]) && (itkIndex3b[1] == itkIndex3[1]) && (itkIndex3b[2] == itkIndex3[2]))); // Testing itk::index<3> for IndexToWorld/WorldToIndex consistency dummyGeometry->IndexToWorld(itkIndex4, point); dummyGeometry->WorldToIndex(point, itkIndex4b); CPPUNIT_ASSERT(((itkIndex4b[0] == itkIndex4[0]) && (itkIndex4b[1] == itkIndex4[1]) && (itkIndex4b[2] == itkIndex4[2]) && (itkIndex4b[3] == 0))); // Testing itk::index<3> for IndexToWorld/WorldToIndex consistency dummyGeometry->IndexToWorld(mitkIndex, point); dummyGeometry->WorldToIndex(point, mitkIndexb); CPPUNIT_ASSERT( ((mitkIndexb[0] == mitkIndex[0]) && (mitkIndexb[1] == mitkIndex[1]) && (mitkIndexb[2] == mitkIndex[2]))); // Testing mitk::Index for IndexToWorld/WorldToIndex consistency return EXIT_SUCCESS; } void TestIndexToWorld() { DummyTestClass::Pointer dummy = DummyTestClass::New(); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); // Geometry must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy index to world"); // Test with other geometries dummy->SetOrigin(anotherPoint); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); dummy->SetIndexToWorldTransform(anotherTransform); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); dummy->SetOrigin(anotherPoint); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); dummy->SetSpacing(anotherSpacing); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); } void TestExecuteOperation() { DummyTestClass::Pointer dummy = DummyTestClass::New(); // Do same Operations with new Dummy and compare DummyTestClass::Pointer newDummy = DummyTestClass::New(); // Test operation Nothing auto opN = new mitk::Operation(mitk::OpNOTHING); dummy->ExecuteOperation(opN); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy execute operation 1"); // Test operation Move auto opP = new mitk::PointOperation(mitk::OpMOVE, anotherPoint); dummy->ExecuteOperation(opP); CPPUNIT_ASSERT(mitk::Equal(anotherPoint, dummy->GetOrigin())); newDummy->SetOrigin(anotherPoint); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy execute operation 2"); // Test operation Scale, Scale sets spacing to scale+1 mitk::Point3D spacing; spacing[0] = anotherSpacing[0] - 1.; spacing[1] = anotherSpacing[1] - 1.; spacing[2] = anotherSpacing[2] - 1.; auto opS = new mitk::ScaleOperation(mitk::OpSCALE, spacing, anotherPoint); dummy->ExecuteOperation(opS); CPPUNIT_ASSERT(mitk::Equal(anotherSpacing, dummy->GetSpacing())); newDummy->SetSpacing(anotherSpacing); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy execute operation 3"); // change Geometry to test more cases dummy->SetIndexToWorldTransform(anotherTransform); dummy->SetSpacing(anotherSpacing); // Testing a rotation of the geometry double angle = 35.0; mitk::Vector3D rotationVector; mitk::FillVector3D(rotationVector, 1, 0, 0); mitk::Point3D center = dummy->GetCenter(); auto opR = new mitk::RotationOperation(mitk::OpROTATE, center, rotationVector, angle); dummy->ExecuteOperation(opR); mitk::Matrix3D rotation; mitk::GetRotation(dummy, rotation); mitk::Vector3D voxelStep = rotation * anotherSpacing; mitk::Vector3D voxelStepIndex; dummy->WorldToIndex(voxelStep, voxelStepIndex); mitk::Vector3D expectedVoxelStepIndex; expectedVoxelStepIndex.Fill(1); CPPUNIT_ASSERT(mitk::Equal(voxelStepIndex, expectedVoxelStepIndex)); delete opR; delete opN; delete opS; delete opP; } void TestCalculateBoundingBoxRelToTransform() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetExtentInMM(0, 15); dummy->SetExtentInMM(1, 20); dummy->SetExtentInMM(2, 8); mitk::BoundingBox::Pointer dummyBoundingBox = dummy->CalculateBoundingBoxRelativeToTransform(anotherTransform); mitk::BoundingBox::PointsContainer::Pointer pointscontainer = mitk::BoundingBox::PointsContainer::New(); mitk::BoundingBox::PointIdentifier pointid = 0; unsigned char i; mitk::AffineTransform3D::Pointer inverse = mitk::AffineTransform3D::New(); anotherTransform->GetInverse(inverse); for (i = 0; i < 8; ++i) pointscontainer->InsertElement(pointid++, inverse->TransformPoint(dummy->GetCornerPoint(i))); mitk::BoundingBox::Pointer result = mitk::BoundingBox::New(); result->SetPoints(pointscontainer); result->ComputeBoundingBox(); MITK_ASSERT_EQUAL(result, dummyBoundingBox, "BBox rel to transform"); // dummy still needs to be unchanged, except for extend DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetExtentInMM(0, 15); newDummy->SetExtentInMM(1, 20); newDummy->SetExtentInMM(2, 8); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy BBox"); } // void TestSetTimeBounds(){ // mitk::TimeBounds timeBounds; // timeBounds[0] = 1; // timeBounds[1] = 9; // DummyTestClass::Pointer dummy = DummyTestClass::New(); // dummy->SetTimeBounds(timeBounds); // mitk::TimeBounds timeBounds2 = dummy->GetTimeBounds(); // CPPUNIT_ASSERT(timeBounds[0]==timeBounds2[0]); // CPPUNIT_ASSERT(timeBounds[1]==timeBounds2[1]); // //undo changes, new and changed object need to be the same! // timeBounds[0]=mitk::ScalarTypeNumericTraits::NonpositiveMin(); // timeBounds[1]=mitk::ScalarTypeNumericTraits::max(); // DummyTestClass::Pointer newDummy = DummyTestClass::New(); // CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); //} void TestIs2DConvertable() { DummyTestClass::Pointer dummy = DummyTestClass::New(); // new initialized geometry is 2D convertable CPPUNIT_ASSERT(dummy->Is2DConvertable()); // Wrong Spacing needs to fail dummy->SetSpacing(anotherSpacing); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); // undo dummy->SetSpacing(aSpacing); CPPUNIT_ASSERT(dummy->Is2DConvertable()); // Wrong Origin needs to fail dummy->SetOrigin(anotherPoint); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); // undo dummy->SetOrigin(aPoint); CPPUNIT_ASSERT(dummy->Is2DConvertable()); // third dimension must not be transformed mitk::AffineTransform3D::Pointer dummyTransform = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType dummyMatrix; dummyMatrix.SetIdentity(); dummyTransform->SetMatrix(dummyMatrix); dummy->SetIndexToWorldTransform(dummyTransform); // identity matrix is 2DConvertable CPPUNIT_ASSERT(dummy->Is2DConvertable()); dummyMatrix(0, 2) = 3; dummyTransform->SetMatrix(dummyMatrix); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); dummyMatrix.SetIdentity(); dummyMatrix(1, 2) = 0.4; dummyTransform->SetMatrix(dummyMatrix); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); dummyMatrix.SetIdentity(); dummyMatrix(2, 2) = 3; dummyTransform->SetMatrix(dummyMatrix); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); dummyMatrix.SetIdentity(); dummyMatrix(2, 1) = 3; dummyTransform->SetMatrix(dummyMatrix); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); dummyMatrix.SetIdentity(); dummyMatrix(2, 0) = 3; dummyTransform->SetMatrix(dummyMatrix); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); // undo changes, new and changed object need to be the same! dummyMatrix.SetIdentity(); dummyTransform->SetMatrix(dummyMatrix); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Is 2D convertable"); } void TestGetCornerPoint() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); double bounds[6] = {0, 11, 0, 12, 0, 13}; dummy->SetFloatBounds(bounds); mitk::Point3D corner, refCorner; // Corner 0 mitk::FillVector3D(refCorner, bounds[0], bounds[2], bounds[4]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(0); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(true, true, true); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 1 mitk::FillVector3D(refCorner, bounds[0], bounds[2], bounds[5]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(1); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(true, true, false); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 2 mitk::FillVector3D(refCorner, bounds[0], bounds[3], bounds[4]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(2); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(true, false, true); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 3 mitk::FillVector3D(refCorner, bounds[0], bounds[3], bounds[5]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(3); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(true, false, false); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 4 mitk::FillVector3D(refCorner, bounds[1], bounds[2], bounds[4]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(4); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(false, true, true); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 5 mitk::FillVector3D(refCorner, bounds[1], bounds[2], bounds[5]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(5); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(false, true, false); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 6 mitk::FillVector3D(refCorner, bounds[1], bounds[3], bounds[4]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(6); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(false, false, true); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 7 mitk::FillVector3D(refCorner, bounds[1], bounds[3], bounds[5]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(7); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(false, false, false); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Wrong Corner needs to fail CPPUNIT_ASSERT_THROW(dummy->GetCornerPoint(20), itk::ExceptionObject); // dummy geometry must not have changed! DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetIndexToWorldTransform(anotherTransform); newDummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(dummy, newDummy, "Corner point"); } void TestExtentInMM() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetExtentInMM(0, 50); CPPUNIT_ASSERT(mitk::Equal(50., dummy->GetExtentInMM(0))); // Vnl Matrix has changed. The next line only works because the spacing is 1! CPPUNIT_ASSERT( mitk::Equal(50., dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).magnitude())); // Smaller extent than original dummy->SetExtentInMM(0, 5); CPPUNIT_ASSERT(mitk::Equal(5., dummy->GetExtentInMM(0))); CPPUNIT_ASSERT( mitk::Equal(5., dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).magnitude())); dummy->SetExtentInMM(1, 4); CPPUNIT_ASSERT(mitk::Equal(4., dummy->GetExtentInMM(1))); CPPUNIT_ASSERT( mitk::Equal(4., dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).magnitude())); dummy->SetExtentInMM(2, 2.5); CPPUNIT_ASSERT(mitk::Equal(2.5, dummy->GetExtentInMM(2))); CPPUNIT_ASSERT( mitk::Equal(2.5, dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).magnitude())); } void TestGetAxisVector() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); double bounds[6] = {0, 11, 0, 12, 0, 13}; dummy->SetFloatBounds(bounds); mitk::Vector3D vector; mitk::FillVector3D(vector, bounds[1], 0, 0); dummy->IndexToWorld(vector, vector); CPPUNIT_ASSERT(mitk::Equal(dummy->GetAxisVector(0), vector)); mitk::FillVector3D(vector, 0, bounds[3], 0); dummy->IndexToWorld(vector, vector); CPPUNIT_ASSERT(mitk::Equal(dummy->GetAxisVector(1), vector)); mitk::FillVector3D(vector, 0, 0, bounds[5]); dummy->IndexToWorld(vector, vector); CPPUNIT_ASSERT(mitk::Equal(dummy->GetAxisVector(2), vector)); } void TestGetCenter() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); double bounds[6] = {0, 11, 2, 12, 1, 13}; dummy->SetFloatBounds(bounds); mitk::Point3D refCenter; for (int i = 0; i < 3; i++) refCenter.SetElement(i, (bounds[2 * i] + bounds[2 * i + 1]) / 2.0); dummy->IndexToWorld(refCenter, refCenter); CPPUNIT_ASSERT(mitk::Equal(dummy->GetCenter(), refCenter)); } void TestGetDiagonalLength() { DummyTestClass::Pointer dummy = DummyTestClass::New(); double bounds[6] = {1, 3, 5, 8, 7.5, 11.5}; dummy->SetFloatBounds(bounds); // 3-1=2, 8-5=3, 11.5-7.5=4; 2^2+3^2+4^2 = 29 double expectedLength = sqrt(29.); CPPUNIT_ASSERT(mitk::Equal(expectedLength, dummy->GetDiagonalLength(), mitk::eps, true)); CPPUNIT_ASSERT(mitk::Equal(29., dummy->GetDiagonalLength2(), mitk::eps, true)); // dummy must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(dummy, newDummy, "Diagonal length"); } void TestGetExtent() { DummyTestClass::Pointer dummy = DummyTestClass::New(); double bounds[6] = {1, 3, 5, 8, 7.5, 11.5}; dummy->SetFloatBounds(bounds); CPPUNIT_ASSERT(mitk::Equal(2., dummy->GetExtent(0))); CPPUNIT_ASSERT(mitk::Equal(3., dummy->GetExtent(1))); CPPUNIT_ASSERT(mitk::Equal(4., dummy->GetExtent(2))); // dummy must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(dummy, newDummy, "Extend"); } void TestIsInside() { DummyTestClass::Pointer dummy = DummyTestClass::New(); double bounds[6] = {1, 3, 5, 8, 7.5, 11.5}; dummy->SetFloatBounds(bounds); mitk::Point3D insidePoint; mitk::Point3D outsidePoint; mitk::FillVector3D(insidePoint, 2, 6, 7.6); mitk::FillVector3D(outsidePoint, 0, 9, 8.2); CPPUNIT_ASSERT(dummy->IsIndexInside(insidePoint)); CPPUNIT_ASSERT(false == dummy->IsIndexInside(outsidePoint)); dummy->IndexToWorld(insidePoint, insidePoint); dummy->IndexToWorld(outsidePoint, outsidePoint); CPPUNIT_ASSERT(dummy->IsInside(insidePoint)); CPPUNIT_ASSERT(false == dummy->IsInside(outsidePoint)); // dummy must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(dummy, newDummy, "Is inside"); } void TestInitialize() { // test standard constructor DummyTestClass::Pointer dummy1 = DummyTestClass::New(); DummyTestClass::Pointer dummy2 = DummyTestClass::New(); dummy2->SetOrigin(anotherPoint); dummy2->SetBounds(anotherBoundingBox->GetBounds()); // mitk::TimeBounds timeBounds; // timeBounds[0] = 1; // timeBounds[1] = 9; // dummy2->SetTimeBounds(timeBounds); dummy2->SetIndexToWorldTransform(anotherTransform); dummy2->SetSpacing(anotherSpacing); dummy1->InitializeGeometry(dummy2); MITK_ASSERT_EQUAL(dummy1, dummy2, "Initialize 1"); dummy1->Initialize(); DummyTestClass::Pointer dummy3 = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy3, dummy1, "Initialize 2"); } void TestGetMatrixColumn() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); mitk::Vector3D testVector, refVector; testVector.SetVnlVector(dummy->GetMatrixColumn(0)); mitk::FillVector3D(refVector, 1, 0, 0); CPPUNIT_ASSERT(testVector == refVector); testVector.SetVnlVector(dummy->GetMatrixColumn(1)); mitk::FillVector3D(refVector, 0, 2, 0); CPPUNIT_ASSERT(testVector == refVector); testVector.SetVnlVector(dummy->GetMatrixColumn(2)); mitk::FillVector3D(refVector, 0, 0, 1); CPPUNIT_ASSERT(testVector == refVector); // dummy must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetIndexToWorldTransform(anotherTransform); MITK_ASSERT_EQUAL(dummy, newDummy, "GetMatrixColumn"); } void IsSubGeometry_Spacing() { CPPUNIT_ASSERT(mitk::IsSubGeometry(*aDummyGeometry, *aDummyGeometry, mitk::eps, true)); for (unsigned int i = 0; i < 3; ++i) { mitk::Vector3D wrongSpacing = aDummyGeometry->GetSpacing(); wrongSpacing[i] += mitk::eps * 2; auto wrongGeometry = aDummyGeometry->Clone(); wrongGeometry->SetSpacing(wrongSpacing); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); } for (unsigned int i = 0; i < 3; ++i) { mitk::Vector3D wrongSpacing = aDummyGeometry->GetSpacing(); wrongSpacing[i] -= mitk::eps * 2; auto wrongGeometry = aDummyGeometry->Clone(); wrongGeometry->SetSpacing(wrongSpacing); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); } } void IsSubGeometry_TransformMatrix() { CPPUNIT_ASSERT(mitk::IsSubGeometry(*aDummyGeometry, *aDummyGeometry, mitk::eps, true)); for (unsigned int i = 0; i < 3; ++i) { for (unsigned int j = 0; j < 3; ++j) { itk::Matrix wrongMatrix = aDummyGeometry->GetIndexToWorldTransform()->GetMatrix(); wrongMatrix[i][j] += mitk::eps * 2; auto wrongGeometry = aDummyGeometry->Clone(); wrongGeometry->GetIndexToWorldTransform()->SetMatrix(wrongMatrix); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); } } } void IsSubGeometry_Bounds() { IsSubGeometry_Bounds_internal(false); IsSubGeometry_Bounds_internal(true); } void IsSubGeometry_Bounds_internal(bool isImage) { auto newBounds = aDummyGeometry->GetBounds(); newBounds[0] = 10; newBounds[1] = 20; newBounds[2] = 10; newBounds[3] = 20; newBounds[4] = 10; newBounds[5] = 20; aDummyGeometry->SetBounds(newBounds); aDummyGeometry->SetImageGeometry(isImage); CPPUNIT_ASSERT(mitk::IsSubGeometry(*aDummyGeometry, *aDummyGeometry, mitk::eps, true)); for (unsigned int i = 0; i < 6; ++i) { auto legalBounds = newBounds; if (i % 2 == 0) { legalBounds[i] += 1; } else { legalBounds[i] -= 1; } auto legalGeometry = aDummyGeometry->Clone(); legalGeometry->SetBounds(legalBounds); CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometry, mitk::eps, true)); } for (unsigned int i = 0; i < 6; ++i) { auto wrongBounds = aDummyGeometry->GetBounds(); if (i % 2 == 0) { wrongBounds[i] -= 1; } else { wrongBounds[i] += 1; } auto wrongGeometry = aDummyGeometry->Clone(); wrongGeometry->SetBounds(wrongBounds); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); } } void IsSubGeometry_Grid() { IsSubGeometry_Grid_internal(true); IsSubGeometry_Grid_internal(false); } void IsSubGeometry_Grid_internal(bool isImage) { auto newBounds = aDummyGeometry->GetBounds(); newBounds[0] = 0; newBounds[1] = 20; newBounds[2] = 0; newBounds[3] = 20; newBounds[4] = 0; newBounds[5] = 20; aDummyGeometry->SetBounds(newBounds); aDummyGeometry->SetImageGeometry(isImage); auto smallerGeometry = aDummyGeometry->Clone(); newBounds[0] = 5; newBounds[1] = 10; newBounds[2] = 5; newBounds[3] = 10; newBounds[4] = 5; newBounds[5] = 10; smallerGeometry->SetBounds(newBounds); //legal negative shift for (unsigned int i = 0; i < 3; ++i) { auto legalOrigin = smallerGeometry->GetOrigin(); legalOrigin[i] -= smallerGeometry->GetSpacing()[i]; auto legalGeometry = smallerGeometry->Clone(); legalGeometry->SetOrigin(legalOrigin); CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometry, mitk::eps, true)); } //legal positive shift for (unsigned int i = 0; i < 3; ++i) { auto legalOrigin = smallerGeometry->GetOrigin(); legalOrigin[i] += smallerGeometry->GetSpacing()[i]; auto legalGeometry = smallerGeometry->Clone(); legalGeometry->SetOrigin(legalOrigin); CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometry, mitk::eps, true)); } //wrong negative shift for (unsigned int i = 0; i < 3; ++i) { auto wrongOrigin = smallerGeometry->GetOrigin(); wrongOrigin[i] -= 2 * mitk::eps; auto wrongGeometry = smallerGeometry->Clone(); wrongGeometry->SetOrigin(wrongOrigin); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); } //wrong positive shift for (unsigned int i = 0; i < 3; ++i) { auto wrongOrigin = smallerGeometry->GetOrigin(); wrongOrigin[i] += 2 * mitk::eps; auto wrongGeometry = smallerGeometry->Clone(); wrongGeometry->SetOrigin(wrongOrigin); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); } } /* void (){ DummyTestClass::Pointer dummy = DummyTestClass::New(); CPPUNIT_ASSERT(); //undo changes, new and changed object need to be the same! DummyTestClass::Pointer newDummy = DummyTestClass::New(); CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); } */ }; // end class mitkBaseGeometryTestSuite MITK_TEST_SUITE_REGISTRATION(mitkBaseGeometry) diff --git a/Modules/ImageStatistics/files.cmake b/Modules/ImageStatistics/files.cmake index d41c612329..454e2941ce 100644 --- a/Modules/ImageStatistics/files.cmake +++ b/Modules/ImageStatistics/files.cmake @@ -1,47 +1,50 @@ set(CPP_FILES mitkImageStatisticsCalculator.cpp mitkImageStatisticsContainer.cpp mitkPointSetStatisticsCalculator.cpp mitkPointSetDifferenceStatisticsCalculator.cpp mitkIntensityProfile.cpp mitkHotspotMaskGenerator.cpp mitkMaskGenerator.cpp mitkPlanarFigureMaskGenerator.cpp mitkMultiLabelMaskGenerator.cpp mitkImageMaskGenerator.cpp mitkHistogramStatisticsCalculator.cpp - mitkMaskUtilities.cpp mitkIgnorePixelMaskGenerator.cpp mitkImageStatisticsPredicateHelper.cpp mitkImageStatisticsContainerNodeHelper.cpp mitkImageStatisticsContainerManager.cpp mitkStatisticsToImageRelationRule.cpp mitkStatisticsToMaskRelationRule.cpp mitkImageStatisticsConstants.cpp ) set(H_FILES mitkImageStatisticsCalculator.h mitkImageStatisticsContainer.h mitkPointSetDifferenceStatisticsCalculator.h mitkPointSetStatisticsCalculator.h mitkExtendedStatisticsImageFilter.h mitkExtendedLabelStatisticsImageFilter.h mitkHotspotMaskGenerator.h mitkMaskGenerator.h mitkPlanarFigureMaskGenerator.h mitkMultiLabelMaskGenerator.h mitkImageMaskGenerator.h mitkHistogramStatisticsCalculator.h mitkMaskUtilities.h mitkitkMaskImageFilter.h mitkIgnorePixelMaskGenerator.h mitkMinMaxImageFilterWithIndex.h mitkMinMaxLabelmageFilterWithIndex.h mitkImageStatisticsPredicateHelper.h mitkImageStatisticsContainerNodeHelper.h mitkImageStatisticsContainerManager.h mitkStatisticsToImageRelationRule.h mitkStatisticsToMaskRelationRule.h mitkImageStatisticsConstants.h ) + +set(TPP_FILES + mitkMaskUtilities.tpp +) \ No newline at end of file diff --git a/Modules/ImageStatistics/mitkMaskUtilities.h b/Modules/ImageStatistics/mitkMaskUtilities.h index d8de340c32..15835e4172 100644 --- a/Modules/ImageStatistics/mitkMaskUtilities.h +++ b/Modules/ImageStatistics/mitkMaskUtilities.h @@ -1,79 +1,88 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKMASKUTIL #define MITKMASKUTIL #include #include +#include #include namespace mitk { /** * @brief Utility class for mask operations. It checks whether an image and a mask are compatible (spacing, orientation, etc...) * and it can also crop an image to the LargestPossibleRegion of the Mask */ template -class MITKIMAGESTATISTICS_EXPORT MaskUtilities: public itk::Object +class MaskUtilities: public itk::Object { public: /** Standard Self typedef */ typedef MaskUtilities Self; typedef itk::Object Superclass; typedef itk::SmartPointer< Self > Pointer; typedef itk::SmartPointer< const Self > ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Runtime information support. */ itkTypeMacro(MaskUtilities, itk::Object); typedef itk::Image ImageType; typedef itk::Image MaskType; /** * @brief Set image */ void SetImage(ImageType* image); /** * @brief Set mask */ void SetMask(MaskType* mask); /** * @brief Checks whether mask and image are compatible for joint access (as via iterators). * Spacing and direction must be the same between the two and they must be aligned. Also, the mask must be completely inside the image */ bool CheckMaskSanity(); /** * @brief Crops the image to the LargestPossibleRegion of the mask */ typename itk::Image::Pointer ExtractMaskImageRegion(); protected: MaskUtilities(): m_Image(nullptr), m_Mask(nullptr){} ~MaskUtilities() override{} private: itk::Image* m_Image; itk::Image* m_Mask; }; + +/** Tolerance used to check if the mask and input image are compatible for + * coordinate aspects (orgin, size, grid alignment).*/ +constexpr double MASK_SUITABILITY_TOLERANCE_COORDINATE = NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION; +/** Tolerance used to check if the mask and input image are compatible for + * direction aspects (orientation of mask and image).*/ +constexpr double MASK_SUITABILITY_TOLERANCE_DIRECTION = NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION; + } #ifndef ITK_MANUAL_INSTANTIATION -#include +#include #endif #endif diff --git a/Modules/ImageStatistics/mitkMaskUtilities.cpp b/Modules/ImageStatistics/mitkMaskUtilities.tpp similarity index 90% rename from Modules/ImageStatistics/mitkMaskUtilities.cpp rename to Modules/ImageStatistics/mitkMaskUtilities.tpp index dd478f69bb..25a8b4a2ff 100644 --- a/Modules/ImageStatistics/mitkMaskUtilities.cpp +++ b/Modules/ImageStatistics/mitkMaskUtilities.tpp @@ -1,201 +1,196 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ -#ifndef MITKMASKUTIL_CPP -#define MITKMASKUTIL_CPP +#ifndef MITKMASKUTIL_TPP +#define MITKMASKUTIL_TPP #include -//#include #include #include #include #include namespace mitk { template void MaskUtilities::SetImage(ImageType* image) { if (image != m_Image) { m_Image = image; } } template void MaskUtilities::SetMask(MaskType* mask) { if (mask != m_Mask) { m_Mask = mask; } } template bool MaskUtilities::CheckMaskSanity() { if (m_Mask==nullptr || m_Image==nullptr) { MITK_ERROR << "Set an image and a mask first"; } typedef itk::Image< TPixel, VImageDimension > ImageType; typedef typename ImageType::PointType PointType; typedef typename ImageType::DirectionType DirectionType; bool maskSanity = true; if (m_Mask==nullptr) { MITK_ERROR << "Something went wrong when casting the mitk mask image to an itk mask image. Do the mask and the input image have the same dimension?"; // note to self: We could try to convert say a 2d mask to a 3d mask if the image is 3d. (mask and image dimension have to match.) } // check direction DirectionType imageDirection = m_Image->GetDirection(); DirectionType maskDirection = m_Mask->GetDirection(); for(unsigned int i = 0; i < imageDirection.ColumnDimensions; ++i ) { for(unsigned int j = 0; j < imageDirection.ColumnDimensions; ++j ) { double differenceDirection = imageDirection[i][j] - maskDirection[i][j]; - if ( fabs( differenceDirection ) > mitk::eps ) + if (fabs(differenceDirection) > MASK_SUITABILITY_TOLERANCE_DIRECTION) { - double differenceDirection = imageDirection[i][j] - maskDirection[i][j]; - if ( fabs( differenceDirection ) > 0.001 /*mitk::eps*/ ) // TODO: temp fix (bug 17121) - { - maskSanity = false; - MITK_INFO << "Mask needs to have same direction as image! (Image direction: " << imageDirection << "; Mask direction: " << maskDirection << ")"; - } + maskSanity = false; + MITK_INFO << "Mask needs to have same direction as image! (Image direction: " << imageDirection << "; Mask direction: " << maskDirection << ")"; } } } // check spacing PointType imageSpacing = m_Image->GetSpacing(); PointType maskSpacing = m_Mask->GetSpacing(); for (unsigned int i = 0; i < VImageDimension; i++) { - if ( fabs( maskSpacing[i] - imageSpacing[i] ) > mitk::eps ) + if ( fabs( maskSpacing[i] - imageSpacing[i] ) > MASK_SUITABILITY_TOLERANCE_COORDINATE ) { maskSanity = false; MITK_INFO << "Spacing of mask and image is not equal. Mask: " << maskSpacing << " image: " << imageSpacing; } } // check alignment // Make sure that the voxels of mask and image are correctly "aligned", i.e., voxel boundaries are the same in both images PointType imageOrigin = m_Image->GetOrigin(); PointType maskOrigin = m_Mask->GetOrigin(); typedef itk::ContinuousIndex ContinousIndexType; ContinousIndexType maskOriginContinousIndex, imageOriginContinousIndex; m_Image->TransformPhysicalPointToContinuousIndex(maskOrigin, maskOriginContinousIndex); m_Image->TransformPhysicalPointToContinuousIndex(imageOrigin, imageOriginContinousIndex); for ( unsigned int i = 0; i < ImageType::ImageDimension; ++i ) { double misalignment = maskOriginContinousIndex[i] - floor( maskOriginContinousIndex[i] + 0.5 ); // misalignment must be a multiple (int) of spacing in that direction - if ( fmod(misalignment,imageSpacing[i]) > mitk::eps ) + if ( fmod(misalignment,imageSpacing[i]) > MASK_SUITABILITY_TOLERANCE_COORDINATE) { maskSanity = false; MITK_INFO << "Pixels/voxels of mask and image are not sufficiently aligned! (Misalignment: " << fmod(misalignment,imageSpacing[i]) << ")"; } } // mask must be completely inside image region // Make sure that mask region is contained within image region if ( m_Mask!=nullptr && !m_Image->GetLargestPossibleRegion().IsInside( m_Mask->GetLargestPossibleRegion() ) ) { maskSanity = false; MITK_INFO << "Mask region needs to be inside of image region! (Image region: " << m_Image->GetLargestPossibleRegion() << "; Mask region: " << m_Mask->GetLargestPossibleRegion() << ")"; } return maskSanity; } template typename itk::Image::Pointer MaskUtilities::ExtractMaskImageRegion() { if (m_Mask==nullptr || m_Image==nullptr) { MITK_ERROR << "Set an image and a mask first"; } bool maskSanity = CheckMaskSanity(); if (!maskSanity) { MITK_ERROR << "Mask and image are not compatible"; } typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::Image< unsigned short, VImageDimension > MaskType; typedef itk::ExtractImageFilter< ImageType, ImageType > ExtractImageFilterType; typename ImageType::SizeType imageSize = m_Image->GetBufferedRegion().GetSize(); typename ImageType::SizeType maskSize = m_Mask->GetBufferedRegion().GetSize(); typename itk::Image::Pointer extractedImg = itk::Image::New(); bool maskSmallerImage = false; for ( unsigned int i = 0; i < ImageType::ImageDimension; ++i ) { if ( maskSize[i] < imageSize[i] ) { maskSmallerImage = true; } } if ( maskSmallerImage ) { typename ExtractImageFilterType::Pointer extractImageFilter = ExtractImageFilterType::New(); typename MaskType::PointType maskOrigin = m_Mask->GetOrigin(); typename ImageType::PointType imageOrigin = m_Image->GetOrigin(); typename MaskType::SpacingType maskSpacing = m_Mask->GetSpacing(); typename ImageType::RegionType extractionRegion; typename ImageType::IndexType extractionRegionIndex; for (unsigned int i=0; i < maskOrigin.GetPointDimension(); i++) { extractionRegionIndex[i] = (maskOrigin[i] - imageOrigin[i]) / maskSpacing[i]; } extractionRegion.SetIndex(extractionRegionIndex); extractionRegion.SetSize(m_Mask->GetLargestPossibleRegion().GetSize()); extractImageFilter->SetInput( m_Image ); extractImageFilter->SetExtractionRegion( extractionRegion ); - extractImageFilter->SetCoordinateTolerance( 0.001 ); - extractImageFilter->SetDirectionTolerance( 0.001 ); + extractImageFilter->SetCoordinateTolerance(MASK_SUITABILITY_TOLERANCE_COORDINATE); + extractImageFilter->SetDirectionTolerance(MASK_SUITABILITY_TOLERANCE_DIRECTION); extractImageFilter->Update(); extractedImg = extractImageFilter->GetOutput(); extractedImg->SetOrigin(m_Mask->GetOrigin()); extractedImg->SetLargestPossibleRegion(m_Mask->GetLargestPossibleRegion()); extractedImg->SetBufferedRegion(m_Mask->GetBufferedRegion()); } else { extractedImg = m_Image; } return extractedImg; } } #endif